mountd.c revision 349126
187419Sdes/*
287419Sdes * Copyright (c) 1989, 1993
387419Sdes *	The Regents of the University of California.  All rights reserved.
487419Sdes *
587419Sdes * This code is derived from software contributed to Berkeley by
687419Sdes * Herb Hasler and Rick Macklem at The University of Guelph.
787419Sdes *
8110608Sdes * Redistribution and use in source and binary forms, with or without
9110608Sdes * modification, are permitted provided that the following conditions
10110608Sdes * are met:
11170510Syar * 1. Redistributions of source code must retain the above copyright
12170510Syar *    notice, this list of conditions and the following disclaimer.
13170510Syar * 2. Redistributions in binary form must reproduce the above copyright
14170771Syar *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#ifndef lint
34static const char copyright[] =
35"@(#) Copyright (c) 1989, 1993\n\
36	The Regents of the University of California.  All rights reserved.\n";
37#endif /*not lint*/
38
39#if 0
40#ifndef lint
41static char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95";
42#endif /*not lint*/
43#endif
44
45#include <sys/cdefs.h>
46__FBSDID("$FreeBSD: stable/11/usr.sbin/mountd/mountd.c 349126 2019-06-17 00:29:40Z rmacklem $");
47
48#include <sys/param.h>
49#include <sys/fcntl.h>
50#include <sys/linker.h>
51#include <sys/module.h>
52#include <sys/mount.h>
53#include <sys/queue.h>
54#include <sys/stat.h>
55#include <sys/sysctl.h>
56#include <sys/syslog.h>
57
58#include <rpc/rpc.h>
59#include <rpc/rpc_com.h>
60#include <rpc/pmap_clnt.h>
61#include <rpc/pmap_prot.h>
62#include <rpcsvc/mount.h>
63#include <nfs/nfsproto.h>
64#include <nfs/nfssvc.h>
65#include <nfsserver/nfs.h>
66
67#include <fs/nfs/nfsport.h>
68
69#include <arpa/inet.h>
70
71#include <ctype.h>
72#include <err.h>
73#include <errno.h>
74#include <grp.h>
75#include <libutil.h>
76#include <limits.h>
77#include <netdb.h>
78#include <pwd.h>
79#include <signal.h>
80#include <stdio.h>
81#include <stdlib.h>
82#include <string.h>
83#include <unistd.h>
84#include "pathnames.h"
85#include "mntopts.h"
86
87#ifdef DEBUG
88#include <stdarg.h>
89#endif
90
91/*
92 * Structures for keeping the mount list and export list
93 */
94struct mountlist {
95	char	ml_host[MNTNAMLEN+1];
96	char	ml_dirp[MNTPATHLEN+1];
97
98	SLIST_ENTRY(mountlist)	next;
99};
100
101struct dirlist {
102	struct dirlist	*dp_left;
103	struct dirlist	*dp_right;
104	int		dp_flag;
105	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
106	char		*dp_dirp;
107};
108/* dp_flag bits */
109#define	DP_DEFSET	0x1
110#define DP_HOSTSET	0x2
111
112struct exportlist {
113	struct dirlist	*ex_dirl;
114	struct dirlist	*ex_defdir;
115	int		ex_flag;
116	fsid_t		ex_fs;
117	char		*ex_fsdir;
118	char		*ex_indexfile;
119	int		ex_numsecflavors;
120	int		ex_secflavors[MAXSECFLAVORS];
121	int		ex_defnumsecflavors;
122	int		ex_defsecflavors[MAXSECFLAVORS];
123
124	SLIST_ENTRY(exportlist) entries;
125};
126/* ex_flag bits */
127#define	EX_LINKED	0x1
128
129SLIST_HEAD(exportlisthead, exportlist);
130
131struct netmsk {
132	struct sockaddr_storage nt_net;
133	struct sockaddr_storage nt_mask;
134	char		*nt_name;
135};
136
137union grouptypes {
138	struct addrinfo *gt_addrinfo;
139	struct netmsk	gt_net;
140};
141
142struct grouplist {
143	int gr_type;
144	union grouptypes gr_ptr;
145	struct grouplist *gr_next;
146	int gr_numsecflavors;
147	int gr_secflavors[MAXSECFLAVORS];
148};
149/* Group types */
150#define	GT_NULL		0x0
151#define	GT_HOST		0x1
152#define	GT_NET		0x2
153#define	GT_DEFAULT	0x3
154#define GT_IGNORE	0x5
155
156struct hostlist {
157	int		 ht_flag;	/* Uses DP_xx bits */
158	struct grouplist *ht_grp;
159	struct hostlist	 *ht_next;
160};
161
162struct fhreturn {
163	int	fhr_flag;
164	int	fhr_vers;
165	nfsfh_t	fhr_fh;
166	int	fhr_numsecflavors;
167	int	*fhr_secflavors;
168};
169
170#define	GETPORT_MAXTRY	20	/* Max tries to get a port # */
171
172/* Global defs */
173static char	*add_expdir(struct dirlist **, char *, int);
174static void	add_dlist(struct dirlist **, struct dirlist *,
175		    struct grouplist *, int, struct exportlist *);
176static void	add_mlist(char *, char *);
177static int	check_dirpath(char *);
178static int	check_options(struct dirlist *);
179static int	checkmask(struct sockaddr *sa);
180static int	chk_host(struct dirlist *, struct sockaddr *, int *, int *,
181		    int *, int **);
182static char	*strsep_quote(char **stringp, const char *delim);
183static int	create_service(struct netconfig *nconf);
184static void	complete_service(struct netconfig *nconf, char *port_str);
185static void	clearout_service(void);
186static void	del_mlist(char *hostp, char *dirp);
187static struct dirlist	*dirp_search(struct dirlist *, char *);
188static int	do_mount(struct exportlist *, struct grouplist *, int,
189		    struct xucred *, char *, int, struct statfs *);
190static int	do_opt(char **, char **, struct exportlist *,
191		    struct grouplist *, int *, int *, struct xucred *);
192static struct exportlist	*ex_search(fsid_t *, struct exportlisthead *);
193static struct exportlist	*get_exp(void);
194static void	free_dir(struct dirlist *);
195static void	free_exp(struct exportlist *);
196static void	free_grp(struct grouplist *);
197static void	free_host(struct hostlist *);
198static void	get_exportlist(void);
199static void	insert_exports(struct exportlist *, struct exportlisthead *);
200static void	free_exports(struct exportlisthead *);
201static void	read_exportfile(void);
202static void	delete_export(struct iovec *, int, struct statfs *, char *);
203static int	get_host(char *, struct grouplist *, struct grouplist *);
204static struct hostlist *get_ht(void);
205static int	get_line(void);
206static void	get_mountlist(void);
207static int	get_net(char *, struct netmsk *, int);
208static void	getexp_err(struct exportlist *, struct grouplist *, const char *);
209static struct grouplist	*get_grp(void);
210static void	hang_dirp(struct dirlist *, struct grouplist *,
211				struct exportlist *, int);
212static void	huphandler(int sig);
213static int	makemask(struct sockaddr_storage *ssp, int bitlen);
214static void	mntsrv(struct svc_req *, SVCXPRT *);
215static void	nextfield(char **, char **);
216static void	out_of_mem(void);
217static void	parsecred(char *, struct xucred *);
218static int	parsesec(char *, struct exportlist *);
219static int	put_exlist(struct dirlist *, XDR *, struct dirlist *,
220		    int *, int);
221static void	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
222static int	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
223		    struct sockaddr *samask);
224static int	scan_tree(struct dirlist *, struct sockaddr *);
225static void	usage(void);
226static int	xdr_dir(XDR *, char *);
227static int	xdr_explist(XDR *, caddr_t);
228static int	xdr_explist_brief(XDR *, caddr_t);
229static int	xdr_explist_common(XDR *, caddr_t, int);
230static int	xdr_fhs(XDR *, caddr_t);
231static int	xdr_mlist(XDR *, caddr_t);
232static void	terminate(int);
233
234static struct exportlisthead exphead = SLIST_HEAD_INITIALIZER(&exphead);
235static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead);
236static struct grouplist *grphead;
237static char *exnames_default[2] = { _PATH_EXPORTS, NULL };
238static char **exnames;
239static char **hosts = NULL;
240static struct xucred def_anon = {
241	XUCRED_VERSION,
242	(uid_t)-2,
243	1,
244	{ (gid_t)-2 },
245	NULL
246};
247static int force_v2 = 0;
248static int resvport_only = 1;
249static int nhosts = 0;
250static int dir_only = 1;
251static int dolog = 0;
252static int got_sighup = 0;
253static int xcreated = 0;
254
255static char *svcport_str = NULL;
256static int mallocd_svcport = 0;
257static int *sock_fd;
258static int sock_fdcnt;
259static int sock_fdpos;
260static int suspend_nfsd = 0;
261
262static int opt_flags;
263static int have_v6 = 1;
264
265static int v4root_phase = 0;
266static char v4root_dirpath[PATH_MAX + 1];
267static int has_publicfh = 0;
268
269static struct pidfh *pfh = NULL;
270/* Bits for opt_flags above */
271#define	OP_MAPROOT	0x01
272#define	OP_MAPALL	0x02
273/* 0x4 free */
274#define	OP_MASK		0x08
275#define	OP_NET		0x10
276#define	OP_ALLDIRS	0x40
277#define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
278#define	OP_QUIET	0x100
279#define OP_MASKLEN	0x200
280#define OP_SEC		0x400
281
282#ifdef DEBUG
283static int debug = 1;
284static void	SYSLOG(int, const char *, ...) __printflike(2, 3);
285#define syslog SYSLOG
286#else
287static int debug = 0;
288#endif
289
290/*
291 * Similar to strsep(), but it allows for quoted strings
292 * and escaped characters.
293 *
294 * It returns the string (or NULL, if *stringp is NULL),
295 * which is a de-quoted version of the string if necessary.
296 *
297 * It modifies *stringp in place.
298 */
299static char *
300strsep_quote(char **stringp, const char *delim)
301{
302	char *srcptr, *dstptr, *retval;
303	char quot = 0;
304
305	if (stringp == NULL || *stringp == NULL)
306		return (NULL);
307
308	srcptr = dstptr = retval = *stringp;
309
310	while (*srcptr) {
311		/*
312		 * We're looking for several edge cases here.
313		 * First:  if we're in quote state (quot != 0),
314		 * then we ignore the delim characters, but otherwise
315		 * process as normal, unless it is the quote character.
316		 * Second:  if the current character is a backslash,
317		 * we take the next character as-is, without checking
318		 * for delim, quote, or backslash.  Exception:  if the
319		 * next character is a NUL, that's the end of the string.
320		 * Third:  if the character is a quote character, we toggle
321		 * quote state.
322		 * Otherwise:  check the current character for NUL, or
323		 * being in delim, and end the string if either is true.
324		 */
325		if (*srcptr == '\\') {
326			srcptr++;
327			/*
328			 * The edge case here is if the next character
329			 * is NUL, we want to stop processing.  But if
330			 * it's not NUL, then we simply want to copy it.
331			 */
332			if (*srcptr) {
333				*dstptr++ = *srcptr++;
334			}
335			continue;
336		}
337		if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) {
338			quot = *srcptr++;
339			continue;
340		}
341		if (quot && *srcptr == quot) {
342			/* End of the quoted part */
343			quot = 0;
344			srcptr++;
345			continue;
346		}
347		if (!quot && strchr(delim, *srcptr))
348			break;
349		*dstptr++ = *srcptr++;
350	}
351
352	*dstptr = 0; /* Terminate the string */
353	*stringp = (*srcptr == '\0') ? NULL : srcptr + 1;
354	return (retval);
355}
356
357/*
358 * Mountd server for NFS mount protocol as described in:
359 * NFS: Network File System Protocol Specification, RFC1094, Appendix A
360 * The optional arguments are the exports file name
361 * default: _PATH_EXPORTS
362 * and "-n" to allow nonroot mount.
363 */
364int
365main(int argc, char **argv)
366{
367	fd_set readfds;
368	struct netconfig *nconf;
369	char *endptr, **hosts_bak;
370	void *nc_handle;
371	pid_t otherpid;
372	in_port_t svcport;
373	int c, k, s;
374	int maxrec = RPC_MAXDATASIZE;
375	int attempt_cnt, port_len, port_pos, ret;
376	char **port_list;
377
378	/* Check that another mountd isn't already running. */
379	pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
380	if (pfh == NULL) {
381		if (errno == EEXIST)
382			errx(1, "mountd already running, pid: %d.", otherpid);
383		warn("cannot open or create pidfile");
384	}
385
386	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
387	if (s < 0)
388		have_v6 = 0;
389	else
390		close(s);
391
392	while ((c = getopt(argc, argv, "2deh:lnp:rS")) != -1)
393		switch (c) {
394		case '2':
395			force_v2 = 1;
396			break;
397		case 'e':
398			/* now a no-op, since this is the default */
399			break;
400		case 'n':
401			resvport_only = 0;
402			break;
403		case 'r':
404			dir_only = 0;
405			break;
406		case 'd':
407			debug = debug ? 0 : 1;
408			break;
409		case 'l':
410			dolog = 1;
411			break;
412		case 'p':
413			endptr = NULL;
414			svcport = (in_port_t)strtoul(optarg, &endptr, 10);
415			if (endptr == NULL || *endptr != '\0' ||
416			    svcport == 0 || svcport >= IPPORT_MAX)
417				usage();
418			svcport_str = strdup(optarg);
419			break;
420		case 'h':
421			++nhosts;
422			hosts_bak = hosts;
423			hosts_bak = realloc(hosts, nhosts * sizeof(char *));
424			if (hosts_bak == NULL) {
425				if (hosts != NULL) {
426					for (k = 0; k < nhosts; k++)
427						free(hosts[k]);
428					free(hosts);
429					out_of_mem();
430				}
431			}
432			hosts = hosts_bak;
433			hosts[nhosts - 1] = strdup(optarg);
434			if (hosts[nhosts - 1] == NULL) {
435				for (k = 0; k < (nhosts - 1); k++)
436					free(hosts[k]);
437				free(hosts);
438				out_of_mem();
439			}
440			break;
441		case 'S':
442			suspend_nfsd = 1;
443			break;
444		default:
445			usage();
446		}
447
448	if (modfind("nfsd") < 0) {
449		/* Not present in kernel, try loading it */
450		if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
451			errx(1, "NFS server is not available");
452	}
453
454	argc -= optind;
455	argv += optind;
456	grphead = (struct grouplist *)NULL;
457	if (argc > 0)
458		exnames = argv;
459	else
460		exnames = exnames_default;
461	openlog("mountd", LOG_PID, LOG_DAEMON);
462	if (debug)
463		warnx("getting export list");
464	get_exportlist();
465	if (debug)
466		warnx("getting mount list");
467	get_mountlist();
468	if (debug)
469		warnx("here we go");
470	if (debug == 0) {
471		daemon(0, 0);
472		signal(SIGINT, SIG_IGN);
473		signal(SIGQUIT, SIG_IGN);
474	}
475	signal(SIGHUP, huphandler);
476	signal(SIGTERM, terminate);
477	signal(SIGPIPE, SIG_IGN);
478
479	pidfile_write(pfh);
480
481	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
482	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
483	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
484
485	if (!resvport_only) {
486		if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL,
487		    &resvport_only, sizeof(resvport_only)) != 0 &&
488		    errno != ENOENT) {
489			syslog(LOG_ERR, "sysctl: %m");
490			exit(1);
491		}
492	}
493
494	/*
495	 * If no hosts were specified, add a wildcard entry to bind to
496	 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
497	 * list.
498	 */
499	if (nhosts == 0) {
500		hosts = malloc(sizeof(char *));
501		if (hosts == NULL)
502			out_of_mem();
503		hosts[0] = "*";
504		nhosts = 1;
505	} else {
506		hosts_bak = hosts;
507		if (have_v6) {
508			hosts_bak = realloc(hosts, (nhosts + 2) *
509			    sizeof(char *));
510			if (hosts_bak == NULL) {
511				for (k = 0; k < nhosts; k++)
512					free(hosts[k]);
513		    		free(hosts);
514		    		out_of_mem();
515			} else
516				hosts = hosts_bak;
517			nhosts += 2;
518			hosts[nhosts - 2] = "::1";
519		} else {
520			hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
521			if (hosts_bak == NULL) {
522				for (k = 0; k < nhosts; k++)
523					free(hosts[k]);
524				free(hosts);
525				out_of_mem();
526			} else {
527				nhosts += 1;
528				hosts = hosts_bak;
529			}
530		}
531
532		hosts[nhosts - 1] = "127.0.0.1";
533	}
534
535	attempt_cnt = 1;
536	sock_fdcnt = 0;
537	sock_fd = NULL;
538	port_list = NULL;
539	port_len = 0;
540	nc_handle = setnetconfig();
541	while ((nconf = getnetconfig(nc_handle))) {
542		if (nconf->nc_flag & NC_VISIBLE) {
543			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
544			    "inet6") == 0) {
545				/* DO NOTHING */
546			} else {
547				ret = create_service(nconf);
548				if (ret == 1)
549					/* Ignore this call */
550					continue;
551				if (ret < 0) {
552					/*
553					 * Failed to bind port, so close off
554					 * all sockets created and try again
555					 * if the port# was dynamically
556					 * assigned via bind(2).
557					 */
558					clearout_service();
559					if (mallocd_svcport != 0 &&
560					    attempt_cnt < GETPORT_MAXTRY) {
561						free(svcport_str);
562						svcport_str = NULL;
563						mallocd_svcport = 0;
564					} else {
565						errno = EADDRINUSE;
566						syslog(LOG_ERR,
567						    "bindresvport_sa: %m");
568						exit(1);
569					}
570
571					/* Start over at the first service. */
572					free(sock_fd);
573					sock_fdcnt = 0;
574					sock_fd = NULL;
575					nc_handle = setnetconfig();
576					attempt_cnt++;
577				} else if (mallocd_svcport != 0 &&
578				    attempt_cnt == GETPORT_MAXTRY) {
579					/*
580					 * For the last attempt, allow
581					 * different port #s for each nconf
582					 * by saving the svcport_str and
583					 * setting it back to NULL.
584					 */
585					port_list = realloc(port_list,
586					    (port_len + 1) * sizeof(char *));
587					if (port_list == NULL)
588						out_of_mem();
589					port_list[port_len++] = svcport_str;
590					svcport_str = NULL;
591					mallocd_svcport = 0;
592				}
593			}
594		}
595	}
596
597	/*
598	 * Successfully bound the ports, so call complete_service() to
599	 * do the rest of the setup on the service(s).
600	 */
601	sock_fdpos = 0;
602	port_pos = 0;
603	nc_handle = setnetconfig();
604	while ((nconf = getnetconfig(nc_handle))) {
605		if (nconf->nc_flag & NC_VISIBLE) {
606			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
607			    "inet6") == 0) {
608				/* DO NOTHING */
609			} else if (port_list != NULL) {
610				if (port_pos >= port_len) {
611					syslog(LOG_ERR, "too many port#s");
612					exit(1);
613				}
614				complete_service(nconf, port_list[port_pos++]);
615			} else
616				complete_service(nconf, svcport_str);
617		}
618	}
619	endnetconfig(nc_handle);
620	free(sock_fd);
621	if (port_list != NULL) {
622		for (port_pos = 0; port_pos < port_len; port_pos++)
623			free(port_list[port_pos]);
624		free(port_list);
625	}
626
627	if (xcreated == 0) {
628		syslog(LOG_ERR, "could not create any services");
629		exit(1);
630	}
631
632	/* Expand svc_run() here so that we can call get_exportlist(). */
633	for (;;) {
634		if (got_sighup) {
635			get_exportlist();
636			got_sighup = 0;
637		}
638		readfds = svc_fdset;
639		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
640		case -1:
641			if (errno == EINTR)
642                                continue;
643			syslog(LOG_ERR, "mountd died: select: %m");
644			exit(1);
645		case 0:
646			continue;
647		default:
648			svc_getreqset(&readfds);
649		}
650	}
651}
652
653/*
654 * This routine creates and binds sockets on the appropriate
655 * addresses. It gets called one time for each transport.
656 * It returns 0 upon success, 1 for ingore the call and -1 to indicate
657 * bind failed with EADDRINUSE.
658 * Any file descriptors that have been created are stored in sock_fd and
659 * the total count of them is maintained in sock_fdcnt.
660 */
661static int
662create_service(struct netconfig *nconf)
663{
664	struct addrinfo hints, *res = NULL;
665	struct sockaddr_in *sin;
666	struct sockaddr_in6 *sin6;
667	struct __rpc_sockinfo si;
668	int aicode;
669	int fd;
670	int nhostsbak;
671	int one = 1;
672	int r;
673	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
674	int mallocd_res;
675
676	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
677	    (nconf->nc_semantics != NC_TPI_COTS) &&
678	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
679		return (1);	/* not my type */
680
681	/*
682	 * XXX - using RPC library internal functions.
683	 */
684	if (!__rpc_nconf2sockinfo(nconf, &si)) {
685		syslog(LOG_ERR, "cannot get information for %s",
686		    nconf->nc_netid);
687		return (1);
688	}
689
690	/* Get mountd's address on this transport */
691	memset(&hints, 0, sizeof hints);
692	hints.ai_family = si.si_af;
693	hints.ai_socktype = si.si_socktype;
694	hints.ai_protocol = si.si_proto;
695
696	/*
697	 * Bind to specific IPs if asked to
698	 */
699	nhostsbak = nhosts;
700	while (nhostsbak > 0) {
701		--nhostsbak;
702		sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
703		if (sock_fd == NULL)
704			out_of_mem();
705		sock_fd[sock_fdcnt++] = -1;	/* Set invalid for now. */
706		mallocd_res = 0;
707
708		hints.ai_flags = AI_PASSIVE;
709
710		/*
711		 * XXX - using RPC library internal functions.
712		 */
713		if ((fd = __rpc_nconf2fd(nconf)) < 0) {
714			int non_fatal = 0;
715	    		if (errno == EAFNOSUPPORT &&
716			    nconf->nc_semantics != NC_TPI_CLTS)
717				non_fatal = 1;
718
719			syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
720			    "cannot create socket for %s", nconf->nc_netid);
721			if (non_fatal != 0)
722				continue;
723			exit(1);
724		}
725
726		switch (hints.ai_family) {
727		case AF_INET:
728			if (inet_pton(AF_INET, hosts[nhostsbak],
729			    host_addr) == 1) {
730				hints.ai_flags |= AI_NUMERICHOST;
731			} else {
732				/*
733				 * Skip if we have an AF_INET6 address.
734				 */
735				if (inet_pton(AF_INET6, hosts[nhostsbak],
736				    host_addr) == 1) {
737					close(fd);
738					continue;
739				}
740			}
741			break;
742		case AF_INET6:
743			if (inet_pton(AF_INET6, hosts[nhostsbak],
744			    host_addr) == 1) {
745				hints.ai_flags |= AI_NUMERICHOST;
746			} else {
747				/*
748				 * Skip if we have an AF_INET address.
749				 */
750				if (inet_pton(AF_INET, hosts[nhostsbak],
751				    host_addr) == 1) {
752					close(fd);
753					continue;
754				}
755			}
756
757			/*
758			 * We're doing host-based access checks here, so don't
759			 * allow v4-in-v6 to confuse things. The kernel will
760			 * disable it by default on NFS sockets too.
761			 */
762			if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
763			    sizeof one) < 0) {
764				syslog(LOG_ERR,
765				    "can't disable v4-in-v6 on IPv6 socket");
766				exit(1);
767			}
768			break;
769		default:
770			break;
771		}
772
773		/*
774		 * If no hosts were specified, just bind to INADDR_ANY
775		 */
776		if (strcmp("*", hosts[nhostsbak]) == 0) {
777			if (svcport_str == NULL) {
778				res = malloc(sizeof(struct addrinfo));
779				if (res == NULL)
780					out_of_mem();
781				mallocd_res = 1;
782				res->ai_flags = hints.ai_flags;
783				res->ai_family = hints.ai_family;
784				res->ai_protocol = hints.ai_protocol;
785				switch (res->ai_family) {
786				case AF_INET:
787					sin = malloc(sizeof(struct sockaddr_in));
788					if (sin == NULL)
789						out_of_mem();
790					sin->sin_family = AF_INET;
791					sin->sin_port = htons(0);
792					sin->sin_addr.s_addr = htonl(INADDR_ANY);
793					res->ai_addr = (struct sockaddr*) sin;
794					res->ai_addrlen = (socklen_t)
795					    sizeof(struct sockaddr_in);
796					break;
797				case AF_INET6:
798					sin6 = malloc(sizeof(struct sockaddr_in6));
799					if (sin6 == NULL)
800						out_of_mem();
801					sin6->sin6_family = AF_INET6;
802					sin6->sin6_port = htons(0);
803					sin6->sin6_addr = in6addr_any;
804					res->ai_addr = (struct sockaddr*) sin6;
805					res->ai_addrlen = (socklen_t)
806					    sizeof(struct sockaddr_in6);
807					break;
808				default:
809					syslog(LOG_ERR, "bad addr fam %d",
810					    res->ai_family);
811					exit(1);
812				}
813			} else {
814				if ((aicode = getaddrinfo(NULL, svcport_str,
815				    &hints, &res)) != 0) {
816					syslog(LOG_ERR,
817					    "cannot get local address for %s: %s",
818					    nconf->nc_netid,
819					    gai_strerror(aicode));
820					close(fd);
821					continue;
822				}
823			}
824		} else {
825			if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
826			    &hints, &res)) != 0) {
827				syslog(LOG_ERR,
828				    "cannot get local address for %s: %s",
829				    nconf->nc_netid, gai_strerror(aicode));
830				close(fd);
831				continue;
832			}
833		}
834
835		/* Store the fd. */
836		sock_fd[sock_fdcnt - 1] = fd;
837
838		/* Now, attempt the bind. */
839		r = bindresvport_sa(fd, res->ai_addr);
840		if (r != 0) {
841			if (errno == EADDRINUSE && mallocd_svcport != 0) {
842				if (mallocd_res != 0) {
843					free(res->ai_addr);
844					free(res);
845				} else
846					freeaddrinfo(res);
847				return (-1);
848			}
849			syslog(LOG_ERR, "bindresvport_sa: %m");
850			exit(1);
851		}
852
853		if (svcport_str == NULL) {
854			svcport_str = malloc(NI_MAXSERV * sizeof(char));
855			if (svcport_str == NULL)
856				out_of_mem();
857			mallocd_svcport = 1;
858
859			if (getnameinfo(res->ai_addr,
860			    res->ai_addr->sa_len, NULL, NI_MAXHOST,
861			    svcport_str, NI_MAXSERV * sizeof(char),
862			    NI_NUMERICHOST | NI_NUMERICSERV))
863				errx(1, "Cannot get port number");
864		}
865		if (mallocd_res != 0) {
866			free(res->ai_addr);
867			free(res);
868		} else
869			freeaddrinfo(res);
870		res = NULL;
871	}
872	return (0);
873}
874
875/*
876 * Called after all the create_service() calls have succeeded, to complete
877 * the setup and registration.
878 */
879static void
880complete_service(struct netconfig *nconf, char *port_str)
881{
882	struct addrinfo hints, *res = NULL;
883	struct __rpc_sockinfo si;
884	struct netbuf servaddr;
885	SVCXPRT	*transp = NULL;
886	int aicode, fd, nhostsbak;
887	int registered = 0;
888
889	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
890	    (nconf->nc_semantics != NC_TPI_COTS) &&
891	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
892		return;	/* not my type */
893
894	/*
895	 * XXX - using RPC library internal functions.
896	 */
897	if (!__rpc_nconf2sockinfo(nconf, &si)) {
898		syslog(LOG_ERR, "cannot get information for %s",
899		    nconf->nc_netid);
900		return;
901	}
902
903	nhostsbak = nhosts;
904	while (nhostsbak > 0) {
905		--nhostsbak;
906		if (sock_fdpos >= sock_fdcnt) {
907			/* Should never happen. */
908			syslog(LOG_ERR, "Ran out of socket fd's");
909			return;
910		}
911		fd = sock_fd[sock_fdpos++];
912		if (fd < 0)
913			continue;
914
915		/*
916		 * Using -1 tells listen(2) to use
917		 * kern.ipc.soacceptqueue for the backlog.
918		 */
919		if (nconf->nc_semantics != NC_TPI_CLTS)
920			listen(fd, -1);
921
922		if (nconf->nc_semantics == NC_TPI_CLTS )
923			transp = svc_dg_create(fd, 0, 0);
924		else
925			transp = svc_vc_create(fd, RPC_MAXDATASIZE,
926			    RPC_MAXDATASIZE);
927
928		if (transp != (SVCXPRT *) NULL) {
929			if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv,
930			    NULL))
931				syslog(LOG_ERR,
932				    "can't register %s MOUNTVERS service",
933				    nconf->nc_netid);
934			if (!force_v2) {
935				if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3,
936				    mntsrv, NULL))
937					syslog(LOG_ERR,
938					    "can't register %s MOUNTVERS3 service",
939					    nconf->nc_netid);
940			}
941		} else
942			syslog(LOG_WARNING, "can't create %s services",
943			    nconf->nc_netid);
944
945		if (registered == 0) {
946			registered = 1;
947			memset(&hints, 0, sizeof hints);
948			hints.ai_flags = AI_PASSIVE;
949			hints.ai_family = si.si_af;
950			hints.ai_socktype = si.si_socktype;
951			hints.ai_protocol = si.si_proto;
952
953			if ((aicode = getaddrinfo(NULL, port_str, &hints,
954			    &res)) != 0) {
955				syslog(LOG_ERR, "cannot get local address: %s",
956				    gai_strerror(aicode));
957				exit(1);
958			}
959
960			servaddr.buf = malloc(res->ai_addrlen);
961			memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
962			servaddr.len = res->ai_addrlen;
963
964			rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr);
965			rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr);
966
967			xcreated++;
968			freeaddrinfo(res);
969		}
970	} /* end while */
971}
972
973/*
974 * Clear out sockets after a failure to bind one of them, so that the
975 * cycle of socket creation/binding can start anew.
976 */
977static void
978clearout_service(void)
979{
980	int i;
981
982	for (i = 0; i < sock_fdcnt; i++) {
983		if (sock_fd[i] >= 0) {
984			shutdown(sock_fd[i], SHUT_RDWR);
985			close(sock_fd[i]);
986		}
987	}
988}
989
990static void
991usage(void)
992{
993	fprintf(stderr,
994		"usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
995		"[-S] [-h <bindip>] [export_file ...]\n");
996	exit(1);
997}
998
999/*
1000 * The mount rpc service
1001 */
1002void
1003mntsrv(struct svc_req *rqstp, SVCXPRT *transp)
1004{
1005	struct exportlist *ep;
1006	struct dirlist *dp;
1007	struct fhreturn fhr;
1008	struct stat stb;
1009	struct statfs fsb;
1010	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
1011	int lookup_failed = 1;
1012	struct sockaddr *saddr;
1013	u_short sport;
1014	char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
1015	int bad = 0, defset, hostset;
1016	sigset_t sighup_mask;
1017	int numsecflavors, *secflavorsp;
1018
1019	sigemptyset(&sighup_mask);
1020	sigaddset(&sighup_mask, SIGHUP);
1021	saddr = svc_getrpccaller(transp)->buf;
1022	switch (saddr->sa_family) {
1023	case AF_INET6:
1024		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
1025		break;
1026	case AF_INET:
1027		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
1028		break;
1029	default:
1030		syslog(LOG_ERR, "request from unknown address family");
1031		return;
1032	}
1033	switch (rqstp->rq_proc) {
1034	case MOUNTPROC_MNT:
1035	case MOUNTPROC_UMNT:
1036	case MOUNTPROC_UMNTALL:
1037		lookup_failed = getnameinfo(saddr, saddr->sa_len, host,
1038		    sizeof host, NULL, 0, 0);
1039	}
1040	getnameinfo(saddr, saddr->sa_len, numerichost,
1041	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
1042	switch (rqstp->rq_proc) {
1043	case NULLPROC:
1044		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
1045			syslog(LOG_ERR, "can't send reply");
1046		return;
1047	case MOUNTPROC_MNT:
1048		if (sport >= IPPORT_RESERVED && resvport_only) {
1049			syslog(LOG_NOTICE,
1050			    "mount request from %s from unprivileged port",
1051			    numerichost);
1052			svcerr_weakauth(transp);
1053			return;
1054		}
1055		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1056			syslog(LOG_NOTICE, "undecodable mount request from %s",
1057			    numerichost);
1058			svcerr_decode(transp);
1059			return;
1060		}
1061
1062		/*
1063		 * Get the real pathname and make sure it is a directory
1064		 * or a regular file if the -r option was specified
1065		 * and it exists.
1066		 */
1067		if (realpath(rpcpath, dirpath) == NULL ||
1068		    stat(dirpath, &stb) < 0 ||
1069		    statfs(dirpath, &fsb) < 0) {
1070			chdir("/");	/* Just in case realpath doesn't */
1071			syslog(LOG_NOTICE,
1072			    "mount request from %s for non existent path %s",
1073			    numerichost, dirpath);
1074			if (debug)
1075				warnx("stat failed on %s", dirpath);
1076			bad = ENOENT;	/* We will send error reply later */
1077		}
1078		if (!bad &&
1079		    !S_ISDIR(stb.st_mode) &&
1080		    (dir_only || !S_ISREG(stb.st_mode))) {
1081			syslog(LOG_NOTICE,
1082			    "mount request from %s for non-directory path %s",
1083			    numerichost, dirpath);
1084			if (debug)
1085				warnx("mounting non-directory %s", dirpath);
1086			bad = ENOTDIR;	/* We will send error reply later */
1087		}
1088
1089		/* Check in the exports list */
1090		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1091		if (bad)
1092			ep = NULL;
1093		else
1094			ep = ex_search(&fsb.f_fsid, &exphead);
1095		hostset = defset = 0;
1096		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset,
1097		    &numsecflavors, &secflavorsp) ||
1098		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
1099		      chk_host(dp, saddr, &defset, &hostset, &numsecflavors,
1100		       &secflavorsp)) ||
1101		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
1102		     scan_tree(ep->ex_dirl, saddr) == 0))) {
1103			if (bad) {
1104				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1105				    (caddr_t)&bad))
1106					syslog(LOG_ERR, "can't send reply");
1107				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1108				return;
1109			}
1110			if (hostset & DP_HOSTSET) {
1111				fhr.fhr_flag = hostset;
1112				fhr.fhr_numsecflavors = numsecflavors;
1113				fhr.fhr_secflavors = secflavorsp;
1114			} else {
1115				fhr.fhr_flag = defset;
1116				fhr.fhr_numsecflavors = ep->ex_defnumsecflavors;
1117				fhr.fhr_secflavors = ep->ex_defsecflavors;
1118			}
1119			fhr.fhr_vers = rqstp->rq_vers;
1120			/* Get the file handle */
1121			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
1122			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
1123				bad = errno;
1124				syslog(LOG_ERR, "can't get fh for %s", dirpath);
1125				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1126				    (caddr_t)&bad))
1127					syslog(LOG_ERR, "can't send reply");
1128				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1129				return;
1130			}
1131			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
1132			    (caddr_t)&fhr))
1133				syslog(LOG_ERR, "can't send reply");
1134			if (!lookup_failed)
1135				add_mlist(host, dirpath);
1136			else
1137				add_mlist(numerichost, dirpath);
1138			if (debug)
1139				warnx("mount successful");
1140			if (dolog)
1141				syslog(LOG_NOTICE,
1142				    "mount request succeeded from %s for %s",
1143				    numerichost, dirpath);
1144		} else {
1145			if (!bad)
1146				bad = EACCES;
1147			syslog(LOG_NOTICE,
1148			    "mount request denied from %s for %s",
1149			    numerichost, dirpath);
1150		}
1151
1152		if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
1153		    (caddr_t)&bad))
1154			syslog(LOG_ERR, "can't send reply");
1155		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1156		return;
1157	case MOUNTPROC_DUMP:
1158		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
1159			syslog(LOG_ERR, "can't send reply");
1160		else if (dolog)
1161			syslog(LOG_NOTICE,
1162			    "dump request succeeded from %s",
1163			    numerichost);
1164		return;
1165	case MOUNTPROC_UMNT:
1166		if (sport >= IPPORT_RESERVED && resvport_only) {
1167			syslog(LOG_NOTICE,
1168			    "umount request from %s from unprivileged port",
1169			    numerichost);
1170			svcerr_weakauth(transp);
1171			return;
1172		}
1173		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1174			syslog(LOG_NOTICE, "undecodable umount request from %s",
1175			    numerichost);
1176			svcerr_decode(transp);
1177			return;
1178		}
1179		if (realpath(rpcpath, dirpath) == NULL) {
1180			syslog(LOG_NOTICE, "umount request from %s "
1181			    "for non existent path %s",
1182			    numerichost, dirpath);
1183		}
1184		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1185			syslog(LOG_ERR, "can't send reply");
1186		if (!lookup_failed)
1187			del_mlist(host, dirpath);
1188		del_mlist(numerichost, dirpath);
1189		if (dolog)
1190			syslog(LOG_NOTICE,
1191			    "umount request succeeded from %s for %s",
1192			    numerichost, dirpath);
1193		return;
1194	case MOUNTPROC_UMNTALL:
1195		if (sport >= IPPORT_RESERVED && resvport_only) {
1196			syslog(LOG_NOTICE,
1197			    "umountall request from %s from unprivileged port",
1198			    numerichost);
1199			svcerr_weakauth(transp);
1200			return;
1201		}
1202		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1203			syslog(LOG_ERR, "can't send reply");
1204		if (!lookup_failed)
1205			del_mlist(host, NULL);
1206		del_mlist(numerichost, NULL);
1207		if (dolog)
1208			syslog(LOG_NOTICE,
1209			    "umountall request succeeded from %s",
1210			    numerichost);
1211		return;
1212	case MOUNTPROC_EXPORT:
1213		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
1214			if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
1215			    (caddr_t)NULL))
1216				syslog(LOG_ERR, "can't send reply");
1217		if (dolog)
1218			syslog(LOG_NOTICE,
1219			    "export request succeeded from %s",
1220			    numerichost);
1221		return;
1222	default:
1223		svcerr_noproc(transp);
1224		return;
1225	}
1226}
1227
1228/*
1229 * Xdr conversion for a dirpath string
1230 */
1231static int
1232xdr_dir(XDR *xdrsp, char *dirp)
1233{
1234	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
1235}
1236
1237/*
1238 * Xdr routine to generate file handle reply
1239 */
1240static int
1241xdr_fhs(XDR *xdrsp, caddr_t cp)
1242{
1243	struct fhreturn *fhrp = (struct fhreturn *)cp;
1244	u_long ok = 0, len, auth;
1245	int i;
1246
1247	if (!xdr_long(xdrsp, &ok))
1248		return (0);
1249	switch (fhrp->fhr_vers) {
1250	case 1:
1251		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
1252	case 3:
1253		len = NFSX_V3FH;
1254		if (!xdr_long(xdrsp, &len))
1255			return (0);
1256		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
1257			return (0);
1258		if (fhrp->fhr_numsecflavors) {
1259			if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
1260				return (0);
1261			for (i = 0; i < fhrp->fhr_numsecflavors; i++)
1262				if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
1263					return (0);
1264			return (1);
1265		} else {
1266			auth = AUTH_SYS;
1267			len = 1;
1268			if (!xdr_long(xdrsp, &len))
1269				return (0);
1270			return (xdr_long(xdrsp, &auth));
1271		}
1272	}
1273	return (0);
1274}
1275
1276static int
1277xdr_mlist(XDR *xdrsp, caddr_t cp __unused)
1278{
1279	struct mountlist *mlp;
1280	int true = 1;
1281	int false = 0;
1282	char *strp;
1283
1284	SLIST_FOREACH(mlp, &mlhead, next) {
1285		if (!xdr_bool(xdrsp, &true))
1286			return (0);
1287		strp = &mlp->ml_host[0];
1288		if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
1289			return (0);
1290		strp = &mlp->ml_dirp[0];
1291		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1292			return (0);
1293	}
1294	if (!xdr_bool(xdrsp, &false))
1295		return (0);
1296	return (1);
1297}
1298
1299/*
1300 * Xdr conversion for export list
1301 */
1302static int
1303xdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
1304{
1305	struct exportlist *ep;
1306	int false = 0;
1307	int putdef;
1308	sigset_t sighup_mask;
1309
1310	sigemptyset(&sighup_mask);
1311	sigaddset(&sighup_mask, SIGHUP);
1312	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1313
1314	SLIST_FOREACH(ep, &exphead, entries) {
1315		putdef = 0;
1316		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1317			       &putdef, brief))
1318			goto errout;
1319		if (ep->ex_defdir && putdef == 0 &&
1320			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
1321			&putdef, brief))
1322			goto errout;
1323	}
1324	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1325	if (!xdr_bool(xdrsp, &false))
1326		return (0);
1327	return (1);
1328errout:
1329	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1330	return (0);
1331}
1332
1333/*
1334 * Called from xdr_explist() to traverse the tree and export the
1335 * directory paths.
1336 */
1337static int
1338put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp,
1339	int brief)
1340{
1341	struct grouplist *grp;
1342	struct hostlist *hp;
1343	int true = 1;
1344	int false = 0;
1345	int gotalldir = 0;
1346	char *strp;
1347
1348	if (dp) {
1349		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
1350			return (1);
1351		if (!xdr_bool(xdrsp, &true))
1352			return (1);
1353		strp = dp->dp_dirp;
1354		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1355			return (1);
1356		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
1357			gotalldir = 1;
1358			*putdefp = 1;
1359		}
1360		if (brief) {
1361			if (!xdr_bool(xdrsp, &true))
1362				return (1);
1363			strp = "(...)";
1364			if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1365				return (1);
1366		} else if ((dp->dp_flag & DP_DEFSET) == 0 &&
1367		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
1368			hp = dp->dp_hosts;
1369			while (hp) {
1370				grp = hp->ht_grp;
1371				if (grp->gr_type == GT_HOST) {
1372					if (!xdr_bool(xdrsp, &true))
1373						return (1);
1374					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
1375					if (!xdr_string(xdrsp, &strp,
1376					    MNTNAMLEN))
1377						return (1);
1378				} else if (grp->gr_type == GT_NET) {
1379					if (!xdr_bool(xdrsp, &true))
1380						return (1);
1381					strp = grp->gr_ptr.gt_net.nt_name;
1382					if (!xdr_string(xdrsp, &strp,
1383					    MNTNAMLEN))
1384						return (1);
1385				}
1386				hp = hp->ht_next;
1387				if (gotalldir && hp == (struct hostlist *)NULL) {
1388					hp = adp->dp_hosts;
1389					gotalldir = 0;
1390				}
1391			}
1392		}
1393		if (!xdr_bool(xdrsp, &false))
1394			return (1);
1395		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
1396			return (1);
1397	}
1398	return (0);
1399}
1400
1401static int
1402xdr_explist(XDR *xdrsp, caddr_t cp)
1403{
1404
1405	return xdr_explist_common(xdrsp, cp, 0);
1406}
1407
1408static int
1409xdr_explist_brief(XDR *xdrsp, caddr_t cp)
1410{
1411
1412	return xdr_explist_common(xdrsp, cp, 1);
1413}
1414
1415static char *line;
1416static size_t linesize;
1417static FILE *exp_file;
1418
1419/*
1420 * Get the export list from one, currently open file
1421 */
1422static void
1423get_exportlist_one(void)
1424{
1425	struct exportlist *ep;
1426	struct grouplist *grp, *tgrp;
1427	struct dirlist *dirhead;
1428	struct statfs fsb;
1429	struct xucred anon;
1430	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1431	int len, has_host, exflags, got_nondir, dirplen, netgrp;
1432
1433	v4root_phase = 0;
1434	dirhead = (struct dirlist *)NULL;
1435	while (get_line()) {
1436		if (debug)
1437			warnx("got line %s", line);
1438		cp = line;
1439		nextfield(&cp, &endcp);
1440		if (*cp == '#')
1441			goto nextline;
1442
1443		/*
1444		 * Set defaults.
1445		 */
1446		has_host = FALSE;
1447		anon = def_anon;
1448		exflags = MNT_EXPORTED;
1449		got_nondir = 0;
1450		opt_flags = 0;
1451		ep = (struct exportlist *)NULL;
1452		dirp = NULL;
1453
1454		/*
1455		 * Handle the V4 root dir.
1456		 */
1457		if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1458			/*
1459			 * V4: just indicates that it is the v4 root point,
1460			 * so skip over that and set v4root_phase.
1461			 */
1462			if (v4root_phase > 0) {
1463				syslog(LOG_ERR, "V4:duplicate line, ignored");
1464				goto nextline;
1465			}
1466			v4root_phase = 1;
1467			cp += 3;
1468			nextfield(&cp, &endcp);
1469		}
1470
1471		/*
1472		 * Create new exports list entry
1473		 */
1474		len = endcp-cp;
1475		tgrp = grp = get_grp();
1476		while (len > 0) {
1477			if (len > MNTNAMLEN) {
1478			    getexp_err(ep, tgrp, "mountpoint too long");
1479			    goto nextline;
1480			}
1481			if (*cp == '-') {
1482			    if (ep == (struct exportlist *)NULL) {
1483				getexp_err(ep, tgrp,
1484				    "flag before export path definition");
1485				goto nextline;
1486			    }
1487			    if (debug)
1488				warnx("doing opt %s", cp);
1489			    got_nondir = 1;
1490			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
1491				&exflags, &anon)) {
1492				getexp_err(ep, tgrp, NULL);
1493				goto nextline;
1494			    }
1495			} else if (*cp == '/') {
1496			    savedc = *endcp;
1497			    *endcp = '\0';
1498			    if (v4root_phase > 1) {
1499				    if (dirp != NULL) {
1500					getexp_err(ep, tgrp, "Multiple V4 dirs");
1501					goto nextline;
1502				    }
1503			    }
1504			    if (check_dirpath(cp) &&
1505				statfs(cp, &fsb) >= 0) {
1506				if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0)
1507				    syslog(LOG_ERR, "Warning: exporting of "
1508					"automounted fs %s not supported", cp);
1509				if (got_nondir) {
1510				    getexp_err(ep, tgrp, "dirs must be first");
1511				    goto nextline;
1512				}
1513				if (v4root_phase == 1) {
1514				    if (dirp != NULL) {
1515					getexp_err(ep, tgrp, "Multiple V4 dirs");
1516					goto nextline;
1517				    }
1518				    if (strlen(v4root_dirpath) == 0) {
1519					strlcpy(v4root_dirpath, cp,
1520					    sizeof (v4root_dirpath));
1521				    } else if (strcmp(v4root_dirpath, cp)
1522					!= 0) {
1523					syslog(LOG_ERR,
1524					    "different V4 dirpath %s", cp);
1525					getexp_err(ep, tgrp, NULL);
1526					goto nextline;
1527				    }
1528				    dirp = cp;
1529				    v4root_phase = 2;
1530				    got_nondir = 1;
1531				    ep = get_exp();
1532				} else {
1533				    if (ep) {
1534					if (ep->ex_fs.val[0] !=
1535					    fsb.f_fsid.val[0] ||
1536					    ep->ex_fs.val[1] !=
1537					    fsb.f_fsid.val[1]) {
1538						getexp_err(ep, tgrp,
1539						    "fsid mismatch");
1540						goto nextline;
1541					}
1542				    } else {
1543					/*
1544					 * See if this directory is already
1545					 * in the list.
1546					 */
1547					ep = ex_search(&fsb.f_fsid, &exphead);
1548					if (ep == (struct exportlist *)NULL) {
1549					    ep = get_exp();
1550					    ep->ex_fs = fsb.f_fsid;
1551					    ep->ex_fsdir = strdup(fsb.f_mntonname);
1552					    if (ep->ex_fsdir == NULL)
1553						out_of_mem();
1554					    if (debug)
1555						warnx(
1556						  "making new ep fs=0x%x,0x%x",
1557						  fsb.f_fsid.val[0],
1558						  fsb.f_fsid.val[1]);
1559					} else if (debug)
1560					    warnx("found ep fs=0x%x,0x%x",
1561						fsb.f_fsid.val[0],
1562						fsb.f_fsid.val[1]);
1563				    }
1564
1565				    /*
1566				     * Add dirpath to export mount point.
1567				     */
1568				    dirp = add_expdir(&dirhead, cp, len);
1569				    dirplen = len;
1570				}
1571			    } else {
1572				getexp_err(ep, tgrp,
1573				    "symbolic link in export path or statfs failed");
1574				goto nextline;
1575			    }
1576			    *endcp = savedc;
1577			} else {
1578			    savedc = *endcp;
1579			    *endcp = '\0';
1580			    got_nondir = 1;
1581			    if (ep == (struct exportlist *)NULL) {
1582				getexp_err(ep, tgrp,
1583				    "host(s) before export path definition");
1584				goto nextline;
1585			    }
1586
1587			    /*
1588			     * Get the host or netgroup.
1589			     */
1590			    setnetgrent(cp);
1591			    netgrp = getnetgrent(&hst, &usr, &dom);
1592			    do {
1593				if (has_host) {
1594				    grp->gr_next = get_grp();
1595				    grp = grp->gr_next;
1596				}
1597				if (netgrp) {
1598				    if (hst == 0) {
1599					syslog(LOG_ERR,
1600				"null hostname in netgroup %s, skipping", cp);
1601					grp->gr_type = GT_IGNORE;
1602				    } else if (get_host(hst, grp, tgrp)) {
1603					syslog(LOG_ERR,
1604			"bad host %s in netgroup %s, skipping", hst, cp);
1605					grp->gr_type = GT_IGNORE;
1606				    }
1607				} else if (get_host(cp, grp, tgrp)) {
1608				    syslog(LOG_ERR, "bad host %s, skipping", cp);
1609				    grp->gr_type = GT_IGNORE;
1610				}
1611				has_host = TRUE;
1612			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
1613			    endnetgrent();
1614			    *endcp = savedc;
1615			}
1616			cp = endcp;
1617			nextfield(&cp, &endcp);
1618			len = endcp - cp;
1619		}
1620		if (check_options(dirhead)) {
1621			getexp_err(ep, tgrp, NULL);
1622			goto nextline;
1623		}
1624		if (!has_host) {
1625			grp->gr_type = GT_DEFAULT;
1626			if (debug)
1627				warnx("adding a default entry");
1628
1629		/*
1630		 * Don't allow a network export coincide with a list of
1631		 * host(s) on the same line.
1632		 */
1633		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1634			getexp_err(ep, tgrp, "network/host conflict");
1635			goto nextline;
1636
1637		/*
1638		 * If an export list was specified on this line, make sure
1639		 * that we have at least one valid entry, otherwise skip it.
1640		 */
1641		} else {
1642			grp = tgrp;
1643			while (grp && grp->gr_type == GT_IGNORE)
1644				grp = grp->gr_next;
1645			if (! grp) {
1646			    getexp_err(ep, tgrp, "no valid entries");
1647			    goto nextline;
1648			}
1649		}
1650
1651		if (v4root_phase == 1) {
1652			getexp_err(ep, tgrp, "V4:root, no dirp, ignored");
1653			goto nextline;
1654		}
1655
1656		/*
1657		 * Loop through hosts, pushing the exports into the kernel.
1658		 * After loop, tgrp points to the start of the list and
1659		 * grp points to the last entry in the list.
1660		 */
1661		grp = tgrp;
1662		do {
1663			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
1664			    &fsb)) {
1665				getexp_err(ep, tgrp, NULL);
1666				goto nextline;
1667			}
1668		} while (grp->gr_next && (grp = grp->gr_next));
1669
1670		/*
1671		 * For V4: don't enter in mount lists.
1672		 */
1673		if (v4root_phase > 0 && v4root_phase <= 2) {
1674			/*
1675			 * Since these structures aren't used by mountd,
1676			 * free them up now.
1677			 */
1678			if (ep != NULL)
1679				free_exp(ep);
1680			while (tgrp != NULL) {
1681				grp = tgrp;
1682				tgrp = tgrp->gr_next;
1683				free_grp(grp);
1684			}
1685			goto nextline;
1686		}
1687
1688		/*
1689		 * Success. Update the data structures.
1690		 */
1691		if (has_host) {
1692			hang_dirp(dirhead, tgrp, ep, opt_flags);
1693			grp->gr_next = grphead;
1694			grphead = tgrp;
1695		} else {
1696			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1697				opt_flags);
1698			free_grp(grp);
1699		}
1700		dirhead = (struct dirlist *)NULL;
1701		if ((ep->ex_flag & EX_LINKED) == 0) {
1702			insert_exports(ep, &exphead);
1703
1704			ep->ex_flag |= EX_LINKED;
1705		}
1706nextline:
1707		v4root_phase = 0;
1708		if (dirhead) {
1709			free_dir(dirhead);
1710			dirhead = (struct dirlist *)NULL;
1711		}
1712	}
1713}
1714
1715/*
1716 * Get the export list from all specified files
1717 */
1718static void
1719get_exportlist(void)
1720{
1721	struct grouplist *grp, *tgrp;
1722	struct export_args export;
1723	struct iovec *iov;
1724	struct statfs *mntbufp;
1725	char errmsg[255];
1726	int num, i;
1727	int iovlen;
1728	struct nfsex_args eargs;
1729
1730	if (suspend_nfsd != 0)
1731		(void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
1732	v4root_dirpath[0] = '\0';
1733	bzero(&export, sizeof(export));
1734	export.ex_flags = MNT_DELEXPORT;
1735	iov = NULL;
1736	iovlen = 0;
1737	bzero(errmsg, sizeof(errmsg));
1738
1739	/*
1740	 * First, get rid of the old list
1741	 */
1742	free_exports(&exphead);
1743
1744	grp = grphead;
1745	while (grp) {
1746		tgrp = grp;
1747		grp = grp->gr_next;
1748		free_grp(tgrp);
1749	}
1750	grphead = (struct grouplist *)NULL;
1751
1752	/*
1753	 * and the old V4 root dir.
1754	 */
1755	bzero(&eargs, sizeof (eargs));
1756	eargs.export.ex_flags = MNT_DELEXPORT;
1757	if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 &&
1758	    errno != ENOENT)
1759		syslog(LOG_ERR, "Can't delete exports for V4:");
1760
1761	/*
1762	 * and clear flag that notes if a public fh has been exported.
1763	 */
1764	has_publicfh = 0;
1765
1766	/*
1767	 * And delete exports that are in the kernel for all local
1768	 * filesystems.
1769	 * XXX: Should know how to handle all local exportable filesystems.
1770	 */
1771	num = getmntinfo(&mntbufp, MNT_NOWAIT);
1772
1773	if (num > 0) {
1774		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1775		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1776		build_iovec(&iov, &iovlen, "from", NULL, 0);
1777		build_iovec(&iov, &iovlen, "update", NULL, 0);
1778		build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
1779		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1780	}
1781
1782	for (i = 0; i < num; i++)
1783		delete_export(iov, iovlen, &mntbufp[i], errmsg);
1784
1785	if (iov != NULL) {
1786		/* Free strings allocated by strdup() in getmntopts.c */
1787		free(iov[0].iov_base); /* fstype */
1788		free(iov[2].iov_base); /* fspath */
1789		free(iov[4].iov_base); /* from */
1790		free(iov[6].iov_base); /* update */
1791		free(iov[8].iov_base); /* export */
1792		free(iov[10].iov_base); /* errmsg */
1793
1794		/* free iov, allocated by realloc() */
1795		free(iov);
1796		iovlen = 0;
1797	}
1798
1799	read_exportfile();
1800
1801	/*
1802	 * If there was no public fh, clear any previous one set.
1803	 */
1804	if (has_publicfh == 0)
1805		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
1806
1807	/* Resume the nfsd. If they weren't suspended, this is harmless. */
1808	(void)nfssvc(NFSSVC_RESUMENFSD, NULL);
1809}
1810
1811/*
1812 * Insert an export entry in the appropriate list.
1813 */
1814static void
1815insert_exports(struct exportlist *ep, struct exportlisthead *exhp)
1816{
1817
1818	SLIST_INSERT_HEAD(exhp, ep, entries);
1819}
1820
1821/*
1822 * Free up the exports lists passed in as arguments.
1823 */
1824static void
1825free_exports(struct exportlisthead *exhp)
1826{
1827	struct exportlist *ep, *ep2;
1828
1829	SLIST_FOREACH_SAFE(ep, exhp, entries, ep2) {
1830		SLIST_REMOVE(exhp, ep, exportlist, entries);
1831		free_exp(ep);
1832	}
1833	SLIST_INIT(exhp);
1834}
1835
1836/*
1837 * Read the exports file(s) and call get_exportlist_one() for each line.
1838 */
1839static void
1840read_exportfile(void)
1841{
1842	int done, i;
1843
1844	/*
1845	 * Read in the exports file and build the list, calling
1846	 * nmount() as we go along to push the export rules into the kernel.
1847	 */
1848	done = 0;
1849	for (i = 0; exnames[i] != NULL; i++) {
1850		if (debug)
1851			warnx("reading exports from %s", exnames[i]);
1852		if ((exp_file = fopen(exnames[i], "r")) == NULL) {
1853			syslog(LOG_WARNING, "can't open %s", exnames[i]);
1854			continue;
1855		}
1856		get_exportlist_one();
1857		fclose(exp_file);
1858		done++;
1859	}
1860	if (done == 0) {
1861		syslog(LOG_ERR, "can't open any exports file");
1862		exit(2);
1863	}
1864}
1865
1866/*
1867 * Delete an exports entry.
1868 */
1869static void
1870delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg)
1871{
1872	struct xvfsconf vfc;
1873
1874	if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
1875		syslog(LOG_ERR, "getvfsbyname() failed for %s",
1876		    fsp->f_fstypename);
1877		return;
1878	}
1879
1880	/*
1881	 * We do not need to delete "export" flag from
1882	 * filesystems that do not have it set.
1883	 */
1884	if (!(fsp->f_flags & MNT_EXPORTED))
1885		return;
1886	/*
1887	 * Do not delete export for network filesystem by
1888	 * passing "export" arg to nmount().
1889	 * It only makes sense to do this for local filesystems.
1890	 */
1891	if (vfc.vfc_flags & VFCF_NETWORK)
1892		return;
1893
1894	iov[1].iov_base = fsp->f_fstypename;
1895	iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
1896	iov[3].iov_base = fsp->f_mntonname;
1897	iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
1898	iov[5].iov_base = fsp->f_mntfromname;
1899	iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
1900	errmsg[0] = '\0';
1901
1902	/*
1903	 * EXDEV is returned when path exists but is not a
1904	 * mount point.  May happens if raced with unmount.
1905	 */
1906	if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT &&
1907	    errno != ENOTSUP && errno != EXDEV) {
1908		syslog(LOG_ERR,
1909		    "can't delete exports for %s: %m %s",
1910		    fsp->f_mntonname, errmsg);
1911	}
1912}
1913
1914/*
1915 * Allocate an export list element
1916 */
1917static struct exportlist *
1918get_exp(void)
1919{
1920	struct exportlist *ep;
1921
1922	ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
1923	if (ep == (struct exportlist *)NULL)
1924		out_of_mem();
1925	return (ep);
1926}
1927
1928/*
1929 * Allocate a group list element
1930 */
1931static struct grouplist *
1932get_grp(void)
1933{
1934	struct grouplist *gp;
1935
1936	gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
1937	if (gp == (struct grouplist *)NULL)
1938		out_of_mem();
1939	return (gp);
1940}
1941
1942/*
1943 * Clean up upon an error in get_exportlist().
1944 */
1945static void
1946getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason)
1947{
1948	struct grouplist *tgrp;
1949
1950	if (!(opt_flags & OP_QUIET)) {
1951		if (reason != NULL)
1952			syslog(LOG_ERR, "bad exports list line '%s': %s", line,
1953			    reason);
1954		else
1955			syslog(LOG_ERR, "bad exports list line '%s'", line);
1956	}
1957	if (ep && (ep->ex_flag & EX_LINKED) == 0)
1958		free_exp(ep);
1959	while (grp) {
1960		tgrp = grp;
1961		grp = grp->gr_next;
1962		free_grp(tgrp);
1963	}
1964}
1965
1966/*
1967 * Search the export list for a matching fs.
1968 */
1969static struct exportlist *
1970ex_search(fsid_t *fsid, struct exportlisthead *exhp)
1971{
1972	struct exportlist *ep;
1973
1974	SLIST_FOREACH(ep, exhp, entries) {
1975		if (ep->ex_fs.val[0] == fsid->val[0] &&
1976		    ep->ex_fs.val[1] == fsid->val[1])
1977			return (ep);
1978	}
1979
1980	return (ep);
1981}
1982
1983/*
1984 * Add a directory path to the list.
1985 */
1986static char *
1987add_expdir(struct dirlist **dpp, char *cp, int len)
1988{
1989	struct dirlist *dp;
1990
1991	dp = malloc(sizeof (struct dirlist));
1992	if (dp == (struct dirlist *)NULL)
1993		out_of_mem();
1994	dp->dp_left = *dpp;
1995	dp->dp_right = (struct dirlist *)NULL;
1996	dp->dp_flag = 0;
1997	dp->dp_hosts = (struct hostlist *)NULL;
1998	dp->dp_dirp = strndup(cp, len);
1999	if (dp->dp_dirp == NULL)
2000		out_of_mem();
2001	*dpp = dp;
2002	return (dp->dp_dirp);
2003}
2004
2005/*
2006 * Hang the dir list element off the dirpath binary tree as required
2007 * and update the entry for host.
2008 */
2009static void
2010hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
2011	int flags)
2012{
2013	struct hostlist *hp;
2014	struct dirlist *dp2;
2015
2016	if (flags & OP_ALLDIRS) {
2017		if (ep->ex_defdir)
2018			free((caddr_t)dp);
2019		else
2020			ep->ex_defdir = dp;
2021		if (grp == (struct grouplist *)NULL) {
2022			ep->ex_defdir->dp_flag |= DP_DEFSET;
2023			/* Save the default security flavors list. */
2024			ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2025			if (ep->ex_numsecflavors > 0)
2026				memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2027				    sizeof(ep->ex_secflavors));
2028		} else while (grp) {
2029			hp = get_ht();
2030			hp->ht_grp = grp;
2031			hp->ht_next = ep->ex_defdir->dp_hosts;
2032			ep->ex_defdir->dp_hosts = hp;
2033			/* Save the security flavors list for this host set. */
2034			grp->gr_numsecflavors = ep->ex_numsecflavors;
2035			if (ep->ex_numsecflavors > 0)
2036				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2037				    sizeof(ep->ex_secflavors));
2038			grp = grp->gr_next;
2039		}
2040	} else {
2041
2042		/*
2043		 * Loop through the directories adding them to the tree.
2044		 */
2045		while (dp) {
2046			dp2 = dp->dp_left;
2047			add_dlist(&ep->ex_dirl, dp, grp, flags, ep);
2048			dp = dp2;
2049		}
2050	}
2051}
2052
2053/*
2054 * Traverse the binary tree either updating a node that is already there
2055 * for the new directory or adding the new node.
2056 */
2057static void
2058add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
2059	int flags, struct exportlist *ep)
2060{
2061	struct dirlist *dp;
2062	struct hostlist *hp;
2063	int cmp;
2064
2065	dp = *dpp;
2066	if (dp) {
2067		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
2068		if (cmp > 0) {
2069			add_dlist(&dp->dp_left, newdp, grp, flags, ep);
2070			return;
2071		} else if (cmp < 0) {
2072			add_dlist(&dp->dp_right, newdp, grp, flags, ep);
2073			return;
2074		} else
2075			free((caddr_t)newdp);
2076	} else {
2077		dp = newdp;
2078		dp->dp_left = (struct dirlist *)NULL;
2079		*dpp = dp;
2080	}
2081	if (grp) {
2082
2083		/*
2084		 * Hang all of the host(s) off of the directory point.
2085		 */
2086		do {
2087			hp = get_ht();
2088			hp->ht_grp = grp;
2089			hp->ht_next = dp->dp_hosts;
2090			dp->dp_hosts = hp;
2091			/* Save the security flavors list for this host set. */
2092			grp->gr_numsecflavors = ep->ex_numsecflavors;
2093			if (ep->ex_numsecflavors > 0)
2094				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2095				    sizeof(ep->ex_secflavors));
2096			grp = grp->gr_next;
2097		} while (grp);
2098	} else {
2099		dp->dp_flag |= DP_DEFSET;
2100		/* Save the default security flavors list. */
2101		ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2102		if (ep->ex_numsecflavors > 0)
2103			memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2104			    sizeof(ep->ex_secflavors));
2105	}
2106}
2107
2108/*
2109 * Search for a dirpath on the export point.
2110 */
2111static struct dirlist *
2112dirp_search(struct dirlist *dp, char *dirp)
2113{
2114	int cmp;
2115
2116	if (dp) {
2117		cmp = strcmp(dp->dp_dirp, dirp);
2118		if (cmp > 0)
2119			return (dirp_search(dp->dp_left, dirp));
2120		else if (cmp < 0)
2121			return (dirp_search(dp->dp_right, dirp));
2122		else
2123			return (dp);
2124	}
2125	return (dp);
2126}
2127
2128/*
2129 * Scan for a host match in a directory tree.
2130 */
2131static int
2132chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2133	int *hostsetp, int *numsecflavors, int **secflavorsp)
2134{
2135	struct hostlist *hp;
2136	struct grouplist *grp;
2137	struct addrinfo *ai;
2138
2139	if (dp) {
2140		if (dp->dp_flag & DP_DEFSET)
2141			*defsetp = dp->dp_flag;
2142		hp = dp->dp_hosts;
2143		while (hp) {
2144			grp = hp->ht_grp;
2145			switch (grp->gr_type) {
2146			case GT_HOST:
2147				ai = grp->gr_ptr.gt_addrinfo;
2148				for (; ai; ai = ai->ai_next) {
2149					if (!sacmp(ai->ai_addr, saddr, NULL)) {
2150						*hostsetp =
2151						    (hp->ht_flag | DP_HOSTSET);
2152						if (numsecflavors != NULL) {
2153							*numsecflavors =
2154							    grp->gr_numsecflavors;
2155							*secflavorsp =
2156							    grp->gr_secflavors;
2157						}
2158						return (1);
2159					}
2160				}
2161				break;
2162			case GT_NET:
2163				if (!sacmp(saddr, (struct sockaddr *)
2164				    &grp->gr_ptr.gt_net.nt_net,
2165				    (struct sockaddr *)
2166				    &grp->gr_ptr.gt_net.nt_mask)) {
2167					*hostsetp = (hp->ht_flag | DP_HOSTSET);
2168					if (numsecflavors != NULL) {
2169						*numsecflavors =
2170						    grp->gr_numsecflavors;
2171						*secflavorsp =
2172						    grp->gr_secflavors;
2173					}
2174					return (1);
2175				}
2176				break;
2177			}
2178			hp = hp->ht_next;
2179		}
2180	}
2181	return (0);
2182}
2183
2184/*
2185 * Scan tree for a host that matches the address.
2186 */
2187static int
2188scan_tree(struct dirlist *dp, struct sockaddr *saddr)
2189{
2190	int defset, hostset;
2191
2192	if (dp) {
2193		if (scan_tree(dp->dp_left, saddr))
2194			return (1);
2195		if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
2196			return (1);
2197		if (scan_tree(dp->dp_right, saddr))
2198			return (1);
2199	}
2200	return (0);
2201}
2202
2203/*
2204 * Traverse the dirlist tree and free it up.
2205 */
2206static void
2207free_dir(struct dirlist *dp)
2208{
2209
2210	if (dp) {
2211		free_dir(dp->dp_left);
2212		free_dir(dp->dp_right);
2213		free_host(dp->dp_hosts);
2214		free(dp->dp_dirp);
2215		free(dp);
2216	}
2217}
2218
2219/*
2220 * Parse a colon separated list of security flavors
2221 */
2222static int
2223parsesec(char *seclist, struct exportlist *ep)
2224{
2225	char *cp, savedc;
2226	int flavor;
2227
2228	ep->ex_numsecflavors = 0;
2229	for (;;) {
2230		cp = strchr(seclist, ':');
2231		if (cp) {
2232			savedc = *cp;
2233			*cp = '\0';
2234		}
2235
2236		if (!strcmp(seclist, "sys"))
2237			flavor = AUTH_SYS;
2238		else if (!strcmp(seclist, "krb5"))
2239			flavor = RPCSEC_GSS_KRB5;
2240		else if (!strcmp(seclist, "krb5i"))
2241			flavor = RPCSEC_GSS_KRB5I;
2242		else if (!strcmp(seclist, "krb5p"))
2243			flavor = RPCSEC_GSS_KRB5P;
2244		else {
2245			if (cp)
2246				*cp = savedc;
2247			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2248			return (1);
2249		}
2250		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2251			if (cp)
2252				*cp = savedc;
2253			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2254			return (1);
2255		}
2256		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2257		ep->ex_numsecflavors++;
2258		if (cp) {
2259			*cp = savedc;
2260			seclist = cp + 1;
2261		} else {
2262			break;
2263		}
2264	}
2265	return (0);
2266}
2267
2268/*
2269 * Parse the option string and update fields.
2270 * Option arguments may either be -<option>=<value> or
2271 * -<option> <value>
2272 */
2273static int
2274do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2275	int *has_hostp, int *exflagsp, struct xucred *cr)
2276{
2277	char *cpoptarg, *cpoptend;
2278	char *cp, *endcp, *cpopt, savedc, savedc2;
2279	int allflag, usedarg;
2280
2281	savedc2 = '\0';
2282	cpopt = *cpp;
2283	cpopt++;
2284	cp = *endcpp;
2285	savedc = *cp;
2286	*cp = '\0';
2287	while (cpopt && *cpopt) {
2288		allflag = 1;
2289		usedarg = -2;
2290		if ((cpoptend = strchr(cpopt, ','))) {
2291			*cpoptend++ = '\0';
2292			if ((cpoptarg = strchr(cpopt, '=')))
2293				*cpoptarg++ = '\0';
2294		} else {
2295			if ((cpoptarg = strchr(cpopt, '=')))
2296				*cpoptarg++ = '\0';
2297			else {
2298				*cp = savedc;
2299				nextfield(&cp, &endcp);
2300				**endcpp = '\0';
2301				if (endcp > cp && *cp != '-') {
2302					cpoptarg = cp;
2303					savedc2 = *endcp;
2304					*endcp = '\0';
2305					usedarg = 0;
2306				}
2307			}
2308		}
2309		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
2310			*exflagsp |= MNT_EXRDONLY;
2311		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
2312		    !(allflag = strcmp(cpopt, "mapall")) ||
2313		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
2314			usedarg++;
2315			parsecred(cpoptarg, cr);
2316			if (allflag == 0) {
2317				*exflagsp |= MNT_EXPORTANON;
2318				opt_flags |= OP_MAPALL;
2319			} else
2320				opt_flags |= OP_MAPROOT;
2321		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
2322		    !strcmp(cpopt, "m"))) {
2323			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
2324				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
2325				return (1);
2326			}
2327			usedarg++;
2328			opt_flags |= OP_MASK;
2329		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
2330			!strcmp(cpopt, "n"))) {
2331			if (strchr(cpoptarg, '/') != NULL) {
2332				if (debug)
2333					fprintf(stderr, "setting OP_MASKLEN\n");
2334				opt_flags |= OP_MASKLEN;
2335			}
2336			if (grp->gr_type != GT_NULL) {
2337				syslog(LOG_ERR, "network/host conflict");
2338				return (1);
2339			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
2340				syslog(LOG_ERR, "bad net: %s", cpoptarg);
2341				return (1);
2342			}
2343			grp->gr_type = GT_NET;
2344			*has_hostp = 1;
2345			usedarg++;
2346			opt_flags |= OP_NET;
2347		} else if (!strcmp(cpopt, "alldirs")) {
2348			opt_flags |= OP_ALLDIRS;
2349		} else if (!strcmp(cpopt, "public")) {
2350			*exflagsp |= MNT_EXPUBLIC;
2351		} else if (!strcmp(cpopt, "webnfs")) {
2352			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
2353			opt_flags |= OP_MAPALL;
2354		} else if (cpoptarg && !strcmp(cpopt, "index")) {
2355			ep->ex_indexfile = strdup(cpoptarg);
2356		} else if (!strcmp(cpopt, "quiet")) {
2357			opt_flags |= OP_QUIET;
2358		} else if (cpoptarg && !strcmp(cpopt, "sec")) {
2359			if (parsesec(cpoptarg, ep))
2360				return (1);
2361			opt_flags |= OP_SEC;
2362			usedarg++;
2363		} else {
2364			syslog(LOG_ERR, "bad opt %s", cpopt);
2365			return (1);
2366		}
2367		if (usedarg >= 0) {
2368			*endcp = savedc2;
2369			**endcpp = savedc;
2370			if (usedarg > 0) {
2371				*cpp = cp;
2372				*endcpp = endcp;
2373			}
2374			return (0);
2375		}
2376		cpopt = cpoptend;
2377	}
2378	**endcpp = savedc;
2379	return (0);
2380}
2381
2382/*
2383 * Translate a character string to the corresponding list of network
2384 * addresses for a hostname.
2385 */
2386static int
2387get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
2388{
2389	struct grouplist *checkgrp;
2390	struct addrinfo *ai, *tai, hints;
2391	int ecode;
2392	char host[NI_MAXHOST];
2393
2394	if (grp->gr_type != GT_NULL) {
2395		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
2396		return (1);
2397	}
2398	memset(&hints, 0, sizeof hints);
2399	hints.ai_flags = AI_CANONNAME;
2400	hints.ai_protocol = IPPROTO_UDP;
2401	ecode = getaddrinfo(cp, NULL, &hints, &ai);
2402	if (ecode != 0) {
2403		syslog(LOG_ERR,"can't get address info for host %s", cp);
2404		return 1;
2405	}
2406	grp->gr_ptr.gt_addrinfo = ai;
2407	while (ai != NULL) {
2408		if (ai->ai_canonname == NULL) {
2409			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2410			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
2411				strlcpy(host, "?", sizeof(host));
2412			ai->ai_canonname = strdup(host);
2413			ai->ai_flags |= AI_CANONNAME;
2414		}
2415		if (debug)
2416			fprintf(stderr, "got host %s\n", ai->ai_canonname);
2417		/*
2418		 * Sanity check: make sure we don't already have an entry
2419		 * for this host in the grouplist.
2420		 */
2421		for (checkgrp = tgrp; checkgrp != NULL;
2422		    checkgrp = checkgrp->gr_next) {
2423			if (checkgrp->gr_type != GT_HOST)
2424				continue;
2425			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
2426			    tai = tai->ai_next) {
2427				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
2428					continue;
2429				if (debug)
2430					fprintf(stderr,
2431					    "ignoring duplicate host %s\n",
2432					    ai->ai_canonname);
2433				grp->gr_type = GT_IGNORE;
2434				return (0);
2435			}
2436		}
2437		ai = ai->ai_next;
2438	}
2439	grp->gr_type = GT_HOST;
2440	return (0);
2441}
2442
2443/*
2444 * Free up an exports list component
2445 */
2446static void
2447free_exp(struct exportlist *ep)
2448{
2449
2450	if (ep->ex_defdir) {
2451		free_host(ep->ex_defdir->dp_hosts);
2452		free((caddr_t)ep->ex_defdir);
2453	}
2454	if (ep->ex_fsdir)
2455		free(ep->ex_fsdir);
2456	if (ep->ex_indexfile)
2457		free(ep->ex_indexfile);
2458	free_dir(ep->ex_dirl);
2459	free((caddr_t)ep);
2460}
2461
2462/*
2463 * Free hosts.
2464 */
2465static void
2466free_host(struct hostlist *hp)
2467{
2468	struct hostlist *hp2;
2469
2470	while (hp) {
2471		hp2 = hp;
2472		hp = hp->ht_next;
2473		free((caddr_t)hp2);
2474	}
2475}
2476
2477static struct hostlist *
2478get_ht(void)
2479{
2480	struct hostlist *hp;
2481
2482	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
2483	if (hp == (struct hostlist *)NULL)
2484		out_of_mem();
2485	hp->ht_next = (struct hostlist *)NULL;
2486	hp->ht_flag = 0;
2487	return (hp);
2488}
2489
2490/*
2491 * Out of memory, fatal
2492 */
2493static void
2494out_of_mem(void)
2495{
2496
2497	syslog(LOG_ERR, "out of memory");
2498	exit(2);
2499}
2500
2501/*
2502 * Do the nmount() syscall with the update flag to push the export info into
2503 * the kernel.
2504 */
2505static int
2506do_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
2507    struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
2508{
2509	struct statfs fsb1;
2510	struct addrinfo *ai;
2511	struct export_args *eap;
2512	char errmsg[255];
2513	char *cp;
2514	int done;
2515	char savedc;
2516	struct iovec *iov;
2517	int i, iovlen;
2518	int ret;
2519	struct nfsex_args nfsea;
2520
2521	eap = &nfsea.export;
2522
2523	cp = NULL;
2524	savedc = '\0';
2525	iov = NULL;
2526	iovlen = 0;
2527	ret = 0;
2528
2529	bzero(eap, sizeof (struct export_args));
2530	bzero(errmsg, sizeof(errmsg));
2531	eap->ex_flags = exflags;
2532	eap->ex_anon = *anoncrp;
2533	eap->ex_indexfile = ep->ex_indexfile;
2534	if (grp->gr_type == GT_HOST)
2535		ai = grp->gr_ptr.gt_addrinfo;
2536	else
2537		ai = NULL;
2538	eap->ex_numsecflavors = ep->ex_numsecflavors;
2539	for (i = 0; i < eap->ex_numsecflavors; i++)
2540		eap->ex_secflavors[i] = ep->ex_secflavors[i];
2541	if (eap->ex_numsecflavors == 0) {
2542		eap->ex_numsecflavors = 1;
2543		eap->ex_secflavors[0] = AUTH_SYS;
2544	}
2545	done = FALSE;
2546
2547	if (v4root_phase == 0) {
2548		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2549		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2550		build_iovec(&iov, &iovlen, "from", NULL, 0);
2551		build_iovec(&iov, &iovlen, "update", NULL, 0);
2552		build_iovec(&iov, &iovlen, "export", eap,
2553		    sizeof (struct export_args));
2554		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
2555	}
2556
2557	while (!done) {
2558		switch (grp->gr_type) {
2559		case GT_HOST:
2560			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
2561				goto skip;
2562			eap->ex_addr = ai->ai_addr;
2563			eap->ex_addrlen = ai->ai_addrlen;
2564			eap->ex_masklen = 0;
2565			break;
2566		case GT_NET:
2567			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
2568			    have_v6 == 0)
2569				goto skip;
2570			eap->ex_addr =
2571			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
2572			eap->ex_addrlen =
2573			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
2574			eap->ex_mask =
2575			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
2576			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
2577			break;
2578		case GT_DEFAULT:
2579			eap->ex_addr = NULL;
2580			eap->ex_addrlen = 0;
2581			eap->ex_mask = NULL;
2582			eap->ex_masklen = 0;
2583			break;
2584		case GT_IGNORE:
2585			ret = 0;
2586			goto error_exit;
2587			break;
2588		default:
2589			syslog(LOG_ERR, "bad grouptype");
2590			if (cp)
2591				*cp = savedc;
2592			ret = 1;
2593			goto error_exit;
2594		}
2595
2596		/*
2597		 * For V4:, use the nfssvc() syscall, instead of mount().
2598		 */
2599		if (v4root_phase == 2) {
2600			nfsea.fspec = v4root_dirpath;
2601			if (nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) {
2602				syslog(LOG_ERR, "Exporting V4: failed");
2603				return (2);
2604			}
2605		} else {
2606			/*
2607			 * XXX:
2608			 * Maybe I should just use the fsb->f_mntonname path
2609			 * instead of looping back up the dirp to the mount
2610			 * point??
2611			 * Also, needs to know how to export all types of local
2612			 * exportable filesystems and not just "ufs".
2613			 */
2614			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
2615			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
2616			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
2617			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
2618			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
2619			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
2620			errmsg[0] = '\0';
2621
2622			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
2623				if (cp)
2624					*cp-- = savedc;
2625				else
2626					cp = dirp + dirplen - 1;
2627				if (opt_flags & OP_QUIET) {
2628					ret = 1;
2629					goto error_exit;
2630				}
2631				if (errno == EPERM) {
2632					if (debug)
2633						warnx("can't change attributes for %s: %s",
2634						    dirp, errmsg);
2635					syslog(LOG_ERR,
2636					   "can't change attributes for %s: %s",
2637					    dirp, errmsg);
2638					ret = 1;
2639					goto error_exit;
2640				}
2641				if (opt_flags & OP_ALLDIRS) {
2642					if (errno == EINVAL)
2643						syslog(LOG_ERR,
2644		"-alldirs requested but %s is not a filesystem mountpoint",
2645						    dirp);
2646					else
2647						syslog(LOG_ERR,
2648						    "could not remount %s: %m",
2649						    dirp);
2650					ret = 1;
2651					goto error_exit;
2652				}
2653				/* back up over the last component */
2654				while (*cp == '/' && cp > dirp)
2655					cp--;
2656				while (*(cp - 1) != '/' && cp > dirp)
2657					cp--;
2658				if (cp == dirp) {
2659					if (debug)
2660						warnx("mnt unsucc");
2661					syslog(LOG_ERR, "can't export %s %s",
2662					    dirp, errmsg);
2663					ret = 1;
2664					goto error_exit;
2665				}
2666				savedc = *cp;
2667				*cp = '\0';
2668				/*
2669				 * Check that we're still on the same
2670				 * filesystem.
2671				 */
2672				if (statfs(dirp, &fsb1) != 0 ||
2673				    bcmp(&fsb1.f_fsid, &fsb->f_fsid,
2674				    sizeof (fsb1.f_fsid)) != 0) {
2675					*cp = savedc;
2676					syslog(LOG_ERR,
2677					    "can't export %s %s", dirp,
2678					    errmsg);
2679					ret = 1;
2680					goto error_exit;
2681				}
2682			}
2683		}
2684
2685		/*
2686		 * For the experimental server:
2687		 * If this is the public directory, get the file handle
2688		 * and load it into the kernel via the nfssvc() syscall.
2689		 */
2690		if ((exflags & MNT_EXPUBLIC) != 0) {
2691			fhandle_t fh;
2692			char *public_name;
2693
2694			if (eap->ex_indexfile != NULL)
2695				public_name = eap->ex_indexfile;
2696			else
2697				public_name = dirp;
2698			if (getfh(public_name, &fh) < 0)
2699				syslog(LOG_ERR,
2700				    "Can't get public fh for %s", public_name);
2701			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
2702				syslog(LOG_ERR,
2703				    "Can't set public fh for %s", public_name);
2704			else
2705				has_publicfh = 1;
2706		}
2707skip:
2708		if (ai != NULL)
2709			ai = ai->ai_next;
2710		if (ai == NULL)
2711			done = TRUE;
2712	}
2713	if (cp)
2714		*cp = savedc;
2715error_exit:
2716	/* free strings allocated by strdup() in getmntopts.c */
2717	if (iov != NULL) {
2718		free(iov[0].iov_base); /* fstype */
2719		free(iov[2].iov_base); /* fspath */
2720		free(iov[4].iov_base); /* from */
2721		free(iov[6].iov_base); /* update */
2722		free(iov[8].iov_base); /* export */
2723		free(iov[10].iov_base); /* errmsg */
2724
2725		/* free iov, allocated by realloc() */
2726		free(iov);
2727	}
2728	return (ret);
2729}
2730
2731/*
2732 * Translate a net address.
2733 *
2734 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
2735 */
2736static int
2737get_net(char *cp, struct netmsk *net, int maskflg)
2738{
2739	struct netent *np = NULL;
2740	char *name, *p, *prefp;
2741	struct sockaddr_in sin;
2742	struct sockaddr *sa = NULL;
2743	struct addrinfo hints, *ai = NULL;
2744	char netname[NI_MAXHOST];
2745	long preflen;
2746
2747	p = prefp = NULL;
2748	if ((opt_flags & OP_MASKLEN) && !maskflg) {
2749		p = strchr(cp, '/');
2750		*p = '\0';
2751		prefp = p + 1;
2752	}
2753
2754	/*
2755	 * Check for a numeric address first. We wish to avoid
2756	 * possible DNS lookups in getnetbyname().
2757	 */
2758	if (isxdigit(*cp) || *cp == ':') {
2759		memset(&hints, 0, sizeof hints);
2760		/* Ensure the mask and the network have the same family. */
2761		if (maskflg && (opt_flags & OP_NET))
2762			hints.ai_family = net->nt_net.ss_family;
2763		else if (!maskflg && (opt_flags & OP_HAVEMASK))
2764			hints.ai_family = net->nt_mask.ss_family;
2765		else
2766			hints.ai_family = AF_UNSPEC;
2767		hints.ai_flags = AI_NUMERICHOST;
2768		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
2769			sa = ai->ai_addr;
2770		if (sa != NULL && ai->ai_family == AF_INET) {
2771			/*
2772			 * The address in `cp' is really a network address, so
2773			 * use inet_network() to re-interpret this correctly.
2774			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
2775			 */
2776			bzero(&sin, sizeof sin);
2777			sin.sin_family = AF_INET;
2778			sin.sin_len = sizeof sin;
2779			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
2780			if (debug)
2781				fprintf(stderr, "get_net: v4 addr %s\n",
2782				    inet_ntoa(sin.sin_addr));
2783			sa = (struct sockaddr *)&sin;
2784		}
2785	}
2786	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
2787		bzero(&sin, sizeof sin);
2788		sin.sin_family = AF_INET;
2789		sin.sin_len = sizeof sin;
2790		sin.sin_addr = inet_makeaddr(np->n_net, 0);
2791		sa = (struct sockaddr *)&sin;
2792	}
2793	if (sa == NULL)
2794		goto fail;
2795
2796	if (maskflg) {
2797		/* The specified sockaddr is a mask. */
2798		if (checkmask(sa) != 0)
2799			goto fail;
2800		bcopy(sa, &net->nt_mask, sa->sa_len);
2801		opt_flags |= OP_HAVEMASK;
2802	} else {
2803		/* The specified sockaddr is a network address. */
2804		bcopy(sa, &net->nt_net, sa->sa_len);
2805
2806		/* Get a network name for the export list. */
2807		if (np) {
2808			name = np->n_name;
2809		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2810		   NULL, 0, NI_NUMERICHOST) == 0) {
2811			name = netname;
2812		} else {
2813			goto fail;
2814		}
2815		if ((net->nt_name = strdup(name)) == NULL)
2816			out_of_mem();
2817
2818		/*
2819		 * Extract a mask from either a "/<masklen>" suffix, or
2820		 * from the class of an IPv4 address.
2821		 */
2822		if (opt_flags & OP_MASKLEN) {
2823			preflen = strtol(prefp, NULL, 10);
2824			if (preflen < 0L || preflen == LONG_MAX)
2825				goto fail;
2826			bcopy(sa, &net->nt_mask, sa->sa_len);
2827			if (makemask(&net->nt_mask, (int)preflen) != 0)
2828				goto fail;
2829			opt_flags |= OP_HAVEMASK;
2830			*p = '/';
2831		} else if (sa->sa_family == AF_INET &&
2832		    (opt_flags & OP_MASK) == 0) {
2833			in_addr_t addr;
2834
2835			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
2836			if (IN_CLASSA(addr))
2837				preflen = 8;
2838			else if (IN_CLASSB(addr))
2839				preflen = 16;
2840			else if (IN_CLASSC(addr))
2841				preflen = 24;
2842			else if (IN_CLASSD(addr))
2843				preflen = 28;
2844			else
2845				preflen = 32;	/* XXX */
2846
2847			bcopy(sa, &net->nt_mask, sa->sa_len);
2848			makemask(&net->nt_mask, (int)preflen);
2849			opt_flags |= OP_HAVEMASK;
2850		}
2851	}
2852
2853	if (ai)
2854		freeaddrinfo(ai);
2855	return 0;
2856
2857fail:
2858	if (ai)
2859		freeaddrinfo(ai);
2860	return 1;
2861}
2862
2863/*
2864 * Parse out the next white space separated field
2865 */
2866static void
2867nextfield(char **cp, char **endcp)
2868{
2869	char *p;
2870	char quot = 0;
2871
2872	p = *cp;
2873	while (*p == ' ' || *p == '\t')
2874		p++;
2875	*cp = p;
2876	while (*p != '\0') {
2877		if (quot) {
2878			if (*p == quot)
2879				quot = 0;
2880		} else {
2881			if (*p == '\\' && *(p + 1) != '\0')
2882				p++;
2883			else if (*p == '\'' || *p == '"')
2884				quot = *p;
2885			else if (*p == ' ' || *p == '\t')
2886				break;
2887		}
2888		p++;
2889	};
2890	*endcp = p;
2891}
2892
2893/*
2894 * Get an exports file line. Skip over blank lines and handle line
2895 * continuations.
2896 */
2897static int
2898get_line(void)
2899{
2900	char *p, *cp;
2901	size_t len;
2902	int totlen, cont_line;
2903
2904	/*
2905	 * Loop around ignoring blank lines and getting all continuation lines.
2906	 */
2907	p = line;
2908	totlen = 0;
2909	do {
2910		if ((p = fgetln(exp_file, &len)) == NULL)
2911			return (0);
2912		cp = p + len - 1;
2913		cont_line = 0;
2914		while (cp >= p &&
2915		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
2916			if (*cp == '\\')
2917				cont_line = 1;
2918			cp--;
2919			len--;
2920		}
2921		if (cont_line) {
2922			*++cp = ' ';
2923			len++;
2924		}
2925		if (linesize < len + totlen + 1) {
2926			linesize = len + totlen + 1;
2927			line = realloc(line, linesize);
2928			if (line == NULL)
2929				out_of_mem();
2930		}
2931		memcpy(line + totlen, p, len);
2932		totlen += len;
2933		line[totlen] = '\0';
2934	} while (totlen == 0 || cont_line);
2935	return (1);
2936}
2937
2938/*
2939 * Parse a description of a credential.
2940 */
2941static void
2942parsecred(char *namelist, struct xucred *cr)
2943{
2944	char *name;
2945	int cnt;
2946	char *names;
2947	struct passwd *pw;
2948	struct group *gr;
2949	gid_t groups[XU_NGROUPS + 1];
2950	int ngroups;
2951
2952	cr->cr_version = XUCRED_VERSION;
2953	/*
2954	 * Set up the unprivileged user.
2955	 */
2956	cr->cr_uid = -2;
2957	cr->cr_groups[0] = -2;
2958	cr->cr_ngroups = 1;
2959	/*
2960	 * Get the user's password table entry.
2961	 */
2962	names = namelist;
2963	name = strsep_quote(&names, ":");
2964	/* Bug?  name could be NULL here */
2965	if (isdigit(*name) || *name == '-')
2966		pw = getpwuid(atoi(name));
2967	else
2968		pw = getpwnam(name);
2969	/*
2970	 * Credentials specified as those of a user.
2971	 */
2972	if (names == NULL) {
2973		if (pw == NULL) {
2974			syslog(LOG_ERR, "unknown user: %s", name);
2975			return;
2976		}
2977		cr->cr_uid = pw->pw_uid;
2978		ngroups = XU_NGROUPS + 1;
2979		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) {
2980			syslog(LOG_ERR, "too many groups");
2981			ngroups = XU_NGROUPS + 1;
2982		}
2983
2984		/*
2985		 * Compress out duplicate.
2986		 */
2987		cr->cr_ngroups = ngroups - 1;
2988		cr->cr_groups[0] = groups[0];
2989		for (cnt = 2; cnt < ngroups; cnt++)
2990			cr->cr_groups[cnt - 1] = groups[cnt];
2991		return;
2992	}
2993	/*
2994	 * Explicit credential specified as a colon separated list:
2995	 *	uid:gid:gid:...
2996	 */
2997	if (pw != NULL)
2998		cr->cr_uid = pw->pw_uid;
2999	else if (isdigit(*name) || *name == '-')
3000		cr->cr_uid = atoi(name);
3001	else {
3002		syslog(LOG_ERR, "unknown user: %s", name);
3003		return;
3004	}
3005	cr->cr_ngroups = 0;
3006	while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
3007		name = strsep_quote(&names, ":");
3008		if (isdigit(*name) || *name == '-') {
3009			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
3010		} else {
3011			if ((gr = getgrnam(name)) == NULL) {
3012				syslog(LOG_ERR, "unknown group: %s", name);
3013				continue;
3014			}
3015			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
3016		}
3017	}
3018	if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
3019		syslog(LOG_ERR, "too many groups");
3020}
3021
3022#define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
3023/*
3024 * Routines that maintain the remote mounttab
3025 */
3026static void
3027get_mountlist(void)
3028{
3029	struct mountlist *mlp;
3030	char *host, *dirp, *cp;
3031	char str[STRSIZ];
3032	FILE *mlfile;
3033
3034	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
3035		if (errno == ENOENT)
3036			return;
3037		else {
3038			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
3039			return;
3040		}
3041	}
3042	while (fgets(str, STRSIZ, mlfile) != NULL) {
3043		cp = str;
3044		host = strsep(&cp, " \t\n");
3045		dirp = strsep(&cp, " \t\n");
3046		if (host == NULL || dirp == NULL)
3047			continue;
3048		mlp = (struct mountlist *)malloc(sizeof (*mlp));
3049		if (mlp == (struct mountlist *)NULL)
3050			out_of_mem();
3051		strncpy(mlp->ml_host, host, MNTNAMLEN);
3052		mlp->ml_host[MNTNAMLEN] = '\0';
3053		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3054		mlp->ml_dirp[MNTPATHLEN] = '\0';
3055
3056		SLIST_INSERT_HEAD(&mlhead, mlp, next);
3057	}
3058	fclose(mlfile);
3059}
3060
3061static void
3062del_mlist(char *hostp, char *dirp)
3063{
3064	struct mountlist *mlp, *mlp2;
3065	FILE *mlfile;
3066	int fnd = 0;
3067
3068	SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) {
3069		if (!strcmp(mlp->ml_host, hostp) &&
3070		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
3071			fnd = 1;
3072			SLIST_REMOVE(&mlhead, mlp, mountlist, next);
3073			free((caddr_t)mlp);
3074		}
3075	}
3076	if (fnd) {
3077		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
3078			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
3079			return;
3080		}
3081		SLIST_FOREACH(mlp, &mlhead, next) {
3082			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3083		}
3084		fclose(mlfile);
3085	}
3086}
3087
3088static void
3089add_mlist(char *hostp, char *dirp)
3090{
3091	struct mountlist *mlp;
3092	FILE *mlfile;
3093
3094	SLIST_FOREACH(mlp, &mlhead, next) {
3095		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
3096			return;
3097	}
3098
3099	mlp = (struct mountlist *)malloc(sizeof (*mlp));
3100	if (mlp == (struct mountlist *)NULL)
3101		out_of_mem();
3102	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
3103	mlp->ml_host[MNTNAMLEN] = '\0';
3104	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3105	mlp->ml_dirp[MNTPATHLEN] = '\0';
3106	SLIST_INSERT_HEAD(&mlhead, mlp, next);
3107	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
3108		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
3109		return;
3110	}
3111	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3112	fclose(mlfile);
3113}
3114
3115/*
3116 * Free up a group list.
3117 */
3118static void
3119free_grp(struct grouplist *grp)
3120{
3121	if (grp->gr_type == GT_HOST) {
3122		if (grp->gr_ptr.gt_addrinfo != NULL)
3123			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
3124	} else if (grp->gr_type == GT_NET) {
3125		if (grp->gr_ptr.gt_net.nt_name)
3126			free(grp->gr_ptr.gt_net.nt_name);
3127	}
3128	free((caddr_t)grp);
3129}
3130
3131#ifdef DEBUG
3132static void
3133SYSLOG(int pri, const char *fmt, ...)
3134{
3135	va_list ap;
3136
3137	va_start(ap, fmt);
3138	vfprintf(stderr, fmt, ap);
3139	va_end(ap);
3140}
3141#endif /* DEBUG */
3142
3143/*
3144 * Check options for consistency.
3145 */
3146static int
3147check_options(struct dirlist *dp)
3148{
3149
3150	if (v4root_phase == 0 && dp == NULL)
3151	    return (1);
3152	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
3153	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
3154	    return (1);
3155	}
3156	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
3157		syslog(LOG_ERR, "-mask requires -network");
3158		return (1);
3159	}
3160	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
3161		syslog(LOG_ERR, "-network requires mask specification");
3162		return (1);
3163	}
3164	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
3165		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
3166		return (1);
3167	}
3168	if (v4root_phase > 0 &&
3169	    (opt_flags &
3170	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3171	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3172	    return (1);
3173	}
3174	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3175	    syslog(LOG_ERR, "-alldirs has multiple directories");
3176	    return (1);
3177	}
3178	return (0);
3179}
3180
3181/*
3182 * Check an absolute directory path for any symbolic links. Return true
3183 */
3184static int
3185check_dirpath(char *dirp)
3186{
3187	char *cp;
3188	int ret = 1;
3189	struct stat sb;
3190
3191	cp = dirp + 1;
3192	while (*cp && ret) {
3193		if (*cp == '/') {
3194			*cp = '\0';
3195			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3196				ret = 0;
3197			*cp = '/';
3198		}
3199		cp++;
3200	}
3201	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3202		ret = 0;
3203	return (ret);
3204}
3205
3206/*
3207 * Make a netmask according to the specified prefix length. The ss_family
3208 * and other non-address fields must be initialised before calling this.
3209 */
3210static int
3211makemask(struct sockaddr_storage *ssp, int bitlen)
3212{
3213	u_char *p;
3214	int bits, i, len;
3215
3216	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
3217		return (-1);
3218	if (bitlen > len * CHAR_BIT)
3219		return (-1);
3220
3221	for (i = 0; i < len; i++) {
3222		bits = MIN(CHAR_BIT, bitlen);
3223		*p++ = (u_char)~0 << (CHAR_BIT - bits);
3224		bitlen -= bits;
3225	}
3226	return 0;
3227}
3228
3229/*
3230 * Check that the sockaddr is a valid netmask. Returns 0 if the mask
3231 * is acceptable (i.e. of the form 1...10....0).
3232 */
3233static int
3234checkmask(struct sockaddr *sa)
3235{
3236	u_char *mask;
3237	int i, len;
3238
3239	if ((mask = sa_rawaddr(sa, &len)) == NULL)
3240		return (-1);
3241
3242	for (i = 0; i < len; i++)
3243		if (mask[i] != 0xff)
3244			break;
3245	if (i < len) {
3246		if (~mask[i] & (u_char)(~mask[i] + 1))
3247			return (-1);
3248		i++;
3249	}
3250	for (; i < len; i++)
3251		if (mask[i] != 0)
3252			return (-1);
3253	return (0);
3254}
3255
3256/*
3257 * Compare two sockaddrs according to a specified mask. Return zero if
3258 * `sa1' matches `sa2' when filtered by the netmask in `samask'.
3259 * If samask is NULL, perform a full comparison.
3260 */
3261static int
3262sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
3263{
3264	unsigned char *p1, *p2, *mask;
3265	int len, i;
3266
3267	if (sa1->sa_family != sa2->sa_family ||
3268	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
3269	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
3270		return (1);
3271
3272	switch (sa1->sa_family) {
3273	case AF_INET6:
3274		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
3275		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
3276			return (1);
3277		break;
3278	}
3279
3280	/* Simple binary comparison if no mask specified. */
3281	if (samask == NULL)
3282		return (memcmp(p1, p2, len));
3283
3284	/* Set up the mask, and do a mask-based comparison. */
3285	if (sa1->sa_family != samask->sa_family ||
3286	    (mask = sa_rawaddr(samask, NULL)) == NULL)
3287		return (1);
3288
3289	for (i = 0; i < len; i++)
3290		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
3291			return (1);
3292	return (0);
3293}
3294
3295/*
3296 * Return a pointer to the part of the sockaddr that contains the
3297 * raw address, and set *nbytes to its length in bytes. Returns
3298 * NULL if the address family is unknown.
3299 */
3300static void *
3301sa_rawaddr(struct sockaddr *sa, int *nbytes) {
3302	void *p;
3303	int len;
3304
3305	switch (sa->sa_family) {
3306	case AF_INET:
3307		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
3308		p = &((struct sockaddr_in *)sa)->sin_addr;
3309		break;
3310	case AF_INET6:
3311		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
3312		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
3313		break;
3314	default:
3315		p = NULL;
3316		len = 0;
3317	}
3318
3319	if (nbytes != NULL)
3320		*nbytes = len;
3321	return (p);
3322}
3323
3324static void
3325huphandler(int sig __unused)
3326{
3327
3328	got_sighup = 1;
3329}
3330
3331static void
3332terminate(int sig __unused)
3333{
3334	pidfile_remove(pfh);
3335	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
3336	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
3337	exit (0);
3338}
3339