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