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