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