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