mcstransd.c revision 7e0f0124743d241354afa888f3bfe23355679bc9
1/* Copyright (c) 2006 Trusted Computer Solutions, Inc. */
2
3#include <sys/types.h>
4#include <sys/socket.h>
5#include <sys/poll.h>
6#include <sys/stat.h>
7#include <sys/un.h>
8#include <errno.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <unistd.h>
12#include <stdlib.h>
13#include <signal.h>
14#include <string.h>
15#include <syslog.h>
16#include <selinux/selinux.h>
17#include <sys/types.h>
18#include <sys/capability.h>
19#include <sys/resource.h>
20#include "mcstrans.h"
21
22#ifdef UNUSED
23#elif defined(__GNUC__)
24# define UNUSED(x) UNUSED_ ## x __attribute__((unused))
25#elif defined(__LCLINT__)
26# define UNUSED(x) /*@unused@*/ x
27#else
28# define UNUSED(x) x
29#endif
30
31#define SETRANS_UNIX_SOCKET "/var/run/setrans/.setrans-unix"
32
33#define SETRANS_INIT			1
34#define RAW_TO_TRANS_CONTEXT		2
35#define TRANS_TO_RAW_CONTEXT		3
36#define RAW_CONTEXT_TO_COLOR		4
37#define MAX_DATA_BUF			4096
38#define MAX_DESCRIPTORS			8192
39
40#ifdef DEBUG
41//#define log_debug(fmt, ...) syslog(LOG_DEBUG, fmt, __VA_ARGS__)
42#define log_debug(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
43#else
44#define log_debug(fmt, ...) ;
45#endif
46
47extern int init_translations(void);
48extern void finish_context_translations(void);
49extern int trans_context(const security_context_t, security_context_t *);
50extern int untrans_context(const security_context_t, security_context_t *);
51
52extern int init_colors(void);
53extern void finish_context_colors(void);
54extern int raw_color(const security_context_t, char **);
55
56#define SETRANSD_PATHNAME "/sbin/mcstransd"
57
58/* name of program (for error messages) */
59#define SETRANSD_PROGNAME "mcstransd"
60
61static int sockfd = -1;	/* socket we are listening on */
62
63static volatile int restart_daemon = 0;
64static void cleanup_exit(int ret) __attribute__ ((noreturn));
65static void
66cleanup_exit(int ret)
67{
68	finish_context_colors();
69	finish_context_translations();
70	if (sockfd >=0)
71		(void)unlink(SETRANS_UNIX_SOCKET);
72
73	log_debug("%s\n", "cleanup_exit");
74
75	exit(ret);
76}
77
78static void clean_exit(void);
79static  __attribute__((noreturn)) void clean_exit(void)
80{
81	log_debug("%s\n", "clean_exit");
82	cleanup_exit(0);
83}
84
85static int
86send_response(int fd, uint32_t function, char *data, int32_t ret_val)
87{
88	struct iovec resp_hdr[3];
89	uint32_t data_size;
90	struct iovec resp_data;
91	ssize_t count;
92
93	if (!data)
94		data = "";
95
96	data_size = strlen(data) + 1;
97
98	resp_hdr[0].iov_base = &function;
99	resp_hdr[0].iov_len = sizeof(function);
100	resp_hdr[1].iov_base = &data_size;
101	resp_hdr[1].iov_len = sizeof(data_size);
102	resp_hdr[2].iov_base = &ret_val;
103	resp_hdr[2].iov_len = sizeof(ret_val);
104
105	while (((count = writev(fd, resp_hdr, 3)) < 0) && (errno == EINTR));
106	if (count != (sizeof(function) + sizeof(data_size) + sizeof(ret_val))) {
107		syslog(LOG_ERR, "Failed to write response header");
108		return -1;
109	}
110
111	resp_data.iov_base = data;
112	resp_data.iov_len = data_size;
113
114	while (((count = writev(fd, &resp_data, 1)) < 0) && (errno == EINTR));
115	if (count < 0 || (size_t)count != data_size) {
116		syslog(LOG_ERR, "Failed to write response data");
117		return -1;
118	}
119
120	return ret_val;
121}
122
123static int
124get_peer_pid(int fd, pid_t *pid)
125{
126	int ret;
127	socklen_t size = sizeof(struct ucred);
128	struct ucred peercred;
129
130	/* get the context of the requesting process */
131	ret = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &peercred, &size);
132	if (ret < 0) {
133		syslog(LOG_ERR, "Failed to get PID of client process");
134		return -1;
135	}
136	*pid = peercred.pid;
137	return ret;
138}
139
140
141static int
142get_peer_con(int fd, char **peercon)
143{
144	int ret;
145	pid_t pid;
146	ret = get_peer_pid(fd, &pid);
147	if (ret)
148		return -1;
149	ret = getpidcon_raw(pid, peercon);
150	if (ret) {
151		syslog(LOG_ERR,
152			"Failed to get context of client process (pid=%u)",
153			pid);
154		return -1;
155	}
156	return 0;
157}
158
159static int
160process_request(int fd, uint32_t function, char *data1, char *UNUSED(data2))
161{
162	int32_t result;
163	char *out = NULL;
164	char *peercon = NULL;
165	int ret;
166
167	ret = get_peer_con(fd, &peercon);
168	if (ret)
169		return ret;
170
171	/* TODO: Check if MLS clearance (in peercon) dominates the MLS label
172	 * (in the request input).
173	 */
174
175	switch (function) {
176	case SETRANS_INIT:
177		result = 0;
178		ret = send_response(fd, function, NULL, result);
179		break;
180	case RAW_TO_TRANS_CONTEXT:
181		result = trans_context(data1, &out);
182		ret = send_response(fd, function, out, result);
183		break;
184	case TRANS_TO_RAW_CONTEXT:
185		result = untrans_context(data1, &out);
186		ret = send_response(fd, function, out, result);
187		break;
188	case RAW_CONTEXT_TO_COLOR:
189		result = raw_color(data1, &out);
190		ret = send_response(fd, function, out, result);
191		break;
192	default:
193		result = -1;
194		ret = -1;
195		break;
196	}
197
198	if (result) {
199		pid_t pid = 0;
200		get_peer_pid(fd, &pid);
201		syslog(LOG_ERR, "Invalid request func=%d from=%u",
202		       function, pid);
203	}
204
205	free(out);
206	freecon(peercon);
207
208	return ret;
209}
210
211static int
212service_request(int fd)
213{
214	struct iovec req_hdr[3];
215	uint32_t function;
216	uint32_t data1_size;
217	uint32_t data2_size;
218	struct iovec req_data[2];
219	char *data1;
220	char *data2;
221	int ret;
222	ssize_t count;
223
224	req_hdr[0].iov_base = &function;
225	req_hdr[0].iov_len = sizeof(function);
226	req_hdr[1].iov_base = &data1_size;
227	req_hdr[1].iov_len = sizeof(data1_size);
228	req_hdr[2].iov_base = &data2_size;
229	req_hdr[2].iov_len = sizeof(data2_size);
230
231	while (((count = readv(fd, req_hdr, 3)) < 0) && (errno == EINTR));
232	if (count <= 0) {
233		return 1;
234	}
235	if (count != (sizeof(function) + sizeof(data1_size) +
236	              sizeof(data2_size) )) {
237		log_debug("Failed to read request header %d != %u\n",(int)count,
238			(unsigned)(sizeof(function) + sizeof(data1_size) +
239                      sizeof(data2_size) ));
240		return -1;
241	}
242
243	if (!data1_size || !data2_size || data1_size > MAX_DATA_BUF ||
244						data2_size > MAX_DATA_BUF ) {
245		log_debug("Header invalid data1_size=%u data2_size=%u\n",
246		        data1_size, data2_size);
247		return -1;
248	}
249
250	data1 = malloc(data1_size);
251	if (!data1) {
252		log_debug("Could not allocate %d bytes\n", data1_size);
253		return -1;
254	}
255	data2 = malloc(data2_size);
256	if (!data2) {
257		free(data1);
258		log_debug("Could not allocate %d bytes\n", data2_size);
259		return -1;
260	}
261
262	req_data[0].iov_base = data1;
263	req_data[0].iov_len = data1_size;
264	req_data[1].iov_base = data2;
265	req_data[1].iov_len = data2_size;
266
267	while (((count = readv(fd, req_data, 2)) < 0) && (errno == EINTR));
268	if (count <= 0 || (size_t)count != (data1_size + data2_size) ||
269	    data1[data1_size - 1] != '\0' || data2[data2_size - 1] != '\0') {
270		free(data1);
271		free(data2);
272		log_debug("Failed to read request data (%d)\n", (int)count);
273		return -1;
274	}
275
276	ret = process_request(fd, function, data1, data2);
277
278	free(data1);
279	free(data2);
280
281	return ret;
282}
283
284static int
285add_pollfd(struct pollfd **ufds, int *nfds, int connfd)
286{
287	int ii = 0;
288
289	/* First see if we can find an already invalidated ufd */
290	for (ii = 0; ii < *nfds; ii++) {
291		if ((*ufds)[ii].fd == -1)
292			break;
293	}
294
295	if (ii == *nfds) {
296		struct pollfd *tmp = (struct pollfd *)realloc(*ufds,
297					(*nfds+1)*sizeof(struct pollfd));
298		if (!tmp) {
299			syslog(LOG_ERR, "realloc failed for %d fds", *nfds+1);
300			return -1;
301		}
302
303		*ufds = tmp;
304		(*nfds)++;
305	}
306
307	(*ufds)[ii].fd = connfd;
308	(*ufds)[ii].events = POLLIN|POLLPRI;
309	(*ufds)[ii].revents = 0;
310
311	return 0;
312}
313
314static void
315adj_pollfds(struct pollfd **ufds, int *nfds)
316{
317	int ii, jj;
318
319	jj = 0;
320	for (ii = 0; ii < *nfds; ii++) {
321		if ((*ufds)[ii].fd != -1) {
322			if (jj < ii)
323				(*ufds)[jj] = (*ufds)[ii];
324			jj++;
325		}
326	}
327	*nfds = jj;
328}
329
330static int
331process_events(struct pollfd **ufds, int *nfds)
332{
333	int ii = 0;
334	int ret = 0;
335
336	for (ii = 0; ii < *nfds; ii++) {
337		short revents = (*ufds)[ii].revents;
338		int connfd = (*ufds)[ii].fd;
339
340		if (revents & (POLLIN | POLLPRI)) {
341			if (connfd == sockfd) {
342
343				/* Probably received a connection */
344				if ((connfd = accept(sockfd, NULL, NULL)) < 0) {
345					syslog(LOG_ERR, "accept() failed: %m");
346					return -1;
347				}
348
349				if (add_pollfd(ufds, nfds, connfd)) {
350					syslog(LOG_ERR,
351					  "Failed to add fd (%d) to poll list\n",
352						connfd);
353					return -1;
354				}
355			} else {
356				ret = service_request(connfd);
357				if (ret) {
358					if (ret < 0) {
359						syslog(LOG_ERR,
360							"Servicing of request "
361							"failed for fd (%d)\n",
362							connfd);
363					}
364					/* Setup pollfd for deletion later. */
365					(*ufds)[ii].fd = -1;
366					close(connfd);
367					/* So we don't get bothered later */
368					revents = revents & ~(POLLHUP);
369				}
370			}
371			revents = revents & ~(POLLIN | POLLPRI);
372		}
373		if (revents & POLLHUP) {
374			log_debug("The connection with fd (%d) hung up\n",
375				connfd);
376
377			/* Set the pollfd up for deletion later. */
378			(*ufds)[ii].fd = -1;
379			close(connfd);
380
381			revents = revents & ~(POLLHUP);
382		}
383		if (revents) {
384			syslog(LOG_ERR, "Unknown/error events (%x) encountered"
385					" for fd (%d)\n", revents, connfd);
386
387			/* Set the pollfd up for deletion later. */
388			(*ufds)[ii].fd = -1;
389			close(connfd);
390		}
391
392		(*ufds)[ii].revents = 0;
393	}
394
395	/* Delete any invalidated ufds */
396	adj_pollfds(ufds, nfds);
397
398	return 0;
399}
400
401static void
402process_connections(void) __attribute__ ((noreturn));
403
404static void
405process_connections(void)
406{
407	int ret = 0;
408	int nfds = 1;
409
410	struct pollfd *ufds = (struct pollfd *)malloc(sizeof(struct pollfd));
411	if (!ufds) {
412		syslog(LOG_ERR, "Failed to allocate a pollfd");
413		cleanup_exit(1);
414	}
415	ufds[0].fd = sockfd;
416	ufds[0].events = POLLIN|POLLPRI;
417	ufds[0].revents = 0;
418
419	while (1) {
420		if (restart_daemon) {
421			syslog(LOG_NOTICE, "Reload Translations");
422			finish_context_colors();
423			finish_context_translations();
424			if (init_translations()) {
425				syslog(LOG_ERR, "Failed to initialize label translations");
426				cleanup_exit(1);
427			}
428			if (init_colors()) {
429				syslog(LOG_ERR, "Failed to initialize color translations");
430				syslog(LOG_ERR, "No color information will be available");
431			}
432			restart_daemon = 0;
433		}
434
435		ret = poll(ufds, nfds, -1);
436		if (ret < 0) {
437			if (errno == EINTR) {
438				continue;
439			}
440			syslog(LOG_ERR, "poll() failed: %m");
441			cleanup_exit(1);
442		}
443
444		ret = process_events(&ufds, &nfds);
445		if (ret) {
446			syslog(LOG_ERR, "Error processing events");
447			cleanup_exit(1);
448		}
449	}
450}
451
452static void
453sigterm_handler(int sig) __attribute__ ((noreturn));
454
455static void
456sigterm_handler(int UNUSED(sig))
457{
458	cleanup_exit(0);
459}
460
461static void
462sighup_handler(int UNUSED(sig))
463{
464	restart_daemon = 1;
465}
466
467static void
468initialize(void)
469{
470	struct sigaction act;
471	struct sockaddr_un addr;
472	struct rlimit rl ;
473
474	if (init_translations()) {
475		syslog(LOG_ERR, "Failed to initialize label translations");
476		cleanup_exit(1);
477	}
478	if (init_colors()) {
479		syslog(LOG_ERR, "Failed to initialize color translations");
480		syslog(LOG_ERR, "No color information will be available");
481	}
482
483	/* the socket will be unlinked when the daemon terminates */
484	act.sa_handler = sigterm_handler;
485	sigemptyset(&act.sa_mask);
486	sigaddset(&act.sa_mask, SIGINT);
487	sigaddset(&act.sa_mask, SIGQUIT);
488	sigaddset(&act.sa_mask, SIGTERM);
489	sigaddset(&act.sa_mask, SIGHUP);
490	act.sa_flags = 0;
491	sigaction(SIGINT, &act, NULL);
492	sigaction(SIGQUIT, &act, NULL);
493	sigaction(SIGTERM, &act, NULL);
494
495	/* restart the daemon on SIGHUP */
496	act.sa_handler = sighup_handler;
497	sigemptyset(&act.sa_mask);
498	sigaddset(&act.sa_mask, SIGINT);
499	sigaddset(&act.sa_mask, SIGQUIT);
500	sigaddset(&act.sa_mask, SIGTERM);
501	act.sa_flags = 0;
502	sigaction(SIGHUP, &act, NULL);
503
504	/* ignore SIGPIPE (in case a client terminates after sending request) */
505	act.sa_handler = SIG_IGN;
506	sigemptyset(&act.sa_mask);
507	act.sa_flags = 0;
508	sigaction(SIGPIPE, &act, NULL);
509
510	atexit(clean_exit);
511
512	sockfd = socket(PF_UNIX, SOCK_STREAM, 0);
513	if (sockfd < 0)	{
514		syslog(LOG_ERR, "socket() failed: %m");
515		cleanup_exit(1);
516	}
517
518	memset(&addr, 0, sizeof(addr));
519	addr.sun_family = AF_UNIX;
520	strncpy(addr.sun_path, SETRANS_UNIX_SOCKET, sizeof(addr.sun_path) - 1);
521
522	(void)unlink(SETRANS_UNIX_SOCKET);
523
524	if (bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
525		syslog(LOG_ERR, "bind() failed: %m");
526		cleanup_exit(1);
527	}
528
529	if (listen(sockfd, SOMAXCONN) < 0) {
530		syslog(LOG_ERR, "listen() failed: %m");
531		cleanup_exit(1);
532	}
533
534	if (chmod(SETRANS_UNIX_SOCKET, S_IRWXU | S_IRWXG | S_IRWXO)) {
535		syslog(LOG_ERR, "chmod() failed: %m");
536		cleanup_exit(1);
537	}
538
539	/* Raise the rlimit for file descriptors... */
540	rl.rlim_max = MAX_DESCRIPTORS;
541	rl.rlim_cur = MAX_DESCRIPTORS;
542	setrlimit(RLIMIT_NOFILE, &rl);
543
544}
545
546void dropprivs(void)
547{
548	cap_t new_caps;
549
550	new_caps = cap_init();
551	if (cap_set_proc(new_caps)) {
552		syslog(LOG_ERR, "Error dropping capabilities, aborting: %s\n",
553			 strerror(errno));
554		cleanup_exit(-1);
555	}
556	cap_free(new_caps);
557}
558
559int
560main(int UNUSED(argc), char *argv[])
561{
562#ifndef DEBUG
563	/* Make sure we are root */
564	if (getuid() != 0) {
565		syslog(LOG_ERR, "You must be root to run this program.\n");
566		return 4;
567	}
568#endif
569
570	openlog(SETRANSD_PROGNAME, 0, LOG_DAEMON);
571	syslog(LOG_NOTICE, "%s starting", argv[0]);
572
573	initialize();
574
575#ifndef DEBUG
576	dropprivs();
577
578	/* run in the background as a daemon */
579	if (daemon(0, 0)) {
580		syslog(LOG_ERR, "daemon() failed: %m");
581		cleanup_exit(1);
582	}
583#endif
584
585	syslog(LOG_NOTICE, "%s initialized", argv[0]);
586	process_connections();
587
588	/* we should never get here */
589	return 1;
590}
591
592