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