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