xref: /illumos-gate/usr/src/cmd/idmap/idmapd/idmapd.c (revision d840867f3a8b0ba209ef90762b3f9c72a5f92cc5)
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.
84  *
85  * Someday we'll have gss/mech_krb5 extensions for acquiring initiator
86  * creds with keytabs/raw keys, and someday we'll have extensions to
87  * libsasl to specify creds/name to use on the initiator side, and
88  * someday we'll have extensions to libldap to pass those through to
89  * libsasl.  Until then this interposer will have to do.
90  *
91  * Also, we have to tell lint to shut up: it thinks app_krb5_user_uid()
92  * is defined but not used.
93  */
94 /*LINTLIBRARY*/
95 uid_t
96 app_krb5_user_uid(void)
97 {
98 	return (0);
99 }
100 
101 static void
102 set_signal_handlers() {
103 	(void) sigset(SIGPIPE, SIG_IGN);
104 	(void) sigset(SIGHUP, hup_handler);
105 	(void) sigset(SIGTERM, term_handler);
106 }
107 
108 /*ARGSUSED*/
109 static void
110 hup_handler(int sig) {
111 	(void) idmapdlog(LOG_INFO, "idmapd: Refreshing config.");
112 	WRLOCK_CONFIG();
113 	(void) idmap_cfg_fini(_idmapdstate.cfg);
114 	_idmapdstate.cfg = NULL;
115 	if (load_config() < 0) {
116 		UNLOCK_CONFIG();
117 		(void) idmapdlog(LOG_NOTICE,
118 			"idmapd: Failed to reload config");
119 		term_handler(sig);
120 	}
121 	UNLOCK_CONFIG();
122 	print_idmapdstate();
123 }
124 
125 /*ARGSUSED*/
126 static void
127 term_handler(int sig) {
128 	(void) idmapdlog(LOG_INFO, "idmapd: Terminating.");
129 	fini_idmapd();
130 	_exit(0);
131 }
132 
133 static int pipe_fd = -1;
134 
135 static void
136 daemonize_ready(void) {
137 	char data = '\0';
138 	/*
139 	 * wake the parent
140 	 */
141 	(void) write(pipe_fd, &data, 1);
142 	(void) close(pipe_fd);
143 }
144 
145 static int
146 daemonize_start(void) {
147 	char	data;
148 	int	status;
149 	int	devnull;
150 	int	filedes[2];
151 	pid_t	pid;
152 
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 		struct sigaction act;
167 		act.sa_sigaction = SIG_DFL;
168 		(void) sigemptyset(&act.sa_mask);
169 		act.sa_flags = 0;
170 		(void) sigaction(SIGPIPE, &act, NULL); /* ignore SIGPIPE */
171 		(void) close(filedes[1]);
172 		if (read(filedes[0], &data, 1) == 1) {
173 			/* presume success */
174 			_exit(0);
175 		}
176 		status = -1;
177 		(void) wait4(pid, &status, 0, NULL);
178 		if (WIFEXITED(status))
179 			_exit(WEXITSTATUS(status));
180 		else
181 			_exit(-1);
182 	}
183 
184 	/*
185 	 * child
186 	 */
187 	pipe_fd = filedes[1];
188 	(void) close(filedes[0]);
189 	(void) setsid();
190 	(void) umask(0077);
191 	openlog("idmap", LOG_PID, LOG_DAEMON);
192 	_idmapdstate.daemon_mode = TRUE;
193 	return (0);
194 }
195 
196 
197 int
198 main(int argc, char **argv)
199 {
200 	int c;
201 #ifdef RPC_SVC_FG
202 	bool_t daemonize = FALSE;
203 #else
204 	bool_t daemonize = TRUE;
205 #endif
206 
207 	while ((c = getopt(argc, argv, "d")) != EOF) {
208 		switch (c) {
209 			case 'd':
210 				daemonize = FALSE;
211 				break;
212 			default:
213 				break;
214 		}
215 	}
216 
217 	/* set locale and domain for internationalization */
218 	(void) setlocale(LC_ALL, "");
219 	(void) textdomain(TEXT_DOMAIN);
220 
221 	if (is_system_labeled() && (getzoneid() != GLOBAL_ZONEID)) {
222 		(void) idmapdlog(LOG_ERR,
223 		    "idmapd: With TX, idmapd runs only in the global zone");
224 		exit(1);
225 	}
226 
227 	/* create directories as root and chown to daemon uid */
228 	if (create_directory(IDMAP_DBDIR, DAEMON_UID, DAEMON_GID) < 0)
229 		exit(1);
230 	if (create_directory(IDMAP_CACHEDIR, DAEMON_UID, DAEMON_GID) < 0)
231 		exit(1);
232 
233 	INIT_IDMAPD_STATE();
234 
235 	(void) mutex_init(&_svcstate_lock, USYNC_THREAD, NULL);
236 	set_signal_handlers();
237 
238 	if (daemonize == TRUE) {
239 		if (daemonize_start() < 0) {
240 			(void) perror("idmapd: unable to daemonize");
241 			exit(-1);
242 		}
243 	} else
244 		(void) umask(0077);
245 
246 	init_idmapd();
247 
248 	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET,
249 	    DAEMON_UID, DAEMON_GID,
250 	    PRIV_PROC_AUDIT, PRIV_FILE_DAC_READ,
251 	    (char *)NULL) == -1) {
252 		(void) idmapdlog(LOG_ERR, "idmapd: unable to drop privileges");
253 		exit(1);
254 	}
255 
256 	__fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
257 	    PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL);
258 
259 	if (daemonize == TRUE)
260 		daemonize_ready();
261 
262 	/* With doors RPC this just wastes this thread, oh well */
263 	svc_run();
264 	return (0);
265 }
266 
267 static void
268 init_idmapd() {
269 	int	error;
270 
271 	memset(&_idmapdstate, 0, sizeof (_idmapdstate));
272 
273 	if (sysinfo(SI_HOSTNAME, _idmapdstate.hostname,
274 			sizeof (_idmapdstate.hostname)) == -1) {
275 		error = errno;
276 		idmapdlog(LOG_ERR,
277 	"idmapd: unable to determine hostname, error: %d",
278 			error);
279 		exit(1);
280 	}
281 
282 	if (sysinfo(SI_SRPC_DOMAIN, _idmapdstate.domainname,
283 			sizeof (_idmapdstate.domainname)) == -1) {
284 		error = errno;
285 		idmapdlog(LOG_ERR,
286 	"idmapd: unable to determine name service domain, error: %d",
287 			error);
288 		exit(1);
289 	}
290 
291 	if (init_mapping_system() < 0) {
292 		idmapdlog(LOG_ERR,
293 		"idmapd: unable to initialize mapping system");
294 		exit(1);
295 	}
296 
297 	xprt = svc_door_create(idmap_prog_1, IDMAP_PROG, IDMAP_V1, 0);
298 	if (xprt == NULL) {
299 		idmapdlog(LOG_ERR,
300 		"idmapd: unable to create door RPC service");
301 		goto errout;
302 	}
303 
304 	dfd = xprt->xp_fd;
305 
306 	if (dfd == -1) {
307 		idmapdlog(LOG_ERR, "idmapd: unable to register door");
308 		goto errout;
309 	}
310 	if ((error = idmap_reg(dfd)) != 0) {
311 		idmapdlog(LOG_ERR, "idmapd: unable to register door (%s)",
312 				strerror(error));
313 		goto errout;
314 	}
315 
316 	if ((error = allocids(_idmapdstate.new_eph_db,
317 			8192, &_idmapdstate.next_uid,
318 			8192, &_idmapdstate.next_gid)) != 0) {
319 		idmapdlog(LOG_ERR, "idmapd: unable to allocate ephemeral IDs "
320 			"(%s)", strerror(error));
321 		_idmapdstate.next_uid = _idmapdstate.limit_uid = SENTINEL_PID;
322 		_idmapdstate.next_gid = _idmapdstate.limit_gid = SENTINEL_PID;
323 	} else {
324 		_idmapdstate.limit_uid = _idmapdstate.next_uid + 8192;
325 		_idmapdstate.limit_gid = _idmapdstate.next_gid + 8192;
326 	}
327 
328 	print_idmapdstate();
329 
330 	return;
331 
332 errout:
333 	fini_idmapd();
334 	exit(1);
335 }
336 
337 static void
338 fini_idmapd() {
339 	idmap_unreg(dfd);
340 	fini_mapping_system();
341 	if (xprt != NULL)
342 		svc_destroy(xprt);
343 }
344 
345 void
346 idmapdlog(int pri, const char *format, ...) {
347 	va_list args;
348 
349 	va_start(args, format);
350 	if (_idmapdstate.daemon_mode == FALSE) {
351 		(void) vfprintf(stderr, format, args);
352 		(void) fprintf(stderr, "\n");
353 	}
354 	(void) vsyslog(pri, format, args);
355 	va_end(args);
356 }
357