1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <assert.h> 29 #include <door.h> 30 #include <errno.h> 31 #include <fcntl.h> 32 #include <limits.h> 33 #include <priv.h> 34 #include <procfs.h> 35 #include <pthread.h> 36 #include <signal.h> 37 #include <stdarg.h> 38 #include <stdio.h> 39 #include <stdio_ext.h> 40 #include <stdlib.h> 41 #include <string.h> 42 #include <syslog.h> 43 #include <sys/corectl.h> 44 #include <sys/resource.h> 45 #include <sys/stat.h> 46 #include <sys/wait.h> 47 #include <ucontext.h> 48 #include <unistd.h> 49 50 #include "configd.h" 51 52 /* 53 * This file manages the overall startup and shutdown of configd, as well 54 * as managing its door thread pool and per-thread datastructures. 55 * 56 * 1. Per-thread Datastructures 57 * ----------------------------- 58 * Each configd thread has an associated thread_info_t which contains its 59 * current state. A pointer is kept to this in TSD, keyed by thread_info_key. 60 * The thread_info_ts for all threads in configd are kept on a single global 61 * list, thread_list. After creation, the state in the thread_info structure 62 * is only modified by the associated thread, so no locking is needed. A TSD 63 * destructor removes the thread_info from the global list and frees it at 64 * pthread_exit() time. 65 * 66 * Threads access their per-thread data using thread_self() 67 * 68 * The thread_list is protected by thread_lock, a leaf lock. 69 * 70 * 2. Door Thread Pool Management 71 * ------------------------------ 72 * Whenever door_return(3door) returns from the kernel and there are no 73 * other configd threads waiting for requests, libdoor automatically 74 * invokes a function registered with door_server_create(), to request a new 75 * door server thread. The default function just creates a thread that calls 76 * door_return(3door). Unfortunately, since it can take a while for the new 77 * thread to *get* to door_return(3door), a stream of requests can cause a 78 * large number of threads to be created, even though they aren't all needed. 79 * 80 * In our callback, new_server_needed(), we limit ourself to two new threads 81 * at a time -- this logic is handled in reserve_new_thread(). This keeps 82 * us from creating an absurd number of threads in response to peaking load. 83 */ 84 static pthread_key_t thread_info_key; 85 static pthread_attr_t thread_attr; 86 87 static pthread_mutex_t thread_lock = PTHREAD_MUTEX_INITIALIZER; 88 int num_started; /* number actually running */ 89 int num_servers; /* number in-progress or running */ 90 static uu_list_pool_t *thread_pool; 91 uu_list_t *thread_list; 92 93 static thread_info_t main_thread_info; 94 95 static int finished; 96 97 static pid_t privileged_pid = 0; 98 static int privileged_psinfo_fd = -1; 99 100 static int privileged_user = 0; 101 102 static priv_set_t *privileged_privs; 103 104 static int log_to_syslog = 0; 105 106 int is_main_repository = 1; 107 108 int max_repository_backups = 4; 109 110 #define CONFIGD_MAX_FDS 262144 111 112 /* 113 * Thanks, Mike 114 */ 115 void 116 abort_handler(int sig, siginfo_t *sip, ucontext_t *ucp) 117 { 118 struct sigaction act; 119 120 (void) sigemptyset(&act.sa_mask); 121 act.sa_handler = SIG_DFL; 122 act.sa_flags = 0; 123 (void) sigaction(sig, &act, NULL); 124 125 (void) printstack(2); 126 127 if (sip != NULL && SI_FROMUSER(sip)) 128 (void) pthread_kill(pthread_self(), sig); 129 (void) sigfillset(&ucp->uc_sigmask); 130 (void) sigdelset(&ucp->uc_sigmask, sig); 131 ucp->uc_flags |= UC_SIGMASK; 132 (void) setcontext(ucp); 133 } 134 135 /* 136 * Don't want to have more than a couple thread creates outstanding 137 */ 138 static int 139 reserve_new_thread(void) 140 { 141 (void) pthread_mutex_lock(&thread_lock); 142 assert(num_started >= 0); 143 if (num_servers > num_started + 1) { 144 (void) pthread_mutex_unlock(&thread_lock); 145 return (0); 146 } 147 ++num_servers; 148 (void) pthread_mutex_unlock(&thread_lock); 149 return (1); 150 } 151 152 static void 153 thread_info_free(thread_info_t *ti) 154 { 155 uu_list_node_fini(ti, &ti->ti_node, thread_pool); 156 if (ti->ti_ucred != NULL) 157 uu_free(ti->ti_ucred); 158 uu_free(ti); 159 } 160 161 static void 162 thread_exiting(void *arg) 163 { 164 thread_info_t *ti = arg; 165 166 if (ti != NULL) 167 log_enter(&ti->ti_log); 168 169 (void) pthread_mutex_lock(&thread_lock); 170 if (ti != NULL) { 171 num_started--; 172 uu_list_remove(thread_list, ti); 173 } 174 assert(num_servers > 0); 175 --num_servers; 176 177 if (num_servers == 0) { 178 configd_critical("no door server threads\n"); 179 abort(); 180 } 181 (void) pthread_mutex_unlock(&thread_lock); 182 183 if (ti != NULL && ti != &main_thread_info) 184 thread_info_free(ti); 185 } 186 187 void 188 thread_newstate(thread_info_t *ti, thread_state_t newstate) 189 { 190 ti->ti_ucred_read = 0; /* invalidate cached ucred */ 191 if (newstate != ti->ti_state) { 192 ti->ti_prev_state = ti->ti_state; 193 ti->ti_state = newstate; 194 ti->ti_lastchange = gethrtime(); 195 } 196 } 197 198 thread_info_t * 199 thread_self(void) 200 { 201 return (pthread_getspecific(thread_info_key)); 202 } 203 204 /* 205 * get_ucred() returns NULL if it was unable to get the credential 206 * information. 207 */ 208 ucred_t * 209 get_ucred(void) 210 { 211 thread_info_t *ti = thread_self(); 212 ucred_t **ret = &ti->ti_ucred; 213 214 if (ti->ti_ucred_read) 215 return (*ret); /* cached value */ 216 217 if (door_ucred(ret) != 0) 218 return (NULL); 219 ti->ti_ucred_read = 1; 220 221 return (*ret); 222 } 223 224 int 225 ucred_is_privileged(ucred_t *uc) 226 { 227 const priv_set_t *ps; 228 229 if ((ps = ucred_getprivset(uc, PRIV_EFFECTIVE)) != NULL) { 230 if (priv_isfullset(ps)) 231 return (1); /* process has all privs */ 232 233 if (privileged_privs != NULL && 234 priv_issubset(privileged_privs, ps)) 235 return (1); /* process has zone privs */ 236 } 237 238 return (0); 239 } 240 241 /* 242 * The purpose of this function is to get the audit session data for use in 243 * generating SMF audit events. We use a single audit session per client. 244 * 245 * get_audit_session() may return NULL. It is legal to use a NULL pointer 246 * in subsequent calls to adt_* functions. 247 */ 248 adt_session_data_t * 249 get_audit_session(void) 250 { 251 thread_info_t *ti = thread_self(); 252 253 return (ti->ti_active_client->rc_adt_session); 254 } 255 256 static void * 257 thread_start(void *arg) 258 { 259 thread_info_t *ti = arg; 260 261 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 262 263 (void) pthread_mutex_lock(&thread_lock); 264 num_started++; 265 (void) uu_list_insert_after(thread_list, uu_list_last(thread_list), 266 ti); 267 (void) pthread_mutex_unlock(&thread_lock); 268 (void) pthread_setspecific(thread_info_key, ti); 269 270 thread_newstate(ti, TI_DOOR_RETURN); 271 272 /* 273 * Start handling door calls 274 */ 275 (void) door_return(NULL, 0, NULL, 0); 276 return (arg); 277 } 278 279 static void 280 new_thread_needed(door_info_t *dip) 281 { 282 thread_info_t *ti; 283 284 sigset_t new, old; 285 286 assert(dip == NULL); 287 288 if (!reserve_new_thread()) 289 return; 290 291 if ((ti = uu_zalloc(sizeof (*ti))) == NULL) 292 goto fail; 293 294 uu_list_node_init(ti, &ti->ti_node, thread_pool); 295 ti->ti_state = TI_CREATED; 296 ti->ti_prev_state = TI_CREATED; 297 298 if ((ti->ti_ucred = uu_zalloc(ucred_size())) == NULL) 299 goto fail; 300 301 (void) sigfillset(&new); 302 (void) pthread_sigmask(SIG_SETMASK, &new, &old); 303 if ((errno = pthread_create(&ti->ti_thread, &thread_attr, thread_start, 304 ti)) != 0) { 305 (void) pthread_sigmask(SIG_SETMASK, &old, NULL); 306 goto fail; 307 } 308 309 (void) pthread_sigmask(SIG_SETMASK, &old, NULL); 310 return; 311 312 fail: 313 /* 314 * Since the thread_info structure was never linked onto the 315 * thread list, thread_exiting() can't handle the cleanup. 316 */ 317 thread_exiting(NULL); 318 if (ti != NULL) 319 thread_info_free(ti); 320 } 321 322 int 323 create_connection(ucred_t *uc, repository_door_request_t *rp, 324 size_t rp_size, int *out_fd) 325 { 326 int flags; 327 int privileged = 0; 328 uint32_t debugflags = 0; 329 psinfo_t info; 330 331 if (privileged_pid != 0) { 332 /* 333 * in privileged pid mode, we only allow connections from 334 * our original parent -- the psinfo read verifies that 335 * it is the same process which we started with. 336 */ 337 if (ucred_getpid(uc) != privileged_pid || 338 read(privileged_psinfo_fd, &info, sizeof (info)) != 339 sizeof (info)) 340 return (REPOSITORY_DOOR_FAIL_PERMISSION_DENIED); 341 342 privileged = 1; /* he gets full privileges */ 343 } else if (privileged_user != 0) { 344 /* 345 * in privileged user mode, only one particular user is 346 * allowed to connect to us, and he can do anything. 347 */ 348 if (ucred_geteuid(uc) != privileged_user) 349 return (REPOSITORY_DOOR_FAIL_PERMISSION_DENIED); 350 351 privileged = 1; 352 } 353 354 /* 355 * Check that rp, of size rp_size, is large enough to 356 * contain field 'f'. If so, write the value into *out, and return 1. 357 * Otherwise, return 0. 358 */ 359 #define GET_ARG(rp, rp_size, f, out) \ 360 (((rp_size) >= offsetofend(repository_door_request_t, f)) ? \ 361 ((*(out) = (rp)->f), 1) : 0) 362 363 if (!GET_ARG(rp, rp_size, rdr_flags, &flags)) 364 return (REPOSITORY_DOOR_FAIL_BAD_REQUEST); 365 366 #if (REPOSITORY_DOOR_FLAG_ALL != REPOSITORY_DOOR_FLAG_DEBUG) 367 #error Need to update flag checks 368 #endif 369 370 if (flags & ~REPOSITORY_DOOR_FLAG_ALL) 371 return (REPOSITORY_DOOR_FAIL_BAD_FLAG); 372 373 if (flags & REPOSITORY_DOOR_FLAG_DEBUG) 374 if (!GET_ARG(rp, rp_size, rdr_debug, &debugflags)) 375 return (REPOSITORY_DOOR_FAIL_BAD_REQUEST); 376 #undef GET_ARG 377 378 return (create_client(ucred_getpid(uc), debugflags, privileged, 379 out_fd)); 380 } 381 382 void 383 configd_vcritical(const char *message, va_list args) 384 { 385 if (log_to_syslog) 386 vsyslog(LOG_CRIT, message, args); 387 else { 388 flockfile(stderr); 389 (void) fprintf(stderr, "svc.configd: Fatal error: "); 390 (void) vfprintf(stderr, message, args); 391 if (message[0] == 0 || message[strlen(message) - 1] != '\n') 392 (void) fprintf(stderr, "\n"); 393 funlockfile(stderr); 394 } 395 } 396 397 void 398 configd_critical(const char *message, ...) 399 { 400 va_list args; 401 va_start(args, message); 402 configd_vcritical(message, args); 403 va_end(args); 404 } 405 406 static void 407 usage(const char *prog, int ret) 408 { 409 (void) fprintf(stderr, 410 "usage: %s [-np] [-d door_path] [-r repository_path]\n" 411 " [-t nonpersist_repository]\n", prog); 412 exit(ret); 413 } 414 415 /*ARGSUSED*/ 416 static void 417 handler(int sig, siginfo_t *info, void *data) 418 { 419 finished = 1; 420 } 421 422 static int pipe_fd = -1; 423 424 static int 425 daemonize_start(void) 426 { 427 char data; 428 int status; 429 430 int filedes[2]; 431 pid_t pid; 432 433 (void) close(0); 434 (void) dup2(2, 1); /* stderr only */ 435 436 if (pipe(filedes) < 0) 437 return (-1); 438 439 if ((pid = fork1()) < 0) 440 return (-1); 441 442 if (pid != 0) { 443 /* 444 * parent 445 */ 446 struct sigaction act; 447 448 act.sa_sigaction = SIG_DFL; 449 (void) sigemptyset(&act.sa_mask); 450 act.sa_flags = 0; 451 452 (void) sigaction(SIGPIPE, &act, NULL); /* ignore SIGPIPE */ 453 454 (void) close(filedes[1]); 455 if (read(filedes[0], &data, 1) == 1) { 456 /* presume success */ 457 _exit(CONFIGD_EXIT_OKAY); 458 } 459 460 status = -1; 461 (void) wait4(pid, &status, 0, NULL); 462 if (WIFEXITED(status)) 463 _exit(WEXITSTATUS(status)); 464 else 465 _exit(-1); 466 } 467 468 /* 469 * child 470 */ 471 pipe_fd = filedes[1]; 472 (void) close(filedes[0]); 473 474 /* 475 * generic Unix setup 476 */ 477 (void) setsid(); 478 (void) umask(0077); 479 480 return (0); 481 } 482 483 static void 484 daemonize_ready(void) 485 { 486 char data = '\0'; 487 488 /* 489 * wake the parent 490 */ 491 (void) write(pipe_fd, &data, 1); 492 (void) close(pipe_fd); 493 } 494 495 const char * 496 regularize_path(const char *dir, const char *base, char *tmpbuf) 497 { 498 if (base == NULL) 499 return (NULL); 500 if (base[0] == '/') 501 return (base); 502 503 if (snprintf(tmpbuf, PATH_MAX, "%s/%s", dir, base) >= PATH_MAX) { 504 (void) fprintf(stderr, "svc.configd: %s/%s: path too long\n", 505 dir, base); 506 exit(CONFIGD_EXIT_BAD_ARGS); 507 } 508 509 return (tmpbuf); 510 } 511 512 int 513 main(int argc, char *argv[]) 514 { 515 thread_info_t *ti = &main_thread_info; 516 517 char pidpath[sizeof ("/proc/" "/psinfo") + 10]; 518 519 struct rlimit fd_new; 520 521 const char *endptr; 522 sigset_t myset; 523 int c; 524 int ret; 525 int fd; 526 527 char curdir[PATH_MAX]; 528 char dbtmp[PATH_MAX]; 529 char npdbtmp[PATH_MAX]; 530 char doortmp[PATH_MAX]; 531 532 const char *dbpath = NULL; 533 const char *npdbpath = NULL; 534 const char *doorpath = REPOSITORY_DOOR_NAME; 535 struct sigaction act; 536 537 int daemonize = 1; /* default to daemonizing */ 538 int have_npdb = 1; 539 540 closefrom(3); /* get rid of extraneous fds */ 541 542 if (getcwd(curdir, sizeof (curdir)) == NULL) { 543 (void) fprintf(stderr, 544 "%s: unable to get current directory: %s\n", 545 argv[0], strerror(errno)); 546 exit(CONFIGD_EXIT_INIT_FAILED); 547 } 548 549 while ((c = getopt(argc, argv, "Dnpd:r:t:")) != -1) { 550 switch (c) { 551 case 'n': 552 daemonize = 0; 553 break; 554 case 'd': 555 doorpath = regularize_path(curdir, optarg, doortmp); 556 have_npdb = 0; /* default to no non-persist */ 557 break; 558 case 'p': 559 log_to_syslog = 0; /* don't use syslog */ 560 561 /* 562 * If our parent exits while we're opening its /proc 563 * psinfo, we're vulnerable to a pid wrapping. To 564 * protect against that, re-check our ppid after 565 * opening it. 566 */ 567 privileged_pid = getppid(); 568 (void) snprintf(pidpath, sizeof (pidpath), 569 "/proc/%d/psinfo", privileged_pid); 570 if ((fd = open(pidpath, O_RDONLY)) < 0 || 571 getppid() != privileged_pid) { 572 (void) fprintf(stderr, 573 "%s: unable to get parent info\n", argv[0]); 574 exit(CONFIGD_EXIT_BAD_ARGS); 575 } 576 privileged_psinfo_fd = fd; 577 break; 578 case 'r': 579 dbpath = regularize_path(curdir, optarg, dbtmp); 580 is_main_repository = 0; 581 break; 582 case 't': 583 npdbpath = regularize_path(curdir, optarg, npdbtmp); 584 is_main_repository = 0; 585 break; 586 default: 587 usage(argv[0], CONFIGD_EXIT_BAD_ARGS); 588 break; 589 } 590 } 591 592 /* 593 * If we're not running as root, allow our euid full access, and 594 * everyone else no access. 595 */ 596 if (privileged_pid == 0 && geteuid() != 0) { 597 privileged_user = geteuid(); 598 } 599 600 privileged_privs = priv_str_to_set("zone", "", &endptr); 601 if (endptr != NULL && privileged_privs != NULL) { 602 priv_freeset(privileged_privs); 603 privileged_privs = NULL; 604 } 605 606 openlog("svc.configd", LOG_PID | LOG_CONS, LOG_DAEMON); 607 (void) setlogmask(LOG_UPTO(LOG_NOTICE)); 608 609 /* 610 * if a non-persist db is specified, always enable it 611 */ 612 if (npdbpath) 613 have_npdb = 1; 614 615 if (optind != argc) 616 usage(argv[0], CONFIGD_EXIT_BAD_ARGS); 617 618 if (daemonize) { 619 if (getuid() == 0) 620 (void) chdir("/"); 621 if (daemonize_start() < 0) { 622 (void) perror("unable to daemonize"); 623 exit(CONFIGD_EXIT_INIT_FAILED); 624 } 625 } 626 if (getuid() == 0) 627 (void) core_set_process_path(CONFIGD_CORE, 628 strlen(CONFIGD_CORE) + 1, getpid()); 629 630 /* 631 * this should be enabled once we can drop privileges and still get 632 * a core dump. 633 */ 634 #if 0 635 /* turn off basic privileges we do not need */ 636 (void) priv_set(PRIV_OFF, PRIV_PERMITTED, PRIV_FILE_LINK_ANY, 637 PRIV_PROC_EXEC, PRIV_PROC_FORK, PRIV_PROC_SESSION, NULL); 638 #endif 639 640 /* not that we can exec, but to be safe, shut them all off... */ 641 (void) priv_set(PRIV_SET, PRIV_INHERITABLE, NULL); 642 643 (void) sigfillset(&act.sa_mask); 644 645 /* signals to ignore */ 646 act.sa_sigaction = SIG_IGN; 647 act.sa_flags = 0; 648 (void) sigaction(SIGPIPE, &act, NULL); 649 (void) sigaction(SIGALRM, &act, NULL); 650 (void) sigaction(SIGUSR1, &act, NULL); 651 (void) sigaction(SIGUSR2, &act, NULL); 652 (void) sigaction(SIGPOLL, &act, NULL); 653 654 /* signals to abort on */ 655 act.sa_sigaction = (void (*)(int, siginfo_t *, void *))&abort_handler; 656 act.sa_flags = SA_SIGINFO; 657 658 (void) sigaction(SIGABRT, &act, NULL); 659 660 /* signals to handle */ 661 act.sa_sigaction = &handler; 662 act.sa_flags = SA_SIGINFO; 663 664 (void) sigaction(SIGHUP, &act, NULL); 665 (void) sigaction(SIGINT, &act, NULL); 666 (void) sigaction(SIGTERM, &act, NULL); 667 668 (void) sigemptyset(&myset); 669 (void) sigaddset(&myset, SIGHUP); 670 (void) sigaddset(&myset, SIGINT); 671 (void) sigaddset(&myset, SIGTERM); 672 673 if ((errno = pthread_attr_init(&thread_attr)) != 0) { 674 (void) perror("initializing"); 675 exit(CONFIGD_EXIT_INIT_FAILED); 676 } 677 678 /* 679 * Set the hard and soft limits to CONFIGD_MAX_FDS. 680 */ 681 fd_new.rlim_max = fd_new.rlim_cur = CONFIGD_MAX_FDS; 682 (void) setrlimit(RLIMIT_NOFILE, &fd_new); 683 684 #ifndef NATIVE_BUILD /* Allow building on snv_38 and earlier; remove later. */ 685 (void) enable_extended_FILE_stdio(-1, -1); 686 #endif 687 688 if ((ret = backend_init(dbpath, npdbpath, have_npdb)) != 689 CONFIGD_EXIT_OKAY) 690 exit(ret); 691 692 if (!client_init()) 693 exit(CONFIGD_EXIT_INIT_FAILED); 694 695 if (!rc_node_init()) 696 exit(CONFIGD_EXIT_INIT_FAILED); 697 698 (void) pthread_attr_setdetachstate(&thread_attr, 699 PTHREAD_CREATE_DETACHED); 700 (void) pthread_attr_setscope(&thread_attr, PTHREAD_SCOPE_SYSTEM); 701 702 if ((errno = pthread_key_create(&thread_info_key, 703 thread_exiting)) != 0) { 704 perror("pthread_key_create"); 705 exit(CONFIGD_EXIT_INIT_FAILED); 706 } 707 708 if ((thread_pool = uu_list_pool_create("thread_pool", 709 sizeof (thread_info_t), offsetof(thread_info_t, ti_node), 710 NULL, UU_LIST_POOL_DEBUG)) == NULL) { 711 configd_critical("uu_list_pool_create: %s\n", 712 uu_strerror(uu_error())); 713 exit(CONFIGD_EXIT_INIT_FAILED); 714 } 715 716 if ((thread_list = uu_list_create(thread_pool, NULL, 0)) == NULL) { 717 configd_critical("uu_list_create: %s\n", 718 uu_strerror(uu_error())); 719 exit(CONFIGD_EXIT_INIT_FAILED); 720 } 721 722 (void) memset(ti, '\0', sizeof (*ti)); 723 uu_list_node_init(ti, &ti->ti_node, thread_pool); 724 (void) uu_list_insert_before(thread_list, uu_list_first(thread_list), 725 ti); 726 727 ti->ti_thread = pthread_self(); 728 ti->ti_state = TI_SIGNAL_WAIT; 729 ti->ti_prev_state = TI_SIGNAL_WAIT; 730 731 (void) pthread_setspecific(thread_info_key, ti); 732 733 (void) door_server_create(new_thread_needed); 734 735 if (!setup_main_door(doorpath)) { 736 configd_critical("Setting up main door failed.\n"); 737 exit(CONFIGD_EXIT_DOOR_INIT_FAILED); 738 } 739 740 if (daemonize) 741 daemonize_ready(); 742 743 (void) pthread_sigmask(SIG_BLOCK, &myset, NULL); 744 while (!finished) { 745 int sig = sigwait(&myset); 746 if (sig > 0) { 747 break; 748 } 749 } 750 751 backend_fini(); 752 753 return (CONFIGD_EXIT_OKAY); 754 } 755