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