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