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 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 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 58 static void term_handler(int); 59 static void init_idmapd(); 60 static void fini_idmapd(); 61 62 idmapd_state_t _idmapdstate; 63 64 SVCXPRT *xprt = NULL; 65 66 static int dfd = -1; /* our door server fildes, for unregistration */ 67 static int degraded = 0; /* whether the FMRI has been marked degraded */ 68 69 70 static uint32_t num_threads = 0; 71 static pthread_key_t create_threads_key; 72 static uint32_t max_threads = 40; 73 74 75 /* 76 * The following structure determines where the log messages from idmapdlog() 77 * go to. It can be stderr (idmapd -d) and/or the real idmapdlog (idmapd). 78 * 79 * logstate.max_pri is integer cutoff necessary to silence low-priority 80 * messages to stderr. idmapdlog has its own means so there a boolean 81 * logstate.write_idmapdlog is enough. 82 * 83 * logstate.degraded is a mode used by idmapd in its degraded state. 84 */ 85 86 static struct { 87 boolean_t write_syslog; 88 int max_pri; /* Max priority written to stderr */ 89 boolean_t degraded; 90 } logstate = {B_FALSE, LOG_DEBUG, B_FALSE}; 91 92 /* 93 * Server door thread start routine. 94 * 95 * Set a TSD value to the door thread. This enables the destructor to 96 * be called when this thread exits. 97 */ 98 /*ARGSUSED*/ 99 static void * 100 idmapd_door_thread_start(void *arg) 101 { 102 static void *value = 0; 103 104 /* 105 * Disable cancellation to avoid memory leaks from not running 106 * the thread cleanup code. 107 */ 108 (void) pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); 109 (void) pthread_setspecific(create_threads_key, value); 110 (void) door_return(NULL, 0, NULL, 0); 111 112 /* make lint happy */ 113 return (NULL); 114 } 115 116 /* 117 * Server door threads creation 118 */ 119 /*ARGSUSED*/ 120 static void 121 idmapd_door_thread_create(door_info_t *dip) 122 { 123 int num; 124 pthread_t thread_id; 125 126 if ((num = atomic_inc_32_nv(&num_threads)) > max_threads) { 127 atomic_dec_32(&num_threads); 128 idmapdlog(LOG_DEBUG, 129 "thread creation refused - %d threads currently active", 130 num - 1); 131 return; 132 } 133 (void) pthread_create(&thread_id, NULL, idmapd_door_thread_start, NULL); 134 idmapdlog(LOG_DEBUG, 135 "created thread ID %d - %d threads currently active", 136 thread_id, num); 137 } 138 139 /* 140 * Server door thread cleanup 141 */ 142 /*ARGSUSED*/ 143 static void 144 idmapd_door_thread_cleanup(void *arg) 145 { 146 int num; 147 148 num = atomic_dec_32_nv(&num_threads); 149 idmapdlog(LOG_DEBUG, 150 "exiting thread ID %d - %d threads currently active", 151 pthread_self(), num); 152 } 153 154 /* 155 * This is needed for mech_krb5 -- we run as daemon, yes, but we want 156 * mech_krb5 to think we're root so it can get host/nodename.fqdn 157 * tickets for us so we can authenticate to AD as the machine account 158 * that we are. For more details look at the entry point in mech_krb5 159 * corresponding to gss_init_sec_context(). 160 * 161 * As a side effect of faking our effective UID to mech_krb5 we will use 162 * root's default ccache (/tmp/krb5cc_0). But if that's created by 163 * another process then we won't have access to it: we run as daemon and 164 * keep PRIV_FILE_DAC_READ, which is insufficient to share the ccache 165 * with others. We putenv("KRB5CCNAME=/var/run/idmap/ccache") in main() 166 * to avoid this issue; see main(). 167 * 168 * Someday we'll have gss/mech_krb5 extensions for acquiring initiator 169 * creds with keytabs/raw keys, and someday we'll have extensions to 170 * libsasl to specify creds/name to use on the initiator side, and 171 * someday we'll have extensions to libldap to pass those through to 172 * libsasl. Until then this interposer will have to do. 173 * 174 * Also, we have to tell lint to shut up: it thinks app_krb5_user_uid() 175 * is defined but not used. 176 */ 177 /*LINTLIBRARY*/ 178 uid_t 179 app_krb5_user_uid(void) 180 { 181 return (0); 182 } 183 184 /*ARGSUSED*/ 185 static void 186 term_handler(int sig) 187 { 188 idmapdlog(LOG_INFO, "Terminating."); 189 fini_idmapd(); 190 _exit(0); 191 } 192 193 /*ARGSUSED*/ 194 static void 195 usr1_handler(int sig) 196 { 197 boolean_t saved_debug_mode = _idmapdstate.debug_mode; 198 199 _idmapdstate.debug_mode = B_TRUE; 200 idmap_log_stderr(LOG_DEBUG); 201 202 print_idmapdstate(); 203 204 _idmapdstate.debug_mode = saved_debug_mode; 205 idmap_log_stderr(_idmapdstate.daemon_mode ? -1 : LOG_DEBUG); 206 207 } 208 209 static int pipe_fd = -1; 210 211 static void 212 daemonize_ready(void) 213 { 214 char data = '\0'; 215 /* 216 * wake the parent 217 */ 218 (void) write(pipe_fd, &data, 1); 219 (void) close(pipe_fd); 220 } 221 222 static int 223 daemonize_start(void) 224 { 225 char data; 226 int status; 227 int devnull; 228 int filedes[2]; 229 pid_t pid; 230 231 (void) sigset(SIGPIPE, SIG_IGN); 232 devnull = open("/dev/null", O_RDONLY); 233 if (devnull < 0) 234 return (-1); 235 (void) dup2(devnull, 0); 236 (void) dup2(2, 1); /* stderr only */ 237 if (pipe(filedes) < 0) 238 return (-1); 239 if ((pid = fork1()) < 0) 240 return (-1); 241 if (pid != 0) { 242 /* 243 * parent 244 */ 245 (void) close(filedes[1]); 246 if (read(filedes[0], &data, 1) == 1) { 247 /* presume success */ 248 _exit(0); 249 } 250 status = -1; 251 (void) wait4(pid, &status, 0, NULL); 252 if (WIFEXITED(status)) 253 _exit(WEXITSTATUS(status)); 254 else 255 _exit(-1); 256 } 257 258 /* 259 * child 260 */ 261 pipe_fd = filedes[1]; 262 (void) close(filedes[0]); 263 (void) setsid(); 264 (void) umask(0077); 265 openlog("idmap", LOG_PID, LOG_DAEMON); 266 267 return (0); 268 } 269 270 271 int 272 main(int argc, char **argv) 273 { 274 int c; 275 struct rlimit rl; 276 277 _idmapdstate.daemon_mode = TRUE; 278 _idmapdstate.debug_mode = FALSE; 279 while ((c = getopt(argc, argv, "d")) != -1) { 280 switch (c) { 281 case 'd': 282 _idmapdstate.daemon_mode = FALSE; 283 break; 284 default: 285 (void) fprintf(stderr, 286 "Usage: /usr/lib/idmapd [-d]\n"); 287 return (SMF_EXIT_ERR_CONFIG); 288 } 289 } 290 291 /* set locale and domain for internationalization */ 292 (void) setlocale(LC_ALL, ""); 293 (void) textdomain(TEXT_DOMAIN); 294 295 idmap_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 = _idmapdstate.limit_uid = SENTINEL_PID; 428 _idmapdstate.next_gid = _idmapdstate.limit_gid = SENTINEL_PID; 429 } else { 430 _idmapdstate.limit_uid = _idmapdstate.next_uid + 8192; 431 _idmapdstate.limit_gid = _idmapdstate.next_gid + 8192; 432 } 433 434 print_idmapdstate(); 435 436 return; 437 438 errout: 439 fini_idmapd(); 440 exit(1); 441 } 442 443 static void 444 fini_idmapd() 445 { 446 idmap_unreg(dfd); 447 fini_mapping_system(); 448 if (xprt != NULL) 449 svc_destroy(xprt); 450 } 451 452 static 453 const char * 454 get_fmri(void) 455 { 456 static char *fmri = NULL; 457 static char buf[60]; 458 char *s; 459 460 membar_consumer(); 461 s = fmri; 462 if (s != NULL && *s == '\0') 463 return (NULL); 464 else if (s != NULL) 465 return (s); 466 467 if ((s = getenv("SMF_FMRI")) == NULL || strlen(s) >= sizeof (buf)) 468 buf[0] = '\0'; 469 else 470 (void) strlcpy(buf, s, sizeof (buf)); 471 472 membar_producer(); 473 fmri = buf; 474 475 return (get_fmri()); 476 } 477 478 /* 479 * Wrappers for smf_degrade/restore_instance() 480 * 481 * smf_restore_instance() is too heavy duty to be calling every time we 482 * have a successful AD name<->SID lookup. 483 */ 484 void 485 degrade_svc(int poke_discovery, const char *reason) 486 { 487 const char *fmri; 488 489 /* 490 * If the config update thread is in a state where auto-discovery could 491 * be re-tried, then this will make it try it -- a sort of auto-refresh. 492 */ 493 if (poke_discovery) 494 idmap_cfg_poke_updates(); 495 496 membar_consumer(); 497 if (degraded) 498 return; 499 500 idmapdlog(LOG_ERR, "Degraded operation (%s). If you are running an " 501 "SMB server in workgroup mode, or if you're not running an SMB " 502 "server, then you can ignore this message", reason); 503 504 membar_producer(); 505 degraded = 1; 506 idmap_log_degraded(B_TRUE); 507 508 if ((fmri = get_fmri()) != NULL) 509 (void) smf_degrade_instance(fmri, 0); 510 } 511 512 void 513 restore_svc(void) 514 { 515 const char *fmri; 516 517 membar_consumer(); 518 if (!degraded) 519 return; 520 521 if ((fmri = get_fmri()) == NULL) 522 (void) smf_restore_instance(fmri); 523 524 membar_producer(); 525 degraded = 0; 526 idmap_log_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 va_list args; 536 537 if (pri <= logstate.max_pri) { 538 va_start(args, format); 539 (void) vfprintf(stderr, format, args); 540 (void) fprintf(stderr, "\n"); 541 va_end(args); 542 } 543 544 /* 545 * We don't want to fill up the logs with useless messages when 546 * we're degraded, but we still want to log. 547 */ 548 if (logstate.degraded) 549 pri = LOG_DEBUG; 550 551 if (logstate.write_syslog) { 552 va_start(args, format); 553 vsyslog(pri, format, args); 554 va_end(args); 555 } 556 } 557 558 void 559 idmap_log_stderr(int pri) 560 { 561 logstate.max_pri = pri; 562 } 563 564 void 565 idmap_log_syslog(boolean_t what) 566 { 567 logstate.write_syslog = what; 568 } 569 570 void 571 idmap_log_degraded(boolean_t what) 572 { 573 logstate.degraded = what; 574 } 575