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