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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <stropts.h>
32 #include <signal.h>
33 #include <fcntl.h>
34 #include <door.h>
35 #include <priv_utils.h>
36 #include <locale.h>
37 #include <strings.h>
38 #include <syslog.h>
39 #include <unistd.h>
40 #include <nfs/nfs4.h>
41 #include <nfs/nfsid_map.h>
42 #include <rpcsvc/daemon_utils.h>
43 #include <arpa/nameser.h>
44 #include <nfs/nfssys.h>
45 #include <errno.h>
46 #include <pwd.h>
47 #include <grp.h>
48 #include "nfsmapid_resolv.h"
49 
50 extern struct group *_uncached_getgrgid_r(gid_t, struct group *, char *, int);
51 extern struct group *_uncached_getgrnam_r(const char *, struct group *,
52     char *, int);
53 extern struct passwd *_uncached_getpwuid_r(uid_t, struct passwd *, char *, int);
54 extern struct passwd *_uncached_getpwnam_r(const char *, struct passwd *,
55     char *, int);
56 
57 /*
58  * seconds to cache nfsmapid domain info
59  */
60 #define	NFSCFG_DEFAULT_DOMAIN_TMOUT	(5 * 60)
61 #define	NFSMAPID_DOOR   "/var/run/nfsmapid_door"
62 
63 extern void	nfsmapid_func(void *, char *, size_t, door_desc_t *, uint_t);
64 
65 extern void	check_domain(int);
66 extern void	idmap_kcall(int);
67 extern rwlock_t domain_cfg_lock;
68 extern void	open_diag_file(void);
69 
70 size_t		pwd_buflen = 0;
71 size_t		grp_buflen = 0;
72 thread_t	sig_thread;
73 static char	*MyName;
74 
75 /*
76  * nfscfg_domain_tmout is used by nfsv4-test scripts to query
77  * the nfsmapid daemon for the proper timeout. Don't delete !
78  */
79 time_t		 nfscfg_domain_tmout = NFSCFG_DEFAULT_DOMAIN_TMOUT;
80 
81 /*
82  * Processing for daemonization
83  */
84 static void
85 daemonize(void)
86 {
87 	switch (fork()) {
88 		case -1:
89 			perror("nfsmapid: can't fork");
90 			exit(2);
91 			/* NOTREACHED */
92 		case 0:		/* child */
93 			break;
94 
95 		default:	/* parent */
96 			_exit(0);
97 	}
98 
99 	if (chdir("/") < 0)
100 		syslog(LOG_ERR, gettext("chdir /: %m"));
101 
102 	/*
103 	 * Close stdin, stdout, and stderr.
104 	 * Open again to redirect input+output
105 	 */
106 	(void) close(0);
107 	(void) close(1);
108 	(void) close(2);
109 	(void) open("/dev/null", O_RDONLY);
110 	(void) open("/dev/null", O_WRONLY);
111 	(void) dup(1);
112 	(void) setsid();
113 }
114 
115 /* ARGSUSED */
116 static void *
117 sig_handler(void *arg)
118 {
119 	siginfo_t	si;
120 	sigset_t	sigset;
121 	struct timespec	tmout;
122 	int		ret;
123 
124 	tmout.tv_sec = nfscfg_domain_tmout;
125 	tmout.tv_nsec = 0;
126 	(void) sigemptyset(&sigset);
127 	(void) sigaddset(&sigset, SIGHUP);
128 	(void) sigaddset(&sigset, SIGTERM);
129 #ifdef	DEBUG
130 	(void) sigaddset(&sigset, SIGINT);
131 #endif
132 	IDMAP_DBG("sig_handler started !", NULL, NULL);
133 
134 	/*CONSTCOND*/
135 	while (1) {
136 		if ((ret = sigtimedwait(&sigset, &si, &tmout)) != 0) {
137 			/*
138 			 * EAGAIN: no signals arrived during timeout.
139 			 * check/update config files and continue.
140 			 */
141 			if (ret == -1 && errno == EAGAIN) {
142 				check_domain(0);
143 				continue;
144 			}
145 
146 			switch (si.si_signo) {
147 				case SIGHUP:
148 					check_domain(1);
149 					break;
150 #ifdef DEBUG
151 				case SIGINT:
152 					exit(0);
153 #endif
154 				case SIGTERM:
155 				default:
156 					exit(si.si_signo);
157 			}
158 		}
159 	}
160 	/*NOTREACHED*/
161 	return (NULL);
162 }
163 
164 /*
165  * Thread initialization. Mask out all signals we want our
166  * signal handler to handle for us from any other threads.
167  */
168 static void
169 thr_init(void)
170 {
171 	sigset_t sigset;
172 	long	 thr_flags = (THR_NEW_LWP|THR_DAEMON|THR_SUSPENDED);
173 
174 	/*
175 	 * Before we kick off any other threads, mask out desired
176 	 * signals from main thread so that any subsequent threads
177 	 * don't receive said signals.
178 	 */
179 	(void) thr_sigsetmask(NULL, NULL, &sigset);
180 	(void) sigaddset(&sigset, SIGHUP);
181 	(void) sigaddset(&sigset, SIGTERM);
182 #ifdef	DEBUG
183 	(void) sigaddset(&sigset, SIGINT);
184 #endif
185 	(void) thr_sigsetmask(SIG_SETMASK, &sigset, NULL);
186 
187 	/*
188 	 * Create the signal handler thread suspended ! We do things
189 	 * this way at setup time to minimize the probability of
190 	 * introducing any race conditions _if_ the process were to
191 	 * get a SIGHUP signal while creating a new DNS query thread
192 	 * in get_dns_txt_domain().
193 	 */
194 	if (thr_create(NULL, 0, sig_handler, 0, thr_flags, &sig_thread)) {
195 		syslog(LOG_ERR,
196 			gettext("Failed to create signal handling thread"));
197 		exit(4);
198 	}
199 }
200 
201 static void
202 daemon_init(void)
203 {
204 	struct passwd pwd;
205 	struct group grp;
206 	char *pwd_buf;
207 	char *grp_buf;
208 
209 	/*
210 	 * Initialize resolver
211 	 */
212 	(void) resolv_init();
213 
214 	/*
215 	 * passwd/group reentrant interfaces limits
216 	 */
217 	pwd_buflen = (size_t)sysconf(_SC_GETPW_R_SIZE_MAX);
218 	grp_buflen = (size_t)sysconf(_SC_GETGR_R_SIZE_MAX);
219 
220 	/*
221 	 * Initialize lock
222 	 */
223 	(void) rwlock_init(&domain_cfg_lock, USYNC_THREAD, NULL);
224 
225 	/*
226 	 * MT initialization is done first so that if there is the
227 	 * need to fire an additional thread to continue to query
228 	 * DNS, that thread is started off with the main thread's
229 	 * sigmask.
230 	 */
231 	thr_init();
232 
233 	/*
234 	 * Determine nfsmapid domain.
235 	 */
236 	check_domain(0);
237 
238 	/*
239 	 * In the case of nfsmapid running diskless, it is important
240 	 * to get the initial connections to the nameservices
241 	 * established to prevent problems like opening a devfs
242 	 * node to contact a nameservice being blocked by the
243 	 * resolution of an active devfs lookup.
244 	 * First issue a set*ent to "open" the databases and then
245 	 * get an entry and finally lookup a bogus entry to trigger
246 	 * any lazy opens.
247 	 */
248 	setpwent();
249 	setgrent();
250 	(void) getpwent();
251 	(void) getgrent();
252 	if ((pwd_buf = malloc(pwd_buflen)) == NULL)
253 		return;
254 
255 	(void) _uncached_getpwnam_r("NF21dmvP", &pwd, pwd_buf, pwd_buflen);
256 	(void) _uncached_getpwuid_r(1181794, &pwd, pwd_buf, pwd_buflen);
257 
258 	if ((grp_buf = realloc(pwd_buf, grp_buflen)) == NULL) {
259 		free(pwd_buf);
260 		return;
261 	}
262 
263 	(void) _uncached_getgrnam_r("NF21dmvP", &grp, grp_buf, grp_buflen);
264 	(void) _uncached_getgrgid_r(1181794, &grp, grp_buf, grp_buflen);
265 	free(grp_buf);
266 }
267 
268 static int
269 start_svcs(void)
270 {
271 	int doorfd = -1;
272 #ifdef DEBUG
273 	int dfd;
274 #endif
275 
276 	if ((doorfd = door_create(nfsmapid_func, NULL,
277 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
278 		syslog(LOG_ERR, "Unable to create door: %m\n");
279 		return (1);
280 	}
281 
282 #ifdef DEBUG
283 	/*
284 	 * Create a file system path for the door
285 	 */
286 	if ((dfd = open(NFSMAPID_DOOR, O_RDWR|O_CREAT|O_TRUNC,
287 				S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
288 		syslog(LOG_ERR, "Unable to open %s: %m\n", NFSMAPID_DOOR);
289 		(void) close(doorfd);
290 		return (1);
291 	}
292 
293 	/*
294 	 * Clean up any stale associations
295 	 */
296 	(void) fdetach(NFSMAPID_DOOR);
297 
298 	/*
299 	 * Register in namespace to pass to the kernel to door_ki_open
300 	 */
301 	if (fattach(doorfd, NFSMAPID_DOOR) == -1) {
302 		syslog(LOG_ERR, "Unable to fattach door: %m\n");
303 		(void) close(dfd);
304 		(void) close(doorfd);
305 		return (1);
306 	}
307 	(void) close(dfd);
308 #endif
309 
310 	/*
311 	 * Now that we're actually running, go
312 	 * ahead and flush the kernel flushes
313 	 * Pass door name to kernel for door_ki_open
314 	 */
315 	idmap_kcall(doorfd);
316 
317 	/*
318 	 * Wait for incoming calls
319 	 */
320 	/*CONSTCOND*/
321 	while (1)
322 		(void) pause();
323 
324 	syslog(LOG_ERR, gettext("Door server exited"));
325 	return (10);
326 }
327 
328 /* ARGSUSED */
329 int
330 main(int argc, char **argv)
331 {
332 	MyName = argv[0];
333 
334 	(void) setlocale(LC_ALL, "");
335 	(void) textdomain(TEXT_DOMAIN);
336 
337 	/* _check_services() framework setup */
338 	(void) _create_daemon_lock(NFSMAPID, DAEMON_UID, DAEMON_GID);
339 
340 	/*
341 	 * Open diag file in /var/run while we've got the perms
342 	 */
343 	open_diag_file();
344 
345 	/*
346 	 * Initialize the daemon to basic + sys_nfs
347 	 */
348 	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET,
349 	    DAEMON_UID, DAEMON_GID, PRIV_SYS_NFS, (char *)NULL) == -1) {
350 		(void) fprintf(stderr, gettext("%s PRIV_SYS_NFS privilege "
351 			"missing\n"), MyName);
352 		exit(1);
353 	}
354 
355 	/*
356 	 * Take away a subset of basic, while this is not the absolute
357 	 * minimum, it is important that it is unique among other
358 	 * daemons to insure that we get a unique cred that will
359 	 * result in a unique open_owner.  If not, we run the risk
360 	 * of a diskless client deadlocking with a thread holding
361 	 * the open_owner seqid lock while upcalling the daemon.
362 	 * XXX This restriction will go away once we stop holding
363 	 * XXX open_owner lock across rfscalls!
364 	 */
365 	(void) priv_set(PRIV_OFF, PRIV_PERMITTED,
366 		PRIV_FILE_LINK_ANY, PRIV_PROC_SESSION,
367 		(char *)NULL);
368 
369 #ifndef DEBUG
370 	daemonize();
371 	switch (_enter_daemon_lock(NFSMAPID)) {
372 		case 0:
373 			break;
374 
375 		case -1:
376 			syslog(LOG_ERR, "error locking for %s: %s", NFSMAPID,
377 			    strerror(errno));
378 			exit(3);
379 
380 		default:
381 			/* daemon was already running */
382 			exit(0);
383 	}
384 #endif
385 	openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON);
386 
387 	/* Initialize daemon subsystems */
388 	daemon_init();
389 
390 	/* start services */
391 	return (start_svcs());
392 }
393