1/*-
2 * Copyright (c) 2009 Rick Macklem, University of Guelph
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 */
27
28#include <sys/param.h>
29#include <sys/errno.h>
30#include <sys/linker.h>
31#include <sys/module.h>
32#include <sys/mount.h>
33#include <sys/socket.h>
34#include <sys/socketvar.h>
35#include <sys/sysctl.h>
36#include <sys/time.h>
37#include <sys/ucred.h>
38#include <sys/vnode.h>
39#include <sys/wait.h>
40
41#include <netinet/in.h>
42
43#include <arpa/inet.h>
44
45#include <nfs/nfssvc.h>
46
47#include <rpc/rpc.h>
48
49#include <fs/nfs/rpcv2.h>
50#include <fs/nfs/nfsproto.h>
51#include <fs/nfs/nfskpiport.h>
52#include <fs/nfs/nfs.h>
53
54#include <ctype.h>
55#include <err.h>
56#include <grp.h>
57#include <netdb.h>
58#include <pwd.h>
59#include <signal.h>
60#include <stdio.h>
61#include <stdlib.h>
62#include <string.h>
63#include <syslog.h>
64#include <unistd.h>
65
66/*
67 * This program loads the password and group databases into the kernel
68 * for NFS V4.
69 */
70
71static void	cleanup_term(int);
72static void	usage(void);
73static void	nfsuserdsrv(struct svc_req *, SVCXPRT *);
74static bool_t	xdr_getid(XDR *, caddr_t);
75static bool_t	xdr_getname(XDR *, caddr_t);
76static bool_t	xdr_retval(XDR *, caddr_t);
77static int	nfsbind_localhost(void);
78
79#define	MAXNAME		1024
80#define	MAXNFSUSERD	20
81#define	DEFNFSUSERD	4
82#define	MAXUSERMAX	100000
83#define	MINUSERMAX	10
84#define	DEFUSERMAX	200
85#define	DEFUSERTIMEOUT	(1 * 60)
86struct info {
87	long	id;
88	long	retval;
89	char	name[MAXNAME + 1];
90};
91
92u_char *dnsname = "default.domain";
93u_char *defaultuser = "nobody";
94uid_t defaultuid = 65534;
95u_char *defaultgroup = "nogroup";
96gid_t defaultgid = 65533;
97int verbose = 0, im_a_server = 0, nfsuserdcnt = -1, forcestart = 0;
98int defusertimeout = DEFUSERTIMEOUT, manage_gids = 0;
99pid_t servers[MAXNFSUSERD];
100static struct sockaddr_storage fromip;
101#ifdef INET6
102static struct in6_addr in6loopback = IN6ADDR_LOOPBACK_INIT;
103#endif
104
105int
106main(int argc, char *argv[])
107{
108	int i, j;
109	int error, fnd_dup, len, mustfreeai = 0, start_uidpos;
110	struct nfsd_idargs nid;
111	struct passwd *pwd;
112	struct group *grp;
113	int sock, one = 1;
114	SVCXPRT *udptransp;
115	struct nfsuserd_args nargs;
116	sigset_t signew;
117	char hostname[MAXHOSTNAMELEN + 1], *cp;
118	struct addrinfo *aip, hints;
119	static uid_t check_dups[MAXUSERMAX];
120	gid_t grps[NGROUPS];
121	int ngroup;
122#ifdef INET
123	struct sockaddr_in *sin;
124#endif
125#ifdef INET6
126	struct sockaddr_in6 *sin6;
127#endif
128	int jailed, s;
129	size_t jailed_size;
130
131	if (modfind("nfscommon") < 0) {
132		/* Not present in kernel, try loading it */
133		if (kldload("nfscommon") < 0 ||
134		    modfind("nfscommon") < 0)
135			errx(1, "Experimental nfs subsystem is not available");
136	}
137
138	/*
139	 * First, figure out what our domain name and Kerberos Realm
140	 * seem to be. Command line args may override these later.
141	 */
142	if (gethostname(hostname, MAXHOSTNAMELEN) == 0) {
143		if ((cp = strchr(hostname, '.')) != NULL &&
144		    *(cp + 1) != '\0') {
145			dnsname = cp + 1;
146		} else {
147			memset((void *)&hints, 0, sizeof (hints));
148			hints.ai_flags = AI_CANONNAME;
149			error = getaddrinfo(hostname, NULL, &hints, &aip);
150			if (error == 0) {
151			    if (aip->ai_canonname != NULL &&
152				(cp = strchr(aip->ai_canonname, '.')) != NULL
153				&& *(cp + 1) != '\0') {
154					dnsname = cp + 1;
155					mustfreeai = 1;
156				} else {
157					freeaddrinfo(aip);
158				}
159			}
160		}
161	}
162
163	/*
164	 * See if this server handles IPv4 or IPv6 and set up the default
165	 * localhost address.
166	 */
167	s = -1;
168#ifdef INET6
169	s = socket(PF_INET6, SOCK_DGRAM, 0);
170	if (s >= 0) {
171		fromip.ss_family = AF_INET6;
172		fromip.ss_len = sizeof(struct sockaddr_in6);
173		sin6 = (struct sockaddr_in6 *)&fromip;
174		sin6->sin6_addr = in6loopback;
175		close(s);
176	}
177#endif	/* INET6 */
178#ifdef INET
179	if (s < 0) {
180		s = socket(PF_INET, SOCK_DGRAM, 0);
181		if (s >= 0) {
182			fromip.ss_family = AF_INET;
183			fromip.ss_len = sizeof(struct sockaddr_in);
184			sin = (struct sockaddr_in *)&fromip;
185			sin->sin_addr.s_addr = htonl(INADDR_LOOPBACK);
186			close(s);
187		}
188	}
189#endif	/* INET */
190	if (s < 0)
191		err(1, "Can't create a inet/inet6 socket");
192
193	nid.nid_usermax = DEFUSERMAX;
194	nid.nid_usertimeout = defusertimeout;
195
196	argc--;
197	argv++;
198	while (argc >= 1) {
199		if (!strcmp(*argv, "-domain")) {
200			if (argc == 1)
201				usage();
202			argc--;
203			argv++;
204			strncpy(hostname, *argv, MAXHOSTNAMELEN);
205			hostname[MAXHOSTNAMELEN] = '\0';
206			dnsname = hostname;
207		} else if (!strcmp(*argv, "-verbose")) {
208			verbose = 1;
209		} else if (!strcmp(*argv, "-force")) {
210			forcestart = 1;
211		} else if (!strcmp(*argv, "-manage-gids")) {
212			manage_gids = 1;
213		} else if (!strcmp(*argv, "-usermax")) {
214			if (argc == 1)
215				usage();
216			argc--;
217			argv++;
218			i = atoi(*argv);
219			if (i < MINUSERMAX || i > MAXUSERMAX) {
220				fprintf(stderr,
221				    "usermax %d out of range %d<->%d\n", i,
222				    MINUSERMAX, MAXUSERMAX);
223				usage();
224			}
225			nid.nid_usermax = i;
226		} else if (!strcmp(*argv, "-usertimeout")) {
227			if (argc == 1)
228				usage();
229			argc--;
230			argv++;
231			i = atoi(*argv);
232			if (i < 0 || i > 100000) {
233				fprintf(stderr,
234				    "usertimeout %d out of range 0<->100000\n",
235				    i);
236				usage();
237			}
238			nid.nid_usertimeout = defusertimeout = i * 60;
239		} else if (nfsuserdcnt == -1) {
240			nfsuserdcnt = atoi(*argv);
241			if (nfsuserdcnt < 1)
242				usage();
243			if (nfsuserdcnt > MAXNFSUSERD) {
244				warnx("nfsuserd count %d; reset to %d",
245				    nfsuserdcnt, DEFNFSUSERD);
246				nfsuserdcnt = DEFNFSUSERD;
247			}
248		} else {
249			usage();
250		}
251		argc--;
252		argv++;
253	}
254	if (nfsuserdcnt < 1)
255		nfsuserdcnt = DEFNFSUSERD;
256
257	/*
258	 * Strip off leading and trailing '.'s in domain name and map
259	 * alphabetics to lower case.
260	 */
261	while (*dnsname == '.')
262		dnsname++;
263	if (*dnsname == '\0')
264		errx(1, "Domain name all '.'");
265	len = strlen(dnsname);
266	cp = dnsname + len - 1;
267	while (*cp == '.') {
268		*cp = '\0';
269		len--;
270		cp--;
271	}
272	for (i = 0; i < len; i++) {
273		if (!isascii(dnsname[i]))
274			errx(1, "Domain name has non-ascii char");
275		if (isupper(dnsname[i]))
276			dnsname[i] = tolower(dnsname[i]);
277	}
278
279	/*
280	 * If the nfsuserd died off ungracefully, this is necessary to
281	 * get them to start again.
282	 */
283	if (forcestart && nfssvc(NFSSVC_NFSUSERDDELPORT, NULL) < 0)
284		errx(1, "Can't do nfssvc() to delete the port");
285
286	if (verbose)
287		fprintf(stderr,
288		    "nfsuserd: domain=%s usermax=%d usertimeout=%d\n",
289		    dnsname, nid.nid_usermax, nid.nid_usertimeout);
290
291	for (i = 0; i < nfsuserdcnt; i++)
292		servers[i] = (pid_t)-1;
293
294	nargs.nuserd_family = fromip.ss_family;
295	/*
296	 * Set up the service port to accept requests via UDP from
297	 * localhost (INADDR_LOOPBACK or IN6ADDR_LOOPBACK_INIT).
298	 */
299	if ((sock = socket(nargs.nuserd_family, SOCK_DGRAM, IPPROTO_UDP)) < 0)
300		err(1, "cannot create udp socket");
301
302	/*
303	 * Not sure what this does, so I'll leave it here for now.
304	 */
305	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
306
307	if ((udptransp = svcudp_create(sock)) == NULL)
308		err(1, "Can't set up socket");
309
310	/*
311	 * By not specifying a protocol, it is linked into the
312	 * dispatch queue, but not registered with portmapper,
313	 * which is just what I want.
314	 */
315	if (!svc_register(udptransp, RPCPROG_NFSUSERD, RPCNFSUSERD_VERS,
316	    nfsuserdsrv, 0))
317		err(1, "Can't register nfsuserd");
318
319	/*
320	 * Tell the kernel what my port# is.
321	 */
322	nargs.nuserd_port = htons(udptransp->xp_port);
323#ifdef DEBUG
324	printf("portnum=0x%x\n", nargs.nuserd_port);
325#else
326	if (nfssvc(NFSSVC_NFSUSERDPORT | NFSSVC_NEWSTRUCT, &nargs) < 0) {
327		if (errno == EPERM) {
328			jailed = 0;
329			jailed_size = sizeof(jailed);
330			sysctlbyname("security.jail.jailed", &jailed,
331			    &jailed_size, NULL, 0);
332			if (jailed != 0) {
333				fprintf(stderr, "Cannot start nfsuserd. "
334				    "allow.nfsd might not be configured\n");
335			} else {
336				fprintf(stderr, "Cannot start nfsuserd "
337				    "when already running.");
338				fprintf(stderr, " If not running, "
339				    "use the -force option.\n");
340			}
341		} else {
342			fprintf(stderr, "Can't do nfssvc() to add port\n");
343		}
344		exit(1);
345	}
346#endif
347
348	pwd = getpwnam(defaultuser);
349	if (pwd)
350		nid.nid_uid = pwd->pw_uid;
351	else
352		nid.nid_uid = defaultuid;
353	grp = getgrnam(defaultgroup);
354	if (grp)
355		nid.nid_gid = grp->gr_gid;
356	else
357		nid.nid_gid = defaultgid;
358	nid.nid_name = dnsname;
359	nid.nid_namelen = strlen(nid.nid_name);
360	nid.nid_ngroup = 0;
361	nid.nid_grps = NULL;
362	nid.nid_flag = NFSID_INITIALIZE;
363#ifdef DEBUG
364	printf("Initialize uid=%d gid=%d dns=%s\n", nid.nid_uid, nid.nid_gid,
365	    nid.nid_name);
366#else
367	error = nfssvc(NFSSVC_IDNAME | NFSSVC_NEWSTRUCT, &nid);
368	if (error)
369		errx(1, "Can't initialize nfs user/groups");
370#endif
371
372	i = 0;
373	/*
374	 * Loop around adding all groups.
375	 */
376	setgrent();
377	while (i < nid.nid_usermax && (grp = getgrent())) {
378		nid.nid_gid = grp->gr_gid;
379		nid.nid_name = grp->gr_name;
380		nid.nid_namelen = strlen(grp->gr_name);
381		nid.nid_ngroup = 0;
382		nid.nid_grps = NULL;
383		nid.nid_flag = NFSID_ADDGID;
384#ifdef DEBUG
385		printf("add gid=%d name=%s\n", nid.nid_gid, nid.nid_name);
386#else
387		error = nfssvc(NFSSVC_IDNAME | NFSSVC_NEWSTRUCT, &nid);
388		if (error)
389			errx(1, "Can't add group %s", grp->gr_name);
390#endif
391		i++;
392	}
393	endgrent();
394
395	/*
396	 * Loop around adding all users.
397	 */
398	start_uidpos = i;
399	setpwent();
400	while (i < nid.nid_usermax && (pwd = getpwent())) {
401		fnd_dup = 0;
402		/*
403		 * Yes, this is inefficient, but it is only done once when
404		 * the daemon is started and will run in a fraction of a second
405		 * for nid_usermax at 10000. If nid_usermax is cranked up to
406		 * 100000, it will take several seconds, depending on the CPU.
407		 */
408		for (j = 0; j < (i - start_uidpos); j++)
409			if (check_dups[j] == pwd->pw_uid) {
410				/* Found another entry for uid, so skip it */
411				fnd_dup = 1;
412				break;
413			}
414		if (fnd_dup != 0)
415			continue;
416		check_dups[i - start_uidpos] = pwd->pw_uid;
417		nid.nid_uid = pwd->pw_uid;
418		nid.nid_name = pwd->pw_name;
419		nid.nid_namelen = strlen(pwd->pw_name);
420		if (manage_gids != 0) {
421			/* Get the group list for this user. */
422			ngroup = NGROUPS;
423			if (getgrouplist(pwd->pw_name, pwd->pw_gid, grps,
424			    &ngroup) < 0)
425				syslog(LOG_ERR, "Group list too small");
426			nid.nid_ngroup = ngroup;
427			nid.nid_grps = grps;
428		} else {
429			nid.nid_ngroup = 0;
430			nid.nid_grps = NULL;
431		}
432		nid.nid_flag = NFSID_ADDUID;
433#ifdef DEBUG
434		printf("add uid=%d name=%s\n", nid.nid_uid, nid.nid_name);
435#else
436		error = nfssvc(NFSSVC_IDNAME | NFSSVC_NEWSTRUCT, &nid);
437		if (error)
438			errx(1, "Can't add user %s", pwd->pw_name);
439#endif
440		i++;
441	}
442	endpwent();
443
444	/*
445	 * I should feel guilty for not calling this for all the above exit()
446	 * upon error cases, but I don't.
447	 */
448	if (mustfreeai)
449		freeaddrinfo(aip);
450
451#ifdef DEBUG
452	exit(0);
453#endif
454	/*
455	 * Temporarily block SIGUSR1 and SIGCHLD, so servers[] can't
456	 * end up bogus.
457	 */
458	sigemptyset(&signew);
459	sigaddset(&signew, SIGUSR1);
460	sigaddset(&signew, SIGCHLD);
461	sigprocmask(SIG_BLOCK, &signew, NULL);
462
463	daemon(0, 0);
464	(void)signal(SIGHUP, SIG_IGN);
465	(void)signal(SIGINT, SIG_IGN);
466	(void)signal(SIGQUIT, SIG_IGN);
467	(void)signal(SIGTERM, SIG_IGN);
468	(void)signal(SIGUSR1, cleanup_term);
469	(void)signal(SIGCHLD, cleanup_term);
470
471	openlog("nfsuserd:", LOG_PID, LOG_DAEMON);
472
473	/*
474	 * Fork off the server daemons that do the work. All the master
475	 * does is terminate them and cleanup.
476	 */
477	for (i = 0; i < nfsuserdcnt; i++) {
478		servers[i] = fork();
479		if (servers[i] == 0) {
480			im_a_server = 1;
481			setproctitle("server");
482			sigemptyset(&signew);
483			sigaddset(&signew, SIGUSR1);
484			sigprocmask(SIG_UNBLOCK, &signew, NULL);
485
486			/*
487			 * and away we go.
488			 */
489			svc_run();
490			syslog(LOG_ERR, "nfsuserd died: %m");
491			exit(1);
492		} else if (servers[i] < 0) {
493			syslog(LOG_ERR, "fork: %m");
494		}
495	}
496
497	/*
498	 * Just wait for SIGUSR1 or a child to die and then...
499	 * As the Governor of California would say, "Terminate them".
500	 */
501	setproctitle("master");
502	sigemptyset(&signew);
503	while (1)
504		sigsuspend(&signew);
505}
506
507/*
508 * The nfsuserd rpc service
509 */
510static void
511nfsuserdsrv(struct svc_req *rqstp, SVCXPRT *transp)
512{
513	struct passwd *pwd;
514	struct group *grp;
515	int error;
516#if defined(INET) || defined(INET6)
517	u_short sport;
518	int ret;
519#endif
520	struct info info;
521	struct nfsd_idargs nid;
522	gid_t grps[NGROUPS];
523	int ngroup;
524#ifdef INET
525	struct sockaddr_in *fromsin, *sin;
526#endif
527#ifdef INET6
528	struct sockaddr_in6 *fromsin6, *sin6;
529	char buf[INET6_ADDRSTRLEN];
530#endif
531
532	/*
533	 * Only handle requests from localhost on a reserved port number.
534	 * If the upcall is from a different address, call nfsbind_localhost()
535	 * to check for a remapping of localhost, due to jails.
536	 * (Since a reserved port # at localhost implies a client with
537	 *  local root, there won't be a security breach. This is about
538	 *  the only case I can think of where a reserved port # means
539	 *  something.)
540	 */
541	if (rqstp->rq_proc != NULLPROC) {
542		switch (fromip.ss_family) {
543#ifdef INET
544		case AF_INET:
545			if (transp->xp_rtaddr.len < sizeof(*sin)) {
546				syslog(LOG_ERR, "xp_rtaddr too small");
547				svcerr_weakauth(transp);
548				return;
549			}
550			sin = (struct sockaddr_in *)transp->xp_rtaddr.buf;
551			fromsin = (struct sockaddr_in *)&fromip;
552			sport = ntohs(sin->sin_port);
553			if (sport >= IPPORT_RESERVED) {
554				syslog(LOG_ERR, "not a reserved port#");
555				svcerr_weakauth(transp);
556				return;
557			}
558			ret = 1;
559			if (sin->sin_addr.s_addr != fromsin->sin_addr.s_addr)
560				ret = nfsbind_localhost();
561			if (ret == 0 || sin->sin_addr.s_addr !=
562			    fromsin->sin_addr.s_addr) {
563				syslog(LOG_ERR, "bad from ip %s",
564				    inet_ntoa(sin->sin_addr));
565				svcerr_weakauth(transp);
566				return;
567			}
568			break;
569#endif	/* INET */
570#ifdef INET6
571		case AF_INET6:
572			if (transp->xp_rtaddr.len < sizeof(*sin6)) {
573				syslog(LOG_ERR, "xp_rtaddr too small");
574				svcerr_weakauth(transp);
575				return;
576			}
577			sin6 = (struct sockaddr_in6 *)transp->xp_rtaddr.buf;
578			fromsin6 = (struct sockaddr_in6 *)&fromip;
579			sport = ntohs(sin6->sin6_port);
580			if (sport >= IPV6PORT_RESERVED) {
581				syslog(LOG_ERR, "not a reserved port#");
582				svcerr_weakauth(transp);
583				return;
584			}
585			ret = 1;
586			if (!IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
587			    &fromsin6->sin6_addr))
588				ret = nfsbind_localhost();
589			if (ret == 0 || !IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
590			    &fromsin6->sin6_addr)) {
591				if (inet_ntop(AF_INET6, &sin6->sin6_addr, buf,
592				    INET6_ADDRSTRLEN) != NULL)
593					syslog(LOG_ERR, "bad from ip %s", buf);
594				else
595					syslog(LOG_ERR, "bad from ip6 addr");
596				svcerr_weakauth(transp);
597				return;
598			}
599			break;
600#endif	/* INET6 */
601		}
602	}
603	switch (rqstp->rq_proc) {
604	case NULLPROC:
605		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
606			syslog(LOG_ERR, "Can't send reply");
607		return;
608	case RPCNFSUSERD_GETUID:
609		if (!svc_getargs(transp, (xdrproc_t)xdr_getid,
610		    (caddr_t)&info)) {
611			svcerr_decode(transp);
612			return;
613		}
614		pwd = getpwuid((uid_t)info.id);
615		info.retval = 0;
616		if (pwd != NULL) {
617			nid.nid_usertimeout = defusertimeout;
618			nid.nid_uid = pwd->pw_uid;
619			nid.nid_name = pwd->pw_name;
620			if (manage_gids != 0) {
621				/* Get the group list for this user. */
622				ngroup = NGROUPS;
623				if (getgrouplist(pwd->pw_name, pwd->pw_gid,
624				    grps, &ngroup) < 0)
625					syslog(LOG_ERR, "Group list too small");
626				nid.nid_ngroup = ngroup;
627				nid.nid_grps = grps;
628			} else {
629				nid.nid_ngroup = 0;
630				nid.nid_grps = NULL;
631			}
632		} else {
633			nid.nid_usertimeout = 5;
634			nid.nid_uid = (uid_t)info.id;
635			nid.nid_name = defaultuser;
636			nid.nid_ngroup = 0;
637			nid.nid_grps = NULL;
638		}
639		nid.nid_namelen = strlen(nid.nid_name);
640		nid.nid_flag = NFSID_ADDUID;
641		error = nfssvc(NFSSVC_IDNAME | NFSSVC_NEWSTRUCT, &nid);
642		if (error) {
643			info.retval = error;
644			syslog(LOG_ERR, "Can't add user %s\n", pwd->pw_name);
645		} else if (verbose) {
646			syslog(LOG_ERR,"Added uid=%d name=%s\n",
647			    nid.nid_uid, nid.nid_name);
648		}
649		if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
650		    (caddr_t)&info))
651			syslog(LOG_ERR, "Can't send reply");
652		return;
653	case RPCNFSUSERD_GETGID:
654		if (!svc_getargs(transp, (xdrproc_t)xdr_getid,
655		    (caddr_t)&info)) {
656			svcerr_decode(transp);
657			return;
658		}
659		grp = getgrgid((gid_t)info.id);
660		info.retval = 0;
661		if (grp != NULL) {
662			nid.nid_usertimeout = defusertimeout;
663			nid.nid_gid = grp->gr_gid;
664			nid.nid_name = grp->gr_name;
665		} else {
666			nid.nid_usertimeout = 5;
667			nid.nid_gid = (gid_t)info.id;
668			nid.nid_name = defaultgroup;
669		}
670		nid.nid_namelen = strlen(nid.nid_name);
671		nid.nid_ngroup = 0;
672		nid.nid_grps = NULL;
673		nid.nid_flag = NFSID_ADDGID;
674		error = nfssvc(NFSSVC_IDNAME | NFSSVC_NEWSTRUCT, &nid);
675		if (error) {
676			info.retval = error;
677			syslog(LOG_ERR, "Can't add group %s\n",
678			    grp->gr_name);
679		} else if (verbose) {
680			syslog(LOG_ERR,"Added gid=%d name=%s\n",
681			    nid.nid_gid, nid.nid_name);
682		}
683		if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
684		    (caddr_t)&info))
685			syslog(LOG_ERR, "Can't send reply");
686		return;
687	case RPCNFSUSERD_GETUSER:
688		if (!svc_getargs(transp, (xdrproc_t)xdr_getname,
689		    (caddr_t)&info)) {
690			svcerr_decode(transp);
691			return;
692		}
693		pwd = getpwnam(info.name);
694		info.retval = 0;
695		if (pwd != NULL) {
696			nid.nid_usertimeout = defusertimeout;
697			nid.nid_uid = pwd->pw_uid;
698			nid.nid_name = pwd->pw_name;
699		} else {
700			nid.nid_usertimeout = 5;
701			nid.nid_uid = defaultuid;
702			nid.nid_name = info.name;
703		}
704		nid.nid_namelen = strlen(nid.nid_name);
705		nid.nid_ngroup = 0;
706		nid.nid_grps = NULL;
707		nid.nid_flag = NFSID_ADDUSERNAME;
708		error = nfssvc(NFSSVC_IDNAME | NFSSVC_NEWSTRUCT, &nid);
709		if (error) {
710			info.retval = error;
711			syslog(LOG_ERR, "Can't add user %s\n", pwd->pw_name);
712		} else if (verbose) {
713			syslog(LOG_ERR,"Added uid=%d name=%s\n",
714			    nid.nid_uid, nid.nid_name);
715		}
716		if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
717		    (caddr_t)&info))
718			syslog(LOG_ERR, "Can't send reply");
719		return;
720	case RPCNFSUSERD_GETGROUP:
721		if (!svc_getargs(transp, (xdrproc_t)xdr_getname,
722		    (caddr_t)&info)) {
723			svcerr_decode(transp);
724			return;
725		}
726		grp = getgrnam(info.name);
727		info.retval = 0;
728		if (grp != NULL) {
729			nid.nid_usertimeout = defusertimeout;
730			nid.nid_gid = grp->gr_gid;
731			nid.nid_name = grp->gr_name;
732		} else {
733			nid.nid_usertimeout = 5;
734			nid.nid_gid = defaultgid;
735			nid.nid_name = info.name;
736		}
737		nid.nid_namelen = strlen(nid.nid_name);
738		nid.nid_ngroup = 0;
739		nid.nid_grps = NULL;
740		nid.nid_flag = NFSID_ADDGROUPNAME;
741		error = nfssvc(NFSSVC_IDNAME | NFSSVC_NEWSTRUCT, &nid);
742		if (error) {
743			info.retval = error;
744			syslog(LOG_ERR, "Can't add group %s\n",
745			    grp->gr_name);
746		} else if (verbose) {
747			syslog(LOG_ERR,"Added gid=%d name=%s\n",
748			    nid.nid_gid, nid.nid_name);
749		}
750		if (!svc_sendreply(transp, (xdrproc_t)xdr_retval,
751		    (caddr_t)&info))
752			syslog(LOG_ERR, "Can't send reply");
753		return;
754	default:
755		svcerr_noproc(transp);
756		return;
757	};
758}
759
760/*
761 * Xdr routine to get an id number
762 */
763static bool_t
764xdr_getid(XDR *xdrsp, caddr_t cp)
765{
766	struct info *ifp = (struct info *)cp;
767
768	return (xdr_long(xdrsp, &ifp->id));
769}
770
771/*
772 * Xdr routine to get a user name
773 */
774static bool_t
775xdr_getname(XDR *xdrsp, caddr_t cp)
776{
777	struct info *ifp = (struct info *)cp;
778	long len;
779
780	if (!xdr_long(xdrsp, &len))
781		return (0);
782	if (len > MAXNAME)
783		return (0);
784	if (!xdr_opaque(xdrsp, ifp->name, len))
785		return (0);
786	ifp->name[len] = '\0';
787	return (1);
788}
789
790/*
791 * Xdr routine to return the value.
792 */
793static bool_t
794xdr_retval(XDR *xdrsp, caddr_t cp)
795{
796	struct info *ifp = (struct info *)cp;
797	long val;
798
799	val = ifp->retval;
800	return (xdr_long(xdrsp, &val));
801}
802
803/*
804 * cleanup_term() called via SIGUSR1.
805 */
806static void
807cleanup_term(int signo __unused)
808{
809	int i, cnt;
810
811	if (im_a_server)
812		exit(0);
813
814	/*
815	 * Ok, so I'm the master.
816	 * As the Governor of California might say, "Terminate them".
817	 */
818	cnt = 0;
819	for (i = 0; i < nfsuserdcnt; i++) {
820		if (servers[i] != (pid_t)-1) {
821			cnt++;
822			kill(servers[i], SIGUSR1);
823		}
824	}
825
826	/*
827	 * and wait for them to die
828	 */
829	for (i = 0; i < cnt; i++)
830		wait3(NULL, 0, NULL);
831
832	/*
833	 * Finally, get rid of the socket
834	 */
835	if (nfssvc(NFSSVC_NFSUSERDDELPORT, NULL) < 0) {
836		syslog(LOG_ERR, "Can't do nfssvc() to delete the port\n");
837		exit(1);
838	}
839	exit(0);
840}
841
842/*
843 * Get the IP address that the localhost address maps to.
844 * This is needed when jails map localhost to another IP address.
845 */
846static int
847nfsbind_localhost(void)
848{
849#ifdef INET
850	struct sockaddr_in sin;
851#endif
852#ifdef INET6
853	struct sockaddr_in6 sin6;
854#endif
855	socklen_t slen;
856	int ret, s;
857
858	switch (fromip.ss_family) {
859#ifdef INET6
860	case AF_INET6:
861		s = socket(PF_INET6, SOCK_DGRAM, 0);
862		if (s < 0)
863			return (0);
864		memset(&sin6, 0, sizeof(sin6));
865		sin6.sin6_len = sizeof(sin6);
866		sin6.sin6_family = AF_INET6;
867		sin6.sin6_addr = in6loopback;
868		sin6.sin6_port = 0;
869		ret = bind(s, (struct sockaddr *)&sin6, sizeof(sin6));
870		if (ret < 0) {
871			close(s);
872			return (0);
873		}
874		break;
875#endif	/* INET6 */
876#ifdef INET
877	case AF_INET:
878		s = socket(PF_INET, SOCK_DGRAM, 0);
879		if (s < 0)
880			return (0);
881		memset(&sin, 0, sizeof(sin));
882		sin.sin_len = sizeof(sin);
883		sin.sin_family = AF_INET;
884		sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
885		sin.sin_port = 0;
886		ret = bind(s, (struct sockaddr *)&sin, sizeof(sin));
887		if (ret < 0) {
888			close(s);
889			return (0);
890		}
891		break;
892#endif	/* INET */
893	}
894	memset(&fromip, 0, sizeof(fromip));
895	slen = sizeof(fromip);
896	ret = getsockname(s, (struct sockaddr *)&fromip, &slen);
897	close(s);
898	if (ret < 0)
899		return (0);
900	return (1);
901}
902
903static void
904usage(void)
905{
906
907	errx(1,
908	    "usage: nfsuserd [-usermax cache_size] [-usertimeout minutes] [-verbose] [-manage-gids] [-domain domain_name] [n]");
909}
910