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