mountd.c revision 283762
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 283762 2015-05-30 01:04:45Z 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	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 ((fsb.f_flags & MNT_AUTOMOUNTED) != 0)
1428				    syslog(LOG_ERR, "Warning: exporting of "
1429					"automounted fs %s not supported", cp);
1430				if (got_nondir) {
1431				    syslog(LOG_ERR, "dirs must be first");
1432				    getexp_err(ep, tgrp);
1433				    goto nextline;
1434				}
1435				if (v4root_phase == 1) {
1436				    if (dirp != NULL) {
1437					syslog(LOG_ERR, "Multiple V4 dirs");
1438					getexp_err(ep, tgrp);
1439					goto nextline;
1440				    }
1441				    if (strlen(v4root_dirpath) == 0) {
1442					strlcpy(v4root_dirpath, cp,
1443					    sizeof (v4root_dirpath));
1444				    } else if (strcmp(v4root_dirpath, cp)
1445					!= 0) {
1446					syslog(LOG_ERR,
1447					    "different V4 dirpath %s", cp);
1448					getexp_err(ep, tgrp);
1449					goto nextline;
1450				    }
1451				    dirp = cp;
1452				    v4root_phase = 2;
1453				    got_nondir = 1;
1454				    ep = get_exp();
1455				} else {
1456				    if (ep) {
1457					if (ep->ex_fs.val[0] !=
1458					    fsb.f_fsid.val[0] ||
1459					    ep->ex_fs.val[1] !=
1460					    fsb.f_fsid.val[1]) {
1461						getexp_err(ep, tgrp);
1462						goto nextline;
1463					}
1464				    } else {
1465					/*
1466					 * See if this directory is already
1467					 * in the list.
1468					 */
1469					ep = ex_search(&fsb.f_fsid);
1470					if (ep == (struct exportlist *)NULL) {
1471					    ep = get_exp();
1472					    ep->ex_fs = fsb.f_fsid;
1473					    ep->ex_fsdir = (char *)malloc
1474					        (strlen(fsb.f_mntonname) + 1);
1475					    if (ep->ex_fsdir)
1476						strcpy(ep->ex_fsdir,
1477						    fsb.f_mntonname);
1478					    else
1479						out_of_mem();
1480					    if (debug)
1481						warnx(
1482						  "making new ep fs=0x%x,0x%x",
1483						  fsb.f_fsid.val[0],
1484						  fsb.f_fsid.val[1]);
1485					} else if (debug)
1486					    warnx("found ep fs=0x%x,0x%x",
1487						fsb.f_fsid.val[0],
1488						fsb.f_fsid.val[1]);
1489				    }
1490
1491				    /*
1492				     * Add dirpath to export mount point.
1493				     */
1494				    dirp = add_expdir(&dirhead, cp, len);
1495				    dirplen = len;
1496				}
1497			    } else {
1498				getexp_err(ep, tgrp);
1499				goto nextline;
1500			    }
1501			    *endcp = savedc;
1502			} else {
1503			    savedc = *endcp;
1504			    *endcp = '\0';
1505			    got_nondir = 1;
1506			    if (ep == (struct exportlist *)NULL) {
1507				getexp_err(ep, tgrp);
1508				goto nextline;
1509			    }
1510
1511			    /*
1512			     * Get the host or netgroup.
1513			     */
1514			    setnetgrent(cp);
1515			    netgrp = getnetgrent(&hst, &usr, &dom);
1516			    do {
1517				if (has_host) {
1518				    grp->gr_next = get_grp();
1519				    grp = grp->gr_next;
1520				}
1521				if (netgrp) {
1522				    if (hst == 0) {
1523					syslog(LOG_ERR,
1524				"null hostname in netgroup %s, skipping", cp);
1525					grp->gr_type = GT_IGNORE;
1526				    } else if (get_host(hst, grp, tgrp)) {
1527					syslog(LOG_ERR,
1528			"bad host %s in netgroup %s, skipping", hst, cp);
1529					grp->gr_type = GT_IGNORE;
1530				    }
1531				} else if (get_host(cp, grp, tgrp)) {
1532				    syslog(LOG_ERR, "bad host %s, skipping", cp);
1533				    grp->gr_type = GT_IGNORE;
1534				}
1535				has_host = TRUE;
1536			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
1537			    endnetgrent();
1538			    *endcp = savedc;
1539			}
1540			cp = endcp;
1541			nextfield(&cp, &endcp);
1542			len = endcp - cp;
1543		}
1544		if (check_options(dirhead)) {
1545			getexp_err(ep, tgrp);
1546			goto nextline;
1547		}
1548		if (!has_host) {
1549			grp->gr_type = GT_DEFAULT;
1550			if (debug)
1551				warnx("adding a default entry");
1552
1553		/*
1554		 * Don't allow a network export coincide with a list of
1555		 * host(s) on the same line.
1556		 */
1557		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1558			syslog(LOG_ERR, "network/host conflict");
1559			getexp_err(ep, tgrp);
1560			goto nextline;
1561
1562		/*
1563		 * If an export list was specified on this line, make sure
1564		 * that we have at least one valid entry, otherwise skip it.
1565		 */
1566		} else {
1567			grp = tgrp;
1568			while (grp && grp->gr_type == GT_IGNORE)
1569				grp = grp->gr_next;
1570			if (! grp) {
1571			    getexp_err(ep, tgrp);
1572			    goto nextline;
1573			}
1574		}
1575
1576		if (v4root_phase == 1) {
1577			syslog(LOG_ERR, "V4:root, no dirp, ignored");
1578			getexp_err(ep, tgrp);
1579			goto nextline;
1580		}
1581
1582		/*
1583		 * Loop through hosts, pushing the exports into the kernel.
1584		 * After loop, tgrp points to the start of the list and
1585		 * grp points to the last entry in the list.
1586		 */
1587		grp = tgrp;
1588		do {
1589			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
1590			    &fsb)) {
1591				getexp_err(ep, tgrp);
1592				goto nextline;
1593			}
1594		} while (grp->gr_next && (grp = grp->gr_next));
1595
1596		/*
1597		 * For V4: don't enter in mount lists.
1598		 */
1599		if (v4root_phase > 0 && v4root_phase <= 2) {
1600			/*
1601			 * Since these structures aren't used by mountd,
1602			 * free them up now.
1603			 */
1604			if (ep != NULL)
1605				free_exp(ep);
1606			while (tgrp != NULL) {
1607				grp = tgrp;
1608				tgrp = tgrp->gr_next;
1609				free_grp(grp);
1610			}
1611			goto nextline;
1612		}
1613
1614		/*
1615		 * Success. Update the data structures.
1616		 */
1617		if (has_host) {
1618			hang_dirp(dirhead, tgrp, ep, opt_flags);
1619			grp->gr_next = grphead;
1620			grphead = tgrp;
1621		} else {
1622			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1623				opt_flags);
1624			free_grp(grp);
1625		}
1626		dirhead = (struct dirlist *)NULL;
1627		if ((ep->ex_flag & EX_LINKED) == 0) {
1628			ep2 = exphead;
1629			epp = &exphead;
1630
1631			/*
1632			 * Insert in the list in alphabetical order.
1633			 */
1634			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1635				epp = &ep2->ex_next;
1636				ep2 = ep2->ex_next;
1637			}
1638			if (ep2)
1639				ep->ex_next = ep2;
1640			*epp = ep;
1641			ep->ex_flag |= EX_LINKED;
1642		}
1643nextline:
1644		v4root_phase = 0;
1645		if (dirhead) {
1646			free_dir(dirhead);
1647			dirhead = (struct dirlist *)NULL;
1648		}
1649	}
1650}
1651
1652/*
1653 * Get the export list from all specified files
1654 */
1655void
1656get_exportlist(void)
1657{
1658	struct exportlist *ep, *ep2;
1659	struct grouplist *grp, *tgrp;
1660	struct export_args export;
1661	struct iovec *iov;
1662	struct statfs *fsp, *mntbufp;
1663	struct xvfsconf vfc;
1664	char errmsg[255];
1665	int num, i;
1666	int iovlen;
1667	int done;
1668	struct nfsex_args eargs;
1669
1670	if (suspend_nfsd != 0)
1671		(void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
1672	v4root_dirpath[0] = '\0';
1673	bzero(&export, sizeof(export));
1674	export.ex_flags = MNT_DELEXPORT;
1675	iov = NULL;
1676	iovlen = 0;
1677	bzero(errmsg, sizeof(errmsg));
1678
1679	/*
1680	 * First, get rid of the old list
1681	 */
1682	ep = exphead;
1683	while (ep) {
1684		ep2 = ep;
1685		ep = ep->ex_next;
1686		free_exp(ep2);
1687	}
1688	exphead = (struct exportlist *)NULL;
1689
1690	grp = grphead;
1691	while (grp) {
1692		tgrp = grp;
1693		grp = grp->gr_next;
1694		free_grp(tgrp);
1695	}
1696	grphead = (struct grouplist *)NULL;
1697
1698	/*
1699	 * and the old V4 root dir.
1700	 */
1701	bzero(&eargs, sizeof (eargs));
1702	eargs.export.ex_flags = MNT_DELEXPORT;
1703	if (run_v4server > 0 &&
1704	    nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 &&
1705	    errno != ENOENT)
1706		syslog(LOG_ERR, "Can't delete exports for V4:");
1707
1708	/*
1709	 * and clear flag that notes if a public fh has been exported.
1710	 */
1711	has_publicfh = 0;
1712
1713	/*
1714	 * And delete exports that are in the kernel for all local
1715	 * filesystems.
1716	 * XXX: Should know how to handle all local exportable filesystems.
1717	 */
1718	num = getmntinfo(&mntbufp, MNT_NOWAIT);
1719
1720	if (num > 0) {
1721		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1722		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1723		build_iovec(&iov, &iovlen, "from", NULL, 0);
1724		build_iovec(&iov, &iovlen, "update", NULL, 0);
1725		build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
1726		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1727	}
1728
1729	for (i = 0; i < num; i++) {
1730		fsp = &mntbufp[i];
1731		if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
1732			syslog(LOG_ERR, "getvfsbyname() failed for %s",
1733			    fsp->f_fstypename);
1734			continue;
1735		}
1736
1737		/*
1738		 * We do not need to delete "export" flag from
1739		 * filesystems that do not have it set.
1740		 */
1741		if (!(fsp->f_flags & MNT_EXPORTED))
1742		    continue;
1743		/*
1744		 * Do not delete export for network filesystem by
1745		 * passing "export" arg to nmount().
1746		 * It only makes sense to do this for local filesystems.
1747		 */
1748		if (vfc.vfc_flags & VFCF_NETWORK)
1749			continue;
1750
1751		iov[1].iov_base = fsp->f_fstypename;
1752		iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
1753		iov[3].iov_base = fsp->f_mntonname;
1754		iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
1755		iov[5].iov_base = fsp->f_mntfromname;
1756		iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
1757		errmsg[0] = '\0';
1758
1759		/*
1760		 * EXDEV is returned when path exists but is not a
1761		 * mount point.  May happens if raced with unmount.
1762		 */
1763		if (nmount(iov, iovlen, fsp->f_flags) < 0 &&
1764		    errno != ENOENT && errno != ENOTSUP && errno != EXDEV) {
1765			syslog(LOG_ERR,
1766			    "can't delete exports for %s: %m %s",
1767			    fsp->f_mntonname, errmsg);
1768		}
1769	}
1770
1771	if (iov != NULL) {
1772		/* Free strings allocated by strdup() in getmntopts.c */
1773		free(iov[0].iov_base); /* fstype */
1774		free(iov[2].iov_base); /* fspath */
1775		free(iov[4].iov_base); /* from */
1776		free(iov[6].iov_base); /* update */
1777		free(iov[8].iov_base); /* export */
1778		free(iov[10].iov_base); /* errmsg */
1779
1780		/* free iov, allocated by realloc() */
1781		free(iov);
1782		iovlen = 0;
1783	}
1784
1785	/*
1786	 * Read in the exports file and build the list, calling
1787	 * nmount() as we go along to push the export rules into the kernel.
1788	 */
1789	done = 0;
1790	for (i = 0; exnames[i] != NULL; i++) {
1791		if (debug)
1792			warnx("reading exports from %s", exnames[i]);
1793		if ((exp_file = fopen(exnames[i], "r")) == NULL) {
1794			syslog(LOG_WARNING, "can't open %s", exnames[i]);
1795			continue;
1796		}
1797		get_exportlist_one();
1798		fclose(exp_file);
1799		done++;
1800	}
1801	if (done == 0) {
1802		syslog(LOG_ERR, "can't open any exports file");
1803		exit(2);
1804	}
1805
1806	/*
1807	 * If there was no public fh, clear any previous one set.
1808	 */
1809	if (run_v4server > 0 && has_publicfh == 0)
1810		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
1811
1812	/* Resume the nfsd. If they weren't suspended, this is harmless. */
1813	(void)nfssvc(NFSSVC_RESUMENFSD, NULL);
1814}
1815
1816/*
1817 * Allocate an export list element
1818 */
1819struct exportlist *
1820get_exp(void)
1821{
1822	struct exportlist *ep;
1823
1824	ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
1825	if (ep == (struct exportlist *)NULL)
1826		out_of_mem();
1827	return (ep);
1828}
1829
1830/*
1831 * Allocate a group list element
1832 */
1833struct grouplist *
1834get_grp(void)
1835{
1836	struct grouplist *gp;
1837
1838	gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
1839	if (gp == (struct grouplist *)NULL)
1840		out_of_mem();
1841	return (gp);
1842}
1843
1844/*
1845 * Clean up upon an error in get_exportlist().
1846 */
1847void
1848getexp_err(struct exportlist *ep, struct grouplist *grp)
1849{
1850	struct grouplist *tgrp;
1851
1852	if (!(opt_flags & OP_QUIET))
1853		syslog(LOG_ERR, "bad exports list line %s", line);
1854	if (ep && (ep->ex_flag & EX_LINKED) == 0)
1855		free_exp(ep);
1856	while (grp) {
1857		tgrp = grp;
1858		grp = grp->gr_next;
1859		free_grp(tgrp);
1860	}
1861}
1862
1863/*
1864 * Search the export list for a matching fs.
1865 */
1866struct exportlist *
1867ex_search(fsid_t *fsid)
1868{
1869	struct exportlist *ep;
1870
1871	ep = exphead;
1872	while (ep) {
1873		if (ep->ex_fs.val[0] == fsid->val[0] &&
1874		    ep->ex_fs.val[1] == fsid->val[1])
1875			return (ep);
1876		ep = ep->ex_next;
1877	}
1878	return (ep);
1879}
1880
1881/*
1882 * Add a directory path to the list.
1883 */
1884char *
1885add_expdir(struct dirlist **dpp, char *cp, int len)
1886{
1887	struct dirlist *dp;
1888
1889	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1890	if (dp == (struct dirlist *)NULL)
1891		out_of_mem();
1892	dp->dp_left = *dpp;
1893	dp->dp_right = (struct dirlist *)NULL;
1894	dp->dp_flag = 0;
1895	dp->dp_hosts = (struct hostlist *)NULL;
1896	strcpy(dp->dp_dirp, cp);
1897	*dpp = dp;
1898	return (dp->dp_dirp);
1899}
1900
1901/*
1902 * Hang the dir list element off the dirpath binary tree as required
1903 * and update the entry for host.
1904 */
1905void
1906hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
1907	int flags)
1908{
1909	struct hostlist *hp;
1910	struct dirlist *dp2;
1911
1912	if (flags & OP_ALLDIRS) {
1913		if (ep->ex_defdir)
1914			free((caddr_t)dp);
1915		else
1916			ep->ex_defdir = dp;
1917		if (grp == (struct grouplist *)NULL) {
1918			ep->ex_defdir->dp_flag |= DP_DEFSET;
1919			/* Save the default security flavors list. */
1920			ep->ex_defnumsecflavors = ep->ex_numsecflavors;
1921			if (ep->ex_numsecflavors > 0)
1922				memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
1923				    sizeof(ep->ex_secflavors));
1924		} else while (grp) {
1925			hp = get_ht();
1926			hp->ht_grp = grp;
1927			hp->ht_next = ep->ex_defdir->dp_hosts;
1928			ep->ex_defdir->dp_hosts = hp;
1929			/* Save the security flavors list for this host set. */
1930			grp->gr_numsecflavors = ep->ex_numsecflavors;
1931			if (ep->ex_numsecflavors > 0)
1932				memcpy(grp->gr_secflavors, ep->ex_secflavors,
1933				    sizeof(ep->ex_secflavors));
1934			grp = grp->gr_next;
1935		}
1936	} else {
1937
1938		/*
1939		 * Loop through the directories adding them to the tree.
1940		 */
1941		while (dp) {
1942			dp2 = dp->dp_left;
1943			add_dlist(&ep->ex_dirl, dp, grp, flags, ep);
1944			dp = dp2;
1945		}
1946	}
1947}
1948
1949/*
1950 * Traverse the binary tree either updating a node that is already there
1951 * for the new directory or adding the new node.
1952 */
1953void
1954add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
1955	int flags, struct exportlist *ep)
1956{
1957	struct dirlist *dp;
1958	struct hostlist *hp;
1959	int cmp;
1960
1961	dp = *dpp;
1962	if (dp) {
1963		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1964		if (cmp > 0) {
1965			add_dlist(&dp->dp_left, newdp, grp, flags, ep);
1966			return;
1967		} else if (cmp < 0) {
1968			add_dlist(&dp->dp_right, newdp, grp, flags, ep);
1969			return;
1970		} else
1971			free((caddr_t)newdp);
1972	} else {
1973		dp = newdp;
1974		dp->dp_left = (struct dirlist *)NULL;
1975		*dpp = dp;
1976	}
1977	if (grp) {
1978
1979		/*
1980		 * Hang all of the host(s) off of the directory point.
1981		 */
1982		do {
1983			hp = get_ht();
1984			hp->ht_grp = grp;
1985			hp->ht_next = dp->dp_hosts;
1986			dp->dp_hosts = hp;
1987			/* Save the security flavors list for this host set. */
1988			grp->gr_numsecflavors = ep->ex_numsecflavors;
1989			if (ep->ex_numsecflavors > 0)
1990				memcpy(grp->gr_secflavors, ep->ex_secflavors,
1991				    sizeof(ep->ex_secflavors));
1992			grp = grp->gr_next;
1993		} while (grp);
1994	} else {
1995		dp->dp_flag |= DP_DEFSET;
1996		/* Save the default security flavors list. */
1997		ep->ex_defnumsecflavors = ep->ex_numsecflavors;
1998		if (ep->ex_numsecflavors > 0)
1999			memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2000			    sizeof(ep->ex_secflavors));
2001	}
2002}
2003
2004/*
2005 * Search for a dirpath on the export point.
2006 */
2007struct dirlist *
2008dirp_search(struct dirlist *dp, char *dirp)
2009{
2010	int cmp;
2011
2012	if (dp) {
2013		cmp = strcmp(dp->dp_dirp, dirp);
2014		if (cmp > 0)
2015			return (dirp_search(dp->dp_left, dirp));
2016		else if (cmp < 0)
2017			return (dirp_search(dp->dp_right, dirp));
2018		else
2019			return (dp);
2020	}
2021	return (dp);
2022}
2023
2024/*
2025 * Scan for a host match in a directory tree.
2026 */
2027int
2028chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2029	int *hostsetp, int *numsecflavors, int **secflavorsp)
2030{
2031	struct hostlist *hp;
2032	struct grouplist *grp;
2033	struct addrinfo *ai;
2034
2035	if (dp) {
2036		if (dp->dp_flag & DP_DEFSET)
2037			*defsetp = dp->dp_flag;
2038		hp = dp->dp_hosts;
2039		while (hp) {
2040			grp = hp->ht_grp;
2041			switch (grp->gr_type) {
2042			case GT_HOST:
2043				ai = grp->gr_ptr.gt_addrinfo;
2044				for (; ai; ai = ai->ai_next) {
2045					if (!sacmp(ai->ai_addr, saddr, NULL)) {
2046						*hostsetp =
2047						    (hp->ht_flag | DP_HOSTSET);
2048						if (numsecflavors != NULL) {
2049							*numsecflavors =
2050							    grp->gr_numsecflavors;
2051							*secflavorsp =
2052							    grp->gr_secflavors;
2053						}
2054						return (1);
2055					}
2056				}
2057				break;
2058			case GT_NET:
2059				if (!sacmp(saddr, (struct sockaddr *)
2060				    &grp->gr_ptr.gt_net.nt_net,
2061				    (struct sockaddr *)
2062				    &grp->gr_ptr.gt_net.nt_mask)) {
2063					*hostsetp = (hp->ht_flag | DP_HOSTSET);
2064					if (numsecflavors != NULL) {
2065						*numsecflavors =
2066						    grp->gr_numsecflavors;
2067						*secflavorsp =
2068						    grp->gr_secflavors;
2069					}
2070					return (1);
2071				}
2072				break;
2073			}
2074			hp = hp->ht_next;
2075		}
2076	}
2077	return (0);
2078}
2079
2080/*
2081 * Scan tree for a host that matches the address.
2082 */
2083int
2084scan_tree(struct dirlist *dp, struct sockaddr *saddr)
2085{
2086	int defset, hostset;
2087
2088	if (dp) {
2089		if (scan_tree(dp->dp_left, saddr))
2090			return (1);
2091		if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
2092			return (1);
2093		if (scan_tree(dp->dp_right, saddr))
2094			return (1);
2095	}
2096	return (0);
2097}
2098
2099/*
2100 * Traverse the dirlist tree and free it up.
2101 */
2102void
2103free_dir(struct dirlist *dp)
2104{
2105
2106	if (dp) {
2107		free_dir(dp->dp_left);
2108		free_dir(dp->dp_right);
2109		free_host(dp->dp_hosts);
2110		free((caddr_t)dp);
2111	}
2112}
2113
2114/*
2115 * Parse a colon separated list of security flavors
2116 */
2117int
2118parsesec(char *seclist, struct exportlist *ep)
2119{
2120	char *cp, savedc;
2121	int flavor;
2122
2123	ep->ex_numsecflavors = 0;
2124	for (;;) {
2125		cp = strchr(seclist, ':');
2126		if (cp) {
2127			savedc = *cp;
2128			*cp = '\0';
2129		}
2130
2131		if (!strcmp(seclist, "sys"))
2132			flavor = AUTH_SYS;
2133		else if (!strcmp(seclist, "krb5"))
2134			flavor = RPCSEC_GSS_KRB5;
2135		else if (!strcmp(seclist, "krb5i"))
2136			flavor = RPCSEC_GSS_KRB5I;
2137		else if (!strcmp(seclist, "krb5p"))
2138			flavor = RPCSEC_GSS_KRB5P;
2139		else {
2140			if (cp)
2141				*cp = savedc;
2142			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2143			return (1);
2144		}
2145		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2146			if (cp)
2147				*cp = savedc;
2148			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2149			return (1);
2150		}
2151		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2152		ep->ex_numsecflavors++;
2153		if (cp) {
2154			*cp = savedc;
2155			seclist = cp + 1;
2156		} else {
2157			break;
2158		}
2159	}
2160	return (0);
2161}
2162
2163/*
2164 * Parse the option string and update fields.
2165 * Option arguments may either be -<option>=<value> or
2166 * -<option> <value>
2167 */
2168int
2169do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2170	int *has_hostp, int *exflagsp, struct xucred *cr)
2171{
2172	char *cpoptarg, *cpoptend;
2173	char *cp, *endcp, *cpopt, savedc, savedc2;
2174	int allflag, usedarg;
2175
2176	savedc2 = '\0';
2177	cpopt = *cpp;
2178	cpopt++;
2179	cp = *endcpp;
2180	savedc = *cp;
2181	*cp = '\0';
2182	while (cpopt && *cpopt) {
2183		allflag = 1;
2184		usedarg = -2;
2185		if ((cpoptend = strchr(cpopt, ','))) {
2186			*cpoptend++ = '\0';
2187			if ((cpoptarg = strchr(cpopt, '=')))
2188				*cpoptarg++ = '\0';
2189		} else {
2190			if ((cpoptarg = strchr(cpopt, '=')))
2191				*cpoptarg++ = '\0';
2192			else {
2193				*cp = savedc;
2194				nextfield(&cp, &endcp);
2195				**endcpp = '\0';
2196				if (endcp > cp && *cp != '-') {
2197					cpoptarg = cp;
2198					savedc2 = *endcp;
2199					*endcp = '\0';
2200					usedarg = 0;
2201				}
2202			}
2203		}
2204		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
2205			*exflagsp |= MNT_EXRDONLY;
2206		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
2207		    !(allflag = strcmp(cpopt, "mapall")) ||
2208		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
2209			usedarg++;
2210			parsecred(cpoptarg, cr);
2211			if (allflag == 0) {
2212				*exflagsp |= MNT_EXPORTANON;
2213				opt_flags |= OP_MAPALL;
2214			} else
2215				opt_flags |= OP_MAPROOT;
2216		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
2217		    !strcmp(cpopt, "m"))) {
2218			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
2219				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
2220				return (1);
2221			}
2222			usedarg++;
2223			opt_flags |= OP_MASK;
2224		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
2225			!strcmp(cpopt, "n"))) {
2226			if (strchr(cpoptarg, '/') != NULL) {
2227				if (debug)
2228					fprintf(stderr, "setting OP_MASKLEN\n");
2229				opt_flags |= OP_MASKLEN;
2230			}
2231			if (grp->gr_type != GT_NULL) {
2232				syslog(LOG_ERR, "network/host conflict");
2233				return (1);
2234			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
2235				syslog(LOG_ERR, "bad net: %s", cpoptarg);
2236				return (1);
2237			}
2238			grp->gr_type = GT_NET;
2239			*has_hostp = 1;
2240			usedarg++;
2241			opt_flags |= OP_NET;
2242		} else if (!strcmp(cpopt, "alldirs")) {
2243			opt_flags |= OP_ALLDIRS;
2244		} else if (!strcmp(cpopt, "public")) {
2245			*exflagsp |= MNT_EXPUBLIC;
2246		} else if (!strcmp(cpopt, "webnfs")) {
2247			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
2248			opt_flags |= OP_MAPALL;
2249		} else if (cpoptarg && !strcmp(cpopt, "index")) {
2250			ep->ex_indexfile = strdup(cpoptarg);
2251		} else if (!strcmp(cpopt, "quiet")) {
2252			opt_flags |= OP_QUIET;
2253		} else if (cpoptarg && !strcmp(cpopt, "sec")) {
2254			if (parsesec(cpoptarg, ep))
2255				return (1);
2256			opt_flags |= OP_SEC;
2257			usedarg++;
2258		} else {
2259			syslog(LOG_ERR, "bad opt %s", cpopt);
2260			return (1);
2261		}
2262		if (usedarg >= 0) {
2263			*endcp = savedc2;
2264			**endcpp = savedc;
2265			if (usedarg > 0) {
2266				*cpp = cp;
2267				*endcpp = endcp;
2268			}
2269			return (0);
2270		}
2271		cpopt = cpoptend;
2272	}
2273	**endcpp = savedc;
2274	return (0);
2275}
2276
2277/*
2278 * Translate a character string to the corresponding list of network
2279 * addresses for a hostname.
2280 */
2281int
2282get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
2283{
2284	struct grouplist *checkgrp;
2285	struct addrinfo *ai, *tai, hints;
2286	int ecode;
2287	char host[NI_MAXHOST];
2288
2289	if (grp->gr_type != GT_NULL) {
2290		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
2291		return (1);
2292	}
2293	memset(&hints, 0, sizeof hints);
2294	hints.ai_flags = AI_CANONNAME;
2295	hints.ai_protocol = IPPROTO_UDP;
2296	ecode = getaddrinfo(cp, NULL, &hints, &ai);
2297	if (ecode != 0) {
2298		syslog(LOG_ERR,"can't get address info for host %s", cp);
2299		return 1;
2300	}
2301	grp->gr_ptr.gt_addrinfo = ai;
2302	while (ai != NULL) {
2303		if (ai->ai_canonname == NULL) {
2304			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2305			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
2306				strlcpy(host, "?", sizeof(host));
2307			ai->ai_canonname = strdup(host);
2308			ai->ai_flags |= AI_CANONNAME;
2309		}
2310		if (debug)
2311			fprintf(stderr, "got host %s\n", ai->ai_canonname);
2312		/*
2313		 * Sanity check: make sure we don't already have an entry
2314		 * for this host in the grouplist.
2315		 */
2316		for (checkgrp = tgrp; checkgrp != NULL;
2317		    checkgrp = checkgrp->gr_next) {
2318			if (checkgrp->gr_type != GT_HOST)
2319				continue;
2320			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
2321			    tai = tai->ai_next) {
2322				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
2323					continue;
2324				if (debug)
2325					fprintf(stderr,
2326					    "ignoring duplicate host %s\n",
2327					    ai->ai_canonname);
2328				grp->gr_type = GT_IGNORE;
2329				return (0);
2330			}
2331		}
2332		ai = ai->ai_next;
2333	}
2334	grp->gr_type = GT_HOST;
2335	return (0);
2336}
2337
2338/*
2339 * Free up an exports list component
2340 */
2341void
2342free_exp(struct exportlist *ep)
2343{
2344
2345	if (ep->ex_defdir) {
2346		free_host(ep->ex_defdir->dp_hosts);
2347		free((caddr_t)ep->ex_defdir);
2348	}
2349	if (ep->ex_fsdir)
2350		free(ep->ex_fsdir);
2351	if (ep->ex_indexfile)
2352		free(ep->ex_indexfile);
2353	free_dir(ep->ex_dirl);
2354	free((caddr_t)ep);
2355}
2356
2357/*
2358 * Free hosts.
2359 */
2360void
2361free_host(struct hostlist *hp)
2362{
2363	struct hostlist *hp2;
2364
2365	while (hp) {
2366		hp2 = hp;
2367		hp = hp->ht_next;
2368		free((caddr_t)hp2);
2369	}
2370}
2371
2372struct hostlist *
2373get_ht(void)
2374{
2375	struct hostlist *hp;
2376
2377	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
2378	if (hp == (struct hostlist *)NULL)
2379		out_of_mem();
2380	hp->ht_next = (struct hostlist *)NULL;
2381	hp->ht_flag = 0;
2382	return (hp);
2383}
2384
2385/*
2386 * Out of memory, fatal
2387 */
2388void
2389out_of_mem(void)
2390{
2391
2392	syslog(LOG_ERR, "out of memory");
2393	exit(2);
2394}
2395
2396/*
2397 * Do the nmount() syscall with the update flag to push the export info into
2398 * the kernel.
2399 */
2400int
2401do_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
2402    struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
2403{
2404	struct statfs fsb1;
2405	struct addrinfo *ai;
2406	struct export_args ea, *eap;
2407	char errmsg[255];
2408	char *cp;
2409	int done;
2410	char savedc;
2411	struct iovec *iov;
2412	int i, iovlen;
2413	int ret;
2414	struct nfsex_args nfsea;
2415
2416	if (run_v4server > 0)
2417		eap = &nfsea.export;
2418	else
2419		eap = &ea;
2420
2421	cp = NULL;
2422	savedc = '\0';
2423	iov = NULL;
2424	iovlen = 0;
2425	ret = 0;
2426
2427	bzero(eap, sizeof (struct export_args));
2428	bzero(errmsg, sizeof(errmsg));
2429	eap->ex_flags = exflags;
2430	eap->ex_anon = *anoncrp;
2431	eap->ex_indexfile = ep->ex_indexfile;
2432	if (grp->gr_type == GT_HOST)
2433		ai = grp->gr_ptr.gt_addrinfo;
2434	else
2435		ai = NULL;
2436	eap->ex_numsecflavors = ep->ex_numsecflavors;
2437	for (i = 0; i < eap->ex_numsecflavors; i++)
2438		eap->ex_secflavors[i] = ep->ex_secflavors[i];
2439	if (eap->ex_numsecflavors == 0) {
2440		eap->ex_numsecflavors = 1;
2441		eap->ex_secflavors[0] = AUTH_SYS;
2442	}
2443	done = FALSE;
2444
2445	if (v4root_phase == 0) {
2446		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2447		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2448		build_iovec(&iov, &iovlen, "from", NULL, 0);
2449		build_iovec(&iov, &iovlen, "update", NULL, 0);
2450		build_iovec(&iov, &iovlen, "export", eap,
2451		    sizeof (struct export_args));
2452		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
2453	}
2454
2455	while (!done) {
2456		switch (grp->gr_type) {
2457		case GT_HOST:
2458			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
2459				goto skip;
2460			eap->ex_addr = ai->ai_addr;
2461			eap->ex_addrlen = ai->ai_addrlen;
2462			eap->ex_masklen = 0;
2463			break;
2464		case GT_NET:
2465			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
2466			    have_v6 == 0)
2467				goto skip;
2468			eap->ex_addr =
2469			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
2470			eap->ex_addrlen =
2471			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
2472			eap->ex_mask =
2473			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
2474			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
2475			break;
2476		case GT_DEFAULT:
2477			eap->ex_addr = NULL;
2478			eap->ex_addrlen = 0;
2479			eap->ex_mask = NULL;
2480			eap->ex_masklen = 0;
2481			break;
2482		case GT_IGNORE:
2483			ret = 0;
2484			goto error_exit;
2485			break;
2486		default:
2487			syslog(LOG_ERR, "bad grouptype");
2488			if (cp)
2489				*cp = savedc;
2490			ret = 1;
2491			goto error_exit;
2492		};
2493
2494		/*
2495		 * For V4:, use the nfssvc() syscall, instead of mount().
2496		 */
2497		if (v4root_phase == 2) {
2498			nfsea.fspec = v4root_dirpath;
2499			if (run_v4server > 0 &&
2500			    nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) {
2501				syslog(LOG_ERR, "Exporting V4: failed");
2502				return (2);
2503			}
2504		} else {
2505			/*
2506			 * XXX:
2507			 * Maybe I should just use the fsb->f_mntonname path
2508			 * instead of looping back up the dirp to the mount
2509			 * point??
2510			 * Also, needs to know how to export all types of local
2511			 * exportable filesystems and not just "ufs".
2512			 */
2513			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
2514			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
2515			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
2516			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
2517			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
2518			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
2519			errmsg[0] = '\0';
2520
2521			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
2522				if (cp)
2523					*cp-- = savedc;
2524				else
2525					cp = dirp + dirplen - 1;
2526				if (opt_flags & OP_QUIET) {
2527					ret = 1;
2528					goto error_exit;
2529				}
2530				if (errno == EPERM) {
2531					if (debug)
2532						warnx("can't change attributes for %s: %s",
2533						    dirp, errmsg);
2534					syslog(LOG_ERR,
2535					   "can't change attributes for %s: %s",
2536					    dirp, errmsg);
2537					ret = 1;
2538					goto error_exit;
2539				}
2540				if (opt_flags & OP_ALLDIRS) {
2541					if (errno == EINVAL)
2542						syslog(LOG_ERR,
2543		"-alldirs requested but %s is not a filesystem mountpoint",
2544						    dirp);
2545					else
2546						syslog(LOG_ERR,
2547						    "could not remount %s: %m",
2548						    dirp);
2549					ret = 1;
2550					goto error_exit;
2551				}
2552				/* back up over the last component */
2553				while (*cp == '/' && cp > dirp)
2554					cp--;
2555				while (*(cp - 1) != '/' && cp > dirp)
2556					cp--;
2557				if (cp == dirp) {
2558					if (debug)
2559						warnx("mnt unsucc");
2560					syslog(LOG_ERR, "can't export %s %s",
2561					    dirp, errmsg);
2562					ret = 1;
2563					goto error_exit;
2564				}
2565				savedc = *cp;
2566				*cp = '\0';
2567				/*
2568				 * Check that we're still on the same
2569				 * filesystem.
2570				 */
2571				if (statfs(dirp, &fsb1) != 0 ||
2572				    bcmp(&fsb1.f_fsid, &fsb->f_fsid,
2573				    sizeof (fsb1.f_fsid)) != 0) {
2574					*cp = savedc;
2575					syslog(LOG_ERR,
2576					    "can't export %s %s", dirp,
2577					    errmsg);
2578					ret = 1;
2579					goto error_exit;
2580				}
2581			}
2582		}
2583
2584		/*
2585		 * For the experimental server:
2586		 * If this is the public directory, get the file handle
2587		 * and load it into the kernel via the nfssvc() syscall.
2588		 */
2589		if (run_v4server > 0 && (exflags & MNT_EXPUBLIC) != 0) {
2590			fhandle_t fh;
2591			char *public_name;
2592
2593			if (eap->ex_indexfile != NULL)
2594				public_name = eap->ex_indexfile;
2595			else
2596				public_name = dirp;
2597			if (getfh(public_name, &fh) < 0)
2598				syslog(LOG_ERR,
2599				    "Can't get public fh for %s", public_name);
2600			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
2601				syslog(LOG_ERR,
2602				    "Can't set public fh for %s", public_name);
2603			else
2604				has_publicfh = 1;
2605		}
2606skip:
2607		if (ai != NULL)
2608			ai = ai->ai_next;
2609		if (ai == NULL)
2610			done = TRUE;
2611	}
2612	if (cp)
2613		*cp = savedc;
2614error_exit:
2615	/* free strings allocated by strdup() in getmntopts.c */
2616	if (iov != NULL) {
2617		free(iov[0].iov_base); /* fstype */
2618		free(iov[2].iov_base); /* fspath */
2619		free(iov[4].iov_base); /* from */
2620		free(iov[6].iov_base); /* update */
2621		free(iov[8].iov_base); /* export */
2622		free(iov[10].iov_base); /* errmsg */
2623
2624		/* free iov, allocated by realloc() */
2625		free(iov);
2626	}
2627	return (ret);
2628}
2629
2630/*
2631 * Translate a net address.
2632 *
2633 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
2634 */
2635int
2636get_net(char *cp, struct netmsk *net, int maskflg)
2637{
2638	struct netent *np = NULL;
2639	char *name, *p, *prefp;
2640	struct sockaddr_in sin;
2641	struct sockaddr *sa = NULL;
2642	struct addrinfo hints, *ai = NULL;
2643	char netname[NI_MAXHOST];
2644	long preflen;
2645
2646	p = prefp = NULL;
2647	if ((opt_flags & OP_MASKLEN) && !maskflg) {
2648		p = strchr(cp, '/');
2649		*p = '\0';
2650		prefp = p + 1;
2651	}
2652
2653	/*
2654	 * Check for a numeric address first. We wish to avoid
2655	 * possible DNS lookups in getnetbyname().
2656	 */
2657	if (isxdigit(*cp) || *cp == ':') {
2658		memset(&hints, 0, sizeof hints);
2659		/* Ensure the mask and the network have the same family. */
2660		if (maskflg && (opt_flags & OP_NET))
2661			hints.ai_family = net->nt_net.ss_family;
2662		else if (!maskflg && (opt_flags & OP_HAVEMASK))
2663			hints.ai_family = net->nt_mask.ss_family;
2664		else
2665			hints.ai_family = AF_UNSPEC;
2666		hints.ai_flags = AI_NUMERICHOST;
2667		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
2668			sa = ai->ai_addr;
2669		if (sa != NULL && ai->ai_family == AF_INET) {
2670			/*
2671			 * The address in `cp' is really a network address, so
2672			 * use inet_network() to re-interpret this correctly.
2673			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
2674			 */
2675			bzero(&sin, sizeof sin);
2676			sin.sin_family = AF_INET;
2677			sin.sin_len = sizeof sin;
2678			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
2679			if (debug)
2680				fprintf(stderr, "get_net: v4 addr %s\n",
2681				    inet_ntoa(sin.sin_addr));
2682			sa = (struct sockaddr *)&sin;
2683		}
2684	}
2685	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
2686		bzero(&sin, sizeof sin);
2687		sin.sin_family = AF_INET;
2688		sin.sin_len = sizeof sin;
2689		sin.sin_addr = inet_makeaddr(np->n_net, 0);
2690		sa = (struct sockaddr *)&sin;
2691	}
2692	if (sa == NULL)
2693		goto fail;
2694
2695	if (maskflg) {
2696		/* The specified sockaddr is a mask. */
2697		if (checkmask(sa) != 0)
2698			goto fail;
2699		bcopy(sa, &net->nt_mask, sa->sa_len);
2700		opt_flags |= OP_HAVEMASK;
2701	} else {
2702		/* The specified sockaddr is a network address. */
2703		bcopy(sa, &net->nt_net, sa->sa_len);
2704
2705		/* Get a network name for the export list. */
2706		if (np) {
2707			name = np->n_name;
2708		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2709		   NULL, 0, NI_NUMERICHOST) == 0) {
2710			name = netname;
2711		} else {
2712			goto fail;
2713		}
2714		if ((net->nt_name = strdup(name)) == NULL)
2715			out_of_mem();
2716
2717		/*
2718		 * Extract a mask from either a "/<masklen>" suffix, or
2719		 * from the class of an IPv4 address.
2720		 */
2721		if (opt_flags & OP_MASKLEN) {
2722			preflen = strtol(prefp, NULL, 10);
2723			if (preflen < 0L || preflen == LONG_MAX)
2724				goto fail;
2725			bcopy(sa, &net->nt_mask, sa->sa_len);
2726			if (makemask(&net->nt_mask, (int)preflen) != 0)
2727				goto fail;
2728			opt_flags |= OP_HAVEMASK;
2729			*p = '/';
2730		} else if (sa->sa_family == AF_INET &&
2731		    (opt_flags & OP_MASK) == 0) {
2732			in_addr_t addr;
2733
2734			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
2735			if (IN_CLASSA(addr))
2736				preflen = 8;
2737			else if (IN_CLASSB(addr))
2738				preflen = 16;
2739			else if (IN_CLASSC(addr))
2740				preflen = 24;
2741			else if (IN_CLASSD(addr))
2742				preflen = 28;
2743			else
2744				preflen = 32;	/* XXX */
2745
2746			bcopy(sa, &net->nt_mask, sa->sa_len);
2747			makemask(&net->nt_mask, (int)preflen);
2748			opt_flags |= OP_HAVEMASK;
2749		}
2750	}
2751
2752	if (ai)
2753		freeaddrinfo(ai);
2754	return 0;
2755
2756fail:
2757	if (ai)
2758		freeaddrinfo(ai);
2759	return 1;
2760}
2761
2762/*
2763 * Parse out the next white space separated field
2764 */
2765void
2766nextfield(char **cp, char **endcp)
2767{
2768	char *p;
2769
2770	p = *cp;
2771	while (*p == ' ' || *p == '\t')
2772		p++;
2773	if (*p == '\n' || *p == '\0')
2774		*cp = *endcp = p;
2775	else {
2776		*cp = p++;
2777		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
2778			p++;
2779		*endcp = p;
2780	}
2781}
2782
2783/*
2784 * Get an exports file line. Skip over blank lines and handle line
2785 * continuations.
2786 */
2787int
2788get_line(void)
2789{
2790	char *p, *cp;
2791	size_t len;
2792	int totlen, cont_line;
2793
2794	/*
2795	 * Loop around ignoring blank lines and getting all continuation lines.
2796	 */
2797	p = line;
2798	totlen = 0;
2799	do {
2800		if ((p = fgetln(exp_file, &len)) == NULL)
2801			return (0);
2802		cp = p + len - 1;
2803		cont_line = 0;
2804		while (cp >= p &&
2805		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
2806			if (*cp == '\\')
2807				cont_line = 1;
2808			cp--;
2809			len--;
2810		}
2811		if (cont_line) {
2812			*++cp = ' ';
2813			len++;
2814		}
2815		if (linesize < len + totlen + 1) {
2816			linesize = len + totlen + 1;
2817			line = realloc(line, linesize);
2818			if (line == NULL)
2819				out_of_mem();
2820		}
2821		memcpy(line + totlen, p, len);
2822		totlen += len;
2823		line[totlen] = '\0';
2824	} while (totlen == 0 || cont_line);
2825	return (1);
2826}
2827
2828/*
2829 * Parse a description of a credential.
2830 */
2831void
2832parsecred(char *namelist, struct xucred *cr)
2833{
2834	char *name;
2835	int cnt;
2836	char *names;
2837	struct passwd *pw;
2838	struct group *gr;
2839	gid_t groups[XU_NGROUPS + 1];
2840	int ngroups;
2841
2842	cr->cr_version = XUCRED_VERSION;
2843	/*
2844	 * Set up the unprivileged user.
2845	 */
2846	cr->cr_uid = -2;
2847	cr->cr_groups[0] = -2;
2848	cr->cr_ngroups = 1;
2849	/*
2850	 * Get the user's password table entry.
2851	 */
2852	names = strsep(&namelist, " \t\n");
2853	name = strsep(&names, ":");
2854	if (isdigit(*name) || *name == '-')
2855		pw = getpwuid(atoi(name));
2856	else
2857		pw = getpwnam(name);
2858	/*
2859	 * Credentials specified as those of a user.
2860	 */
2861	if (names == NULL) {
2862		if (pw == NULL) {
2863			syslog(LOG_ERR, "unknown user: %s", name);
2864			return;
2865		}
2866		cr->cr_uid = pw->pw_uid;
2867		ngroups = XU_NGROUPS + 1;
2868		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
2869			syslog(LOG_ERR, "too many groups");
2870		/*
2871		 * Compress out duplicate.
2872		 */
2873		cr->cr_ngroups = ngroups - 1;
2874		cr->cr_groups[0] = groups[0];
2875		for (cnt = 2; cnt < ngroups; cnt++)
2876			cr->cr_groups[cnt - 1] = groups[cnt];
2877		return;
2878	}
2879	/*
2880	 * Explicit credential specified as a colon separated list:
2881	 *	uid:gid:gid:...
2882	 */
2883	if (pw != NULL)
2884		cr->cr_uid = pw->pw_uid;
2885	else if (isdigit(*name) || *name == '-')
2886		cr->cr_uid = atoi(name);
2887	else {
2888		syslog(LOG_ERR, "unknown user: %s", name);
2889		return;
2890	}
2891	cr->cr_ngroups = 0;
2892	while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
2893		name = strsep(&names, ":");
2894		if (isdigit(*name) || *name == '-') {
2895			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
2896		} else {
2897			if ((gr = getgrnam(name)) == NULL) {
2898				syslog(LOG_ERR, "unknown group: %s", name);
2899				continue;
2900			}
2901			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2902		}
2903	}
2904	if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
2905		syslog(LOG_ERR, "too many groups");
2906}
2907
2908#define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
2909/*
2910 * Routines that maintain the remote mounttab
2911 */
2912void
2913get_mountlist(void)
2914{
2915	struct mountlist *mlp, **mlpp;
2916	char *host, *dirp, *cp;
2917	char str[STRSIZ];
2918	FILE *mlfile;
2919
2920	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2921		if (errno == ENOENT)
2922			return;
2923		else {
2924			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
2925			return;
2926		}
2927	}
2928	mlpp = &mlhead;
2929	while (fgets(str, STRSIZ, mlfile) != NULL) {
2930		cp = str;
2931		host = strsep(&cp, " \t\n");
2932		dirp = strsep(&cp, " \t\n");
2933		if (host == NULL || dirp == NULL)
2934			continue;
2935		mlp = (struct mountlist *)malloc(sizeof (*mlp));
2936		if (mlp == (struct mountlist *)NULL)
2937			out_of_mem();
2938		strncpy(mlp->ml_host, host, MNTNAMLEN);
2939		mlp->ml_host[MNTNAMLEN] = '\0';
2940		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
2941		mlp->ml_dirp[MNTPATHLEN] = '\0';
2942		mlp->ml_next = (struct mountlist *)NULL;
2943		*mlpp = mlp;
2944		mlpp = &mlp->ml_next;
2945	}
2946	fclose(mlfile);
2947}
2948
2949void
2950del_mlist(char *hostp, char *dirp)
2951{
2952	struct mountlist *mlp, **mlpp;
2953	struct mountlist *mlp2;
2954	FILE *mlfile;
2955	int fnd = 0;
2956
2957	mlpp = &mlhead;
2958	mlp = mlhead;
2959	while (mlp) {
2960		if (!strcmp(mlp->ml_host, hostp) &&
2961		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2962			fnd = 1;
2963			mlp2 = mlp;
2964			*mlpp = mlp = mlp->ml_next;
2965			free((caddr_t)mlp2);
2966		} else {
2967			mlpp = &mlp->ml_next;
2968			mlp = mlp->ml_next;
2969		}
2970	}
2971	if (fnd) {
2972		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
2973			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
2974			return;
2975		}
2976		mlp = mlhead;
2977		while (mlp) {
2978			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2979			mlp = mlp->ml_next;
2980		}
2981		fclose(mlfile);
2982	}
2983}
2984
2985void
2986add_mlist(char *hostp, char *dirp)
2987{
2988	struct mountlist *mlp, **mlpp;
2989	FILE *mlfile;
2990
2991	mlpp = &mlhead;
2992	mlp = mlhead;
2993	while (mlp) {
2994		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2995			return;
2996		mlpp = &mlp->ml_next;
2997		mlp = mlp->ml_next;
2998	}
2999	mlp = (struct mountlist *)malloc(sizeof (*mlp));
3000	if (mlp == (struct mountlist *)NULL)
3001		out_of_mem();
3002	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
3003	mlp->ml_host[MNTNAMLEN] = '\0';
3004	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3005	mlp->ml_dirp[MNTPATHLEN] = '\0';
3006	mlp->ml_next = (struct mountlist *)NULL;
3007	*mlpp = mlp;
3008	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
3009		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
3010		return;
3011	}
3012	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3013	fclose(mlfile);
3014}
3015
3016/*
3017 * Free up a group list.
3018 */
3019void
3020free_grp(struct grouplist *grp)
3021{
3022	if (grp->gr_type == GT_HOST) {
3023		if (grp->gr_ptr.gt_addrinfo != NULL)
3024			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
3025	} else if (grp->gr_type == GT_NET) {
3026		if (grp->gr_ptr.gt_net.nt_name)
3027			free(grp->gr_ptr.gt_net.nt_name);
3028	}
3029	free((caddr_t)grp);
3030}
3031
3032#ifdef DEBUG
3033void
3034SYSLOG(int pri, const char *fmt, ...)
3035{
3036	va_list ap;
3037
3038	va_start(ap, fmt);
3039	vfprintf(stderr, fmt, ap);
3040	va_end(ap);
3041}
3042#endif /* DEBUG */
3043
3044/*
3045 * Check options for consistency.
3046 */
3047int
3048check_options(struct dirlist *dp)
3049{
3050
3051	if (v4root_phase == 0 && dp == NULL)
3052	    return (1);
3053	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
3054	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
3055	    return (1);
3056	}
3057	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
3058		syslog(LOG_ERR, "-mask requires -network");
3059		return (1);
3060	}
3061	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
3062		syslog(LOG_ERR, "-network requires mask specification");
3063		return (1);
3064	}
3065	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
3066		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
3067		return (1);
3068	}
3069	if (v4root_phase > 0 &&
3070	    (opt_flags &
3071	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3072	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3073	    return (1);
3074	}
3075	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3076	    syslog(LOG_ERR, "-alldirs has multiple directories");
3077	    return (1);
3078	}
3079	return (0);
3080}
3081
3082/*
3083 * Check an absolute directory path for any symbolic links. Return true
3084 */
3085int
3086check_dirpath(char *dirp)
3087{
3088	char *cp;
3089	int ret = 1;
3090	struct stat sb;
3091
3092	cp = dirp + 1;
3093	while (*cp && ret) {
3094		if (*cp == '/') {
3095			*cp = '\0';
3096			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3097				ret = 0;
3098			*cp = '/';
3099		}
3100		cp++;
3101	}
3102	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3103		ret = 0;
3104	return (ret);
3105}
3106
3107/*
3108 * Make a netmask according to the specified prefix length. The ss_family
3109 * and other non-address fields must be initialised before calling this.
3110 */
3111int
3112makemask(struct sockaddr_storage *ssp, int bitlen)
3113{
3114	u_char *p;
3115	int bits, i, len;
3116
3117	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
3118		return (-1);
3119	if (bitlen > len * CHAR_BIT)
3120		return (-1);
3121
3122	for (i = 0; i < len; i++) {
3123		bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen;
3124		*p++ = (u_char)~0 << (CHAR_BIT - bits);
3125		bitlen -= bits;
3126	}
3127	return 0;
3128}
3129
3130/*
3131 * Check that the sockaddr is a valid netmask. Returns 0 if the mask
3132 * is acceptable (i.e. of the form 1...10....0).
3133 */
3134int
3135checkmask(struct sockaddr *sa)
3136{
3137	u_char *mask;
3138	int i, len;
3139
3140	if ((mask = sa_rawaddr(sa, &len)) == NULL)
3141		return (-1);
3142
3143	for (i = 0; i < len; i++)
3144		if (mask[i] != 0xff)
3145			break;
3146	if (i < len) {
3147		if (~mask[i] & (u_char)(~mask[i] + 1))
3148			return (-1);
3149		i++;
3150	}
3151	for (; i < len; i++)
3152		if (mask[i] != 0)
3153			return (-1);
3154	return (0);
3155}
3156
3157/*
3158 * Compare two sockaddrs according to a specified mask. Return zero if
3159 * `sa1' matches `sa2' when filtered by the netmask in `samask'.
3160 * If samask is NULL, perform a full comparison.
3161 */
3162int
3163sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
3164{
3165	unsigned char *p1, *p2, *mask;
3166	int len, i;
3167
3168	if (sa1->sa_family != sa2->sa_family ||
3169	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
3170	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
3171		return (1);
3172
3173	switch (sa1->sa_family) {
3174	case AF_INET6:
3175		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
3176		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
3177			return (1);
3178		break;
3179	}
3180
3181	/* Simple binary comparison if no mask specified. */
3182	if (samask == NULL)
3183		return (memcmp(p1, p2, len));
3184
3185	/* Set up the mask, and do a mask-based comparison. */
3186	if (sa1->sa_family != samask->sa_family ||
3187	    (mask = sa_rawaddr(samask, NULL)) == NULL)
3188		return (1);
3189
3190	for (i = 0; i < len; i++)
3191		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
3192			return (1);
3193	return (0);
3194}
3195
3196/*
3197 * Return a pointer to the part of the sockaddr that contains the
3198 * raw address, and set *nbytes to its length in bytes. Returns
3199 * NULL if the address family is unknown.
3200 */
3201void *
3202sa_rawaddr(struct sockaddr *sa, int *nbytes) {
3203	void *p;
3204	int len;
3205
3206	switch (sa->sa_family) {
3207	case AF_INET:
3208		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
3209		p = &((struct sockaddr_in *)sa)->sin_addr;
3210		break;
3211	case AF_INET6:
3212		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
3213		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
3214		break;
3215	default:
3216		p = NULL;
3217		len = 0;
3218	}
3219
3220	if (nbytes != NULL)
3221		*nbytes = len;
3222	return (p);
3223}
3224
3225void
3226huphandler(int sig __unused)
3227{
3228	got_sighup = 1;
3229}
3230
3231void terminate(int sig __unused)
3232{
3233	pidfile_remove(pfh);
3234	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
3235	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
3236	exit (0);
3237}
3238
3239