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