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 59 static void term_handler(int); 60 static void init_idmapd(); 61 static void fini_idmapd(); 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 * This is needed for mech_krb5 -- we run as daemon, yes, but we want 77 * mech_krb5 to think we're root so it can get host/nodename.fqdn 78 * tickets for us so we can authenticate to AD as the machine account 79 * that we are. For more details look at the entry point in mech_krb5 80 * corresponding to gss_init_sec_context(). 81 * 82 * As a side effect of faking our effective UID to mech_krb5 we will use 83 * root's default ccache (/tmp/krb5cc_0). But if that's created by 84 * another process then we won't have access to it: we run as daemon and 85 * keep PRIV_FILE_DAC_READ, which is insufficient to share the ccache 86 * with others. We putenv("KRB5CCNAME=/var/run/idmap/ccache") in main() 87 * to avoid this issue; see main(). 88 * 89 * Someday we'll have gss/mech_krb5 extensions for acquiring initiator 90 * creds with keytabs/raw keys, and someday we'll have extensions to 91 * libsasl to specify creds/name to use on the initiator side, and 92 * someday we'll have extensions to libldap to pass those through to 93 * libsasl. Until then this interposer will have to do. 94 * 95 * Also, we have to tell lint to shut up: it thinks app_krb5_user_uid() 96 * is defined but not used. 97 */ 98 /*LINTLIBRARY*/ 99 uid_t 100 app_krb5_user_uid(void) 101 { 102 return (0); 103 } 104 105 /*ARGSUSED*/ 106 static void 107 term_handler(int sig) 108 { 109 idmapdlog(LOG_INFO, "Terminating."); 110 fini_idmapd(); 111 _exit(0); 112 } 113 114 /*ARGSUSED*/ 115 static void 116 usr1_handler(int sig) 117 { 118 bool_t saved_debug_mode = _idmapdstate.debug_mode; 119 120 _idmapdstate.debug_mode = TRUE; 121 print_idmapdstate(); 122 _idmapdstate.debug_mode = saved_debug_mode; 123 } 124 125 static int pipe_fd = -1; 126 127 static void 128 daemonize_ready(void) 129 { 130 char data = '\0'; 131 /* 132 * wake the parent 133 */ 134 (void) write(pipe_fd, &data, 1); 135 (void) close(pipe_fd); 136 } 137 138 static int 139 daemonize_start(void) 140 { 141 char data; 142 int status; 143 int devnull; 144 int filedes[2]; 145 pid_t pid; 146 147 (void) sigset(SIGPIPE, SIG_IGN); 148 devnull = open("/dev/null", O_RDONLY); 149 if (devnull < 0) 150 return (-1); 151 (void) dup2(devnull, 0); 152 (void) dup2(2, 1); /* stderr only */ 153 if (pipe(filedes) < 0) 154 return (-1); 155 if ((pid = fork1()) < 0) 156 return (-1); 157 if (pid != 0) { 158 /* 159 * parent 160 */ 161 (void) close(filedes[1]); 162 if (read(filedes[0], &data, 1) == 1) { 163 /* presume success */ 164 _exit(0); 165 } 166 status = -1; 167 (void) wait4(pid, &status, 0, NULL); 168 if (WIFEXITED(status)) 169 _exit(WEXITSTATUS(status)); 170 else 171 _exit(-1); 172 } 173 174 /* 175 * child 176 */ 177 pipe_fd = filedes[1]; 178 (void) close(filedes[0]); 179 (void) setsid(); 180 (void) umask(0077); 181 openlog("idmap", LOG_PID, LOG_DAEMON); 182 return (0); 183 } 184 185 186 int 187 main(int argc, char **argv) 188 { 189 int c; 190 struct rlimit rl; 191 192 _idmapdstate.daemon_mode = TRUE; 193 _idmapdstate.debug_mode = FALSE; 194 while ((c = getopt(argc, argv, "d")) != -1) { 195 switch (c) { 196 case 'd': 197 _idmapdstate.daemon_mode = FALSE; 198 break; 199 default: 200 fprintf(stderr, "Usage: /usr/lib/idmapd"); 201 return (SMF_EXIT_ERR_CONFIG); 202 break; 203 } 204 } 205 206 /* set locale and domain for internationalization */ 207 (void) setlocale(LC_ALL, ""); 208 (void) textdomain(TEXT_DOMAIN); 209 210 if (is_system_labeled() && getzoneid() != GLOBAL_ZONEID) { 211 idmapdlog(LOG_ERR, 212 "with Trusted Extensions idmapd runs only in the " 213 "global zone"); 214 exit(1); 215 } 216 217 /* 218 * Raise the fd limit to max 219 */ 220 if (getrlimit(RLIMIT_NOFILE, &rl) != 0) { 221 idmapdlog(LOG_ERR, "getrlimit failed"); 222 } else if (rl.rlim_cur < rl.rlim_max) { 223 rl.rlim_cur = rl.rlim_max; 224 if (setrlimit(RLIMIT_NOFILE, &rl) != 0) 225 idmapdlog(LOG_ERR, 226 "Unable to raise RLIMIT_NOFILE to %d", 227 rl.rlim_cur); 228 } 229 230 (void) mutex_init(&_svcstate_lock, USYNC_THREAD, NULL); 231 232 if (_idmapdstate.daemon_mode == TRUE) { 233 if (daemonize_start() < 0) { 234 (void) idmapdlog(LOG_ERR, "unable to daemonize"); 235 exit(-1); 236 } 237 } else 238 (void) umask(0077); 239 240 idmap_init_tsd_key(); 241 242 init_idmapd(); 243 244 /* signal handlers that should run only after we're initialized */ 245 (void) sigset(SIGTERM, term_handler); 246 (void) sigset(SIGUSR1, usr1_handler); 247 (void) sigset(SIGHUP, idmap_cfg_hup_handler); 248 249 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 250 DAEMON_UID, DAEMON_GID, 251 PRIV_PROC_AUDIT, PRIV_FILE_DAC_READ, 252 (char *)NULL) == -1) { 253 idmapdlog(LOG_ERR, "unable to drop privileges"); 254 exit(1); 255 } 256 257 __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION, 258 PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL); 259 260 if (_idmapdstate.daemon_mode == TRUE) 261 daemonize_ready(); 262 263 /* With doors RPC this just wastes this thread, oh well */ 264 svc_run(); 265 return (0); 266 } 267 268 static void 269 init_idmapd() 270 { 271 int error; 272 int connmaxrec = IDMAP_MAX_DOOR_RPC; 273 274 /* create directories as root and chown to daemon uid */ 275 if (create_directory(IDMAP_DBDIR, DAEMON_UID, DAEMON_GID) < 0) 276 exit(1); 277 if (create_directory(IDMAP_CACHEDIR, DAEMON_UID, DAEMON_GID) < 0) 278 exit(1); 279 280 /* 281 * Set KRB5CCNAME in the environment. See app_krb5_user_uid() 282 * for more details. We blow away the existing one, if there is 283 * one. 284 */ 285 (void) unlink(IDMAP_CACHEDIR "/ccache"); 286 putenv("KRB5CCNAME=" IDMAP_CACHEDIR "/ccache"); 287 288 if (sysinfo(SI_HOSTNAME, _idmapdstate.hostname, 289 sizeof (_idmapdstate.hostname)) == -1) { 290 error = errno; 291 idmapdlog(LOG_ERR, "unable to determine hostname, error: %d", 292 error); 293 exit(1); 294 } 295 296 if ((error = init_mapping_system()) < 0) { 297 idmapdlog(LOG_ERR, "unable to initialize mapping system"); 298 exit(error < -2 ? SMF_EXIT_ERR_CONFIG : 1); 299 } 300 301 xprt = svc_door_create(idmap_prog_1, IDMAP_PROG, IDMAP_V1, connmaxrec); 302 if (xprt == NULL) { 303 idmapdlog(LOG_ERR, "unable to create door RPC service"); 304 goto errout; 305 } 306 307 if (!svc_control(xprt, SVCSET_CONNMAXREC, &connmaxrec)) { 308 idmapdlog(LOG_ERR, "unable to limit RPC request size"); 309 goto errout; 310 } 311 312 dfd = xprt->xp_fd; 313 314 if (dfd == -1) { 315 idmapdlog(LOG_ERR, "unable to register door"); 316 goto errout; 317 } 318 if ((error = idmap_reg(dfd)) != 0) { 319 idmapdlog(LOG_ERR, "unable to register door (%s)", 320 strerror(errno)); 321 goto errout; 322 } 323 324 if ((error = allocids(_idmapdstate.new_eph_db, 325 8192, &_idmapdstate.next_uid, 326 8192, &_idmapdstate.next_gid)) != 0) { 327 idmapdlog(LOG_ERR, "unable to allocate ephemeral IDs (%s)", 328 strerror(errno)); 329 _idmapdstate.next_uid = _idmapdstate.limit_uid = SENTINEL_PID; 330 _idmapdstate.next_gid = _idmapdstate.limit_gid = SENTINEL_PID; 331 } else { 332 _idmapdstate.limit_uid = _idmapdstate.next_uid + 8192; 333 _idmapdstate.limit_gid = _idmapdstate.next_gid + 8192; 334 } 335 336 print_idmapdstate(); 337 338 return; 339 340 errout: 341 fini_idmapd(); 342 exit(1); 343 } 344 345 static void 346 fini_idmapd() 347 { 348 idmap_unreg(dfd); 349 fini_mapping_system(); 350 if (xprt != NULL) 351 svc_destroy(xprt); 352 } 353 354 static 355 const char * 356 get_fmri(void) 357 { 358 static char *fmri = NULL; 359 static char buf[60]; 360 char *s; 361 362 membar_consumer(); 363 s = fmri; 364 if (s != NULL && *s == '\0') 365 return (NULL); 366 else if (s != NULL) 367 return (s); 368 369 if ((s = getenv("SMF_FMRI")) == NULL || strlen(s) >= sizeof (buf)) 370 buf[0] = '\0'; 371 else 372 (void) strlcpy(buf, s, sizeof (buf)); 373 374 membar_producer(); 375 fmri = buf; 376 377 return (get_fmri()); 378 } 379 380 /* 381 * Wrappers for smf_degrade/restore_instance() 382 * 383 * smf_restore_instance() is too heavy duty to be calling every time we 384 * have a successful AD name<->SID lookup. 385 */ 386 void 387 degrade_svc(int poke_discovery, const char *reason) 388 { 389 const char *fmri; 390 391 /* 392 * If the config update thread is in a state where auto-discovery could 393 * be re-tried, then this will make it try it -- a sort of auto-refresh. 394 */ 395 if (poke_discovery) 396 idmap_cfg_poke_updates(); 397 398 membar_consumer(); 399 if (degraded) 400 return; 401 402 idmapdlog(LOG_ERR, "Degraded operation (%s). If you are running an " 403 "SMB server in workgroup mode, or if you're not running an SMB " 404 "server, then you can ignore this message", reason); 405 406 membar_producer(); 407 degraded = 1; 408 409 if ((fmri = get_fmri()) != NULL) 410 (void) smf_degrade_instance(fmri, 0); 411 } 412 413 void 414 restore_svc(void) 415 { 416 const char *fmri; 417 418 membar_consumer(); 419 if (!degraded) 420 return; 421 422 if ((fmri = get_fmri()) == NULL) 423 (void) smf_restore_instance(fmri); 424 425 membar_producer(); 426 degraded = 0; 427 idmapdlog(LOG_NOTICE, "Normal operation restored"); 428 } 429 430 void 431 idmapdlog(int pri, const char *format, ...) 432 { 433 va_list args; 434 435 va_start(args, format); 436 437 if (_idmapdstate.debug_mode == TRUE || 438 _idmapdstate.daemon_mode == FALSE) { 439 (void) vfprintf(stderr, format, args); 440 (void) fprintf(stderr, "\n"); 441 } 442 443 /* 444 * We don't want to fill up the logs with useless messages when 445 * we're degraded, but we still want to log. 446 */ 447 if (degraded) 448 pri = LOG_DEBUG; 449 450 (void) vsyslog(pri, format, args); 451 va_end(args); 452 } 453