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