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