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