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 191 _idmapdstate.daemon_mode = TRUE; 192 _idmapdstate.debug_mode = FALSE; 193 while ((c = getopt(argc, argv, "d")) != -1) { 194 switch (c) { 195 case 'd': 196 _idmapdstate.daemon_mode = FALSE; 197 break; 198 default: 199 fprintf(stderr, "Usage: /usr/lib/idmapd"); 200 return (SMF_EXIT_ERR_CONFIG); 201 break; 202 } 203 } 204 205 /* set locale and domain for internationalization */ 206 (void) setlocale(LC_ALL, ""); 207 (void) textdomain(TEXT_DOMAIN); 208 209 if (is_system_labeled() && getzoneid() != GLOBAL_ZONEID) { 210 idmapdlog(LOG_ERR, 211 "with Trusted Extensions idmapd runs only in the " 212 "global zone"); 213 exit(1); 214 } 215 216 (void) mutex_init(&_svcstate_lock, USYNC_THREAD, NULL); 217 218 if (_idmapdstate.daemon_mode == TRUE) { 219 if (daemonize_start() < 0) { 220 (void) idmapdlog(LOG_ERR, "unable to daemonize"); 221 exit(-1); 222 } 223 } else 224 (void) umask(0077); 225 226 idmap_init_tsd_key(); 227 228 init_idmapd(); 229 230 /* signal handlers that should run only after we're initialized */ 231 (void) sigset(SIGTERM, term_handler); 232 (void) sigset(SIGUSR1, usr1_handler); 233 (void) sigset(SIGHUP, idmap_cfg_hup_handler); 234 235 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 236 DAEMON_UID, DAEMON_GID, 237 PRIV_PROC_AUDIT, PRIV_FILE_DAC_READ, 238 (char *)NULL) == -1) { 239 idmapdlog(LOG_ERR, "unable to drop privileges"); 240 exit(1); 241 } 242 243 __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION, 244 PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL); 245 246 if (_idmapdstate.daemon_mode == TRUE) 247 daemonize_ready(); 248 249 /* With doors RPC this just wastes this thread, oh well */ 250 svc_run(); 251 return (0); 252 } 253 254 static void 255 init_idmapd() 256 { 257 int error; 258 int connmaxrec = IDMAP_MAX_DOOR_RPC; 259 260 /* create directories as root and chown to daemon uid */ 261 if (create_directory(IDMAP_DBDIR, DAEMON_UID, DAEMON_GID) < 0) 262 exit(1); 263 if (create_directory(IDMAP_CACHEDIR, DAEMON_UID, DAEMON_GID) < 0) 264 exit(1); 265 266 /* 267 * Set KRB5CCNAME in the environment. See app_krb5_user_uid() 268 * for more details. We blow away the existing one, if there is 269 * one. 270 */ 271 (void) unlink(IDMAP_CACHEDIR "/ccache"); 272 putenv("KRB5CCNAME=" IDMAP_CACHEDIR "/ccache"); 273 274 if (sysinfo(SI_HOSTNAME, _idmapdstate.hostname, 275 sizeof (_idmapdstate.hostname)) == -1) { 276 error = errno; 277 idmapdlog(LOG_ERR, "unable to determine hostname, error: %d", 278 error); 279 exit(1); 280 } 281 282 if ((error = init_mapping_system()) < 0) { 283 idmapdlog(LOG_ERR, "unable to initialize mapping system"); 284 exit(error < -2 ? SMF_EXIT_ERR_CONFIG : 1); 285 } 286 287 xprt = svc_door_create(idmap_prog_1, IDMAP_PROG, IDMAP_V1, connmaxrec); 288 if (xprt == NULL) { 289 idmapdlog(LOG_ERR, "unable to create door RPC service"); 290 goto errout; 291 } 292 293 if (!svc_control(xprt, SVCSET_CONNMAXREC, &connmaxrec)) { 294 idmapdlog(LOG_ERR, "unable to limit RPC request size"); 295 goto errout; 296 } 297 298 dfd = xprt->xp_fd; 299 300 if (dfd == -1) { 301 idmapdlog(LOG_ERR, "unable to register door"); 302 goto errout; 303 } 304 if ((error = idmap_reg(dfd)) != 0) { 305 idmapdlog(LOG_ERR, "unable to register door (%s)", 306 strerror(errno)); 307 goto errout; 308 } 309 310 if ((error = allocids(_idmapdstate.new_eph_db, 311 8192, &_idmapdstate.next_uid, 312 8192, &_idmapdstate.next_gid)) != 0) { 313 idmapdlog(LOG_ERR, "unable to allocate ephemeral IDs (%s)", 314 strerror(errno)); 315 _idmapdstate.next_uid = _idmapdstate.limit_uid = SENTINEL_PID; 316 _idmapdstate.next_gid = _idmapdstate.limit_gid = SENTINEL_PID; 317 } else { 318 _idmapdstate.limit_uid = _idmapdstate.next_uid + 8192; 319 _idmapdstate.limit_gid = _idmapdstate.next_gid + 8192; 320 } 321 322 print_idmapdstate(); 323 324 return; 325 326 errout: 327 fini_idmapd(); 328 exit(1); 329 } 330 331 static void 332 fini_idmapd() 333 { 334 idmap_unreg(dfd); 335 fini_mapping_system(); 336 if (xprt != NULL) 337 svc_destroy(xprt); 338 } 339 340 static 341 const char * 342 get_fmri(void) 343 { 344 static char *fmri = NULL; 345 static char buf[60]; 346 char *s; 347 348 membar_consumer(); 349 s = fmri; 350 if (s != NULL && *s == '\0') 351 return (NULL); 352 else if (s != NULL) 353 return (s); 354 355 if ((s = getenv("SMF_FMRI")) == NULL || strlen(s) >= sizeof (buf)) 356 buf[0] = '\0'; 357 else 358 (void) strlcpy(buf, s, sizeof (buf)); 359 360 membar_producer(); 361 fmri = buf; 362 363 return (get_fmri()); 364 } 365 366 /* 367 * Wrappers for smf_degrade/restore_instance() 368 * 369 * smf_restore_instance() is too heavy duty to be calling every time we 370 * have a successful AD name<->SID lookup. 371 */ 372 void 373 degrade_svc(int poke_discovery, const char *reason) 374 { 375 const char *fmri; 376 377 /* 378 * If the config update thread is in a state where auto-discovery could 379 * be re-tried, then this will make it try it -- a sort of auto-refresh. 380 */ 381 if (poke_discovery) 382 idmap_cfg_poke_updates(); 383 384 membar_consumer(); 385 if (degraded) 386 return; 387 388 idmapdlog(LOG_ERR, "Degraded operation (%s). If you are running an " 389 "SMB server in workgroup mode, or if you're not running an SMB " 390 "server, then you can ignore this message", reason); 391 392 membar_producer(); 393 degraded = 1; 394 395 if ((fmri = get_fmri()) != NULL) 396 (void) smf_degrade_instance(fmri, 0); 397 } 398 399 void 400 restore_svc(void) 401 { 402 const char *fmri; 403 404 membar_consumer(); 405 if (!degraded) 406 return; 407 408 if ((fmri = get_fmri()) == NULL) 409 (void) smf_restore_instance(fmri); 410 411 membar_producer(); 412 degraded = 0; 413 idmapdlog(LOG_NOTICE, "Normal operation restored"); 414 } 415 416 void 417 idmapdlog(int pri, const char *format, ...) 418 { 419 va_list args; 420 421 va_start(args, format); 422 423 if (_idmapdstate.debug_mode == TRUE || 424 _idmapdstate.daemon_mode == FALSE) { 425 (void) vfprintf(stderr, format, args); 426 (void) fprintf(stderr, "\n"); 427 } 428 429 /* 430 * We don't want to fill up the logs with useless messages when 431 * we're degraded, but we still want to log. 432 */ 433 if (degraded) 434 pri = LOG_DEBUG; 435 436 (void) vsyslog(pri, format, args); 437 va_end(args); 438 } 439