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