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