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