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