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 print_idmapdstate(); 190 _idmapdstate.debug_mode = saved_debug_mode; 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 return (0); 251 } 252 253 254 int 255 main(int argc, char **argv) 256 { 257 int c; 258 struct rlimit rl; 259 260 _idmapdstate.daemon_mode = TRUE; 261 _idmapdstate.debug_mode = FALSE; 262 while ((c = getopt(argc, argv, "d")) != -1) { 263 switch (c) { 264 case 'd': 265 _idmapdstate.daemon_mode = FALSE; 266 break; 267 default: 268 fprintf(stderr, "Usage: /usr/lib/idmapd"); 269 return (SMF_EXIT_ERR_CONFIG); 270 break; 271 } 272 } 273 274 /* set locale and domain for internationalization */ 275 (void) setlocale(LC_ALL, ""); 276 (void) textdomain(TEXT_DOMAIN); 277 278 if (is_system_labeled() && getzoneid() != GLOBAL_ZONEID) { 279 idmapdlog(LOG_ERR, 280 "with Trusted Extensions idmapd runs only in the " 281 "global zone"); 282 exit(1); 283 } 284 285 /* 286 * Raise the fd limit to max 287 */ 288 if (getrlimit(RLIMIT_NOFILE, &rl) != 0) { 289 idmapdlog(LOG_ERR, "getrlimit failed"); 290 } else if (rl.rlim_cur < rl.rlim_max) { 291 rl.rlim_cur = rl.rlim_max; 292 if (setrlimit(RLIMIT_NOFILE, &rl) != 0) 293 idmapdlog(LOG_ERR, 294 "Unable to raise RLIMIT_NOFILE to %d", 295 rl.rlim_cur); 296 } 297 298 (void) mutex_init(&_svcstate_lock, USYNC_THREAD, NULL); 299 300 if (_idmapdstate.daemon_mode == TRUE) { 301 if (daemonize_start() < 0) { 302 (void) idmapdlog(LOG_ERR, "unable to daemonize"); 303 exit(-1); 304 } 305 } else 306 (void) umask(0077); 307 308 idmap_init_tsd_key(); 309 310 init_idmapd(); 311 312 /* signal handlers that should run only after we're initialized */ 313 (void) sigset(SIGTERM, term_handler); 314 (void) sigset(SIGUSR1, usr1_handler); 315 (void) sigset(SIGHUP, idmap_cfg_hup_handler); 316 317 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 318 DAEMON_UID, DAEMON_GID, 319 PRIV_PROC_AUDIT, PRIV_FILE_DAC_READ, 320 (char *)NULL) == -1) { 321 idmapdlog(LOG_ERR, "unable to drop privileges"); 322 exit(1); 323 } 324 325 __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION, 326 PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL); 327 328 if (_idmapdstate.daemon_mode == TRUE) 329 daemonize_ready(); 330 331 /* With doors RPC this just wastes this thread, oh well */ 332 svc_run(); 333 return (0); 334 } 335 336 static void 337 init_idmapd() 338 { 339 int error; 340 int connmaxrec = IDMAP_MAX_DOOR_RPC; 341 342 343 /* create directories as root and chown to daemon uid */ 344 if (create_directory(IDMAP_DBDIR, DAEMON_UID, DAEMON_GID) < 0) 345 exit(1); 346 if (create_directory(IDMAP_CACHEDIR, DAEMON_UID, DAEMON_GID) < 0) 347 exit(1); 348 349 /* 350 * Set KRB5CCNAME in the environment. See app_krb5_user_uid() 351 * for more details. We blow away the existing one, if there is 352 * one. 353 */ 354 (void) unlink(IDMAP_CACHEDIR "/ccache"); 355 putenv("KRB5CCNAME=" IDMAP_CACHEDIR "/ccache"); 356 357 if (sysinfo(SI_HOSTNAME, _idmapdstate.hostname, 358 sizeof (_idmapdstate.hostname)) == -1) { 359 error = errno; 360 idmapdlog(LOG_ERR, "unable to determine hostname, error: %d", 361 error); 362 exit(1); 363 } 364 365 if ((error = init_mapping_system()) < 0) { 366 idmapdlog(LOG_ERR, "unable to initialize mapping system"); 367 exit(error < -2 ? SMF_EXIT_ERR_CONFIG : 1); 368 } 369 370 (void) door_server_create(idmapd_door_thread_create); 371 if ((error = pthread_key_create(&create_threads_key, 372 idmapd_door_thread_cleanup)) != 0) { 373 idmapdlog(LOG_ERR, "unable to create threads key (%s)", 374 strerror(error)); 375 goto errout; 376 } 377 378 xprt = svc_door_create(idmap_prog_1, IDMAP_PROG, IDMAP_V1, connmaxrec); 379 if (xprt == NULL) { 380 idmapdlog(LOG_ERR, "unable to create door RPC service"); 381 goto errout; 382 } 383 384 if (!svc_control(xprt, SVCSET_CONNMAXREC, &connmaxrec)) { 385 idmapdlog(LOG_ERR, "unable to limit RPC request size"); 386 goto errout; 387 } 388 389 dfd = xprt->xp_fd; 390 391 if (dfd == -1) { 392 idmapdlog(LOG_ERR, "unable to register door"); 393 goto errout; 394 } 395 if ((error = idmap_reg(dfd)) != 0) { 396 idmapdlog(LOG_ERR, "unable to register door (%s)", 397 strerror(errno)); 398 goto errout; 399 } 400 401 if ((error = allocids(_idmapdstate.new_eph_db, 402 8192, &_idmapdstate.next_uid, 403 8192, &_idmapdstate.next_gid)) != 0) { 404 idmapdlog(LOG_ERR, "unable to allocate ephemeral IDs (%s)", 405 strerror(errno)); 406 _idmapdstate.next_uid = _idmapdstate.limit_uid = SENTINEL_PID; 407 _idmapdstate.next_gid = _idmapdstate.limit_gid = SENTINEL_PID; 408 } else { 409 _idmapdstate.limit_uid = _idmapdstate.next_uid + 8192; 410 _idmapdstate.limit_gid = _idmapdstate.next_gid + 8192; 411 } 412 413 print_idmapdstate(); 414 415 return; 416 417 errout: 418 fini_idmapd(); 419 exit(1); 420 } 421 422 static void 423 fini_idmapd() 424 { 425 idmap_unreg(dfd); 426 fini_mapping_system(); 427 if (xprt != NULL) 428 svc_destroy(xprt); 429 } 430 431 static 432 const char * 433 get_fmri(void) 434 { 435 static char *fmri = NULL; 436 static char buf[60]; 437 char *s; 438 439 membar_consumer(); 440 s = fmri; 441 if (s != NULL && *s == '\0') 442 return (NULL); 443 else if (s != NULL) 444 return (s); 445 446 if ((s = getenv("SMF_FMRI")) == NULL || strlen(s) >= sizeof (buf)) 447 buf[0] = '\0'; 448 else 449 (void) strlcpy(buf, s, sizeof (buf)); 450 451 membar_producer(); 452 fmri = buf; 453 454 return (get_fmri()); 455 } 456 457 /* 458 * Wrappers for smf_degrade/restore_instance() 459 * 460 * smf_restore_instance() is too heavy duty to be calling every time we 461 * have a successful AD name<->SID lookup. 462 */ 463 void 464 degrade_svc(int poke_discovery, const char *reason) 465 { 466 const char *fmri; 467 468 /* 469 * If the config update thread is in a state where auto-discovery could 470 * be re-tried, then this will make it try it -- a sort of auto-refresh. 471 */ 472 if (poke_discovery) 473 idmap_cfg_poke_updates(); 474 475 membar_consumer(); 476 if (degraded) 477 return; 478 479 idmapdlog(LOG_ERR, "Degraded operation (%s). If you are running an " 480 "SMB server in workgroup mode, or if you're not running an SMB " 481 "server, then you can ignore this message", reason); 482 483 membar_producer(); 484 degraded = 1; 485 486 if ((fmri = get_fmri()) != NULL) 487 (void) smf_degrade_instance(fmri, 0); 488 } 489 490 void 491 restore_svc(void) 492 { 493 const char *fmri; 494 495 membar_consumer(); 496 if (!degraded) 497 return; 498 499 if ((fmri = get_fmri()) == NULL) 500 (void) smf_restore_instance(fmri); 501 502 membar_producer(); 503 degraded = 0; 504 idmapdlog(LOG_NOTICE, "Normal operation restored"); 505 } 506 507 void 508 idmapdlog(int pri, const char *format, ...) 509 { 510 va_list args; 511 512 va_start(args, format); 513 514 if (_idmapdstate.debug_mode == TRUE || 515 _idmapdstate.daemon_mode == FALSE) { 516 (void) vfprintf(stderr, format, args); 517 (void) fprintf(stderr, "\n"); 518 } 519 520 /* 521 * We don't want to fill up the logs with useless messages when 522 * we're degraded, but we still want to log. 523 */ 524 if (degraded) 525 pri = LOG_DEBUG; 526 527 (void) vsyslog(pri, format, args); 528 va_end(args); 529 } 530