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