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