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