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