1c5c4113dSnw /* 2c5c4113dSnw * CDDL HEADER START 3c5c4113dSnw * 4c5c4113dSnw * The contents of this file are subject to the terms of the 5c5c4113dSnw * Common Development and Distribution License (the "License"). 6c5c4113dSnw * You may not use this file except in compliance with the License. 7c5c4113dSnw * 8c5c4113dSnw * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9c5c4113dSnw * or http://www.opensolaris.org/os/licensing. 10c5c4113dSnw * See the License for the specific language governing permissions 11c5c4113dSnw * and limitations under the License. 12c5c4113dSnw * 13c5c4113dSnw * When distributing Covered Code, include this CDDL HEADER in each 14c5c4113dSnw * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15c5c4113dSnw * If applicable, add the following below this CDDL HEADER, with the 16c5c4113dSnw * fields enclosed by brackets "[]" replaced with your own identifying 17c5c4113dSnw * information: Portions Copyright [yyyy] [name of copyright owner] 18c5c4113dSnw * 19c5c4113dSnw * CDDL HEADER END 20c5c4113dSnw */ 21c5c4113dSnw /* 22c5c4113dSnw * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23c5c4113dSnw * Use is subject to license terms. 24c5c4113dSnw */ 25c5c4113dSnw 26c5c4113dSnw #pragma ident "%Z%%M% %I% %E% SMI" 27c5c4113dSnw 28c5c4113dSnw /* 29c5c4113dSnw * main() of idmapd(1M) 30c5c4113dSnw */ 31c5c4113dSnw 32c5c4113dSnw #include "idmapd.h" 33c5c4113dSnw #include <signal.h> 34c5c4113dSnw #include <rpc/pmap_clnt.h> /* for pmap_unset */ 35c5c4113dSnw #include <string.h> /* strcmp */ 36c5c4113dSnw #include <unistd.h> /* setsid */ 37c5c4113dSnw #include <sys/types.h> 38c5c4113dSnw #include <memory.h> 39c5c4113dSnw #include <stropts.h> 40c5c4113dSnw #include <netconfig.h> 41c5c4113dSnw #include <sys/resource.h> /* rlimit */ 42c5c4113dSnw #include <syslog.h> 43c5c4113dSnw #include <rpcsvc/daemon_utils.h> /* DAEMON_UID and DAEMON_GID */ 44c5c4113dSnw #include <priv_utils.h> /* privileges */ 45c5c4113dSnw #include <locale.h> 46c5c4113dSnw #include <sys/systeminfo.h> 47c5c4113dSnw #include <errno.h> 48c5c4113dSnw #include <sys/wait.h> 49c5c4113dSnw #include <sys/time.h> 50c5c4113dSnw #include <zone.h> 51c5c4113dSnw #include <door.h> 52c5c4113dSnw #include <tsol/label.h> 53c5c4113dSnw #include <sys/resource.h> 54c5c4113dSnw #include <sys/sid.h> 55c5c4113dSnw #include <sys/idmap.h> 56c5c4113dSnw 57c5c4113dSnw static void hup_handler(int); 58c5c4113dSnw static void term_handler(int); 59c5c4113dSnw static void init_idmapd(); 60c5c4113dSnw static void fini_idmapd(); 61c5c4113dSnw 62c5c4113dSnw #ifndef SIG_PF 63c5c4113dSnw #define SIG_PF void(*)(int) 64c5c4113dSnw #endif 65c5c4113dSnw 66c5c4113dSnw #define _RPCSVC_CLOSEDOWN 120 67c5c4113dSnw 68c5c4113dSnw int _rpcsvcstate = _IDLE; /* Set when a request is serviced */ 69c5c4113dSnw int _rpcsvccount = 0; /* Number of requests being serviced */ 70c5c4113dSnw mutex_t _svcstate_lock; /* lock for _rpcsvcstate, _rpcsvccount */ 71c5c4113dSnw idmapd_state_t _idmapdstate; 72c5c4113dSnw 73c5c4113dSnw SVCXPRT *xprt = NULL; 74c5c4113dSnw 75c5c4113dSnw static int dfd = -1; /* our door server fildes, for unregistration */ 76c5c4113dSnw 77c5c4113dSnw #ifdef DEBUG 78c5c4113dSnw #define RPC_SVC_FG 79c5c4113dSnw #endif 80c5c4113dSnw 81c5c4113dSnw /* 82c5c4113dSnw * This is needed for mech_krb5 -- we run as daemon, yes, but we want 83*78b2cb9aSnw * mech_krb5 to think we're root so it can get host/nodename.fqdn 84*78b2cb9aSnw * tickets for us so we can authenticate to AD as the machine account 85*78b2cb9aSnw * that we are. For more details look at the entry point in mech_krb5 86*78b2cb9aSnw * corresponding to gss_init_sec_context(). 87*78b2cb9aSnw * 88*78b2cb9aSnw * As a side effect of faking our effective UID to mech_krb5 we will use 89*78b2cb9aSnw * root's default ccache (/tmp/krb5cc_0). But if that's created by 90*78b2cb9aSnw * another process then we won't have access to it: we run as daemon and 91*78b2cb9aSnw * keep PRIV_FILE_DAC_READ, which is insufficient to share the ccache 92*78b2cb9aSnw * with others. We putenv("KRB5CCNAME=/var/run/idmap/ccache") in main() 93*78b2cb9aSnw * to avoid this issue; see main(). 94c5c4113dSnw * 95c5c4113dSnw * Someday we'll have gss/mech_krb5 extensions for acquiring initiator 96c5c4113dSnw * creds with keytabs/raw keys, and someday we'll have extensions to 97c5c4113dSnw * libsasl to specify creds/name to use on the initiator side, and 98c5c4113dSnw * someday we'll have extensions to libldap to pass those through to 99c5c4113dSnw * libsasl. Until then this interposer will have to do. 100c5c4113dSnw * 101c5c4113dSnw * Also, we have to tell lint to shut up: it thinks app_krb5_user_uid() 102c5c4113dSnw * is defined but not used. 103c5c4113dSnw */ 104c5c4113dSnw /*LINTLIBRARY*/ 105c5c4113dSnw uid_t 106c5c4113dSnw app_krb5_user_uid(void) 107c5c4113dSnw { 108c5c4113dSnw return (0); 109c5c4113dSnw } 110c5c4113dSnw 111c5c4113dSnw /*ARGSUSED*/ 112c5c4113dSnw static void 113c5c4113dSnw hup_handler(int sig) { 114c5c4113dSnw (void) idmapdlog(LOG_INFO, "idmapd: Refreshing config."); 115c5c4113dSnw WRLOCK_CONFIG(); 116c5c4113dSnw (void) idmap_cfg_fini(_idmapdstate.cfg); 117c5c4113dSnw _idmapdstate.cfg = NULL; 118c5c4113dSnw if (load_config() < 0) { 119c5c4113dSnw UNLOCK_CONFIG(); 120c5c4113dSnw (void) idmapdlog(LOG_NOTICE, 121c5c4113dSnw "idmapd: Failed to reload config"); 122c5c4113dSnw term_handler(sig); 123c5c4113dSnw } 124c5c4113dSnw UNLOCK_CONFIG(); 125c5c4113dSnw print_idmapdstate(); 126c5c4113dSnw } 127c5c4113dSnw 128c5c4113dSnw /*ARGSUSED*/ 129c5c4113dSnw static void 130c5c4113dSnw term_handler(int sig) { 131c5c4113dSnw (void) idmapdlog(LOG_INFO, "idmapd: Terminating."); 132c5c4113dSnw fini_idmapd(); 133c5c4113dSnw _exit(0); 134c5c4113dSnw } 135c5c4113dSnw 136c5c4113dSnw static int pipe_fd = -1; 137c5c4113dSnw 138c5c4113dSnw static void 139c5c4113dSnw daemonize_ready(void) { 140c5c4113dSnw char data = '\0'; 141c5c4113dSnw /* 142c5c4113dSnw * wake the parent 143c5c4113dSnw */ 144c5c4113dSnw (void) write(pipe_fd, &data, 1); 145c5c4113dSnw (void) close(pipe_fd); 146c5c4113dSnw } 147c5c4113dSnw 148c5c4113dSnw static int 149c5c4113dSnw daemonize_start(void) { 150c5c4113dSnw char data; 151c5c4113dSnw int status; 152c5c4113dSnw int devnull; 153c5c4113dSnw int filedes[2]; 154c5c4113dSnw pid_t pid; 155c5c4113dSnw 156*78b2cb9aSnw (void) sigset(SIGPIPE, SIG_IGN); 157c5c4113dSnw devnull = open("/dev/null", O_RDONLY); 158c5c4113dSnw if (devnull < 0) 159c5c4113dSnw return (-1); 160c5c4113dSnw (void) dup2(devnull, 0); 161c5c4113dSnw (void) dup2(2, 1); /* stderr only */ 162c5c4113dSnw if (pipe(filedes) < 0) 163c5c4113dSnw return (-1); 164c5c4113dSnw if ((pid = fork1()) < 0) 165c5c4113dSnw return (-1); 166c5c4113dSnw if (pid != 0) { 167c5c4113dSnw /* 168c5c4113dSnw * parent 169c5c4113dSnw */ 170c5c4113dSnw (void) close(filedes[1]); 171c5c4113dSnw if (read(filedes[0], &data, 1) == 1) { 172c5c4113dSnw /* presume success */ 173c5c4113dSnw _exit(0); 174c5c4113dSnw } 175c5c4113dSnw status = -1; 176c5c4113dSnw (void) wait4(pid, &status, 0, NULL); 177c5c4113dSnw if (WIFEXITED(status)) 178c5c4113dSnw _exit(WEXITSTATUS(status)); 179c5c4113dSnw else 180c5c4113dSnw _exit(-1); 181c5c4113dSnw } 182c5c4113dSnw 183c5c4113dSnw /* 184c5c4113dSnw * child 185c5c4113dSnw */ 186c5c4113dSnw pipe_fd = filedes[1]; 187c5c4113dSnw (void) close(filedes[0]); 188c5c4113dSnw (void) setsid(); 189c5c4113dSnw (void) umask(0077); 190c5c4113dSnw openlog("idmap", LOG_PID, LOG_DAEMON); 191c5c4113dSnw _idmapdstate.daemon_mode = TRUE; 192c5c4113dSnw return (0); 193c5c4113dSnw } 194c5c4113dSnw 195c5c4113dSnw 196c5c4113dSnw int 197c5c4113dSnw main(int argc, char **argv) 198c5c4113dSnw { 199c5c4113dSnw int c; 200c5c4113dSnw #ifdef RPC_SVC_FG 201c5c4113dSnw bool_t daemonize = FALSE; 202c5c4113dSnw #else 203c5c4113dSnw bool_t daemonize = TRUE; 204c5c4113dSnw #endif 205c5c4113dSnw 206c5c4113dSnw while ((c = getopt(argc, argv, "d")) != EOF) { 207c5c4113dSnw switch (c) { 208c5c4113dSnw case 'd': 209c5c4113dSnw daemonize = FALSE; 210c5c4113dSnw break; 211c5c4113dSnw default: 212c5c4113dSnw break; 213c5c4113dSnw } 214c5c4113dSnw } 215c5c4113dSnw 216c5c4113dSnw /* set locale and domain for internationalization */ 217c5c4113dSnw (void) setlocale(LC_ALL, ""); 218c5c4113dSnw (void) textdomain(TEXT_DOMAIN); 219c5c4113dSnw 220*78b2cb9aSnw if (getzoneid() != GLOBAL_ZONEID) { 221c5c4113dSnw (void) idmapdlog(LOG_ERR, 222*78b2cb9aSnw "idmapd: idmapd runs only in the global zone"); 223c5c4113dSnw exit(1); 224c5c4113dSnw } 225c5c4113dSnw 226c5c4113dSnw (void) mutex_init(&_svcstate_lock, USYNC_THREAD, NULL); 227c5c4113dSnw 228c5c4113dSnw if (daemonize == TRUE) { 229c5c4113dSnw if (daemonize_start() < 0) { 230c5c4113dSnw (void) perror("idmapd: unable to daemonize"); 231c5c4113dSnw exit(-1); 232c5c4113dSnw } 233c5c4113dSnw } else 234c5c4113dSnw (void) umask(0077); 235c5c4113dSnw 23684decf41Sjp idmap_init_tsd_key(); 23784decf41Sjp 238c5c4113dSnw init_idmapd(); 239c5c4113dSnw 240*78b2cb9aSnw /* signal handlers that should run only after we're initialized */ 241*78b2cb9aSnw (void) sigset(SIGTERM, term_handler); 242*78b2cb9aSnw (void) sigset(SIGHUP, hup_handler); 243*78b2cb9aSnw 244c5c4113dSnw if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 245c5c4113dSnw DAEMON_UID, DAEMON_GID, 246c5c4113dSnw PRIV_PROC_AUDIT, PRIV_FILE_DAC_READ, 247c5c4113dSnw (char *)NULL) == -1) { 248651c0131Sbaban (void) idmapdlog(LOG_ERR, "idmapd: unable to drop privileges"); 249c5c4113dSnw exit(1); 250c5c4113dSnw } 251c5c4113dSnw 252c5c4113dSnw __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION, 253c5c4113dSnw PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL); 254c5c4113dSnw 255c5c4113dSnw if (daemonize == TRUE) 256c5c4113dSnw daemonize_ready(); 257c5c4113dSnw 258c5c4113dSnw /* With doors RPC this just wastes this thread, oh well */ 259c5c4113dSnw svc_run(); 260c5c4113dSnw return (0); 261c5c4113dSnw } 262c5c4113dSnw 263c5c4113dSnw static void 264c5c4113dSnw init_idmapd() { 265c5c4113dSnw int error; 266c5c4113dSnw 267*78b2cb9aSnw /* create directories as root and chown to daemon uid */ 268*78b2cb9aSnw if (create_directory(IDMAP_DBDIR, DAEMON_UID, DAEMON_GID) < 0) 269*78b2cb9aSnw exit(1); 270*78b2cb9aSnw if (create_directory(IDMAP_CACHEDIR, DAEMON_UID, DAEMON_GID) < 0) 271*78b2cb9aSnw exit(1); 272*78b2cb9aSnw 273*78b2cb9aSnw /* 274*78b2cb9aSnw * Set KRB5CCNAME in the environment. See app_krb5_user_uid() 275*78b2cb9aSnw * for more details. 276*78b2cb9aSnw */ 277*78b2cb9aSnw putenv("KRB5CCNAME=" IDMAP_CACHEDIR "/ccache"); 278*78b2cb9aSnw 279c5c4113dSnw memset(&_idmapdstate, 0, sizeof (_idmapdstate)); 280c5c4113dSnw 281c5c4113dSnw if (sysinfo(SI_HOSTNAME, _idmapdstate.hostname, 282c5c4113dSnw sizeof (_idmapdstate.hostname)) == -1) { 283c5c4113dSnw error = errno; 284c5c4113dSnw idmapdlog(LOG_ERR, 285c5c4113dSnw "idmapd: unable to determine hostname, error: %d", 286c5c4113dSnw error); 287c5c4113dSnw exit(1); 288c5c4113dSnw } 289c5c4113dSnw 290c5c4113dSnw if (sysinfo(SI_SRPC_DOMAIN, _idmapdstate.domainname, 291c5c4113dSnw sizeof (_idmapdstate.domainname)) == -1) { 292c5c4113dSnw error = errno; 293c5c4113dSnw idmapdlog(LOG_ERR, 294c5c4113dSnw "idmapd: unable to determine name service domain, error: %d", 295c5c4113dSnw error); 296c5c4113dSnw exit(1); 297c5c4113dSnw } 298c5c4113dSnw 299c5c4113dSnw if (init_mapping_system() < 0) { 300c5c4113dSnw idmapdlog(LOG_ERR, 301c5c4113dSnw "idmapd: unable to initialize mapping system"); 302c5c4113dSnw exit(1); 303c5c4113dSnw } 304c5c4113dSnw 305c5c4113dSnw xprt = svc_door_create(idmap_prog_1, IDMAP_PROG, IDMAP_V1, 0); 306c5c4113dSnw if (xprt == NULL) { 307c5c4113dSnw idmapdlog(LOG_ERR, 308c5c4113dSnw "idmapd: unable to create door RPC service"); 309c5c4113dSnw goto errout; 310c5c4113dSnw } 311c5c4113dSnw 312c5c4113dSnw dfd = xprt->xp_fd; 313c5c4113dSnw 314c5c4113dSnw if (dfd == -1) { 315c5c4113dSnw idmapdlog(LOG_ERR, "idmapd: unable to register door"); 316c5c4113dSnw goto errout; 317c5c4113dSnw } 318c5c4113dSnw if ((error = idmap_reg(dfd)) != 0) { 319c5c4113dSnw idmapdlog(LOG_ERR, "idmapd: unable to register door (%s)", 320c5c4113dSnw strerror(error)); 321c5c4113dSnw goto errout; 322c5c4113dSnw } 323c5c4113dSnw 324c5c4113dSnw if ((error = allocids(_idmapdstate.new_eph_db, 325c5c4113dSnw 8192, &_idmapdstate.next_uid, 326c5c4113dSnw 8192, &_idmapdstate.next_gid)) != 0) { 327c5c4113dSnw idmapdlog(LOG_ERR, "idmapd: unable to allocate ephemeral IDs " 328c5c4113dSnw "(%s)", strerror(error)); 329c5c4113dSnw _idmapdstate.next_uid = _idmapdstate.limit_uid = SENTINEL_PID; 330c5c4113dSnw _idmapdstate.next_gid = _idmapdstate.limit_gid = SENTINEL_PID; 331c5c4113dSnw } else { 332c5c4113dSnw _idmapdstate.limit_uid = _idmapdstate.next_uid + 8192; 333c5c4113dSnw _idmapdstate.limit_gid = _idmapdstate.next_gid + 8192; 334c5c4113dSnw } 335c5c4113dSnw 336c5c4113dSnw print_idmapdstate(); 337c5c4113dSnw 338c5c4113dSnw return; 339c5c4113dSnw 340c5c4113dSnw errout: 341c5c4113dSnw fini_idmapd(); 342c5c4113dSnw exit(1); 343c5c4113dSnw } 344c5c4113dSnw 345c5c4113dSnw static void 346c5c4113dSnw fini_idmapd() { 347c5c4113dSnw idmap_unreg(dfd); 348c5c4113dSnw fini_mapping_system(); 349c5c4113dSnw if (xprt != NULL) 350c5c4113dSnw svc_destroy(xprt); 351c5c4113dSnw } 352c5c4113dSnw 353c5c4113dSnw void 354c5c4113dSnw idmapdlog(int pri, const char *format, ...) { 355c5c4113dSnw va_list args; 356c5c4113dSnw 357c5c4113dSnw va_start(args, format); 358c5c4113dSnw if (_idmapdstate.daemon_mode == FALSE) { 359c5c4113dSnw (void) vfprintf(stderr, format, args); 360c5c4113dSnw (void) fprintf(stderr, "\n"); 361c5c4113dSnw } 362c5c4113dSnw (void) vsyslog(pri, format, args); 363c5c4113dSnw va_end(args); 364c5c4113dSnw } 365