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