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 (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23 * Copyright 2018 Nexenta Systems, Inc. All rights reserved. 24 * Copyright 2023 RackTop Systems, Inc. 25 */ 26 27 28 /* 29 * main() of idmapd(8) 30 */ 31 32 #include "idmapd.h" 33 #include <atomic.h> 34 #include <signal.h> 35 #include <rpc/pmap_clnt.h> /* for pmap_unset */ 36 #include <string.h> /* strcmp */ 37 #include <unistd.h> /* setsid */ 38 #include <sys/types.h> 39 #include <memory.h> 40 #include <stropts.h> 41 #include <netconfig.h> 42 #include <sys/resource.h> /* rlimit */ 43 #include <rpcsvc/daemon_utils.h> /* DAEMON_UID and DAEMON_GID */ 44 #include <priv_utils.h> /* privileges */ 45 #include <locale.h> 46 #include <sys/systeminfo.h> 47 #include <errno.h> 48 #include <sys/wait.h> 49 #include <sys/time.h> 50 #include <zone.h> 51 #include <door.h> 52 #include <port.h> 53 #include <tsol/label.h> 54 #include <sys/resource.h> 55 #include <sys/sid.h> 56 #include <sys/idmap.h> 57 #include <pthread.h> 58 #include <stdarg.h> 59 #include <assert.h> 60 #include <note.h> 61 62 #define CBUFSIZ 26 /* ctime(3c) */ 63 64 static void term_handler(int); 65 static void init_idmapd(); 66 static void fini_idmapd(); 67 68 /* The DC Locator lives inside idmap (for now). */ 69 extern void init_dc_locator(void); 70 extern void fini_dc_locator(void); 71 72 idmapd_state_t _idmapdstate; 73 mutex_t _svcstate_lock = ERRORCHECKMUTEX; 74 75 SVCXPRT *xprt = NULL; 76 77 static int dfd = -1; /* our door server fildes, for unregistration */ 78 static boolean_t degraded = B_FALSE; 79 80 81 static uint32_t num_threads = 0; 82 static pthread_key_t create_threads_key; 83 static uint32_t max_threads = 40; 84 85 /* 86 * Server door thread start routine. 87 * 88 * Set a TSD value to the door thread. This enables the destructor to 89 * be called when this thread exits. Note that we need a non-NULL 90 * value for this or the TSD destructor is not called. 91 */ 92 /*ARGSUSED*/ 93 static void * 94 idmapd_door_thread_start(void *arg) 95 { 96 static void *value = "NON-NULL TSD"; 97 98 /* 99 * Disable cancellation to avoid memory leaks from not running 100 * the thread cleanup code. 101 */ 102 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 103 (void) pthread_setspecific(create_threads_key, value); 104 (void) door_return(NULL, 0, NULL, 0); 105 106 /* make lint happy */ 107 return (NULL); 108 } 109 110 /* 111 * Server door threads creation 112 */ 113 /*ARGSUSED*/ 114 static void 115 idmapd_door_thread_create(door_info_t *dip) 116 { 117 int num; 118 pthread_t thread_id; 119 120 if ((num = atomic_inc_32_nv(&num_threads)) > max_threads) { 121 atomic_dec_32(&num_threads); 122 idmapdlog(LOG_DEBUG, 123 "thread creation refused - %d threads currently active", 124 num - 1); 125 return; 126 } 127 (void) pthread_create(&thread_id, NULL, idmapd_door_thread_start, NULL); 128 idmapdlog(LOG_DEBUG, 129 "created thread ID %d - %d threads currently active", 130 thread_id, num); 131 } 132 133 /* 134 * Server door thread cleanup 135 */ 136 /*ARGSUSED*/ 137 static void 138 idmapd_door_thread_cleanup(void *arg) 139 { 140 int num; 141 142 /* set TSD to NULL so we don't loop infinitely */ 143 (void) pthread_setspecific(create_threads_key, NULL); 144 num = atomic_dec_32_nv(&num_threads); 145 idmapdlog(LOG_DEBUG, 146 "exiting thread ID %d - %d threads currently active", 147 pthread_self(), num); 148 } 149 150 /* 151 * This is needed for mech_krb5 -- we run as daemon, yes, but we want 152 * mech_krb5 to think we're root so it can get host/nodename.fqdn 153 * tickets for us so we can authenticate to AD as the machine account 154 * that we are. For more details look at the entry point in mech_krb5 155 * corresponding to gss_init_sec_context(). 156 * 157 * As a side effect of faking our effective UID to mech_krb5 we will use 158 * root's default ccache (/tmp/krb5cc_0). But if that's created by 159 * another process then we won't have access to it: we run as daemon and 160 * keep PRIV_FILE_DAC_READ, which is insufficient to share the ccache 161 * with others. We putenv("KRB5CCNAME=/var/run/idmap/ccache") in main() 162 * to avoid this issue; see main(). 163 * 164 * Someday we'll have gss/mech_krb5 extensions for acquiring initiator 165 * creds with keytabs/raw keys, and someday we'll have extensions to 166 * libsasl to specify creds/name to use on the initiator side, and 167 * someday we'll have extensions to libldap to pass those through to 168 * libsasl. Until then this interposer will have to do. 169 * 170 * Also, we have to tell lint to shut up: it thinks app_krb5_user_uid() 171 * is defined but not used. 172 */ 173 /*LINTLIBRARY*/ 174 uid_t 175 app_krb5_user_uid(void) 176 { 177 return (0); 178 } 179 180 /*ARGSUSED*/ 181 static void 182 term_handler(int sig) 183 { 184 idmapdlog(LOG_INFO, "Terminating."); 185 fini_dc_locator(); 186 fini_idmapd(); 187 _exit(0); 188 } 189 190 /*ARGSUSED*/ 191 static void 192 usr1_handler(int sig) 193 { 194 NOTE(ARGUNUSED(sig)) 195 print_idmapdstate(); 196 } 197 198 static int pipe_fd = -1; 199 200 static void 201 daemonize_ready(void) 202 { 203 char data = '\0'; 204 /* 205 * wake the parent 206 */ 207 (void) write(pipe_fd, &data, 1); 208 (void) close(pipe_fd); 209 } 210 211 static int 212 daemonize_start(void) 213 { 214 char data; 215 int status; 216 int devnull; 217 int filedes[2]; 218 pid_t pid; 219 220 (void) sigset(SIGPIPE, SIG_IGN); 221 devnull = open("/dev/null", O_RDONLY); 222 if (devnull < 0) 223 return (-1); 224 (void) dup2(devnull, 0); 225 (void) dup2(2, 1); /* stderr only */ 226 if (pipe(filedes) < 0) 227 return (-1); 228 if ((pid = fork1()) < 0) 229 return (-1); 230 if (pid != 0) { 231 /* 232 * parent 233 */ 234 (void) close(filedes[1]); 235 if (read(filedes[0], &data, 1) == 1) { 236 /* presume success */ 237 _exit(0); 238 } 239 status = -1; 240 (void) wait4(pid, &status, 0, NULL); 241 if (WIFEXITED(status)) 242 _exit(WEXITSTATUS(status)); 243 else 244 _exit(-1); 245 } 246 247 /* 248 * child 249 */ 250 pipe_fd = filedes[1]; 251 (void) close(filedes[0]); 252 (void) setsid(); 253 (void) umask(0077); 254 openlog("idmap", LOG_PID, LOG_DAEMON); 255 256 return (0); 257 } 258 259 260 int 261 main(int argc, char **argv) 262 { 263 int c; 264 struct rlimit rl; 265 266 if (rwlock_init(&_idmapdstate.rwlk_cfg, USYNC_THREAD, NULL) != 0) 267 return (-1); 268 if (mutex_init(&_idmapdstate.addisc_lk, USYNC_THREAD, NULL) != 0) 269 return (-1); 270 if (cond_init(&_idmapdstate.addisc_cv, USYNC_THREAD, NULL) != 0) 271 return (-1); 272 273 _idmapdstate.daemon_mode = TRUE; 274 while ((c = getopt(argc, argv, "d")) != -1) { 275 switch (c) { 276 case 'd': 277 _idmapdstate.daemon_mode = FALSE; 278 break; 279 default: 280 (void) fprintf(stderr, 281 "Usage: /usr/lib/idmapd [-d]\n"); 282 return (SMF_EXIT_ERR_CONFIG); 283 } 284 } 285 286 /* set locale and domain for internationalization */ 287 (void) setlocale(LC_ALL, ""); 288 (void) textdomain(TEXT_DOMAIN); 289 290 idmap_set_logger(idmapdlog); 291 adutils_set_logger(idmapdlog); 292 293 if (is_system_labeled() && getzoneid() != GLOBAL_ZONEID) { 294 idmapdlog(LOG_ERR, 295 "with Trusted Extensions idmapd runs only in the " 296 "global zone"); 297 exit(1); 298 } 299 300 /* 301 * Raise the fd limit to max 302 */ 303 if (getrlimit(RLIMIT_NOFILE, &rl) != 0) { 304 idmapdlog(LOG_ERR, "getrlimit failed"); 305 } else if (rl.rlim_cur < rl.rlim_max) { 306 rl.rlim_cur = rl.rlim_max; 307 if (setrlimit(RLIMIT_NOFILE, &rl) != 0) 308 idmapdlog(LOG_ERR, 309 "Unable to raise RLIMIT_NOFILE to %d", 310 rl.rlim_cur); 311 } 312 313 if (_idmapdstate.daemon_mode == TRUE) { 314 if (daemonize_start() < 0) { 315 idmapdlog(LOG_ERR, "unable to daemonize"); 316 exit(-1); 317 } 318 } else 319 (void) umask(0077); 320 321 idmap_init_tsd_key(); 322 323 init_idmapd(); 324 init_dc_locator(); 325 326 /* signal handlers that should run only after we're initialized */ 327 (void) sigset(SIGTERM, term_handler); 328 (void) sigset(SIGUSR1, usr1_handler); 329 (void) sigset(SIGHUP, idmap_cfg_hup_handler); 330 331 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 332 DAEMON_UID, DAEMON_GID, 333 PRIV_PROC_AUDIT, PRIV_FILE_DAC_READ, 334 (char *)NULL) == -1) { 335 idmapdlog(LOG_ERR, "unable to drop privileges"); 336 exit(1); 337 } 338 339 __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION, 340 PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL); 341 342 if (_idmapdstate.daemon_mode == TRUE) 343 daemonize_ready(); 344 345 /* With doors RPC this just wastes this thread, oh well */ 346 svc_run(); 347 return (0); 348 } 349 350 static void 351 init_idmapd() 352 { 353 int error; 354 int connmaxrec = IDMAP_MAX_DOOR_RPC; 355 356 357 /* create directories as root and chown to daemon uid */ 358 if (create_directory(IDMAP_DBDIR, DAEMON_UID, DAEMON_GID) < 0) 359 exit(1); 360 if (create_directory(IDMAP_CACHEDIR, DAEMON_UID, DAEMON_GID) < 0) 361 exit(1); 362 363 /* 364 * Set KRB5CCNAME in the environment. See app_krb5_user_uid() 365 * for more details. We blow away the existing one, if there is 366 * one. 367 */ 368 (void) unlink(IDMAP_CACHEDIR "/ccache"); 369 (void) putenv("KRB5CCNAME=" IDMAP_CACHEDIR "/ccache"); 370 (void) putenv("MS_INTEROP=1"); 371 372 if (sysinfo(SI_HOSTNAME, _idmapdstate.hostname, 373 sizeof (_idmapdstate.hostname)) == -1) { 374 error = errno; 375 idmapdlog(LOG_ERR, "unable to determine hostname, error: %d", 376 error); 377 exit(1); 378 } 379 380 if ((error = init_mapping_system()) < 0) { 381 idmapdlog(LOG_ERR, "unable to initialize mapping system"); 382 exit(error < -2 ? SMF_EXIT_ERR_CONFIG : 1); 383 } 384 385 /* 386 * This means max_threads can't be updated without restarting idmap. 387 */ 388 RDLOCK_CONFIG(); 389 max_threads = _idmapdstate.cfg->pgcfg.max_threads; 390 UNLOCK_CONFIG(); 391 392 (void) door_server_create(idmapd_door_thread_create); 393 if ((error = pthread_key_create(&create_threads_key, 394 idmapd_door_thread_cleanup)) != 0) { 395 idmapdlog(LOG_ERR, "unable to create threads key (%s)", 396 strerror(error)); 397 goto errout; 398 } 399 400 xprt = svc_door_create(idmap_prog_1, IDMAP_PROG, IDMAP_V1, connmaxrec); 401 if (xprt == NULL) { 402 idmapdlog(LOG_ERR, "unable to create door RPC service"); 403 goto errout; 404 } 405 406 if (!svc_control(xprt, SVCSET_CONNMAXREC, &connmaxrec)) { 407 idmapdlog(LOG_ERR, "unable to limit RPC request size"); 408 goto errout; 409 } 410 411 dfd = xprt->xp_fd; 412 413 if (dfd == -1) { 414 idmapdlog(LOG_ERR, "unable to register door"); 415 goto errout; 416 } 417 if ((error = __idmap_reg(dfd)) != 0) { 418 idmapdlog(LOG_ERR, "unable to register door (%s)", 419 strerror(errno)); 420 goto errout; 421 } 422 423 if ((error = allocids(_idmapdstate.new_eph_db, 424 8192, &_idmapdstate.next_uid, 425 8192, &_idmapdstate.next_gid)) != 0) { 426 idmapdlog(LOG_ERR, "unable to allocate ephemeral IDs (%s)", 427 strerror(errno)); 428 _idmapdstate.next_uid = IDMAP_SENTINEL_PID; 429 _idmapdstate.limit_uid = IDMAP_SENTINEL_PID; 430 _idmapdstate.next_gid = IDMAP_SENTINEL_PID; 431 _idmapdstate.limit_gid = IDMAP_SENTINEL_PID; 432 } else { 433 _idmapdstate.limit_uid = _idmapdstate.next_uid + 8192; 434 _idmapdstate.limit_gid = _idmapdstate.next_gid + 8192; 435 } 436 437 if (DBG(CONFIG, 1)) 438 print_idmapdstate(); 439 440 return; 441 442 errout: 443 fini_idmapd(); 444 exit(1); 445 } 446 447 static void 448 fini_idmapd() 449 { 450 (void) __idmap_unreg(dfd); 451 fini_mapping_system(); 452 if (xprt != NULL) 453 svc_destroy(xprt); 454 } 455 456 static 457 const char * 458 get_fmri(void) 459 { 460 static char *fmri = NULL; 461 static char buf[60]; 462 char *s; 463 464 membar_consumer(); 465 s = fmri; 466 if (s != NULL && *s == '\0') 467 return (NULL); 468 else if (s != NULL) 469 return (s); 470 471 if ((s = getenv("SMF_FMRI")) == NULL || strlen(s) >= sizeof (buf)) 472 buf[0] = '\0'; 473 else 474 (void) strlcpy(buf, s, sizeof (buf)); 475 476 membar_producer(); 477 fmri = buf; 478 479 return (get_fmri()); 480 } 481 482 /* 483 * Wrappers for smf_degrade/restore_instance() 484 * 485 * smf_restore_instance() is too heavy duty to be calling every time we 486 * have a successful AD name<->SID lookup. 487 */ 488 void 489 degrade_svc(int poke_discovery, const char *reason) 490 { 491 const char *fmri; 492 493 membar_consumer(); 494 if (degraded) 495 return; 496 497 idmapdlog(LOG_ERR, "Degraded operation (%s).", reason); 498 499 membar_producer(); 500 degraded = B_TRUE; 501 502 if ((fmri = get_fmri()) != NULL) 503 (void) smf_degrade_instance(fmri, 0); 504 505 /* 506 * If the config update thread is in a state where auto-discovery could 507 * be re-tried, then this will make it try it -- a sort of auto-refresh. 508 */ 509 if (poke_discovery) 510 idmap_cfg_poke_updates(); 511 } 512 513 void 514 restore_svc(void) 515 { 516 const char *fmri; 517 518 membar_consumer(); 519 if (!degraded) 520 return; 521 522 if ((fmri = get_fmri()) == NULL) 523 (void) smf_restore_instance(fmri); 524 525 membar_producer(); 526 degraded = B_FALSE; 527 528 idmapdlog(LOG_NOTICE, "Normal operation restored"); 529 } 530 531 532 /* printflike */ 533 void 534 idmapdlog(int pri, const char *format, ...) 535 { 536 static time_t prev_ts; 537 va_list args; 538 char cbuf[CBUFSIZ]; 539 time_t ts; 540 541 ts = time(NULL); 542 if (prev_ts != ts) { 543 prev_ts = ts; 544 /* NB: cbuf has \n */ 545 (void) fprintf(stderr, "@ %s", 546 ctime_r(&ts, cbuf, sizeof (cbuf))); 547 } 548 549 va_start(args, format); 550 (void) vfprintf(stderr, format, args); 551 (void) fprintf(stderr, "\n"); 552 va_end(args); 553 554 /* 555 * We don't want to fill up the logs with useless messages when 556 * we're degraded, but we still want to log. 557 */ 558 if (degraded) 559 pri = LOG_DEBUG; 560 561 va_start(args, format); 562 vsyslog(pri, format, args); 563 va_end(args); 564 } 565 566 static void 567 trace_str(nvlist_t *entry, char *n1, char *n2, char *str) 568 { 569 char name[IDMAP_TRACE_NAME_MAX+1]; /* Max used is only about 11 */ 570 571 (void) strlcpy(name, n1, sizeof (name)); 572 if (n2 != NULL) 573 (void) strlcat(name, n2, sizeof (name)); 574 575 (void) nvlist_add_string(entry, name, str); 576 } 577 578 static void 579 trace_int(nvlist_t *entry, char *n1, char *n2, int64_t i) 580 { 581 char name[IDMAP_TRACE_NAME_MAX+1]; /* Max used is only about 11 */ 582 583 (void) strlcpy(name, n1, sizeof (name)); 584 if (n2 != NULL) 585 (void) strlcat(name, n2, sizeof (name)); 586 587 (void) nvlist_add_int64(entry, name, i); 588 } 589 590 static void 591 trace_sid(nvlist_t *entry, char *n1, char *n2, idmap_sid *sid) 592 { 593 char *str; 594 595 (void) asprintf(&str, "%s-%u", sid->prefix, sid->rid); 596 if (str == NULL) 597 return; 598 599 trace_str(entry, n1, n2, str); 600 free(str); 601 } 602 603 static void 604 trace_id(nvlist_t *entry, char *fromto, idmap_id *id, char *name, char *domain) 605 { 606 trace_int(entry, fromto, IDMAP_TRACE_TYPE, (int64_t)id->idtype); 607 if (IS_ID_SID(*id)) { 608 if (name != NULL) { 609 char *str; 610 611 (void) asprintf(&str, "%s%s%s", name, 612 domain == NULL ? "" : "@", 613 domain == NULL ? "" : domain); 614 if (str != NULL) { 615 trace_str(entry, fromto, IDMAP_TRACE_NAME, str); 616 free(str); 617 } 618 } 619 if (id->idmap_id_u.sid.prefix != NULL) { 620 trace_sid(entry, fromto, IDMAP_TRACE_SID, 621 &id->idmap_id_u.sid); 622 } 623 } else if (IS_ID_POSIX(*id)) { 624 if (name != NULL) 625 trace_str(entry, fromto, IDMAP_TRACE_NAME, name); 626 if (id->idmap_id_u.uid != IDMAP_SENTINEL_PID) { 627 trace_int(entry, fromto, IDMAP_TRACE_UNIXID, 628 (int64_t)id->idmap_id_u.uid); 629 } 630 } 631 } 632 633 /* 634 * Record a trace event. TRACE() has already decided whether or not 635 * tracing is required; what we do here is collect the data and send it 636 * to its destination - to the trace log in the response, if 637 * IDMAP_REQ_FLG_TRACE is set, and to the SMF service log, if debug/mapping 638 * is greater than zero. 639 */ 640 int 641 trace(idmap_mapping *req, idmap_id_res *res, char *fmt, ...) 642 { 643 va_list va; 644 char *buf; 645 int err; 646 nvlist_t *entry; 647 648 assert(req != NULL); 649 assert(res != NULL); 650 651 err = nvlist_alloc(&entry, NV_UNIQUE_NAME, 0); 652 if (err != 0) { 653 (void) fprintf(stderr, "trace nvlist_alloc(entry): %s\n", 654 strerror(err)); 655 return (0); 656 } 657 658 trace_id(entry, "from", &req->id1, req->id1name, req->id1domain); 659 trace_id(entry, "to", &res->id, req->id2name, req->id2domain); 660 661 if (IDMAP_ERROR(res->retcode)) { 662 trace_int(entry, IDMAP_TRACE_ERROR, NULL, 663 (int64_t)res->retcode); 664 } 665 666 va_start(va, fmt); 667 (void) vasprintf(&buf, fmt, va); 668 va_end(va); 669 if (buf != NULL) { 670 trace_str(entry, IDMAP_TRACE_MESSAGE, NULL, buf); 671 free(buf); 672 } 673 674 if (DBG(MAPPING, 1)) 675 idmap_trace_print_1(stderr, "", entry); 676 677 if (req->flag & IDMAP_REQ_FLG_TRACE) { 678 /* Lazily allocate the trace list */ 679 if (res->info.trace == NULL) { 680 err = nvlist_alloc(&res->info.trace, 0, 0); 681 if (err != 0) { 682 res->info.trace = NULL; /* just in case */ 683 (void) fprintf(stderr, 684 "trace nvlist_alloc(trace): %s\n", 685 strerror(err)); 686 nvlist_free(entry); 687 return (0); 688 } 689 } 690 (void) nvlist_add_nvlist(res->info.trace, "", entry); 691 /* Note that entry is copied, so we must still free our copy */ 692 } 693 694 nvlist_free(entry); 695 696 return (0); 697 } 698 699 /* 700 * Enable libumem debugging by default on DEBUG builds. 701 * idmapd uses rpcgen, so we can't use #ifdef DEBUG without causing 702 * undesirable behavior. 703 */ 704 #ifdef IDMAPD_DEBUG 705 const char * 706 _umem_debug_init(void) 707 { 708 return ("default,verbose"); /* $UMEM_DEBUG setting */ 709 } 710 711 const char * 712 _umem_logging_init(void) 713 { 714 return ("fail,contents"); /* $UMEM_LOGGING setting */ 715 } 716 #endif 717