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 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 term_handler(int sig) 114 { 115 (void) idmapdlog(LOG_INFO, "idmapd: Terminating."); 116 fini_idmapd(); 117 _exit(0); 118 } 119 120 static int pipe_fd = -1; 121 122 static void 123 daemonize_ready(void) 124 { 125 char data = '\0'; 126 /* 127 * wake the parent 128 */ 129 (void) write(pipe_fd, &data, 1); 130 (void) close(pipe_fd); 131 } 132 133 static int 134 daemonize_start(void) 135 { 136 char data; 137 int status; 138 int devnull; 139 int filedes[2]; 140 pid_t pid; 141 142 (void) sigset(SIGPIPE, SIG_IGN); 143 devnull = open("/dev/null", O_RDONLY); 144 if (devnull < 0) 145 return (-1); 146 (void) dup2(devnull, 0); 147 (void) dup2(2, 1); /* stderr only */ 148 if (pipe(filedes) < 0) 149 return (-1); 150 if ((pid = fork1()) < 0) 151 return (-1); 152 if (pid != 0) { 153 /* 154 * parent 155 */ 156 (void) close(filedes[1]); 157 if (read(filedes[0], &data, 1) == 1) { 158 /* presume success */ 159 _exit(0); 160 } 161 status = -1; 162 (void) wait4(pid, &status, 0, NULL); 163 if (WIFEXITED(status)) 164 _exit(WEXITSTATUS(status)); 165 else 166 _exit(-1); 167 } 168 169 /* 170 * child 171 */ 172 pipe_fd = filedes[1]; 173 (void) close(filedes[0]); 174 (void) setsid(); 175 (void) umask(0077); 176 openlog("idmap", LOG_PID, LOG_DAEMON); 177 _idmapdstate.daemon_mode = TRUE; 178 return (0); 179 } 180 181 182 int 183 main(int argc, char **argv) 184 { 185 int c; 186 #ifdef RPC_SVC_FG 187 bool_t daemonize = FALSE; 188 #else 189 bool_t daemonize = TRUE; 190 #endif 191 192 while ((c = getopt(argc, argv, "d")) != EOF) { 193 switch (c) { 194 case 'd': 195 daemonize = FALSE; 196 break; 197 default: 198 break; 199 } 200 } 201 202 /* set locale and domain for internationalization */ 203 (void) setlocale(LC_ALL, ""); 204 (void) textdomain(TEXT_DOMAIN); 205 206 if (is_system_labeled() && getzoneid() != GLOBAL_ZONEID) { 207 (void) idmapdlog(LOG_ERR, 208 "idmapd: with Trusted Extensions idmapd runs only in the " 209 "global zone"); 210 exit(1); 211 } 212 213 (void) mutex_init(&_svcstate_lock, USYNC_THREAD, NULL); 214 215 if (daemonize == TRUE) { 216 if (daemonize_start() < 0) { 217 (void) perror("idmapd: unable to daemonize"); 218 exit(-1); 219 } 220 } else 221 (void) umask(0077); 222 223 idmap_init_tsd_key(); 224 225 init_idmapd(); 226 227 /* signal handlers that should run only after we're initialized */ 228 (void) sigset(SIGTERM, term_handler); 229 (void) sigset(SIGHUP, idmap_cfg_hup_handler); 230 231 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 232 DAEMON_UID, DAEMON_GID, 233 PRIV_PROC_AUDIT, PRIV_FILE_DAC_READ, 234 (char *)NULL) == -1) { 235 (void) idmapdlog(LOG_ERR, "idmapd: unable to drop privileges"); 236 exit(1); 237 } 238 239 __fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION, 240 PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL); 241 242 if (daemonize == TRUE) 243 daemonize_ready(); 244 245 /* With doors RPC this just wastes this thread, oh well */ 246 svc_run(); 247 return (0); 248 } 249 250 static void 251 init_idmapd() 252 { 253 int error; 254 int connmaxrec = IDMAP_MAX_DOOR_RPC; 255 256 /* create directories as root and chown to daemon uid */ 257 if (create_directory(IDMAP_DBDIR, DAEMON_UID, DAEMON_GID) < 0) 258 exit(1); 259 if (create_directory(IDMAP_CACHEDIR, DAEMON_UID, DAEMON_GID) < 0) 260 exit(1); 261 262 /* 263 * Set KRB5CCNAME in the environment. See app_krb5_user_uid() 264 * for more details. We blow away the existing one, if there is 265 * one. 266 */ 267 (void) unlink(IDMAP_CACHEDIR "/ccache"); 268 putenv("KRB5CCNAME=" IDMAP_CACHEDIR "/ccache"); 269 270 memset(&_idmapdstate, 0, sizeof (_idmapdstate)); 271 272 if (sysinfo(SI_HOSTNAME, _idmapdstate.hostname, 273 sizeof (_idmapdstate.hostname)) == -1) { 274 error = errno; 275 idmapdlog(LOG_ERR, 276 "idmapd: unable to determine hostname, error: %d", 277 error); 278 exit(1); 279 } 280 281 if ((error = init_mapping_system()) < 0) { 282 idmapdlog(LOG_ERR, 283 "idmapd: unable to initialize mapping system"); 284 exit(error < -2 ? SMF_EXIT_ERR_CONFIG : 1); 285 } 286 287 xprt = svc_door_create(idmap_prog_1, IDMAP_PROG, IDMAP_V1, connmaxrec); 288 if (xprt == NULL) { 289 idmapdlog(LOG_ERR, 290 "idmapd: unable to create door RPC service"); 291 goto errout; 292 } 293 294 if (!svc_control(xprt, SVCSET_CONNMAXREC, &connmaxrec)) { 295 idmapdlog(LOG_ERR, 296 "idmapd: unable to limit RPC request size"); 297 goto errout; 298 } 299 300 dfd = xprt->xp_fd; 301 302 if (dfd == -1) { 303 idmapdlog(LOG_ERR, "idmapd: unable to register door"); 304 goto errout; 305 } 306 if ((error = idmap_reg(dfd)) != 0) { 307 idmapdlog(LOG_ERR, "idmapd: unable to register door (%s)", 308 strerror(errno)); 309 goto errout; 310 } 311 312 if ((error = allocids(_idmapdstate.new_eph_db, 313 8192, &_idmapdstate.next_uid, 314 8192, &_idmapdstate.next_gid)) != 0) { 315 idmapdlog(LOG_ERR, "idmapd: unable to allocate ephemeral IDs " 316 "(%s)", strerror(errno)); 317 _idmapdstate.next_uid = _idmapdstate.limit_uid = SENTINEL_PID; 318 _idmapdstate.next_gid = _idmapdstate.limit_gid = SENTINEL_PID; 319 } else { 320 _idmapdstate.limit_uid = _idmapdstate.next_uid + 8192; 321 _idmapdstate.limit_gid = _idmapdstate.next_gid + 8192; 322 } 323 324 print_idmapdstate(); 325 326 return; 327 328 errout: 329 fini_idmapd(); 330 exit(1); 331 } 332 333 static void 334 fini_idmapd() 335 { 336 idmap_unreg(dfd); 337 fini_mapping_system(); 338 if (xprt != NULL) 339 svc_destroy(xprt); 340 } 341 342 void 343 idmapdlog(int pri, const char *format, ...) 344 { 345 va_list args; 346 347 va_start(args, format); 348 if (_idmapdstate.daemon_mode == FALSE) { 349 (void) vfprintf(stderr, format, args); 350 (void) fprintf(stderr, "\n"); 351 } 352 (void) vsyslog(pri, format, args); 353 va_end(args); 354 } 355