mountd.c revision 222623
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 222623 2011-06-02 19:33:33Z rmacklem $");
47
48#include <sys/param.h>
49#include <sys/fcntl.h>
50#include <sys/linker.h>
51#include <sys/module.h>
52#include <sys/mount.h>
53#include <sys/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 *)malloc(sizeof (struct exportlist));
1793	if (ep == (struct exportlist *)NULL)
1794		out_of_mem();
1795	memset(ep, 0, sizeof(struct exportlist));
1796	return (ep);
1797}
1798
1799/*
1800 * Allocate a group list element
1801 */
1802struct grouplist *
1803get_grp(void)
1804{
1805	struct grouplist *gp;
1806
1807	gp = (struct grouplist *)malloc(sizeof (struct grouplist));
1808	if (gp == (struct grouplist *)NULL)
1809		out_of_mem();
1810	memset(gp, 0, sizeof(struct grouplist));
1811	return (gp);
1812}
1813
1814/*
1815 * Clean up upon an error in get_exportlist().
1816 */
1817void
1818getexp_err(struct exportlist *ep, struct grouplist *grp)
1819{
1820	struct grouplist *tgrp;
1821
1822	if (!(opt_flags & OP_QUIET))
1823		syslog(LOG_ERR, "bad exports list line %s", line);
1824	if (ep && (ep->ex_flag & EX_LINKED) == 0)
1825		free_exp(ep);
1826	while (grp) {
1827		tgrp = grp;
1828		grp = grp->gr_next;
1829		free_grp(tgrp);
1830	}
1831}
1832
1833/*
1834 * Search the export list for a matching fs.
1835 */
1836struct exportlist *
1837ex_search(fsid_t *fsid)
1838{
1839	struct exportlist *ep;
1840
1841	ep = exphead;
1842	while (ep) {
1843		if (ep->ex_fs.val[0] == fsid->val[0] &&
1844		    ep->ex_fs.val[1] == fsid->val[1])
1845			return (ep);
1846		ep = ep->ex_next;
1847	}
1848	return (ep);
1849}
1850
1851/*
1852 * Add a directory path to the list.
1853 */
1854char *
1855add_expdir(struct dirlist **dpp, char *cp, int len)
1856{
1857	struct dirlist *dp;
1858
1859	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1860	if (dp == (struct dirlist *)NULL)
1861		out_of_mem();
1862	dp->dp_left = *dpp;
1863	dp->dp_right = (struct dirlist *)NULL;
1864	dp->dp_flag = 0;
1865	dp->dp_hosts = (struct hostlist *)NULL;
1866	strcpy(dp->dp_dirp, cp);
1867	*dpp = dp;
1868	return (dp->dp_dirp);
1869}
1870
1871/*
1872 * Hang the dir list element off the dirpath binary tree as required
1873 * and update the entry for host.
1874 */
1875void
1876hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
1877	int flags)
1878{
1879	struct hostlist *hp;
1880	struct dirlist *dp2;
1881
1882	if (flags & OP_ALLDIRS) {
1883		if (ep->ex_defdir)
1884			free((caddr_t)dp);
1885		else
1886			ep->ex_defdir = dp;
1887		if (grp == (struct grouplist *)NULL) {
1888			ep->ex_defdir->dp_flag |= DP_DEFSET;
1889		} else while (grp) {
1890			hp = get_ht();
1891			hp->ht_grp = grp;
1892			hp->ht_next = ep->ex_defdir->dp_hosts;
1893			ep->ex_defdir->dp_hosts = hp;
1894			grp = grp->gr_next;
1895		}
1896	} else {
1897
1898		/*
1899		 * Loop through the directories adding them to the tree.
1900		 */
1901		while (dp) {
1902			dp2 = dp->dp_left;
1903			add_dlist(&ep->ex_dirl, dp, grp, flags);
1904			dp = dp2;
1905		}
1906	}
1907}
1908
1909/*
1910 * Traverse the binary tree either updating a node that is already there
1911 * for the new directory or adding the new node.
1912 */
1913void
1914add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
1915	int flags)
1916{
1917	struct dirlist *dp;
1918	struct hostlist *hp;
1919	int cmp;
1920
1921	dp = *dpp;
1922	if (dp) {
1923		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1924		if (cmp > 0) {
1925			add_dlist(&dp->dp_left, newdp, grp, flags);
1926			return;
1927		} else if (cmp < 0) {
1928			add_dlist(&dp->dp_right, newdp, grp, flags);
1929			return;
1930		} else
1931			free((caddr_t)newdp);
1932	} else {
1933		dp = newdp;
1934		dp->dp_left = (struct dirlist *)NULL;
1935		*dpp = dp;
1936	}
1937	if (grp) {
1938
1939		/*
1940		 * Hang all of the host(s) off of the directory point.
1941		 */
1942		do {
1943			hp = get_ht();
1944			hp->ht_grp = grp;
1945			hp->ht_next = dp->dp_hosts;
1946			dp->dp_hosts = hp;
1947			grp = grp->gr_next;
1948		} while (grp);
1949	} else {
1950		dp->dp_flag |= DP_DEFSET;
1951	}
1952}
1953
1954/*
1955 * Search for a dirpath on the export point.
1956 */
1957struct dirlist *
1958dirp_search(struct dirlist *dp, char *dirp)
1959{
1960	int cmp;
1961
1962	if (dp) {
1963		cmp = strcmp(dp->dp_dirp, dirp);
1964		if (cmp > 0)
1965			return (dirp_search(dp->dp_left, dirp));
1966		else if (cmp < 0)
1967			return (dirp_search(dp->dp_right, dirp));
1968		else
1969			return (dp);
1970	}
1971	return (dp);
1972}
1973
1974/*
1975 * Scan for a host match in a directory tree.
1976 */
1977int
1978chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
1979	int *hostsetp)
1980{
1981	struct hostlist *hp;
1982	struct grouplist *grp;
1983	struct addrinfo *ai;
1984
1985	if (dp) {
1986		if (dp->dp_flag & DP_DEFSET)
1987			*defsetp = dp->dp_flag;
1988		hp = dp->dp_hosts;
1989		while (hp) {
1990			grp = hp->ht_grp;
1991			switch (grp->gr_type) {
1992			case GT_HOST:
1993				ai = grp->gr_ptr.gt_addrinfo;
1994				for (; ai; ai = ai->ai_next) {
1995					if (!sacmp(ai->ai_addr, saddr, NULL)) {
1996						*hostsetp =
1997						    (hp->ht_flag | DP_HOSTSET);
1998						return (1);
1999					}
2000				}
2001				break;
2002			case GT_NET:
2003				if (!sacmp(saddr, (struct sockaddr *)
2004				    &grp->gr_ptr.gt_net.nt_net,
2005				    (struct sockaddr *)
2006				    &grp->gr_ptr.gt_net.nt_mask)) {
2007					*hostsetp = (hp->ht_flag | DP_HOSTSET);
2008					return (1);
2009				}
2010				break;
2011			}
2012			hp = hp->ht_next;
2013		}
2014	}
2015	return (0);
2016}
2017
2018/*
2019 * Scan tree for a host that matches the address.
2020 */
2021int
2022scan_tree(struct dirlist *dp, struct sockaddr *saddr)
2023{
2024	int defset, hostset;
2025
2026	if (dp) {
2027		if (scan_tree(dp->dp_left, saddr))
2028			return (1);
2029		if (chk_host(dp, saddr, &defset, &hostset))
2030			return (1);
2031		if (scan_tree(dp->dp_right, saddr))
2032			return (1);
2033	}
2034	return (0);
2035}
2036
2037/*
2038 * Traverse the dirlist tree and free it up.
2039 */
2040void
2041free_dir(struct dirlist *dp)
2042{
2043
2044	if (dp) {
2045		free_dir(dp->dp_left);
2046		free_dir(dp->dp_right);
2047		free_host(dp->dp_hosts);
2048		free((caddr_t)dp);
2049	}
2050}
2051
2052/*
2053 * Parse a colon separated list of security flavors
2054 */
2055int
2056parsesec(char *seclist, struct exportlist *ep)
2057{
2058	char *cp, savedc;
2059	int flavor;
2060
2061	ep->ex_numsecflavors = 0;
2062	for (;;) {
2063		cp = strchr(seclist, ':');
2064		if (cp) {
2065			savedc = *cp;
2066			*cp = '\0';
2067		}
2068
2069		if (!strcmp(seclist, "sys"))
2070			flavor = AUTH_SYS;
2071		else if (!strcmp(seclist, "krb5"))
2072			flavor = RPCSEC_GSS_KRB5;
2073		else if (!strcmp(seclist, "krb5i"))
2074			flavor = RPCSEC_GSS_KRB5I;
2075		else if (!strcmp(seclist, "krb5p"))
2076			flavor = RPCSEC_GSS_KRB5P;
2077		else {
2078			if (cp)
2079				*cp = savedc;
2080			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2081			return (1);
2082		}
2083		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2084			if (cp)
2085				*cp = savedc;
2086			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2087			return (1);
2088		}
2089		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2090		ep->ex_numsecflavors++;
2091		if (cp) {
2092			*cp = savedc;
2093			seclist = cp + 1;
2094		} else {
2095			break;
2096		}
2097	}
2098	return (0);
2099}
2100
2101/*
2102 * Parse the option string and update fields.
2103 * Option arguments may either be -<option>=<value> or
2104 * -<option> <value>
2105 */
2106int
2107do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2108	int *has_hostp, int *exflagsp, struct xucred *cr)
2109{
2110	char *cpoptarg, *cpoptend;
2111	char *cp, *endcp, *cpopt, savedc, savedc2;
2112	int allflag, usedarg;
2113
2114	savedc2 = '\0';
2115	cpopt = *cpp;
2116	cpopt++;
2117	cp = *endcpp;
2118	savedc = *cp;
2119	*cp = '\0';
2120	while (cpopt && *cpopt) {
2121		allflag = 1;
2122		usedarg = -2;
2123		if ((cpoptend = strchr(cpopt, ','))) {
2124			*cpoptend++ = '\0';
2125			if ((cpoptarg = strchr(cpopt, '=')))
2126				*cpoptarg++ = '\0';
2127		} else {
2128			if ((cpoptarg = strchr(cpopt, '=')))
2129				*cpoptarg++ = '\0';
2130			else {
2131				*cp = savedc;
2132				nextfield(&cp, &endcp);
2133				**endcpp = '\0';
2134				if (endcp > cp && *cp != '-') {
2135					cpoptarg = cp;
2136					savedc2 = *endcp;
2137					*endcp = '\0';
2138					usedarg = 0;
2139				}
2140			}
2141		}
2142		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
2143			*exflagsp |= MNT_EXRDONLY;
2144		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
2145		    !(allflag = strcmp(cpopt, "mapall")) ||
2146		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
2147			usedarg++;
2148			parsecred(cpoptarg, cr);
2149			if (allflag == 0) {
2150				*exflagsp |= MNT_EXPORTANON;
2151				opt_flags |= OP_MAPALL;
2152			} else
2153				opt_flags |= OP_MAPROOT;
2154		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
2155		    !strcmp(cpopt, "m"))) {
2156			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
2157				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
2158				return (1);
2159			}
2160			usedarg++;
2161			opt_flags |= OP_MASK;
2162		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
2163			!strcmp(cpopt, "n"))) {
2164			if (strchr(cpoptarg, '/') != NULL) {
2165				if (debug)
2166					fprintf(stderr, "setting OP_MASKLEN\n");
2167				opt_flags |= OP_MASKLEN;
2168			}
2169			if (grp->gr_type != GT_NULL) {
2170				syslog(LOG_ERR, "network/host conflict");
2171				return (1);
2172			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
2173				syslog(LOG_ERR, "bad net: %s", cpoptarg);
2174				return (1);
2175			}
2176			grp->gr_type = GT_NET;
2177			*has_hostp = 1;
2178			usedarg++;
2179			opt_flags |= OP_NET;
2180		} else if (!strcmp(cpopt, "alldirs")) {
2181			opt_flags |= OP_ALLDIRS;
2182		} else if (!strcmp(cpopt, "public")) {
2183			*exflagsp |= MNT_EXPUBLIC;
2184		} else if (!strcmp(cpopt, "webnfs")) {
2185			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
2186			opt_flags |= OP_MAPALL;
2187		} else if (cpoptarg && !strcmp(cpopt, "index")) {
2188			ep->ex_indexfile = strdup(cpoptarg);
2189		} else if (!strcmp(cpopt, "quiet")) {
2190			opt_flags |= OP_QUIET;
2191		} else if (!strcmp(cpopt, "sec")) {
2192			if (parsesec(cpoptarg, ep))
2193				return (1);
2194			opt_flags |= OP_SEC;
2195			usedarg++;
2196		} else {
2197			syslog(LOG_ERR, "bad opt %s", cpopt);
2198			return (1);
2199		}
2200		if (usedarg >= 0) {
2201			*endcp = savedc2;
2202			**endcpp = savedc;
2203			if (usedarg > 0) {
2204				*cpp = cp;
2205				*endcpp = endcp;
2206			}
2207			return (0);
2208		}
2209		cpopt = cpoptend;
2210	}
2211	**endcpp = savedc;
2212	return (0);
2213}
2214
2215/*
2216 * Translate a character string to the corresponding list of network
2217 * addresses for a hostname.
2218 */
2219int
2220get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
2221{
2222	struct grouplist *checkgrp;
2223	struct addrinfo *ai, *tai, hints;
2224	int ecode;
2225	char host[NI_MAXHOST];
2226
2227	if (grp->gr_type != GT_NULL) {
2228		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
2229		return (1);
2230	}
2231	memset(&hints, 0, sizeof hints);
2232	hints.ai_flags = AI_CANONNAME;
2233	hints.ai_protocol = IPPROTO_UDP;
2234	ecode = getaddrinfo(cp, NULL, &hints, &ai);
2235	if (ecode != 0) {
2236		syslog(LOG_ERR,"can't get address info for host %s", cp);
2237		return 1;
2238	}
2239	grp->gr_ptr.gt_addrinfo = ai;
2240	while (ai != NULL) {
2241		if (ai->ai_canonname == NULL) {
2242			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2243			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
2244				strlcpy(host, "?", sizeof(host));
2245			ai->ai_canonname = strdup(host);
2246			ai->ai_flags |= AI_CANONNAME;
2247		}
2248		if (debug)
2249			fprintf(stderr, "got host %s\n", ai->ai_canonname);
2250		/*
2251		 * Sanity check: make sure we don't already have an entry
2252		 * for this host in the grouplist.
2253		 */
2254		for (checkgrp = tgrp; checkgrp != NULL;
2255		    checkgrp = checkgrp->gr_next) {
2256			if (checkgrp->gr_type != GT_HOST)
2257				continue;
2258			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
2259			    tai = tai->ai_next) {
2260				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
2261					continue;
2262				if (debug)
2263					fprintf(stderr,
2264					    "ignoring duplicate host %s\n",
2265					    ai->ai_canonname);
2266				grp->gr_type = GT_IGNORE;
2267				return (0);
2268			}
2269		}
2270		ai = ai->ai_next;
2271	}
2272	grp->gr_type = GT_HOST;
2273	return (0);
2274}
2275
2276/*
2277 * Free up an exports list component
2278 */
2279void
2280free_exp(struct exportlist *ep)
2281{
2282
2283	if (ep->ex_defdir) {
2284		free_host(ep->ex_defdir->dp_hosts);
2285		free((caddr_t)ep->ex_defdir);
2286	}
2287	if (ep->ex_fsdir)
2288		free(ep->ex_fsdir);
2289	if (ep->ex_indexfile)
2290		free(ep->ex_indexfile);
2291	free_dir(ep->ex_dirl);
2292	free((caddr_t)ep);
2293}
2294
2295/*
2296 * Free hosts.
2297 */
2298void
2299free_host(struct hostlist *hp)
2300{
2301	struct hostlist *hp2;
2302
2303	while (hp) {
2304		hp2 = hp;
2305		hp = hp->ht_next;
2306		free((caddr_t)hp2);
2307	}
2308}
2309
2310struct hostlist *
2311get_ht(void)
2312{
2313	struct hostlist *hp;
2314
2315	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
2316	if (hp == (struct hostlist *)NULL)
2317		out_of_mem();
2318	hp->ht_next = (struct hostlist *)NULL;
2319	hp->ht_flag = 0;
2320	return (hp);
2321}
2322
2323/*
2324 * Out of memory, fatal
2325 */
2326void
2327out_of_mem(void)
2328{
2329
2330	syslog(LOG_ERR, "out of memory");
2331	exit(2);
2332}
2333
2334/*
2335 * Do the nmount() syscall with the update flag to push the export info into
2336 * the kernel.
2337 */
2338int
2339do_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
2340    struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
2341{
2342	struct statfs fsb1;
2343	struct addrinfo *ai;
2344	struct export_args ea, *eap;
2345	char errmsg[255];
2346	char *cp;
2347	int done;
2348	char savedc;
2349	struct iovec *iov;
2350	int i, iovlen;
2351	int ret;
2352	struct nfsex_args nfsea;
2353
2354	if (run_v4server > 0)
2355		eap = &nfsea.export;
2356	else
2357		eap = &ea;
2358
2359	cp = NULL;
2360	savedc = '\0';
2361	iov = NULL;
2362	iovlen = 0;
2363	ret = 0;
2364
2365	bzero(eap, sizeof (struct export_args));
2366	bzero(errmsg, sizeof(errmsg));
2367	eap->ex_flags = exflags;
2368	eap->ex_anon = *anoncrp;
2369	eap->ex_indexfile = ep->ex_indexfile;
2370	if (grp->gr_type == GT_HOST)
2371		ai = grp->gr_ptr.gt_addrinfo;
2372	else
2373		ai = NULL;
2374	eap->ex_numsecflavors = ep->ex_numsecflavors;
2375	for (i = 0; i < eap->ex_numsecflavors; i++)
2376		eap->ex_secflavors[i] = ep->ex_secflavors[i];
2377	if (eap->ex_numsecflavors == 0) {
2378		eap->ex_numsecflavors = 1;
2379		eap->ex_secflavors[0] = AUTH_SYS;
2380	}
2381	done = FALSE;
2382
2383	if (v4root_phase == 0) {
2384		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2385		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2386		build_iovec(&iov, &iovlen, "from", NULL, 0);
2387		build_iovec(&iov, &iovlen, "update", NULL, 0);
2388		build_iovec(&iov, &iovlen, "export", eap,
2389		    sizeof (struct export_args));
2390		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
2391	}
2392
2393	while (!done) {
2394		switch (grp->gr_type) {
2395		case GT_HOST:
2396			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
2397				goto skip;
2398			eap->ex_addr = ai->ai_addr;
2399			eap->ex_addrlen = ai->ai_addrlen;
2400			eap->ex_masklen = 0;
2401			break;
2402		case GT_NET:
2403			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
2404			    have_v6 == 0)
2405				goto skip;
2406			eap->ex_addr =
2407			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
2408			eap->ex_addrlen =
2409			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
2410			eap->ex_mask =
2411			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
2412			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
2413			break;
2414		case GT_DEFAULT:
2415			eap->ex_addr = NULL;
2416			eap->ex_addrlen = 0;
2417			eap->ex_mask = NULL;
2418			eap->ex_masklen = 0;
2419			break;
2420		case GT_IGNORE:
2421			ret = 0;
2422			goto error_exit;
2423			break;
2424		default:
2425			syslog(LOG_ERR, "bad grouptype");
2426			if (cp)
2427				*cp = savedc;
2428			ret = 1;
2429			goto error_exit;
2430		};
2431
2432		/*
2433		 * For V4:, use the nfssvc() syscall, instead of mount().
2434		 */
2435		if (v4root_phase == 2) {
2436			nfsea.fspec = v4root_dirpath;
2437			if (run_v4server > 0 &&
2438			    nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) {
2439				syslog(LOG_ERR, "Exporting V4: failed");
2440				return (2);
2441			}
2442		} else {
2443			/*
2444			 * XXX:
2445			 * Maybe I should just use the fsb->f_mntonname path
2446			 * instead of looping back up the dirp to the mount
2447			 * point??
2448			 * Also, needs to know how to export all types of local
2449			 * exportable filesystems and not just "ufs".
2450			 */
2451			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
2452			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
2453			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
2454			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
2455			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
2456			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
2457
2458			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
2459				if (cp)
2460					*cp-- = savedc;
2461				else
2462					cp = dirp + dirplen - 1;
2463				if (opt_flags & OP_QUIET) {
2464					ret = 1;
2465					goto error_exit;
2466				}
2467				if (errno == EPERM) {
2468					if (debug)
2469						warnx("can't change attributes for %s",
2470						    dirp);
2471					syslog(LOG_ERR,
2472					   "can't change attributes for %s",
2473					    dirp);
2474					ret = 1;
2475					goto error_exit;
2476				}
2477				if (opt_flags & OP_ALLDIRS) {
2478					if (errno == EINVAL)
2479						syslog(LOG_ERR,
2480		"-alldirs requested but %s is not a filesystem mountpoint",
2481						    dirp);
2482					else
2483						syslog(LOG_ERR,
2484						    "could not remount %s: %m",
2485						    dirp);
2486					ret = 1;
2487					goto error_exit;
2488				}
2489				/* back up over the last component */
2490				while (*cp == '/' && cp > dirp)
2491					cp--;
2492				while (*(cp - 1) != '/' && cp > dirp)
2493					cp--;
2494				if (cp == dirp) {
2495					if (debug)
2496						warnx("mnt unsucc");
2497					syslog(LOG_ERR, "can't export %s %s",
2498					    dirp, errmsg);
2499					ret = 1;
2500					goto error_exit;
2501				}
2502				savedc = *cp;
2503				*cp = '\0';
2504				/*
2505				 * Check that we're still on the same
2506				 * filesystem.
2507				 */
2508				if (statfs(dirp, &fsb1) != 0 ||
2509				    bcmp(&fsb1.f_fsid, &fsb->f_fsid,
2510				    sizeof (fsb1.f_fsid)) != 0) {
2511					*cp = savedc;
2512					syslog(LOG_ERR,
2513					    "can't export %s %s", dirp,
2514					    errmsg);
2515					ret = 1;
2516					goto error_exit;
2517				}
2518			}
2519		}
2520
2521		/*
2522		 * For the experimental server:
2523		 * If this is the public directory, get the file handle
2524		 * and load it into the kernel via the nfssvc() syscall.
2525		 */
2526		if (run_v4server > 0 && (exflags & MNT_EXPUBLIC) != 0) {
2527			fhandle_t fh;
2528			char *public_name;
2529
2530			if (eap->ex_indexfile != NULL)
2531				public_name = eap->ex_indexfile;
2532			else
2533				public_name = dirp;
2534			if (getfh(public_name, &fh) < 0)
2535				syslog(LOG_ERR,
2536				    "Can't get public fh for %s", public_name);
2537			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
2538				syslog(LOG_ERR,
2539				    "Can't set public fh for %s", public_name);
2540			else
2541				has_publicfh = 1;
2542		}
2543skip:
2544		if (ai != NULL)
2545			ai = ai->ai_next;
2546		if (ai == NULL)
2547			done = TRUE;
2548	}
2549	if (cp)
2550		*cp = savedc;
2551error_exit:
2552	/* free strings allocated by strdup() in getmntopts.c */
2553	if (iov != NULL) {
2554		free(iov[0].iov_base); /* fstype */
2555		free(iov[2].iov_base); /* fspath */
2556		free(iov[4].iov_base); /* from */
2557		free(iov[6].iov_base); /* update */
2558		free(iov[8].iov_base); /* export */
2559		free(iov[10].iov_base); /* errmsg */
2560
2561		/* free iov, allocated by realloc() */
2562		free(iov);
2563	}
2564	return (ret);
2565}
2566
2567/*
2568 * Translate a net address.
2569 *
2570 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
2571 */
2572int
2573get_net(char *cp, struct netmsk *net, int maskflg)
2574{
2575	struct netent *np = NULL;
2576	char *name, *p, *prefp;
2577	struct sockaddr_in sin;
2578	struct sockaddr *sa = NULL;
2579	struct addrinfo hints, *ai = NULL;
2580	char netname[NI_MAXHOST];
2581	long preflen;
2582
2583	p = prefp = NULL;
2584	if ((opt_flags & OP_MASKLEN) && !maskflg) {
2585		p = strchr(cp, '/');
2586		*p = '\0';
2587		prefp = p + 1;
2588	}
2589
2590	/*
2591	 * Check for a numeric address first. We wish to avoid
2592	 * possible DNS lookups in getnetbyname().
2593	 */
2594	if (isxdigit(*cp) || *cp == ':') {
2595		memset(&hints, 0, sizeof hints);
2596		/* Ensure the mask and the network have the same family. */
2597		if (maskflg && (opt_flags & OP_NET))
2598			hints.ai_family = net->nt_net.ss_family;
2599		else if (!maskflg && (opt_flags & OP_HAVEMASK))
2600			hints.ai_family = net->nt_mask.ss_family;
2601		else
2602			hints.ai_family = AF_UNSPEC;
2603		hints.ai_flags = AI_NUMERICHOST;
2604		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
2605			sa = ai->ai_addr;
2606		if (sa != NULL && ai->ai_family == AF_INET) {
2607			/*
2608			 * The address in `cp' is really a network address, so
2609			 * use inet_network() to re-interpret this correctly.
2610			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
2611			 */
2612			bzero(&sin, sizeof sin);
2613			sin.sin_family = AF_INET;
2614			sin.sin_len = sizeof sin;
2615			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
2616			if (debug)
2617				fprintf(stderr, "get_net: v4 addr %s\n",
2618				    inet_ntoa(sin.sin_addr));
2619			sa = (struct sockaddr *)&sin;
2620		}
2621	}
2622	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
2623		bzero(&sin, sizeof sin);
2624		sin.sin_family = AF_INET;
2625		sin.sin_len = sizeof sin;
2626		sin.sin_addr = inet_makeaddr(np->n_net, 0);
2627		sa = (struct sockaddr *)&sin;
2628	}
2629	if (sa == NULL)
2630		goto fail;
2631
2632	if (maskflg) {
2633		/* The specified sockaddr is a mask. */
2634		if (checkmask(sa) != 0)
2635			goto fail;
2636		bcopy(sa, &net->nt_mask, sa->sa_len);
2637		opt_flags |= OP_HAVEMASK;
2638	} else {
2639		/* The specified sockaddr is a network address. */
2640		bcopy(sa, &net->nt_net, sa->sa_len);
2641
2642		/* Get a network name for the export list. */
2643		if (np) {
2644			name = np->n_name;
2645		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2646		   NULL, 0, NI_NUMERICHOST) == 0) {
2647			name = netname;
2648		} else {
2649			goto fail;
2650		}
2651		if ((net->nt_name = strdup(name)) == NULL)
2652			out_of_mem();
2653
2654		/*
2655		 * Extract a mask from either a "/<masklen>" suffix, or
2656		 * from the class of an IPv4 address.
2657		 */
2658		if (opt_flags & OP_MASKLEN) {
2659			preflen = strtol(prefp, NULL, 10);
2660			if (preflen < 0L || preflen == LONG_MAX)
2661				goto fail;
2662			bcopy(sa, &net->nt_mask, sa->sa_len);
2663			if (makemask(&net->nt_mask, (int)preflen) != 0)
2664				goto fail;
2665			opt_flags |= OP_HAVEMASK;
2666			*p = '/';
2667		} else if (sa->sa_family == AF_INET &&
2668		    (opt_flags & OP_MASK) == 0) {
2669			in_addr_t addr;
2670
2671			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
2672			if (IN_CLASSA(addr))
2673				preflen = 8;
2674			else if (IN_CLASSB(addr))
2675				preflen = 16;
2676			else if (IN_CLASSC(addr))
2677				preflen = 24;
2678			else if (IN_CLASSD(addr))
2679				preflen = 28;
2680			else
2681				preflen = 32;	/* XXX */
2682
2683			bcopy(sa, &net->nt_mask, sa->sa_len);
2684			makemask(&net->nt_mask, (int)preflen);
2685			opt_flags |= OP_HAVEMASK;
2686		}
2687	}
2688
2689	if (ai)
2690		freeaddrinfo(ai);
2691	return 0;
2692
2693fail:
2694	if (ai)
2695		freeaddrinfo(ai);
2696	return 1;
2697}
2698
2699/*
2700 * Parse out the next white space separated field
2701 */
2702void
2703nextfield(char **cp, char **endcp)
2704{
2705	char *p;
2706
2707	p = *cp;
2708	while (*p == ' ' || *p == '\t')
2709		p++;
2710	if (*p == '\n' || *p == '\0')
2711		*cp = *endcp = p;
2712	else {
2713		*cp = p++;
2714		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
2715			p++;
2716		*endcp = p;
2717	}
2718}
2719
2720/*
2721 * Get an exports file line. Skip over blank lines and handle line
2722 * continuations.
2723 */
2724int
2725get_line(void)
2726{
2727	char *p, *cp;
2728	size_t len;
2729	int totlen, cont_line;
2730
2731	/*
2732	 * Loop around ignoring blank lines and getting all continuation lines.
2733	 */
2734	p = line;
2735	totlen = 0;
2736	do {
2737		if ((p = fgetln(exp_file, &len)) == NULL)
2738			return (0);
2739		cp = p + len - 1;
2740		cont_line = 0;
2741		while (cp >= p &&
2742		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
2743			if (*cp == '\\')
2744				cont_line = 1;
2745			cp--;
2746			len--;
2747		}
2748		if (cont_line) {
2749			*++cp = ' ';
2750			len++;
2751		}
2752		if (linesize < len + totlen + 1) {
2753			linesize = len + totlen + 1;
2754			line = realloc(line, linesize);
2755			if (line == NULL)
2756				out_of_mem();
2757		}
2758		memcpy(line + totlen, p, len);
2759		totlen += len;
2760		line[totlen] = '\0';
2761	} while (totlen == 0 || cont_line);
2762	return (1);
2763}
2764
2765/*
2766 * Parse a description of a credential.
2767 */
2768void
2769parsecred(char *namelist, struct xucred *cr)
2770{
2771	char *name;
2772	int cnt;
2773	char *names;
2774	struct passwd *pw;
2775	struct group *gr;
2776	gid_t groups[XU_NGROUPS + 1];
2777	int ngroups;
2778
2779	cr->cr_version = XUCRED_VERSION;
2780	/*
2781	 * Set up the unprivileged user.
2782	 */
2783	cr->cr_uid = -2;
2784	cr->cr_groups[0] = -2;
2785	cr->cr_ngroups = 1;
2786	/*
2787	 * Get the user's password table entry.
2788	 */
2789	names = strsep(&namelist, " \t\n");
2790	name = strsep(&names, ":");
2791	if (isdigit(*name) || *name == '-')
2792		pw = getpwuid(atoi(name));
2793	else
2794		pw = getpwnam(name);
2795	/*
2796	 * Credentials specified as those of a user.
2797	 */
2798	if (names == NULL) {
2799		if (pw == NULL) {
2800			syslog(LOG_ERR, "unknown user: %s", name);
2801			return;
2802		}
2803		cr->cr_uid = pw->pw_uid;
2804		ngroups = XU_NGROUPS + 1;
2805		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
2806			syslog(LOG_ERR, "too many groups");
2807		/*
2808		 * Compress out duplicate.
2809		 */
2810		cr->cr_ngroups = ngroups - 1;
2811		cr->cr_groups[0] = groups[0];
2812		for (cnt = 2; cnt < ngroups; cnt++)
2813			cr->cr_groups[cnt - 1] = groups[cnt];
2814		return;
2815	}
2816	/*
2817	 * Explicit credential specified as a colon separated list:
2818	 *	uid:gid:gid:...
2819	 */
2820	if (pw != NULL)
2821		cr->cr_uid = pw->pw_uid;
2822	else if (isdigit(*name) || *name == '-')
2823		cr->cr_uid = atoi(name);
2824	else {
2825		syslog(LOG_ERR, "unknown user: %s", name);
2826		return;
2827	}
2828	cr->cr_ngroups = 0;
2829	while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
2830		name = strsep(&names, ":");
2831		if (isdigit(*name) || *name == '-') {
2832			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
2833		} else {
2834			if ((gr = getgrnam(name)) == NULL) {
2835				syslog(LOG_ERR, "unknown group: %s", name);
2836				continue;
2837			}
2838			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2839		}
2840	}
2841	if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
2842		syslog(LOG_ERR, "too many groups");
2843}
2844
2845#define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
2846/*
2847 * Routines that maintain the remote mounttab
2848 */
2849void
2850get_mountlist(void)
2851{
2852	struct mountlist *mlp, **mlpp;
2853	char *host, *dirp, *cp;
2854	char str[STRSIZ];
2855	FILE *mlfile;
2856
2857	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2858		if (errno == ENOENT)
2859			return;
2860		else {
2861			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
2862			return;
2863		}
2864	}
2865	mlpp = &mlhead;
2866	while (fgets(str, STRSIZ, mlfile) != NULL) {
2867		cp = str;
2868		host = strsep(&cp, " \t\n");
2869		dirp = strsep(&cp, " \t\n");
2870		if (host == NULL || dirp == NULL)
2871			continue;
2872		mlp = (struct mountlist *)malloc(sizeof (*mlp));
2873		if (mlp == (struct mountlist *)NULL)
2874			out_of_mem();
2875		strncpy(mlp->ml_host, host, MNTNAMLEN);
2876		mlp->ml_host[MNTNAMLEN] = '\0';
2877		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
2878		mlp->ml_dirp[MNTPATHLEN] = '\0';
2879		mlp->ml_next = (struct mountlist *)NULL;
2880		*mlpp = mlp;
2881		mlpp = &mlp->ml_next;
2882	}
2883	fclose(mlfile);
2884}
2885
2886void
2887del_mlist(char *hostp, char *dirp)
2888{
2889	struct mountlist *mlp, **mlpp;
2890	struct mountlist *mlp2;
2891	FILE *mlfile;
2892	int fnd = 0;
2893
2894	mlpp = &mlhead;
2895	mlp = mlhead;
2896	while (mlp) {
2897		if (!strcmp(mlp->ml_host, hostp) &&
2898		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2899			fnd = 1;
2900			mlp2 = mlp;
2901			*mlpp = mlp = mlp->ml_next;
2902			free((caddr_t)mlp2);
2903		} else {
2904			mlpp = &mlp->ml_next;
2905			mlp = mlp->ml_next;
2906		}
2907	}
2908	if (fnd) {
2909		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
2910			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
2911			return;
2912		}
2913		mlp = mlhead;
2914		while (mlp) {
2915			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2916			mlp = mlp->ml_next;
2917		}
2918		fclose(mlfile);
2919	}
2920}
2921
2922void
2923add_mlist(char *hostp, char *dirp)
2924{
2925	struct mountlist *mlp, **mlpp;
2926	FILE *mlfile;
2927
2928	mlpp = &mlhead;
2929	mlp = mlhead;
2930	while (mlp) {
2931		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2932			return;
2933		mlpp = &mlp->ml_next;
2934		mlp = mlp->ml_next;
2935	}
2936	mlp = (struct mountlist *)malloc(sizeof (*mlp));
2937	if (mlp == (struct mountlist *)NULL)
2938		out_of_mem();
2939	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
2940	mlp->ml_host[MNTNAMLEN] = '\0';
2941	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
2942	mlp->ml_dirp[MNTPATHLEN] = '\0';
2943	mlp->ml_next = (struct mountlist *)NULL;
2944	*mlpp = mlp;
2945	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2946		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
2947		return;
2948	}
2949	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2950	fclose(mlfile);
2951}
2952
2953/*
2954 * Free up a group list.
2955 */
2956void
2957free_grp(struct grouplist *grp)
2958{
2959	if (grp->gr_type == GT_HOST) {
2960		if (grp->gr_ptr.gt_addrinfo != NULL)
2961			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
2962	} else if (grp->gr_type == GT_NET) {
2963		if (grp->gr_ptr.gt_net.nt_name)
2964			free(grp->gr_ptr.gt_net.nt_name);
2965	}
2966	free((caddr_t)grp);
2967}
2968
2969#ifdef DEBUG
2970void
2971SYSLOG(int pri, const char *fmt, ...)
2972{
2973	va_list ap;
2974
2975	va_start(ap, fmt);
2976	vfprintf(stderr, fmt, ap);
2977	va_end(ap);
2978}
2979#endif /* DEBUG */
2980
2981/*
2982 * Check options for consistency.
2983 */
2984int
2985check_options(struct dirlist *dp)
2986{
2987
2988	if (v4root_phase == 0 && dp == NULL)
2989	    return (1);
2990	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
2991	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
2992	    return (1);
2993	}
2994	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2995		syslog(LOG_ERR, "-mask requires -network");
2996		return (1);
2997	}
2998	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
2999		syslog(LOG_ERR, "-network requires mask specification");
3000		return (1);
3001	}
3002	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
3003		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
3004		return (1);
3005	}
3006	if (v4root_phase > 0 &&
3007	    (opt_flags &
3008	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3009	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3010	    return (1);
3011	}
3012	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3013	    syslog(LOG_ERR, "-alldirs has multiple directories");
3014	    return (1);
3015	}
3016	return (0);
3017}
3018
3019/*
3020 * Check an absolute directory path for any symbolic links. Return true
3021 */
3022int
3023check_dirpath(char *dirp)
3024{
3025	char *cp;
3026	int ret = 1;
3027	struct stat sb;
3028
3029	cp = dirp + 1;
3030	while (*cp && ret) {
3031		if (*cp == '/') {
3032			*cp = '\0';
3033			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3034				ret = 0;
3035			*cp = '/';
3036		}
3037		cp++;
3038	}
3039	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3040		ret = 0;
3041	return (ret);
3042}
3043
3044/*
3045 * Make a netmask according to the specified prefix length. The ss_family
3046 * and other non-address fields must be initialised before calling this.
3047 */
3048int
3049makemask(struct sockaddr_storage *ssp, int bitlen)
3050{
3051	u_char *p;
3052	int bits, i, len;
3053
3054	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
3055		return (-1);
3056	if (bitlen > len * CHAR_BIT)
3057		return (-1);
3058
3059	for (i = 0; i < len; i++) {
3060		bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen;
3061		*p++ = (u_char)~0 << (CHAR_BIT - bits);
3062		bitlen -= bits;
3063	}
3064	return 0;
3065}
3066
3067/*
3068 * Check that the sockaddr is a valid netmask. Returns 0 if the mask
3069 * is acceptable (i.e. of the form 1...10....0).
3070 */
3071int
3072checkmask(struct sockaddr *sa)
3073{
3074	u_char *mask;
3075	int i, len;
3076
3077	if ((mask = sa_rawaddr(sa, &len)) == NULL)
3078		return (-1);
3079
3080	for (i = 0; i < len; i++)
3081		if (mask[i] != 0xff)
3082			break;
3083	if (i < len) {
3084		if (~mask[i] & (u_char)(~mask[i] + 1))
3085			return (-1);
3086		i++;
3087	}
3088	for (; i < len; i++)
3089		if (mask[i] != 0)
3090			return (-1);
3091	return (0);
3092}
3093
3094/*
3095 * Compare two sockaddrs according to a specified mask. Return zero if
3096 * `sa1' matches `sa2' when filtered by the netmask in `samask'.
3097 * If samask is NULL, perform a full comparision.
3098 */
3099int
3100sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
3101{
3102	unsigned char *p1, *p2, *mask;
3103	int len, i;
3104
3105	if (sa1->sa_family != sa2->sa_family ||
3106	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
3107	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
3108		return (1);
3109
3110	switch (sa1->sa_family) {
3111	case AF_INET6:
3112		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
3113		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
3114			return (1);
3115		break;
3116	}
3117
3118	/* Simple binary comparison if no mask specified. */
3119	if (samask == NULL)
3120		return (memcmp(p1, p2, len));
3121
3122	/* Set up the mask, and do a mask-based comparison. */
3123	if (sa1->sa_family != samask->sa_family ||
3124	    (mask = sa_rawaddr(samask, NULL)) == NULL)
3125		return (1);
3126
3127	for (i = 0; i < len; i++)
3128		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
3129			return (1);
3130	return (0);
3131}
3132
3133/*
3134 * Return a pointer to the part of the sockaddr that contains the
3135 * raw address, and set *nbytes to its length in bytes. Returns
3136 * NULL if the address family is unknown.
3137 */
3138void *
3139sa_rawaddr(struct sockaddr *sa, int *nbytes) {
3140	void *p;
3141	int len;
3142
3143	switch (sa->sa_family) {
3144	case AF_INET:
3145		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
3146		p = &((struct sockaddr_in *)sa)->sin_addr;
3147		break;
3148	case AF_INET6:
3149		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
3150		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
3151		break;
3152	default:
3153		p = NULL;
3154		len = 0;
3155	}
3156
3157	if (nbytes != NULL)
3158		*nbytes = len;
3159	return (p);
3160}
3161
3162void
3163huphandler(int sig __unused)
3164{
3165	got_sighup = 1;
3166}
3167
3168void terminate(int sig __unused)
3169{
3170	pidfile_remove(pfh);
3171	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
3172	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
3173	exit (0);
3174}
3175
3176