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 <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 (is_system_labeled() && getzoneid() != GLOBAL_ZONEID) { 217 (void) idmapdlog(LOG_ERR, 218 "idmapd: with Trusted Extensions idmapd runs only in the " 219 "global zone"); 220 exit(1); 221 } 222 223 (void) mutex_init(&_svcstate_lock, USYNC_THREAD, NULL); 224 225 if (daemonize == TRUE) { 226 if (daemonize_start() < 0) { 227 (void) perror("idmapd: unable to daemonize"); 228 exit(-1); 229 } 230 } else 231 (void) umask(0077); 232 233 idmap_init_tsd_key(); 234 235 init_idmapd(); 236 237 /* signal handlers that should run only after we're initialized */ 238 (void) sigset(SIGTERM, term_handler); 239 (void) sigset(SIGHUP, hup_handler); 240 241 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 242 DAEMON_UID, DAEMON_GID, 243 PRIV_PROC_AUDIT, PRIV_FILE_DAC_READ, 244 (char *)NULL) == -1) { 245 (void) idmapdlog(LOG_ERR, "idmapd: unable to drop privileges"); 246 exit(1); 247 } 248 249 __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION, 250 PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL); 251 252 if (daemonize == TRUE) 253 daemonize_ready(); 254 255 /* With doors RPC this just wastes this thread, oh well */ 256 svc_run(); 257 return (0); 258 } 259 260 static void 261 init_idmapd() { 262 int error; 263 int connmaxrec = IDMAP_MAX_DOOR_RPC; 264 265 /* create directories as root and chown to daemon uid */ 266 if (create_directory(IDMAP_DBDIR, DAEMON_UID, DAEMON_GID) < 0) 267 exit(1); 268 if (create_directory(IDMAP_CACHEDIR, DAEMON_UID, DAEMON_GID) < 0) 269 exit(1); 270 271 /* 272 * Set KRB5CCNAME in the environment. See app_krb5_user_uid() 273 * for more details. We blow away the existing one, if there is 274 * one. 275 */ 276 (void) unlink(IDMAP_CACHEDIR "/ccache"); 277 putenv("KRB5CCNAME=" IDMAP_CACHEDIR "/ccache"); 278 279 memset(&_idmapdstate, 0, sizeof (_idmapdstate)); 280 281 if (sysinfo(SI_HOSTNAME, _idmapdstate.hostname, 282 sizeof (_idmapdstate.hostname)) == -1) { 283 error = errno; 284 idmapdlog(LOG_ERR, 285 "idmapd: unable to determine hostname, error: %d", 286 error); 287 exit(1); 288 } 289 290 if ((error = init_mapping_system()) < 0) { 291 idmapdlog(LOG_ERR, 292 "idmapd: unable to initialize mapping system"); 293 exit(error < -2 ? SMF_EXIT_ERR_CONFIG : 1); 294 } 295 296 xprt = svc_door_create(idmap_prog_1, IDMAP_PROG, IDMAP_V1, connmaxrec); 297 if (xprt == NULL) { 298 idmapdlog(LOG_ERR, 299 "idmapd: unable to create door RPC service"); 300 goto errout; 301 } 302 303 if (!svc_control(xprt, SVCSET_CONNMAXREC, &connmaxrec)) { 304 idmapdlog(LOG_ERR, 305 "idmapd: unable to limit RPC request size"); 306 goto errout; 307 } 308 309 dfd = xprt->xp_fd; 310 311 if (dfd == -1) { 312 idmapdlog(LOG_ERR, "idmapd: unable to register door"); 313 goto errout; 314 } 315 if ((error = idmap_reg(dfd)) != 0) { 316 idmapdlog(LOG_ERR, "idmapd: unable to register door (%s)", 317 strerror(errno)); 318 goto errout; 319 } 320 321 if ((error = allocids(_idmapdstate.new_eph_db, 322 8192, &_idmapdstate.next_uid, 323 8192, &_idmapdstate.next_gid)) != 0) { 324 idmapdlog(LOG_ERR, "idmapd: unable to allocate ephemeral IDs " 325 "(%s)", strerror(errno)); 326 _idmapdstate.next_uid = _idmapdstate.limit_uid = SENTINEL_PID; 327 _idmapdstate.next_gid = _idmapdstate.limit_gid = SENTINEL_PID; 328 } else { 329 _idmapdstate.limit_uid = _idmapdstate.next_uid + 8192; 330 _idmapdstate.limit_gid = _idmapdstate.next_gid + 8192; 331 } 332 333 print_idmapdstate(); 334 335 return; 336 337 errout: 338 fini_idmapd(); 339 exit(1); 340 } 341 342 static void 343 fini_idmapd() { 344 idmap_unreg(dfd); 345 fini_mapping_system(); 346 if (xprt != NULL) 347 svc_destroy(xprt); 348 } 349 350 void 351 idmapdlog(int pri, const char *format, ...) { 352 va_list args; 353 354 va_start(args, format); 355 if (_idmapdstate.daemon_mode == FALSE) { 356 (void) vfprintf(stderr, format, args); 357 (void) fprintf(stderr, "\n"); 358 } 359 (void) vsyslog(pri, format, args); 360 va_end(args); 361 } 362