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