mountd.c revision 98826
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 * 3. All advertising materials mentioning features or use of this software
17 *    must display the following acknowledgement:
18 *	This product includes software developed by the University of
19 *	California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38static const char copyright[] =
39"@(#) Copyright (c) 1989, 1993\n\
40	The Regents of the University of California.  All rights reserved.\n";
41#endif /*not lint*/
42
43#ifndef lint
44#if 0
45static char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95";
46#endif
47static const char rcsid[] =
48  "$FreeBSD: head/usr.sbin/mountd/mountd.c 98826 2002-06-25 22:55:53Z alfred $";
49#endif /*not lint*/
50
51#include <sys/param.h>
52#include <sys/mount.h>
53#include <sys/fcntl.h>
54#include <sys/stat.h>
55#include <sys/syslog.h>
56#include <sys/sysctl.h>
57#include <sys/linker.h>
58#include <sys/module.h>
59
60#include <rpc/rpc.h>
61#include <rpc/pmap_clnt.h>
62#include <rpc/pmap_prot.h>
63#include <rpcsvc/mount.h>
64#include <nfs/rpcv2.h>
65#include <nfs/nfsproto.h>
66#include <nfsserver/nfs.h>
67#include <ufs/ufs/ufsmount.h>
68#include <fs/msdosfs/msdosfsmount.h>
69#include <fs/ntfs/ntfsmount.h>
70#include <isofs/cd9660/cd9660_mount.h>	/* XXX need isofs in include */
71
72#include <arpa/inet.h>
73
74#include <ctype.h>
75#include <err.h>
76#include <errno.h>
77#include <grp.h>
78#include <netdb.h>
79#include <pwd.h>
80#include <signal.h>
81#include <stdio.h>
82#include <stdlib.h>
83#include <string.h>
84#include <unistd.h>
85#include "pathnames.h"
86
87#ifdef DEBUG
88#include <stdarg.h>
89#endif
90
91#ifndef MOUNTDLOCK
92#define MOUNTDLOCK "/var/run/mountd.lock"
93#endif
94
95/*
96 * Structures for keeping the mount list and export list
97 */
98struct mountlist {
99	struct mountlist *ml_next;
100	char	ml_host[RPCMNT_NAMELEN+1];
101	char	ml_dirp[RPCMNT_PATHLEN+1];
102};
103
104struct dirlist {
105	struct dirlist	*dp_left;
106	struct dirlist	*dp_right;
107	int		dp_flag;
108	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
109	char		dp_dirp[1];	/* Actually malloc'd to size of dir */
110};
111/* dp_flag bits */
112#define	DP_DEFSET	0x1
113#define DP_HOSTSET	0x2
114
115struct exportlist {
116	struct exportlist *ex_next;
117	struct dirlist	*ex_dirl;
118	struct dirlist	*ex_defdir;
119	int		ex_flag;
120	fsid_t		ex_fs;
121	char		*ex_fsdir;
122	char		*ex_indexfile;
123};
124/* ex_flag bits */
125#define	EX_LINKED	0x1
126
127struct netmsk {
128	struct sockaddr_storage nt_net;
129	struct sockaddr_storage nt_mask;
130	char		*nt_name;
131};
132
133union grouptypes {
134	struct addrinfo *gt_addrinfo;
135	struct netmsk	gt_net;
136};
137
138struct grouplist {
139	int gr_type;
140	union grouptypes gr_ptr;
141	struct grouplist *gr_next;
142};
143/* Group types */
144#define	GT_NULL		0x0
145#define	GT_HOST		0x1
146#define	GT_NET		0x2
147#define	GT_DEFAULT	0x3
148#define GT_IGNORE	0x5
149
150struct hostlist {
151	int		 ht_flag;	/* Uses DP_xx bits */
152	struct grouplist *ht_grp;
153	struct hostlist	 *ht_next;
154};
155
156struct fhreturn {
157	int	fhr_flag;
158	int	fhr_vers;
159	nfsfh_t	fhr_fh;
160};
161
162/* Global defs */
163char	*add_expdir(struct dirlist **, char *, int);
164void	add_dlist(struct dirlist **, struct dirlist *,
165				struct grouplist *, int);
166void	add_mlist(char *, char *);
167int	check_dirpath(char *);
168int	check_options(struct dirlist *);
169int	checkmask(struct sockaddr *sa);
170int	chk_host(struct dirlist *, struct sockaddr *, int *, int *);
171void	del_mlist(char *hostp, char *dirp);
172struct dirlist *dirp_search(struct dirlist *, char *);
173int	do_mount(struct exportlist *, struct grouplist *, int,
174		struct xucred *, char *, int, struct statfs *);
175int	do_opt(char **, char **, struct exportlist *, struct grouplist *,
176				int *, int *, struct xucred *);
177struct	exportlist *ex_search(fsid_t *);
178struct	exportlist *get_exp(void);
179void	free_dir(struct dirlist *);
180void	free_exp(struct exportlist *);
181void	free_grp(struct grouplist *);
182void	free_host(struct hostlist *);
183void	get_exportlist(void);
184int	get_host(char *, struct grouplist *, struct grouplist *);
185struct hostlist *get_ht(void);
186int	get_line(void);
187void	get_mountlist(void);
188int	get_net(char *, struct netmsk *, int);
189void	getexp_err(struct exportlist *, struct grouplist *);
190struct grouplist *get_grp(void);
191void	hang_dirp(struct dirlist *, struct grouplist *,
192				struct exportlist *, int);
193void	huphandler(int sig);
194int	makemask(struct sockaddr_storage *ssp, int bitlen);
195void	mntsrv(struct svc_req *, SVCXPRT *);
196void	nextfield(char **, char **);
197void	out_of_mem(void);
198void	parsecred(char *, struct xucred *);
199int	put_exlist(struct dirlist *, XDR *, struct dirlist *, int *);
200void	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
201int	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
202    struct sockaddr *samask);
203int	scan_tree(struct dirlist *, struct sockaddr *);
204static void usage(void);
205int	xdr_dir(XDR *, char *);
206int	xdr_explist(XDR *, caddr_t);
207int	xdr_fhs(XDR *, caddr_t);
208int	xdr_mlist(XDR *, caddr_t);
209void	terminate(int);
210
211struct exportlist *exphead;
212struct mountlist *mlhead;
213struct grouplist *grphead;
214char exname[MAXPATHLEN];
215struct xucred def_anon = {
216	XUCRED_VERSION,
217	(uid_t)-2,
218	1,
219	{ (gid_t)-2 },
220	NULL
221};
222int force_v2 = 0;
223int resvport_only = 1;
224int dir_only = 1;
225int log = 0;
226int got_sighup = 0;
227
228int opt_flags;
229static int have_v6 = 1;
230#ifdef NI_WITHSCOPEID
231static const int ninumeric = NI_NUMERICHOST | NI_WITHSCOPEID;
232#else
233static const int ninumeric = NI_NUMERICHOST;
234#endif
235
236int mountdlockfd;
237/* Bits for opt_flags above */
238#define	OP_MAPROOT	0x01
239#define	OP_MAPALL	0x02
240/* 0x4 free */
241#define	OP_MASK		0x08
242#define	OP_NET		0x10
243#define	OP_ALLDIRS	0x40
244#define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
245#define OP_MASKLEN	0x200
246
247#ifdef DEBUG
248int debug = 1;
249void	SYSLOG(int, const char *, ...) __printflike(2, 3);
250#define syslog SYSLOG
251#else
252int debug = 0;
253#endif
254
255/*
256 * Mountd server for NFS mount protocol as described in:
257 * NFS: Network File System Protocol Specification, RFC1094, Appendix A
258 * The optional arguments are the exports file name
259 * default: _PATH_EXPORTS
260 * and "-n" to allow nonroot mount.
261 */
262int
263main(argc, argv)
264	int argc;
265	char **argv;
266{
267	fd_set readfds;
268	SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp;
269	struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
270	int udpsock, tcpsock, udp6sock, tcp6sock;
271	int xcreated = 0, s;
272	int one = 1;
273	int c;
274
275	udp6conf = tcp6conf = NULL;
276	udp6sock = tcp6sock = NULL;
277
278	/* Check that another mountd isn't already running. */
279	if ((mountdlockfd = (open(MOUNTDLOCK, O_RDONLY|O_CREAT, 0444))) == -1)
280		err(1, "%s", MOUNTDLOCK);
281
282	if(flock(mountdlockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK)
283		errx(1, "another rpc.mountd is already running. Aborting");
284	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
285	if (s < 0)
286		have_v6 = 0;
287	else
288		close(s);
289	if (modfind("nfsserver") < 0) {
290		/* Not present in kernel, try loading it */
291		if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
292			errx(1, "NFS server is not available or loadable");
293	}
294
295	while ((c = getopt(argc, argv, "2dlnr")) != -1)
296		switch (c) {
297		case '2':
298			force_v2 = 1;
299			break;
300		case 'n':
301			resvport_only = 0;
302			break;
303		case 'r':
304			dir_only = 0;
305			break;
306		case 'd':
307			debug = debug ? 0 : 1;
308			break;
309		case 'l':
310			log = 1;
311			break;
312		default:
313			usage();
314		};
315	argc -= optind;
316	argv += optind;
317	grphead = (struct grouplist *)NULL;
318	exphead = (struct exportlist *)NULL;
319	mlhead = (struct mountlist *)NULL;
320	if (argc == 1) {
321		strncpy(exname, *argv, MAXPATHLEN-1);
322		exname[MAXPATHLEN-1] = '\0';
323	} else
324		strcpy(exname, _PATH_EXPORTS);
325	openlog("mountd", LOG_PID, LOG_DAEMON);
326	if (debug)
327		warnx("getting export list");
328	get_exportlist();
329	if (debug)
330		warnx("getting mount list");
331	get_mountlist();
332	if (debug)
333		warnx("here we go");
334	if (debug == 0) {
335		daemon(0, 0);
336		signal(SIGINT, SIG_IGN);
337		signal(SIGQUIT, SIG_IGN);
338	}
339	signal(SIGHUP, huphandler);
340	signal(SIGTERM, terminate);
341	{ FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
342	  if (pidfile != NULL) {
343		fprintf(pidfile, "%d\n", getpid());
344		fclose(pidfile);
345	  }
346	}
347	rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
348	rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
349	udpsock  = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
350	tcpsock  = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
351	udpconf  = getnetconfigent("udp");
352	tcpconf  = getnetconfigent("tcp");
353	if (!have_v6)
354		goto skip_v6;
355	udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
356	tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
357	/*
358	 * We're doing host-based access checks here, so don't allow
359	 * v4-in-v6 to confuse things. The kernel will disable it
360	 * by default on NFS sockets too.
361	 */
362	if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6,
363		IPV6_BINDV6ONLY, &one, sizeof one) < 0){
364		syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
365		exit(1);
366	}
367	if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6,
368		IPV6_BINDV6ONLY, &one, sizeof one) < 0){
369		syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
370		exit(1);
371	}
372	udp6conf = getnetconfigent("udp6");
373	tcp6conf = getnetconfigent("tcp6");
374
375skip_v6:
376	if (!resvport_only) {
377		if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL,
378		    &resvport_only, sizeof(resvport_only)) != 0 &&
379		    errno != ENOENT) {
380			syslog(LOG_ERR, "sysctl: %m");
381			exit(1);
382		}
383	}
384	if (udpsock != -1 && udpconf != NULL) {
385		bindresvport(udpsock, NULL);
386		udptransp = svc_dg_create(udpsock, 0, 0);
387		if (udptransp != NULL) {
388			if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1,
389			    mntsrv, udpconf))
390				syslog(LOG_WARNING, "can't register UDP RPCMNT_VER1 service");
391			else
392				xcreated++;
393			if (!force_v2) {
394				if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3,
395				    mntsrv, udpconf))
396					syslog(LOG_WARNING, "can't register UDP RPCMNT_VER3 service");
397				else
398					xcreated++;
399			}
400		} else
401			syslog(LOG_WARNING, "can't create UDP services");
402
403	}
404	if (tcpsock != -1 && tcpconf != NULL) {
405		bindresvport(tcpsock, NULL);
406		listen(tcpsock, SOMAXCONN);
407		tcptransp = svc_vc_create(tcpsock, 0, 0);
408		if (tcptransp != NULL) {
409			if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1,
410			    mntsrv, tcpconf))
411				syslog(LOG_WARNING, "can't register TCP RPCMNT_VER1 service");
412			else
413				xcreated++;
414			if (!force_v2) {
415				if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3,
416				    mntsrv, tcpconf))
417					syslog(LOG_WARNING, "can't register TCP RPCMNT_VER3 service");
418				else
419					xcreated++;
420			}
421		} else
422			syslog(LOG_WARNING, "can't create TCP service");
423
424	}
425	if (have_v6 && udp6sock != -1 && udp6conf != NULL) {
426		bindresvport(udp6sock, NULL);
427		udp6transp = svc_dg_create(udp6sock, 0, 0);
428		if (udp6transp != NULL) {
429			if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1,
430			    mntsrv, udp6conf))
431				syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER1 service");
432			else
433				xcreated++;
434			if (!force_v2) {
435				if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3,
436				    mntsrv, udp6conf))
437					syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER3 service");
438				else
439					xcreated++;
440			}
441		} else
442			syslog(LOG_WARNING, "can't create UDP6 service");
443
444	}
445	if (have_v6 && tcp6sock != -1 && tcp6conf != NULL) {
446		bindresvport(tcp6sock, NULL);
447		listen(tcp6sock, SOMAXCONN);
448		tcp6transp = svc_vc_create(tcp6sock, 0, 0);
449		if (tcp6transp != NULL) {
450			if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1,
451			    mntsrv, tcp6conf))
452				syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER1 service");
453			else
454				xcreated++;
455			if (!force_v2) {
456				if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3,
457				    mntsrv, tcp6conf))
458					syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER3 service");
459					else
460						xcreated++;
461				}
462		} else
463			syslog(LOG_WARNING, "can't create TCP6 service");
464
465	}
466	if (xcreated == 0) {
467		syslog(LOG_ERR, "could not create any services");
468		exit(1);
469	}
470
471	/* Expand svc_run() here so that we can call get_exportlist(). */
472	for (;;) {
473		if (got_sighup) {
474			get_exportlist();
475			got_sighup = 0;
476		}
477		readfds = svc_fdset;
478		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
479		case -1:
480			if (errno == EINTR)
481                                continue;
482			syslog(LOG_ERR, "mountd died: select: %m");
483			exit(1);
484		case 0:
485			continue;
486		default:
487			svc_getreqset(&readfds);
488		}
489	}
490}
491
492static void
493usage()
494{
495	fprintf(stderr,
496		"usage: mountd [-2] [-d] [-l] [-n] [-r] [export_file]\n");
497	exit(1);
498}
499
500/*
501 * The mount rpc service
502 */
503void
504mntsrv(rqstp, transp)
505	struct svc_req *rqstp;
506	SVCXPRT *transp;
507{
508	struct exportlist *ep;
509	struct dirlist *dp;
510	struct fhreturn fhr;
511	struct stat stb;
512	struct statfs fsb;
513	struct addrinfo *ai;
514	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
515	int lookup_failed = 1;
516	struct sockaddr *saddr;
517	u_short sport;
518	char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
519	int bad = 0, defset, hostset;
520	sigset_t sighup_mask;
521
522	sigemptyset(&sighup_mask);
523	sigaddset(&sighup_mask, SIGHUP);
524	saddr = svc_getrpccaller(transp)->buf;
525	switch (saddr->sa_family) {
526	case AF_INET6:
527		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
528		break;
529	case AF_INET:
530		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
531		break;
532	default:
533		syslog(LOG_ERR, "request from unknown address family");
534		return;
535	}
536	lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
537	    NULL, 0, 0);
538	getnameinfo(saddr, saddr->sa_len, numerichost,
539	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
540	ai = NULL;
541	switch (rqstp->rq_proc) {
542	case NULLPROC:
543		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
544			syslog(LOG_ERR, "can't send reply");
545		return;
546	case RPCMNT_MOUNT:
547		if (sport >= IPPORT_RESERVED && resvport_only) {
548			syslog(LOG_NOTICE,
549			    "mount request from %s from unprivileged port",
550			    numerichost);
551			svcerr_weakauth(transp);
552			return;
553		}
554		if (!svc_getargs(transp, xdr_dir, rpcpath)) {
555			syslog(LOG_NOTICE, "undecodable mount request from %s",
556			    numerichost);
557			svcerr_decode(transp);
558			return;
559		}
560
561		/*
562		 * Get the real pathname and make sure it is a directory
563		 * or a regular file if the -r option was specified
564		 * and it exists.
565		 */
566		if (realpath(rpcpath, dirpath) == NULL ||
567		    stat(dirpath, &stb) < 0 ||
568		    (!S_ISDIR(stb.st_mode) &&
569		    (dir_only || !S_ISREG(stb.st_mode))) ||
570		    statfs(dirpath, &fsb) < 0) {
571			chdir("/");	/* Just in case realpath doesn't */
572			syslog(LOG_NOTICE,
573			    "mount request from %s for non existent path %s",
574			    numerichost, dirpath);
575			if (debug)
576				warnx("stat failed on %s", dirpath);
577			bad = ENOENT;	/* We will send error reply later */
578		}
579
580		/* Check in the exports list */
581		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
582		ep = ex_search(&fsb.f_fsid);
583		hostset = defset = 0;
584		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
585		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
586		      chk_host(dp, saddr, &defset, &hostset)) ||
587		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
588		     scan_tree(ep->ex_dirl, saddr) == 0))) {
589			if (bad) {
590				if (!svc_sendreply(transp, xdr_long,
591				    (caddr_t)&bad))
592					syslog(LOG_ERR, "can't send reply");
593				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
594				return;
595			}
596			if (hostset & DP_HOSTSET)
597				fhr.fhr_flag = hostset;
598			else
599				fhr.fhr_flag = defset;
600			fhr.fhr_vers = rqstp->rq_vers;
601			/* Get the file handle */
602			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
603			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
604				bad = errno;
605				syslog(LOG_ERR, "can't get fh for %s", dirpath);
606				if (!svc_sendreply(transp, xdr_long,
607				    (caddr_t)&bad))
608					syslog(LOG_ERR, "can't send reply");
609				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
610				return;
611			}
612			if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr))
613				syslog(LOG_ERR, "can't send reply");
614			if (!lookup_failed)
615				add_mlist(host, dirpath);
616			else
617				add_mlist(numerichost, dirpath);
618			if (debug)
619				warnx("mount successful");
620			if (log)
621				syslog(LOG_NOTICE,
622				    "mount request succeeded from %s for %s",
623				    numerichost, dirpath);
624		} else {
625			bad = EACCES;
626			syslog(LOG_NOTICE,
627			    "mount request denied from %s for %s",
628			    numerichost, dirpath);
629		}
630
631		if (bad && !svc_sendreply(transp, xdr_long, (caddr_t)&bad))
632			syslog(LOG_ERR, "can't send reply");
633		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
634		return;
635	case RPCMNT_DUMP:
636		if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL))
637			syslog(LOG_ERR, "can't send reply");
638		else if (log)
639			syslog(LOG_NOTICE,
640			    "dump request succeeded from %s",
641			    numerichost);
642		return;
643	case RPCMNT_UMOUNT:
644		if (sport >= IPPORT_RESERVED && resvport_only) {
645			syslog(LOG_NOTICE,
646			    "umount request from %s from unprivileged port",
647			    numerichost);
648			svcerr_weakauth(transp);
649			return;
650		}
651		if (!svc_getargs(transp, xdr_dir, rpcpath)) {
652			syslog(LOG_NOTICE, "undecodable umount request from %s",
653			    numerichost);
654			svcerr_decode(transp);
655			return;
656		}
657		if (realpath(rpcpath, dirpath) == NULL) {
658			syslog(LOG_NOTICE, "umount request from %s "
659			    "for non existent path %s",
660			    numerichost, dirpath);
661		}
662		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
663			syslog(LOG_ERR, "can't send reply");
664		if (!lookup_failed)
665			del_mlist(host, dirpath);
666		del_mlist(numerichost, dirpath);
667		if (log)
668			syslog(LOG_NOTICE,
669			    "umount request succeeded from %s for %s",
670			    numerichost, dirpath);
671		return;
672	case RPCMNT_UMNTALL:
673		if (sport >= IPPORT_RESERVED && resvport_only) {
674			syslog(LOG_NOTICE,
675			    "umountall request from %s from unprivileged port",
676			    numerichost);
677			svcerr_weakauth(transp);
678			return;
679		}
680		if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
681			syslog(LOG_ERR, "can't send reply");
682		if (!lookup_failed)
683			del_mlist(host, NULL);
684		del_mlist(numerichost, NULL);
685		if (log)
686			syslog(LOG_NOTICE,
687			    "umountall request succeeded from %s",
688			    numerichost);
689		return;
690	case RPCMNT_EXPORT:
691		if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))
692			syslog(LOG_ERR, "can't send reply");
693		if (log)
694			syslog(LOG_NOTICE,
695			    "export request succeeded from %s",
696			    numerichost);
697		return;
698	default:
699		svcerr_noproc(transp);
700		return;
701	}
702}
703
704/*
705 * Xdr conversion for a dirpath string
706 */
707int
708xdr_dir(xdrsp, dirp)
709	XDR *xdrsp;
710	char *dirp;
711{
712	return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
713}
714
715/*
716 * Xdr routine to generate file handle reply
717 */
718int
719xdr_fhs(xdrsp, cp)
720	XDR *xdrsp;
721	caddr_t cp;
722{
723	struct fhreturn *fhrp = (struct fhreturn *)cp;
724	u_long ok = 0, len, auth;
725
726	if (!xdr_long(xdrsp, &ok))
727		return (0);
728	switch (fhrp->fhr_vers) {
729	case 1:
730		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
731	case 3:
732		len = NFSX_V3FH;
733		if (!xdr_long(xdrsp, &len))
734			return (0);
735		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
736			return (0);
737		auth = RPCAUTH_UNIX;
738		len = 1;
739		if (!xdr_long(xdrsp, &len))
740			return (0);
741		return (xdr_long(xdrsp, &auth));
742	};
743	return (0);
744}
745
746int
747xdr_mlist(xdrsp, cp)
748	XDR *xdrsp;
749	caddr_t cp;
750{
751	struct mountlist *mlp;
752	int true = 1;
753	int false = 0;
754	char *strp;
755
756	mlp = mlhead;
757	while (mlp) {
758		if (!xdr_bool(xdrsp, &true))
759			return (0);
760		strp = &mlp->ml_host[0];
761		if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
762			return (0);
763		strp = &mlp->ml_dirp[0];
764		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
765			return (0);
766		mlp = mlp->ml_next;
767	}
768	if (!xdr_bool(xdrsp, &false))
769		return (0);
770	return (1);
771}
772
773/*
774 * Xdr conversion for export list
775 */
776int
777xdr_explist(xdrsp, cp)
778	XDR *xdrsp;
779	caddr_t cp;
780{
781	struct exportlist *ep;
782	int false = 0;
783	int putdef;
784	sigset_t sighup_mask;
785
786	sigemptyset(&sighup_mask);
787	sigaddset(&sighup_mask, SIGHUP);
788	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
789	ep = exphead;
790	while (ep) {
791		putdef = 0;
792		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
793			goto errout;
794		if (ep->ex_defdir && putdef == 0 &&
795			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
796			&putdef))
797			goto errout;
798		ep = ep->ex_next;
799	}
800	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
801	if (!xdr_bool(xdrsp, &false))
802		return (0);
803	return (1);
804errout:
805	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
806	return (0);
807}
808
809/*
810 * Called from xdr_explist() to traverse the tree and export the
811 * directory paths.
812 */
813int
814put_exlist(dp, xdrsp, adp, putdefp)
815	struct dirlist *dp;
816	XDR *xdrsp;
817	struct dirlist *adp;
818	int *putdefp;
819{
820	struct grouplist *grp;
821	struct hostlist *hp;
822	int true = 1;
823	int false = 0;
824	int gotalldir = 0;
825	char *strp;
826
827	if (dp) {
828		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
829			return (1);
830		if (!xdr_bool(xdrsp, &true))
831			return (1);
832		strp = dp->dp_dirp;
833		if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
834			return (1);
835		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
836			gotalldir = 1;
837			*putdefp = 1;
838		}
839		if ((dp->dp_flag & DP_DEFSET) == 0 &&
840		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
841			hp = dp->dp_hosts;
842			while (hp) {
843				grp = hp->ht_grp;
844				if (grp->gr_type == GT_HOST) {
845					if (!xdr_bool(xdrsp, &true))
846						return (1);
847					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
848					if (!xdr_string(xdrsp, &strp,
849					    RPCMNT_NAMELEN))
850						return (1);
851				} else if (grp->gr_type == GT_NET) {
852					if (!xdr_bool(xdrsp, &true))
853						return (1);
854					strp = grp->gr_ptr.gt_net.nt_name;
855					if (!xdr_string(xdrsp, &strp,
856					    RPCMNT_NAMELEN))
857						return (1);
858				}
859				hp = hp->ht_next;
860				if (gotalldir && hp == (struct hostlist *)NULL) {
861					hp = adp->dp_hosts;
862					gotalldir = 0;
863				}
864			}
865		}
866		if (!xdr_bool(xdrsp, &false))
867			return (1);
868		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
869			return (1);
870	}
871	return (0);
872}
873
874char *line;
875int linesize;
876FILE *exp_file;
877
878/*
879 * Get the export list
880 */
881void
882get_exportlist()
883{
884	struct exportlist *ep, *ep2;
885	struct grouplist *grp, *tgrp;
886	struct exportlist **epp;
887	struct dirlist *dirhead;
888	struct statfs fsb, *fsp;
889	struct xucred anon;
890	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
891	int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
892
893	dirp = NULL;
894	dirplen = 0;
895
896	/*
897	 * First, get rid of the old list
898	 */
899	ep = exphead;
900	while (ep) {
901		ep2 = ep;
902		ep = ep->ex_next;
903		free_exp(ep2);
904	}
905	exphead = (struct exportlist *)NULL;
906
907	grp = grphead;
908	while (grp) {
909		tgrp = grp;
910		grp = grp->gr_next;
911		free_grp(tgrp);
912	}
913	grphead = (struct grouplist *)NULL;
914
915	/*
916	 * And delete exports that are in the kernel for all local
917	 * filesystems.
918	 * XXX: Should know how to handle all local exportable filesystems
919	 *      instead of just "ufs".
920	 */
921	num = getmntinfo(&fsp, MNT_NOWAIT);
922	for (i = 0; i < num; i++) {
923		union {
924			struct ufs_args ua;
925			struct iso_args ia;
926			struct msdosfs_args da;
927			struct ntfs_args na;
928		} targs;
929
930		if (!strcmp(fsp->f_fstypename, "ufs") ||
931		    !strcmp(fsp->f_fstypename, "msdosfs") ||
932		    !strcmp(fsp->f_fstypename, "ntfs") ||
933		    !strcmp(fsp->f_fstypename, "cd9660")) {
934			targs.ua.fspec = NULL;
935			targs.ua.export.ex_flags = MNT_DELEXPORT;
936			if (mount(fsp->f_fstypename, fsp->f_mntonname,
937			    fsp->f_flags | MNT_UPDATE, (caddr_t)&targs) < 0 &&
938			    errno != ENOENT)
939				syslog(LOG_ERR,
940				    "can't delete exports for %s: %m",
941				    fsp->f_mntonname);
942		}
943		fsp++;
944	}
945
946	/*
947	 * Read in the exports file and build the list, calling
948	 * mount() as we go along to push the export rules into the kernel.
949	 */
950	if ((exp_file = fopen(exname, "r")) == NULL) {
951		syslog(LOG_ERR, "can't open %s", exname);
952		exit(2);
953	}
954	dirhead = (struct dirlist *)NULL;
955	while (get_line()) {
956		if (debug)
957			warnx("got line %s", line);
958		cp = line;
959		nextfield(&cp, &endcp);
960		if (*cp == '#')
961			goto nextline;
962
963		/*
964		 * Set defaults.
965		 */
966		has_host = FALSE;
967		anon = def_anon;
968		exflags = MNT_EXPORTED;
969		got_nondir = 0;
970		opt_flags = 0;
971		ep = (struct exportlist *)NULL;
972
973		/*
974		 * Create new exports list entry
975		 */
976		len = endcp-cp;
977		tgrp = grp = get_grp();
978		while (len > 0) {
979			if (len > RPCMNT_NAMELEN) {
980			    getexp_err(ep, tgrp);
981			    goto nextline;
982			}
983			if (*cp == '-') {
984			    if (ep == (struct exportlist *)NULL) {
985				getexp_err(ep, tgrp);
986				goto nextline;
987			    }
988			    if (debug)
989				warnx("doing opt %s", cp);
990			    got_nondir = 1;
991			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
992				&exflags, &anon)) {
993				getexp_err(ep, tgrp);
994				goto nextline;
995			    }
996			} else if (*cp == '/') {
997			    savedc = *endcp;
998			    *endcp = '\0';
999			    if (check_dirpath(cp) &&
1000				statfs(cp, &fsb) >= 0) {
1001				if (got_nondir) {
1002				    syslog(LOG_ERR, "dirs must be first");
1003				    getexp_err(ep, tgrp);
1004				    goto nextline;
1005				}
1006				if (ep) {
1007				    if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
1008					ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
1009					getexp_err(ep, tgrp);
1010					goto nextline;
1011				    }
1012				} else {
1013				    /*
1014				     * See if this directory is already
1015				     * in the list.
1016				     */
1017				    ep = ex_search(&fsb.f_fsid);
1018				    if (ep == (struct exportlist *)NULL) {
1019					ep = get_exp();
1020					ep->ex_fs = fsb.f_fsid;
1021					ep->ex_fsdir = (char *)
1022					    malloc(strlen(fsb.f_mntonname) + 1);
1023					if (ep->ex_fsdir)
1024					    strcpy(ep->ex_fsdir,
1025						fsb.f_mntonname);
1026					else
1027					    out_of_mem();
1028					if (debug)
1029						warnx("making new ep fs=0x%x,0x%x",
1030						    fsb.f_fsid.val[0],
1031						    fsb.f_fsid.val[1]);
1032				    } else if (debug)
1033					warnx("found ep fs=0x%x,0x%x",
1034					    fsb.f_fsid.val[0],
1035					    fsb.f_fsid.val[1]);
1036				}
1037
1038				/*
1039				 * Add dirpath to export mount point.
1040				 */
1041				dirp = add_expdir(&dirhead, cp, len);
1042				dirplen = len;
1043			    } else {
1044				getexp_err(ep, tgrp);
1045				goto nextline;
1046			    }
1047			    *endcp = savedc;
1048			} else {
1049			    savedc = *endcp;
1050			    *endcp = '\0';
1051			    got_nondir = 1;
1052			    if (ep == (struct exportlist *)NULL) {
1053				getexp_err(ep, tgrp);
1054				goto nextline;
1055			    }
1056
1057			    /*
1058			     * Get the host or netgroup.
1059			     */
1060			    setnetgrent(cp);
1061			    netgrp = getnetgrent(&hst, &usr, &dom);
1062			    do {
1063				if (has_host) {
1064				    grp->gr_next = get_grp();
1065				    grp = grp->gr_next;
1066				}
1067				if (netgrp) {
1068				    if (hst == 0) {
1069					syslog(LOG_ERR,
1070				"null hostname in netgroup %s, skipping", cp);
1071					grp->gr_type = GT_IGNORE;
1072				    } else if (get_host(hst, grp, tgrp)) {
1073					syslog(LOG_ERR,
1074			"bad host %s in netgroup %s, skipping", hst, cp);
1075					grp->gr_type = GT_IGNORE;
1076				    }
1077				} else if (get_host(cp, grp, tgrp)) {
1078				    syslog(LOG_ERR, "bad host %s, skipping", cp);
1079				    grp->gr_type = GT_IGNORE;
1080				}
1081				has_host = TRUE;
1082			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
1083			    endnetgrent();
1084			    *endcp = savedc;
1085			}
1086			cp = endcp;
1087			nextfield(&cp, &endcp);
1088			len = endcp - cp;
1089		}
1090		if (check_options(dirhead)) {
1091			getexp_err(ep, tgrp);
1092			goto nextline;
1093		}
1094		if (!has_host) {
1095			grp->gr_type = GT_DEFAULT;
1096			if (debug)
1097				warnx("adding a default entry");
1098
1099		/*
1100		 * Don't allow a network export coincide with a list of
1101		 * host(s) on the same line.
1102		 */
1103		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1104			syslog(LOG_ERR, "network/host conflict");
1105			getexp_err(ep, tgrp);
1106			goto nextline;
1107
1108		/*
1109		 * If an export list was specified on this line, make sure
1110		 * that we have at least one valid entry, otherwise skip it.
1111		 */
1112		} else {
1113			grp = tgrp;
1114			while (grp && grp->gr_type == GT_IGNORE)
1115				grp = grp->gr_next;
1116			if (! grp) {
1117			    getexp_err(ep, tgrp);
1118			    goto nextline;
1119			}
1120		}
1121
1122		/*
1123		 * Loop through hosts, pushing the exports into the kernel.
1124		 * After loop, tgrp points to the start of the list and
1125		 * grp points to the last entry in the list.
1126		 */
1127		grp = tgrp;
1128		do {
1129			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
1130			    &fsb)) {
1131				getexp_err(ep, tgrp);
1132				goto nextline;
1133			}
1134		} while (grp->gr_next && (grp = grp->gr_next));
1135
1136		/*
1137		 * Success. Update the data structures.
1138		 */
1139		if (has_host) {
1140			hang_dirp(dirhead, tgrp, ep, opt_flags);
1141			grp->gr_next = grphead;
1142			grphead = tgrp;
1143		} else {
1144			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1145				opt_flags);
1146			free_grp(grp);
1147		}
1148		dirhead = (struct dirlist *)NULL;
1149		if ((ep->ex_flag & EX_LINKED) == 0) {
1150			ep2 = exphead;
1151			epp = &exphead;
1152
1153			/*
1154			 * Insert in the list in alphabetical order.
1155			 */
1156			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1157				epp = &ep2->ex_next;
1158				ep2 = ep2->ex_next;
1159			}
1160			if (ep2)
1161				ep->ex_next = ep2;
1162			*epp = ep;
1163			ep->ex_flag |= EX_LINKED;
1164		}
1165nextline:
1166		if (dirhead) {
1167			free_dir(dirhead);
1168			dirhead = (struct dirlist *)NULL;
1169		}
1170	}
1171	fclose(exp_file);
1172}
1173
1174/*
1175 * Allocate an export list element
1176 */
1177struct exportlist *
1178get_exp()
1179{
1180	struct exportlist *ep;
1181
1182	ep = (struct exportlist *)malloc(sizeof (struct exportlist));
1183	if (ep == (struct exportlist *)NULL)
1184		out_of_mem();
1185	memset(ep, 0, sizeof(struct exportlist));
1186	return (ep);
1187}
1188
1189/*
1190 * Allocate a group list element
1191 */
1192struct grouplist *
1193get_grp()
1194{
1195	struct grouplist *gp;
1196
1197	gp = (struct grouplist *)malloc(sizeof (struct grouplist));
1198	if (gp == (struct grouplist *)NULL)
1199		out_of_mem();
1200	memset(gp, 0, sizeof(struct grouplist));
1201	return (gp);
1202}
1203
1204/*
1205 * Clean up upon an error in get_exportlist().
1206 */
1207void
1208getexp_err(ep, grp)
1209	struct exportlist *ep;
1210	struct grouplist *grp;
1211{
1212	struct grouplist *tgrp;
1213
1214	syslog(LOG_ERR, "bad exports list line %s", line);
1215	if (ep && (ep->ex_flag & EX_LINKED) == 0)
1216		free_exp(ep);
1217	while (grp) {
1218		tgrp = grp;
1219		grp = grp->gr_next;
1220		free_grp(tgrp);
1221	}
1222}
1223
1224/*
1225 * Search the export list for a matching fs.
1226 */
1227struct exportlist *
1228ex_search(fsid)
1229	fsid_t *fsid;
1230{
1231	struct exportlist *ep;
1232
1233	ep = exphead;
1234	while (ep) {
1235		if (ep->ex_fs.val[0] == fsid->val[0] &&
1236		    ep->ex_fs.val[1] == fsid->val[1])
1237			return (ep);
1238		ep = ep->ex_next;
1239	}
1240	return (ep);
1241}
1242
1243/*
1244 * Add a directory path to the list.
1245 */
1246char *
1247add_expdir(dpp, cp, len)
1248	struct dirlist **dpp;
1249	char *cp;
1250	int len;
1251{
1252	struct dirlist *dp;
1253
1254	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1255	if (dp == (struct dirlist *)NULL)
1256		out_of_mem();
1257	dp->dp_left = *dpp;
1258	dp->dp_right = (struct dirlist *)NULL;
1259	dp->dp_flag = 0;
1260	dp->dp_hosts = (struct hostlist *)NULL;
1261	strcpy(dp->dp_dirp, cp);
1262	*dpp = dp;
1263	return (dp->dp_dirp);
1264}
1265
1266/*
1267 * Hang the dir list element off the dirpath binary tree as required
1268 * and update the entry for host.
1269 */
1270void
1271hang_dirp(dp, grp, ep, flags)
1272	struct dirlist *dp;
1273	struct grouplist *grp;
1274	struct exportlist *ep;
1275	int flags;
1276{
1277	struct hostlist *hp;
1278	struct dirlist *dp2;
1279
1280	if (flags & OP_ALLDIRS) {
1281		if (ep->ex_defdir)
1282			free((caddr_t)dp);
1283		else
1284			ep->ex_defdir = dp;
1285		if (grp == (struct grouplist *)NULL) {
1286			ep->ex_defdir->dp_flag |= DP_DEFSET;
1287		} else while (grp) {
1288			hp = get_ht();
1289			hp->ht_grp = grp;
1290			hp->ht_next = ep->ex_defdir->dp_hosts;
1291			ep->ex_defdir->dp_hosts = hp;
1292			grp = grp->gr_next;
1293		}
1294	} else {
1295
1296		/*
1297		 * Loop through the directories adding them to the tree.
1298		 */
1299		while (dp) {
1300			dp2 = dp->dp_left;
1301			add_dlist(&ep->ex_dirl, dp, grp, flags);
1302			dp = dp2;
1303		}
1304	}
1305}
1306
1307/*
1308 * Traverse the binary tree either updating a node that is already there
1309 * for the new directory or adding the new node.
1310 */
1311void
1312add_dlist(dpp, newdp, grp, flags)
1313	struct dirlist **dpp;
1314	struct dirlist *newdp;
1315	struct grouplist *grp;
1316	int flags;
1317{
1318	struct dirlist *dp;
1319	struct hostlist *hp;
1320	int cmp;
1321
1322	dp = *dpp;
1323	if (dp) {
1324		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1325		if (cmp > 0) {
1326			add_dlist(&dp->dp_left, newdp, grp, flags);
1327			return;
1328		} else if (cmp < 0) {
1329			add_dlist(&dp->dp_right, newdp, grp, flags);
1330			return;
1331		} else
1332			free((caddr_t)newdp);
1333	} else {
1334		dp = newdp;
1335		dp->dp_left = (struct dirlist *)NULL;
1336		*dpp = dp;
1337	}
1338	if (grp) {
1339
1340		/*
1341		 * Hang all of the host(s) off of the directory point.
1342		 */
1343		do {
1344			hp = get_ht();
1345			hp->ht_grp = grp;
1346			hp->ht_next = dp->dp_hosts;
1347			dp->dp_hosts = hp;
1348			grp = grp->gr_next;
1349		} while (grp);
1350	} else {
1351		dp->dp_flag |= DP_DEFSET;
1352	}
1353}
1354
1355/*
1356 * Search for a dirpath on the export point.
1357 */
1358struct dirlist *
1359dirp_search(dp, dirp)
1360	struct dirlist *dp;
1361	char *dirp;
1362{
1363	int cmp;
1364
1365	if (dp) {
1366		cmp = strcmp(dp->dp_dirp, dirp);
1367		if (cmp > 0)
1368			return (dirp_search(dp->dp_left, dirp));
1369		else if (cmp < 0)
1370			return (dirp_search(dp->dp_right, dirp));
1371		else
1372			return (dp);
1373	}
1374	return (dp);
1375}
1376
1377/*
1378 * Scan for a host match in a directory tree.
1379 */
1380int
1381chk_host(dp, saddr, defsetp, hostsetp)
1382	struct dirlist *dp;
1383	struct sockaddr *saddr;
1384	int *defsetp;
1385	int *hostsetp;
1386{
1387	struct hostlist *hp;
1388	struct grouplist *grp;
1389	struct addrinfo *ai;
1390
1391	if (dp) {
1392		if (dp->dp_flag & DP_DEFSET)
1393			*defsetp = dp->dp_flag;
1394		hp = dp->dp_hosts;
1395		while (hp) {
1396			grp = hp->ht_grp;
1397			switch (grp->gr_type) {
1398			case GT_HOST:
1399				ai = grp->gr_ptr.gt_addrinfo;
1400				for (; ai; ai = ai->ai_next) {
1401					if (!sacmp(ai->ai_addr, saddr, NULL)) {
1402						*hostsetp =
1403						    (hp->ht_flag | DP_HOSTSET);
1404						return (1);
1405					}
1406				}
1407				break;
1408			case GT_NET:
1409				if (!sacmp(saddr, (struct sockaddr *)
1410				    &grp->gr_ptr.gt_net.nt_net,
1411				    (struct sockaddr *)
1412				    &grp->gr_ptr.gt_net.nt_mask)) {
1413					*hostsetp = (hp->ht_flag | DP_HOSTSET);
1414					return (1);
1415				}
1416				break;
1417			}
1418			hp = hp->ht_next;
1419		}
1420	}
1421	return (0);
1422}
1423
1424/*
1425 * Scan tree for a host that matches the address.
1426 */
1427int
1428scan_tree(dp, saddr)
1429	struct dirlist *dp;
1430	struct sockaddr *saddr;
1431{
1432	int defset, hostset;
1433
1434	if (dp) {
1435		if (scan_tree(dp->dp_left, saddr))
1436			return (1);
1437		if (chk_host(dp, saddr, &defset, &hostset))
1438			return (1);
1439		if (scan_tree(dp->dp_right, saddr))
1440			return (1);
1441	}
1442	return (0);
1443}
1444
1445/*
1446 * Traverse the dirlist tree and free it up.
1447 */
1448void
1449free_dir(dp)
1450	struct dirlist *dp;
1451{
1452
1453	if (dp) {
1454		free_dir(dp->dp_left);
1455		free_dir(dp->dp_right);
1456		free_host(dp->dp_hosts);
1457		free((caddr_t)dp);
1458	}
1459}
1460
1461/*
1462 * Parse the option string and update fields.
1463 * Option arguments may either be -<option>=<value> or
1464 * -<option> <value>
1465 */
1466int
1467do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1468	char **cpp, **endcpp;
1469	struct exportlist *ep;
1470	struct grouplist *grp;
1471	int *has_hostp;
1472	int *exflagsp;
1473	struct xucred *cr;
1474{
1475	char *cpoptarg, *cpoptend;
1476	char *cp, *endcp, *cpopt, savedc, savedc2;
1477	int allflag, usedarg;
1478
1479	savedc2 = '\0';
1480	cpopt = *cpp;
1481	cpopt++;
1482	cp = *endcpp;
1483	savedc = *cp;
1484	*cp = '\0';
1485	while (cpopt && *cpopt) {
1486		allflag = 1;
1487		usedarg = -2;
1488		if ((cpoptend = strchr(cpopt, ','))) {
1489			*cpoptend++ = '\0';
1490			if ((cpoptarg = strchr(cpopt, '=')))
1491				*cpoptarg++ = '\0';
1492		} else {
1493			if ((cpoptarg = strchr(cpopt, '=')))
1494				*cpoptarg++ = '\0';
1495			else {
1496				*cp = savedc;
1497				nextfield(&cp, &endcp);
1498				**endcpp = '\0';
1499				if (endcp > cp && *cp != '-') {
1500					cpoptarg = cp;
1501					savedc2 = *endcp;
1502					*endcp = '\0';
1503					usedarg = 0;
1504				}
1505			}
1506		}
1507		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1508			*exflagsp |= MNT_EXRDONLY;
1509		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1510		    !(allflag = strcmp(cpopt, "mapall")) ||
1511		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1512			usedarg++;
1513			parsecred(cpoptarg, cr);
1514			if (allflag == 0) {
1515				*exflagsp |= MNT_EXPORTANON;
1516				opt_flags |= OP_MAPALL;
1517			} else
1518				opt_flags |= OP_MAPROOT;
1519		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1520		    !strcmp(cpopt, "m"))) {
1521			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1522				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
1523				return (1);
1524			}
1525			usedarg++;
1526			opt_flags |= OP_MASK;
1527		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
1528			!strcmp(cpopt, "n"))) {
1529			if (strchr(cpoptarg, '/') != NULL) {
1530				if (debug)
1531					fprintf(stderr, "setting OP_MASKLEN\n");
1532				opt_flags |= OP_MASKLEN;
1533			}
1534			if (grp->gr_type != GT_NULL) {
1535				syslog(LOG_ERR, "network/host conflict");
1536				return (1);
1537			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1538				syslog(LOG_ERR, "bad net: %s", cpoptarg);
1539				return (1);
1540			}
1541			grp->gr_type = GT_NET;
1542			*has_hostp = 1;
1543			usedarg++;
1544			opt_flags |= OP_NET;
1545		} else if (!strcmp(cpopt, "alldirs")) {
1546			opt_flags |= OP_ALLDIRS;
1547		} else if (!strcmp(cpopt, "public")) {
1548			*exflagsp |= MNT_EXPUBLIC;
1549		} else if (!strcmp(cpopt, "webnfs")) {
1550			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
1551			opt_flags |= OP_MAPALL;
1552		} else if (cpoptarg && !strcmp(cpopt, "index")) {
1553			ep->ex_indexfile = strdup(cpoptarg);
1554		} else {
1555			syslog(LOG_ERR, "bad opt %s", cpopt);
1556			return (1);
1557		}
1558		if (usedarg >= 0) {
1559			*endcp = savedc2;
1560			**endcpp = savedc;
1561			if (usedarg > 0) {
1562				*cpp = cp;
1563				*endcpp = endcp;
1564			}
1565			return (0);
1566		}
1567		cpopt = cpoptend;
1568	}
1569	**endcpp = savedc;
1570	return (0);
1571}
1572
1573/*
1574 * Translate a character string to the corresponding list of network
1575 * addresses for a hostname.
1576 */
1577int
1578get_host(cp, grp, tgrp)
1579	char *cp;
1580	struct grouplist *grp;
1581	struct grouplist *tgrp;
1582{
1583	struct grouplist *checkgrp;
1584	struct addrinfo *ai, *tai, hints;
1585	int ecode;
1586	char host[NI_MAXHOST];
1587
1588	if (grp->gr_type != GT_NULL) {
1589		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
1590		return (1);
1591	}
1592	memset(&hints, 0, sizeof hints);
1593	hints.ai_flags = AI_CANONNAME;
1594	hints.ai_protocol = IPPROTO_UDP;
1595	ecode = getaddrinfo(cp, NULL, &hints, &ai);
1596	if (ecode != 0) {
1597		syslog(LOG_ERR,"can't get address info for host %s", cp);
1598		return 1;
1599	}
1600	grp->gr_ptr.gt_addrinfo = ai;
1601	while (ai != NULL) {
1602		if (ai->ai_canonname == NULL) {
1603			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
1604			    sizeof host, NULL, 0, ninumeric) != 0)
1605				strlcpy(host, "?", sizeof(host));
1606			ai->ai_canonname = strdup(host);
1607			ai->ai_flags |= AI_CANONNAME;
1608		}
1609		if (debug)
1610			fprintf(stderr, "got host %s\n", ai->ai_canonname);
1611		/*
1612		 * Sanity check: make sure we don't already have an entry
1613		 * for this host in the grouplist.
1614		 */
1615		for (checkgrp = tgrp; checkgrp != NULL;
1616		    checkgrp = checkgrp->gr_next) {
1617			if (checkgrp->gr_type != GT_HOST)
1618				continue;
1619			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
1620			    tai = tai->ai_next) {
1621				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
1622					continue;
1623				if (debug)
1624					fprintf(stderr,
1625					    "ignoring duplicate host %s\n",
1626					    ai->ai_canonname);
1627				grp->gr_type = GT_IGNORE;
1628				return (0);
1629			}
1630		}
1631		ai = ai->ai_next;
1632	}
1633	grp->gr_type = GT_HOST;
1634	return (0);
1635}
1636
1637/*
1638 * Free up an exports list component
1639 */
1640void
1641free_exp(ep)
1642	struct exportlist *ep;
1643{
1644
1645	if (ep->ex_defdir) {
1646		free_host(ep->ex_defdir->dp_hosts);
1647		free((caddr_t)ep->ex_defdir);
1648	}
1649	if (ep->ex_fsdir)
1650		free(ep->ex_fsdir);
1651	if (ep->ex_indexfile)
1652		free(ep->ex_indexfile);
1653	free_dir(ep->ex_dirl);
1654	free((caddr_t)ep);
1655}
1656
1657/*
1658 * Free hosts.
1659 */
1660void
1661free_host(hp)
1662	struct hostlist *hp;
1663{
1664	struct hostlist *hp2;
1665
1666	while (hp) {
1667		hp2 = hp;
1668		hp = hp->ht_next;
1669		free((caddr_t)hp2);
1670	}
1671}
1672
1673struct hostlist *
1674get_ht()
1675{
1676	struct hostlist *hp;
1677
1678	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
1679	if (hp == (struct hostlist *)NULL)
1680		out_of_mem();
1681	hp->ht_next = (struct hostlist *)NULL;
1682	hp->ht_flag = 0;
1683	return (hp);
1684}
1685
1686/*
1687 * Out of memory, fatal
1688 */
1689void
1690out_of_mem()
1691{
1692
1693	syslog(LOG_ERR, "out of memory");
1694	exit(2);
1695}
1696
1697/*
1698 * Do the mount syscall with the update flag to push the export info into
1699 * the kernel.
1700 */
1701int
1702do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
1703	struct exportlist *ep;
1704	struct grouplist *grp;
1705	int exflags;
1706	struct xucred *anoncrp;
1707	char *dirp;
1708	int dirplen;
1709	struct statfs *fsb;
1710{
1711	struct statfs fsb1;
1712	struct addrinfo *ai;
1713	struct export_args *eap;
1714	char *cp = NULL;
1715	int done;
1716	char savedc = '\0';
1717	union {
1718		struct ufs_args ua;
1719		struct iso_args ia;
1720		struct msdosfs_args da;
1721		struct ntfs_args na;
1722	} args;
1723
1724	bzero(&args, sizeof args);
1725	/* XXX, we assume that all xx_args look like ufs_args. */
1726	args.ua.fspec = 0;
1727	eap = &args.ua.export;
1728
1729	eap->ex_flags = exflags;
1730	eap->ex_anon = *anoncrp;
1731	eap->ex_indexfile = ep->ex_indexfile;
1732	if (grp->gr_type == GT_HOST)
1733		ai = grp->gr_ptr.gt_addrinfo;
1734	else
1735		ai = NULL;
1736	done = FALSE;
1737	while (!done) {
1738		switch (grp->gr_type) {
1739		case GT_HOST:
1740			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
1741				goto skip;
1742			eap->ex_addr = ai->ai_addr;
1743			eap->ex_addrlen = ai->ai_addrlen;
1744			eap->ex_masklen = 0;
1745			break;
1746		case GT_NET:
1747			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
1748			    have_v6 == 0)
1749				goto skip;
1750			eap->ex_addr =
1751			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
1752			eap->ex_addrlen = args.ua.export.ex_addr->sa_len;
1753			eap->ex_mask =
1754			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
1755			eap->ex_masklen = args.ua.export.ex_mask->sa_len;
1756			break;
1757		case GT_DEFAULT:
1758			eap->ex_addr = NULL;
1759			eap->ex_addrlen = 0;
1760			eap->ex_mask = NULL;
1761			eap->ex_masklen = 0;
1762			break;
1763		case GT_IGNORE:
1764			return(0);
1765			break;
1766		default:
1767			syslog(LOG_ERR, "bad grouptype");
1768			if (cp)
1769				*cp = savedc;
1770			return (1);
1771		};
1772
1773		/*
1774		 * XXX:
1775		 * Maybe I should just use the fsb->f_mntonname path instead
1776		 * of looping back up the dirp to the mount point??
1777		 * Also, needs to know how to export all types of local
1778		 * exportable filesystems and not just "ufs".
1779		 */
1780		while (mount(fsb->f_fstypename, dirp,
1781		    fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
1782			if (cp)
1783				*cp-- = savedc;
1784			else
1785				cp = dirp + dirplen - 1;
1786			if (errno == EPERM) {
1787				if (debug)
1788					warnx("can't change attributes for %s",
1789					    dirp);
1790				syslog(LOG_ERR,
1791				   "can't change attributes for %s", dirp);
1792				return (1);
1793			}
1794			if (opt_flags & OP_ALLDIRS) {
1795				syslog(LOG_ERR, "could not remount %s: %m",
1796					dirp);
1797				return (1);
1798			}
1799			/* back up over the last component */
1800			while (*cp == '/' && cp > dirp)
1801				cp--;
1802			while (*(cp - 1) != '/' && cp > dirp)
1803				cp--;
1804			if (cp == dirp) {
1805				if (debug)
1806					warnx("mnt unsucc");
1807				syslog(LOG_ERR, "can't export %s", dirp);
1808				return (1);
1809			}
1810			savedc = *cp;
1811			*cp = '\0';
1812			/* Check that we're still on the same filesystem. */
1813			if (statfs(dirp, &fsb1) != 0 || bcmp(&fsb1.f_fsid,
1814			    &fsb->f_fsid, sizeof(fsb1.f_fsid)) != 0) {
1815				*cp = savedc;
1816				syslog(LOG_ERR, "can't export %s", dirp);
1817				return (1);
1818			}
1819		}
1820skip:
1821		if (ai != NULL)
1822			ai = ai->ai_next;
1823		if (ai == NULL)
1824			done = TRUE;
1825	}
1826	if (cp)
1827		*cp = savedc;
1828	return (0);
1829}
1830
1831/*
1832 * Translate a net address.
1833 *
1834 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
1835 */
1836int
1837get_net(cp, net, maskflg)
1838	char *cp;
1839	struct netmsk *net;
1840	int maskflg;
1841{
1842	struct netent *np = NULL;
1843	char *name, *p, *prefp;
1844	struct sockaddr_in sin;
1845	struct sockaddr *sa = NULL;
1846	struct addrinfo hints, *ai = NULL;
1847	char netname[NI_MAXHOST];
1848	long preflen;
1849
1850	p = prefp = NULL;
1851	if ((opt_flags & OP_MASKLEN) && !maskflg) {
1852		p = strchr(cp, '/');
1853		*p = '\0';
1854		prefp = p + 1;
1855	}
1856
1857	/*
1858	 * Check for a numeric address first. We wish to avoid
1859	 * possible DNS lookups in getnetbyname().
1860	 */
1861	if (isxdigit(*cp) || *cp == ':') {
1862		memset(&hints, 0, sizeof hints);
1863		/* Ensure the mask and the network have the same family. */
1864		if (maskflg && (opt_flags & OP_NET))
1865			hints.ai_family = net->nt_net.ss_family;
1866		else if (!maskflg && (opt_flags & OP_HAVEMASK))
1867			hints.ai_family = net->nt_mask.ss_family;
1868		else
1869			hints.ai_family = AF_UNSPEC;
1870		hints.ai_flags = AI_NUMERICHOST;
1871		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
1872			sa = ai->ai_addr;
1873		if (sa != NULL && ai->ai_family == AF_INET) {
1874			/*
1875			 * The address in `cp' is really a network address, so
1876			 * use inet_network() to re-interpret this correctly.
1877			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
1878			 */
1879			bzero(&sin, sizeof sin);
1880			sin.sin_family = AF_INET;
1881			sin.sin_len = sizeof sin;
1882			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
1883			if (debug)
1884				fprintf(stderr, "get_net: v4 addr %s\n",
1885				    inet_ntoa(sin.sin_addr));
1886			sa = (struct sockaddr *)&sin;
1887		}
1888	}
1889	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
1890		bzero(&sin, sizeof sin);
1891		sin.sin_family = AF_INET;
1892		sin.sin_len = sizeof sin;
1893		sin.sin_addr = inet_makeaddr(np->n_net, 0);
1894		sa = (struct sockaddr *)&sin;
1895	}
1896	if (sa == NULL)
1897		goto fail;
1898
1899	if (maskflg) {
1900		/* The specified sockaddr is a mask. */
1901		if (checkmask(sa) != 0)
1902			goto fail;
1903		bcopy(sa, &net->nt_mask, sa->sa_len);
1904		opt_flags |= OP_HAVEMASK;
1905	} else {
1906		/* The specified sockaddr is a network address. */
1907		bcopy(sa, &net->nt_net, sa->sa_len);
1908
1909		/* Get a network name for the export list. */
1910		if (np) {
1911			name = np->n_name;
1912		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
1913		   NULL, 0, ninumeric) == 0) {
1914			name = netname;
1915		} else {
1916			goto fail;
1917		}
1918		if ((net->nt_name = strdup(name)) == NULL)
1919			out_of_mem();
1920
1921		/*
1922		 * Extract a mask from either a "/<masklen>" suffix, or
1923		 * from the class of an IPv4 address.
1924		 */
1925		if (opt_flags & OP_MASKLEN) {
1926			preflen = strtol(prefp, NULL, 10);
1927			if (preflen < 0L || preflen == LONG_MAX)
1928				goto fail;
1929			bcopy(sa, &net->nt_mask, sa->sa_len);
1930			if (makemask(&net->nt_mask, (int)preflen) != 0)
1931				goto fail;
1932			opt_flags |= OP_HAVEMASK;
1933			*p = '/';
1934		} else if (sa->sa_family == AF_INET &&
1935		    (opt_flags & OP_MASK) == 0) {
1936			in_addr_t addr;
1937
1938			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
1939			if (IN_CLASSA(addr))
1940				preflen = 8;
1941			else if (IN_CLASSB(addr))
1942				preflen = 16;
1943			else if (IN_CLASSC(addr))
1944				preflen = 24;
1945			else if (IN_CLASSD(addr))
1946				preflen = 28;
1947			else
1948				preflen = 32;	/* XXX */
1949
1950			bcopy(sa, &net->nt_mask, sa->sa_len);
1951			makemask(&net->nt_mask, (int)preflen);
1952			opt_flags |= OP_HAVEMASK;
1953		}
1954	}
1955
1956	if (ai)
1957		freeaddrinfo(ai);
1958	return 0;
1959
1960fail:
1961	if (ai)
1962		freeaddrinfo(ai);
1963	return 1;
1964}
1965
1966/*
1967 * Parse out the next white space separated field
1968 */
1969void
1970nextfield(cp, endcp)
1971	char **cp;
1972	char **endcp;
1973{
1974	char *p;
1975
1976	p = *cp;
1977	while (*p == ' ' || *p == '\t')
1978		p++;
1979	if (*p == '\n' || *p == '\0')
1980		*cp = *endcp = p;
1981	else {
1982		*cp = p++;
1983		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
1984			p++;
1985		*endcp = p;
1986	}
1987}
1988
1989/*
1990 * Get an exports file line. Skip over blank lines and handle line
1991 * continuations.
1992 */
1993int
1994get_line()
1995{
1996	char *p, *cp;
1997	size_t len;
1998	int totlen, cont_line;
1999
2000	/*
2001	 * Loop around ignoring blank lines and getting all continuation lines.
2002	 */
2003	p = line;
2004	totlen = 0;
2005	do {
2006		if ((p = fgetln(exp_file, &len)) == NULL)
2007			return (0);
2008		cp = p + len - 1;
2009		cont_line = 0;
2010		while (cp >= p &&
2011		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
2012			if (*cp == '\\')
2013				cont_line = 1;
2014			cp--;
2015			len--;
2016		}
2017		if (cont_line) {
2018			*++cp = ' ';
2019			len++;
2020		}
2021		if (linesize < len + totlen + 1) {
2022			linesize = len + totlen + 1;
2023			line = realloc(line, linesize);
2024			if (line == NULL)
2025				out_of_mem();
2026		}
2027		memcpy(line + totlen, p, len);
2028		totlen += len;
2029		line[totlen] = '\0';
2030	} while (totlen == 0 || cont_line);
2031	return (1);
2032}
2033
2034/*
2035 * Parse a description of a credential.
2036 */
2037void
2038parsecred(namelist, cr)
2039	char *namelist;
2040	struct xucred *cr;
2041{
2042	char *name;
2043	int cnt;
2044	char *names;
2045	struct passwd *pw;
2046	struct group *gr;
2047	int ngroups, groups[NGROUPS + 1];
2048
2049	cr->cr_version = XUCRED_VERSION;
2050	/*
2051	 * Set up the unprivileged user.
2052	 */
2053	cr->cr_uid = -2;
2054	cr->cr_groups[0] = -2;
2055	cr->cr_ngroups = 1;
2056	/*
2057	 * Get the user's password table entry.
2058	 */
2059	names = strsep(&namelist, " \t\n");
2060	name = strsep(&names, ":");
2061	if (isdigit(*name) || *name == '-')
2062		pw = getpwuid(atoi(name));
2063	else
2064		pw = getpwnam(name);
2065	/*
2066	 * Credentials specified as those of a user.
2067	 */
2068	if (names == NULL) {
2069		if (pw == NULL) {
2070			syslog(LOG_ERR, "unknown user: %s", name);
2071			return;
2072		}
2073		cr->cr_uid = pw->pw_uid;
2074		ngroups = NGROUPS + 1;
2075		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
2076			syslog(LOG_ERR, "too many groups");
2077		/*
2078		 * Convert from int's to gid_t's and compress out duplicate
2079		 */
2080		cr->cr_ngroups = ngroups - 1;
2081		cr->cr_groups[0] = groups[0];
2082		for (cnt = 2; cnt < ngroups; cnt++)
2083			cr->cr_groups[cnt - 1] = groups[cnt];
2084		return;
2085	}
2086	/*
2087	 * Explicit credential specified as a colon separated list:
2088	 *	uid:gid:gid:...
2089	 */
2090	if (pw != NULL)
2091		cr->cr_uid = pw->pw_uid;
2092	else if (isdigit(*name) || *name == '-')
2093		cr->cr_uid = atoi(name);
2094	else {
2095		syslog(LOG_ERR, "unknown user: %s", name);
2096		return;
2097	}
2098	cr->cr_ngroups = 0;
2099	while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
2100		name = strsep(&names, ":");
2101		if (isdigit(*name) || *name == '-') {
2102			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
2103		} else {
2104			if ((gr = getgrnam(name)) == NULL) {
2105				syslog(LOG_ERR, "unknown group: %s", name);
2106				continue;
2107			}
2108			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2109		}
2110	}
2111	if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
2112		syslog(LOG_ERR, "too many groups");
2113}
2114
2115#define	STRSIZ	(RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
2116/*
2117 * Routines that maintain the remote mounttab
2118 */
2119void
2120get_mountlist()
2121{
2122	struct mountlist *mlp, **mlpp;
2123	char *host, *dirp, *cp;
2124	char str[STRSIZ];
2125	FILE *mlfile;
2126
2127	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2128		if (errno == ENOENT)
2129			return;
2130		else {
2131			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
2132			return;
2133		}
2134	}
2135	mlpp = &mlhead;
2136	while (fgets(str, STRSIZ, mlfile) != NULL) {
2137		cp = str;
2138		host = strsep(&cp, " \t\n");
2139		dirp = strsep(&cp, " \t\n");
2140		if (host == NULL || dirp == NULL)
2141			continue;
2142		mlp = (struct mountlist *)malloc(sizeof (*mlp));
2143		if (mlp == (struct mountlist *)NULL)
2144			out_of_mem();
2145		strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
2146		mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2147		strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2148		mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2149		mlp->ml_next = (struct mountlist *)NULL;
2150		*mlpp = mlp;
2151		mlpp = &mlp->ml_next;
2152	}
2153	fclose(mlfile);
2154}
2155
2156void
2157del_mlist(char *hostp, char *dirp)
2158{
2159	struct mountlist *mlp, **mlpp;
2160	struct mountlist *mlp2;
2161	FILE *mlfile;
2162	int fnd = 0;
2163
2164	mlpp = &mlhead;
2165	mlp = mlhead;
2166	while (mlp) {
2167		if (!strcmp(mlp->ml_host, hostp) &&
2168		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2169			fnd = 1;
2170			mlp2 = mlp;
2171			*mlpp = mlp = mlp->ml_next;
2172			free((caddr_t)mlp2);
2173		} else {
2174			mlpp = &mlp->ml_next;
2175			mlp = mlp->ml_next;
2176		}
2177	}
2178	if (fnd) {
2179		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
2180			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
2181			return;
2182		}
2183		mlp = mlhead;
2184		while (mlp) {
2185			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2186			mlp = mlp->ml_next;
2187		}
2188		fclose(mlfile);
2189	}
2190}
2191
2192void
2193add_mlist(hostp, dirp)
2194	char *hostp, *dirp;
2195{
2196	struct mountlist *mlp, **mlpp;
2197	FILE *mlfile;
2198
2199	mlpp = &mlhead;
2200	mlp = mlhead;
2201	while (mlp) {
2202		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2203			return;
2204		mlpp = &mlp->ml_next;
2205		mlp = mlp->ml_next;
2206	}
2207	mlp = (struct mountlist *)malloc(sizeof (*mlp));
2208	if (mlp == (struct mountlist *)NULL)
2209		out_of_mem();
2210	strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
2211	mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2212	strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2213	mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2214	mlp->ml_next = (struct mountlist *)NULL;
2215	*mlpp = mlp;
2216	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2217		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
2218		return;
2219	}
2220	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2221	fclose(mlfile);
2222}
2223
2224/*
2225 * Free up a group list.
2226 */
2227void
2228free_grp(grp)
2229	struct grouplist *grp;
2230{
2231	if (grp->gr_type == GT_HOST) {
2232		if (grp->gr_ptr.gt_addrinfo != NULL)
2233			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
2234	} else if (grp->gr_type == GT_NET) {
2235		if (grp->gr_ptr.gt_net.nt_name)
2236			free(grp->gr_ptr.gt_net.nt_name);
2237	}
2238	free((caddr_t)grp);
2239}
2240
2241#ifdef DEBUG
2242void
2243SYSLOG(int pri, const char *fmt, ...)
2244{
2245	va_list ap;
2246
2247	va_start(ap, fmt);
2248	vfprintf(stderr, fmt, ap);
2249	va_end(ap);
2250}
2251#endif /* DEBUG */
2252
2253/*
2254 * Check options for consistency.
2255 */
2256int
2257check_options(dp)
2258	struct dirlist *dp;
2259{
2260
2261	if (dp == (struct dirlist *)NULL)
2262	    return (1);
2263	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
2264	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
2265	    return (1);
2266	}
2267	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2268		syslog(LOG_ERR, "-mask requires -network");
2269		return (1);
2270	}
2271	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
2272		syslog(LOG_ERR, "-network requires mask specification");
2273		return (1);
2274	}
2275	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
2276		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
2277		return (1);
2278	}
2279	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2280	    syslog(LOG_ERR, "-alldirs has multiple directories");
2281	    return (1);
2282	}
2283	return (0);
2284}
2285
2286/*
2287 * Check an absolute directory path for any symbolic links. Return true
2288 */
2289int
2290check_dirpath(dirp)
2291	char *dirp;
2292{
2293	char *cp;
2294	int ret = 1;
2295	struct stat sb;
2296
2297	cp = dirp + 1;
2298	while (*cp && ret) {
2299		if (*cp == '/') {
2300			*cp = '\0';
2301			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2302				ret = 0;
2303			*cp = '/';
2304		}
2305		cp++;
2306	}
2307	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2308		ret = 0;
2309	return (ret);
2310}
2311
2312/*
2313 * Make a netmask according to the specified prefix length. The ss_family
2314 * and other non-address fields must be initialised before calling this.
2315 */
2316int
2317makemask(struct sockaddr_storage *ssp, int bitlen)
2318{
2319	u_char *p;
2320	int bits, i, len;
2321
2322	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
2323		return (-1);
2324	if (bitlen > len * NBBY)
2325		return (-1);
2326
2327	for (i = 0; i < len; i++) {
2328		bits = (bitlen > NBBY) ? NBBY : bitlen;
2329		*p++ = (1 << bits) - 1;
2330		bitlen -= bits;
2331	}
2332	return 0;
2333}
2334
2335/*
2336 * Check that the sockaddr is a valid netmask. Returns 0 if the mask
2337 * is acceptable (i.e. of the form 1...10....0).
2338 */
2339int
2340checkmask(struct sockaddr *sa)
2341{
2342	u_char *mask;
2343	int i, len;
2344
2345	if ((mask = sa_rawaddr(sa, &len)) == NULL)
2346		return (-1);
2347
2348	for (i = 0; i < len; i++)
2349		if (mask[i] != 0xff)
2350			break;
2351	if (i < len) {
2352		if (~mask[i] & (u_char)(~mask[i] + 1))
2353			return (-1);
2354		i++;
2355	}
2356	for (; i < len; i++)
2357		if (mask[i] != 0)
2358			return (-1);
2359	return (0);
2360}
2361
2362/*
2363 * Compare two sockaddrs according to a specified mask. Return zero if
2364 * `sa1' matches `sa2' when filtered by the netmask in `samask'.
2365 * If samask is NULL, perform a full comparision.
2366 */
2367int
2368sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
2369{
2370	unsigned char *p1, *p2, *mask;
2371	int len, i;
2372
2373	if (sa1->sa_family != sa2->sa_family ||
2374	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
2375	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
2376		return (1);
2377
2378	switch (sa1->sa_family) {
2379	case AF_INET6:
2380		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
2381		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
2382			return (1);
2383		break;
2384	}
2385
2386	/* Simple binary comparison if no mask specified. */
2387	if (samask == NULL)
2388		return (memcmp(p1, p2, len));
2389
2390	/* Set up the mask, and do a mask-based comparison. */
2391	if (sa1->sa_family != samask->sa_family ||
2392	    (mask = sa_rawaddr(samask, NULL)) == NULL)
2393		return (1);
2394
2395	for (i = 0; i < len; i++)
2396		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
2397			return (1);
2398	return (0);
2399}
2400
2401/*
2402 * Return a pointer to the part of the sockaddr that contains the
2403 * raw address, and set *nbytes to its length in bytes. Returns
2404 * NULL if the address family is unknown.
2405 */
2406void *
2407sa_rawaddr(struct sockaddr *sa, int *nbytes) {
2408	void *p;
2409	int len;
2410
2411	switch (sa->sa_family) {
2412	case AF_INET:
2413		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
2414		p = &((struct sockaddr_in *)sa)->sin_addr;
2415		break;
2416	case AF_INET6:
2417		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
2418		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
2419		break;
2420	default:
2421		p = NULL;
2422		len = 0;
2423	}
2424
2425	if (nbytes != NULL)
2426		*nbytes = len;
2427	return (p);
2428}
2429
2430void
2431huphandler(int sig)
2432{
2433	got_sighup = 1;
2434}
2435
2436void terminate(sig)
2437int sig;
2438{
2439	close(mountdlockfd);
2440	unlink(MOUNTDLOCK);
2441	rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
2442	rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
2443	exit (0);
2444}
2445