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