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