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