Home | History | Annotate | Download | only in src
      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 
     46 extern int init_translations(void);
     47 extern void finish_context_translations(void);
     48 extern int trans_context(const security_context_t, security_context_t *);
     49 extern int untrans_context(const security_context_t, security_context_t *);
     50 
     51 extern int init_colors(void);
     52 extern void finish_context_colors(void);
     53 extern 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 
     60 static int sockfd = -1;	/* socket we are listening on */
     61 
     62 static volatile int restart_daemon = 0;
     63 static void cleanup_exit(int ret) __attribute__ ((noreturn));
     64 static void
     65 cleanup_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 
     77 static void clean_exit(void);
     78 static  __attribute__((noreturn)) void clean_exit(void)
     79 {
     80 	log_debug("%s\n", "clean_exit");
     81 	cleanup_exit(0);
     82 }
     83 
     84 static int
     85 send_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 
    122 static int
    123 get_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 
    140 static int
    141 process_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 
    192 static int
    193 service_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 
    265 static int
    266 add_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 
    295 static void
    296 adj_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 
    311 static int
    312 process_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 
    382 static void
    383 process_connections(void) __attribute__ ((noreturn));
    384 
    385 static void
    386 process_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 
    433 static void
    434 sigterm_handler(int sig) __attribute__ ((noreturn));
    435 
    436 static void
    437 sigterm_handler(int UNUSED(sig))
    438 {
    439 	cleanup_exit(0);
    440 }
    441 
    442 static void
    443 sighup_handler(int UNUSED(sig))
    444 {
    445 	restart_daemon = 1;
    446 }
    447 
    448 static void
    449 initialize(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 
    527 void 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 
    540 static void usage(char *program)
    541 {
    542 	printf("%s [-f] [-h] \n", program);
    543 }
    544 
    545 int
    546 main(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