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