xref: /freebsd-14.2/usr.sbin/mountd/mountd.c (revision c09ca8f4)
1 /*-
2  * SPDX-License-Identifier: BSD-3-Clause
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 #ifndef lint
36 static const char copyright[] =
37 "@(#) Copyright (c) 1989, 1993\n\
38 	The Regents of the University of California.  All rights reserved.\n";
39 #endif /*not lint*/
40 
41 #if 0
42 #ifndef lint
43 static char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95";
44 #endif /*not lint*/
45 #endif
46 
47 #include <sys/cdefs.h>
48 #include <sys/param.h>
49 #include <sys/conf.h>
50 #include <sys/fcntl.h>
51 #include <sys/fnv_hash.h>
52 #include <sys/linker.h>
53 #include <sys/module.h>
54 #include <sys/mount.h>
55 #include <sys/queue.h>
56 #include <sys/stat.h>
57 #include <sys/sysctl.h>
58 #include <sys/syslog.h>
59 
60 #include <rpc/rpc.h>
61 #include <rpc/rpc_com.h>
62 #include <rpc/pmap_clnt.h>
63 #include <rpc/pmap_prot.h>
64 #include <rpcsvc/mount.h>
65 #include <nfs/nfsproto.h>
66 #include <nfs/nfssvc.h>
67 #include <nfsserver/nfs.h>
68 
69 #include <fs/nfs/nfsport.h>
70 
71 #include <arpa/inet.h>
72 
73 #include <ctype.h>
74 #include <err.h>
75 #include <errno.h>
76 #include <grp.h>
77 #include <libutil.h>
78 #include <limits.h>
79 #include <netdb.h>
80 #include <pwd.h>
81 #include <signal.h>
82 #include <stdio.h>
83 #include <stdlib.h>
84 #include <string.h>
85 #include <unistd.h>
86 #include <vis.h>
87 #include "pathnames.h"
88 #include "mntopts.h"
89 
90 #ifdef DEBUG
91 #include <stdarg.h>
92 #endif
93 
94 /*
95  * Structures for keeping the mount list and export list
96  */
97 struct mountlist {
98 	char	ml_host[MNTNAMLEN+1];
99 	char	ml_dirp[MNTPATHLEN+1];
100 
101 	SLIST_ENTRY(mountlist)	next;
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;
110 };
111 /* dp_flag bits */
112 #define	DP_DEFSET	0x1
113 #define DP_HOSTSET	0x2
114 
115 /*
116  * maproot/mapall credentials.
117  * cr_smallgrps can be used for a group list up to SMALLNGROUPS in size.
118  * Larger group lists are malloc'd/free'd.
119  */
120 #define	SMALLNGROUPS	32
121 struct expcred {
122 	uid_t		cr_uid;
123 	int		cr_ngroups;
124 	gid_t		cr_smallgrps[SMALLNGROUPS];
125 	gid_t		*cr_groups;
126 };
127 
128 struct exportlist {
129 	struct dirlist	*ex_dirl;
130 	struct dirlist	*ex_defdir;
131 	struct grouplist *ex_grphead;
132 	int		ex_flag;
133 	fsid_t		ex_fs;
134 	char		*ex_fsdir;
135 	char		*ex_indexfile;
136 	struct expcred	ex_defanon;
137 	uint64_t	ex_defexflags;
138 	int		ex_numsecflavors;
139 	int		ex_secflavors[MAXSECFLAVORS];
140 	int		ex_defnumsecflavors;
141 	int		ex_defsecflavors[MAXSECFLAVORS];
142 
143 	SLIST_ENTRY(exportlist) entries;
144 };
145 /* ex_flag bits */
146 #define	EX_LINKED	0x01
147 #define	EX_DONE		0x02
148 #define	EX_DEFSET	0x04
149 #define	EX_PUBLICFH	0x08
150 #define	EX_ADMINWARN	0x10
151 
152 SLIST_HEAD(exportlisthead, exportlist);
153 
154 struct netmsk {
155 	struct sockaddr_storage nt_net;
156 	struct sockaddr_storage nt_mask;
157 	char		*nt_name;
158 };
159 
160 union grouptypes {
161 	struct addrinfo *gt_addrinfo;
162 	struct netmsk	gt_net;
163 };
164 
165 struct grouplist {
166 	int gr_type;
167 	union grouptypes gr_ptr;
168 	struct grouplist *gr_next;
169 	struct expcred gr_anon;
170 	uint64_t gr_exflags;
171 	int gr_flag;
172 	int gr_numsecflavors;
173 	int gr_secflavors[MAXSECFLAVORS];
174 };
175 /* Group types */
176 #define	GT_NULL		0x0
177 #define	GT_HOST		0x1
178 #define	GT_NET		0x2
179 #define	GT_DEFAULT	0x3
180 #define GT_IGNORE	0x5
181 
182 /* Group flags */
183 #define	GR_FND		0x1
184 
185 struct hostlist {
186 	int		 ht_flag;	/* Uses DP_xx bits */
187 	struct grouplist *ht_grp;
188 	struct hostlist	 *ht_next;
189 };
190 
191 struct fhreturn {
192 	int	fhr_flag;
193 	int	fhr_vers;
194 	nfsfh_t	fhr_fh;
195 	int	fhr_numsecflavors;
196 	int	*fhr_secflavors;
197 };
198 
199 #define	GETPORT_MAXTRY	20	/* Max tries to get a port # */
200 
201 /*
202  * How long to delay a reload of exports when there are RPC request(s)
203  * to process, in usec.  Must be less than 1second.
204  */
205 #define	RELOADDELAY	250000
206 
207 /* Global defs */
208 static char	*add_expdir(struct dirlist **, char *, int);
209 static void	add_dlist(struct dirlist **, struct dirlist *,
210 		    struct grouplist *, int, struct exportlist *,
211 		    struct expcred *, uint64_t);
212 static void	add_mlist(char *, char *);
213 static int	check_path_component(const char *, char **);
214 static int	check_dirpath(char *, char **);
215 static int	check_statfs(const char *, struct statfs *, char **);
216 static int	check_options(struct dirlist *);
217 static int	checkmask(struct sockaddr *sa);
218 static int	chk_host(struct dirlist *, struct sockaddr *, int *, int *,
219 		    int *, int **);
220 static char	*strsep_quote(char **stringp, const char *delim);
221 static int	create_service(struct netconfig *nconf);
222 static void	complete_service(struct netconfig *nconf, char *port_str);
223 static void	clearout_service(void);
224 static void	del_mlist(char *hostp, char *dirp);
225 static struct dirlist	*dirp_search(struct dirlist *, char *);
226 static int	do_export_mount(struct exportlist *, struct statfs *);
227 static int	do_mount(struct exportlist *, struct grouplist *, uint64_t,
228 		    struct expcred *, char *, int, struct statfs *, int, int *);
229 static int	do_opt(char **, char **, struct exportlist *,
230 		    struct grouplist *, int *, uint64_t *, struct expcred *);
231 static struct exportlist	*ex_search(fsid_t *, struct exportlisthead *);
232 static struct exportlist	*get_exp(void);
233 static void	free_dir(struct dirlist *);
234 static void	free_exp(struct exportlist *);
235 static void	free_grp(struct grouplist *);
236 static void	free_host(struct hostlist *);
237 static void	free_v4rootexp(void);
238 static void	get_exportlist_one(int);
239 static void	get_exportlist(int);
240 static void	insert_exports(struct exportlist *, struct exportlisthead *);
241 static void	free_exports(struct exportlisthead *);
242 static void	read_exportfile(int);
243 static int	compare_nmount_exportlist(struct iovec *, int, char *);
244 static int	compare_export(struct exportlist *, struct exportlist *);
245 static int	compare_cred(struct expcred *, struct expcred *);
246 static int	compare_secflavor(int *, int *, int);
247 static void	delete_export(struct iovec *, int, struct statfs *, char *);
248 static int	get_host(char *, struct grouplist *, struct grouplist *);
249 static struct hostlist *get_ht(void);
250 static int	get_line(void);
251 static void	get_mountlist(void);
252 static int	get_net(char *, struct netmsk *, int);
253 static void	getexp_err(struct exportlist *, struct grouplist *, const char *);
254 static struct grouplist	*get_grp(void);
255 static void	hang_dirp(struct dirlist *, struct grouplist *,
256 		    struct exportlist *, int, struct expcred *, uint64_t);
257 static void	huphandler(int sig);
258 static int	makemask(struct sockaddr_storage *ssp, int bitlen);
259 static void	mntsrv(struct svc_req *, SVCXPRT *);
260 static void	nextfield(char **, char **);
261 static void	out_of_mem(void);
262 static void	parsecred(char *, struct expcred *);
263 static int	parsesec(char *, struct exportlist *);
264 static int	put_exlist(struct dirlist *, XDR *, struct dirlist *,
265 		    int *, int);
266 static void	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
267 static int	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
268 		    struct sockaddr *samask);
269 static int	scan_tree(struct dirlist *, struct sockaddr *);
270 static void	usage(void);
271 static int	xdr_dir(XDR *, char *);
272 static int	xdr_explist(XDR *, caddr_t);
273 static int	xdr_explist_brief(XDR *, caddr_t);
274 static int	xdr_explist_common(XDR *, caddr_t, int);
275 static int	xdr_fhs(XDR *, caddr_t);
276 static int	xdr_mlist(XDR *, caddr_t);
277 static void	terminate(int);
278 static void	cp_cred(struct expcred *, struct expcred *);
279 
280 #define	EXPHASH(f)	(fnv_32_buf((f), sizeof(fsid_t), 0) % exphashsize)
281 static struct exportlisthead *exphead = NULL;
282 static struct exportlisthead *oldexphead = NULL;
283 static int exphashsize = 0;
284 static SLIST_HEAD(, mountlist) mlhead = SLIST_HEAD_INITIALIZER(&mlhead);
285 static char *exnames_default[2] = { _PATH_EXPORTS, NULL };
286 static char **exnames;
287 static char **hosts = NULL;
288 static int force_v2 = 0;
289 static int warn_admin = 1;
290 static int resvport_only = 1;
291 static int nhosts = 0;
292 static int dir_only = 1;
293 static int dolog = 0;
294 static _Atomic(int) got_sighup = 0;
295 static int xcreated = 0;
296 
297 static char *svcport_str = NULL;
298 static int mallocd_svcport = 0;
299 static int *sock_fd;
300 static int sock_fdcnt;
301 static int sock_fdpos;
302 static int suspend_nfsd = 0;
303 
304 static int opt_flags;
305 static int have_v6 = 1;
306 
307 static int v4root_phase = 0;
308 static char v4root_dirpath[PATH_MAX + 1];
309 static struct exportlist *v4root_ep = NULL;
310 static int has_publicfh = 0;
311 static int has_set_publicfh = 0;
312 
313 static struct pidfh *pfh = NULL;
314 /* Bits for opt_flags above */
315 #define	OP_MAPROOT	0x01
316 #define	OP_MAPALL	0x02
317 /* 0x4 free */
318 #define	OP_MASK		0x08
319 #define	OP_NET		0x10
320 #define	OP_ALLDIRS	0x40
321 #define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
322 #define	OP_QUIET	0x100
323 #define OP_MASKLEN	0x200
324 #define OP_SEC		0x400
325 #define OP_CLASSMASK	0x800	/* mask not specified, is Class A/B/C default */
326 
327 #ifdef DEBUG
328 static int debug = 1;
329 static void	SYSLOG(int, const char *, ...) __printflike(2, 3);
330 #define syslog SYSLOG
331 #else
332 static int debug = 0;
333 #endif
334 
335 /*
336  * The LOGDEBUG() syslog() calls are always compiled into the daemon.
337  * To enable them, create a file at _PATH_MOUNTDDEBUG. This file can be empty.
338  * To disable the logging, just delete the file at _PATH_MOUNTDDEBUG.
339  */
340 static int logdebug = 0;
341 #define	LOGDEBUG(format, ...)						\
342     (logdebug ? syslog(LOG_DEBUG, format, ## __VA_ARGS__) : 0)
343 
344 /*
345  * Similar to strsep(), but it allows for quoted strings
346  * and escaped characters.
347  *
348  * It returns the string (or NULL, if *stringp is NULL),
349  * which is a de-quoted version of the string if necessary.
350  *
351  * It modifies *stringp in place.
352  */
353 static char *
strsep_quote(char ** stringp,const char * delim)354 strsep_quote(char **stringp, const char *delim)
355 {
356 	char *srcptr, *dstptr, *retval;
357 	char quot = 0;
358 
359 	if (stringp == NULL || *stringp == NULL)
360 		return (NULL);
361 
362 	srcptr = dstptr = retval = *stringp;
363 
364 	while (*srcptr) {
365 		/*
366 		 * We're looking for several edge cases here.
367 		 * First:  if we're in quote state (quot != 0),
368 		 * then we ignore the delim characters, but otherwise
369 		 * process as normal, unless it is the quote character.
370 		 * Second:  if the current character is a backslash,
371 		 * we take the next character as-is, without checking
372 		 * for delim, quote, or backslash.  Exception:  if the
373 		 * next character is a NUL, that's the end of the string.
374 		 * Third:  if the character is a quote character, we toggle
375 		 * quote state.
376 		 * Otherwise:  check the current character for NUL, or
377 		 * being in delim, and end the string if either is true.
378 		 */
379 		if (*srcptr == '\\') {
380 			srcptr++;
381 			/*
382 			 * The edge case here is if the next character
383 			 * is NUL, we want to stop processing.  But if
384 			 * it's not NUL, then we simply want to copy it.
385 			 */
386 			if (*srcptr) {
387 				*dstptr++ = *srcptr++;
388 			}
389 			continue;
390 		}
391 		if (quot == 0 && (*srcptr == '\'' || *srcptr == '"')) {
392 			quot = *srcptr++;
393 			continue;
394 		}
395 		if (quot && *srcptr == quot) {
396 			/* End of the quoted part */
397 			quot = 0;
398 			srcptr++;
399 			continue;
400 		}
401 		if (!quot && strchr(delim, *srcptr))
402 			break;
403 		*dstptr++ = *srcptr++;
404 	}
405 
406 	*stringp = (*srcptr == '\0') ? NULL : srcptr + 1;
407 	*dstptr = 0; /* Terminate the string */
408 	return (retval);
409 }
410 
411 /*
412  * Mountd server for NFS mount protocol as described in:
413  * NFS: Network File System Protocol Specification, RFC1094, Appendix A
414  * The optional arguments are the exports file name
415  * default: _PATH_EXPORTS
416  * and "-n" to allow nonroot mount.
417  */
418 int
main(int argc,char ** argv)419 main(int argc, char **argv)
420 {
421 	fd_set readfds;
422 	struct netconfig *nconf;
423 	char *endptr, **hosts_bak;
424 	void *nc_handle;
425 	pid_t otherpid;
426 	in_port_t svcport;
427 	int c, k, s;
428 	int maxrec = RPC_MAXDATASIZE;
429 	int attempt_cnt, port_len, port_pos, ret;
430 	char **port_list;
431 	uint64_t curtime, nexttime;
432 	struct timeval tv;
433 	struct timespec tp;
434 	sigset_t sig_mask, sighup_mask;
435 	int enable_rpcbind;
436 
437 	enable_rpcbind = 1;
438 	/* Check that another mountd isn't already running. */
439 	pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
440 	if (pfh == NULL) {
441 		if (errno == EEXIST)
442 			errx(1, "mountd already running, pid: %d.", otherpid);
443 		warn("cannot open or create pidfile");
444 	}
445 
446 	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
447 	if (s < 0)
448 		have_v6 = 0;
449 	else
450 		close(s);
451 
452 	while ((c = getopt(argc, argv, "2Adeh:lnp:RrS")) != -1)
453 		switch (c) {
454 		case '2':
455 			force_v2 = 1;
456 			break;
457 		case 'A':
458 			warn_admin = 0;
459 			break;
460 		case 'e':
461 			/* now a no-op, since this is the default */
462 			break;
463 		case 'n':
464 			resvport_only = 0;
465 			break;
466 		case 'R':
467 			/* Do not support Mount protocol */
468 			enable_rpcbind = 0;
469 			break;
470 		case 'r':
471 			dir_only = 0;
472 			break;
473 		case 'd':
474 			debug = debug ? 0 : 1;
475 			break;
476 		case 'l':
477 			dolog = 1;
478 			break;
479 		case 'p':
480 			endptr = NULL;
481 			svcport = (in_port_t)strtoul(optarg, &endptr, 10);
482 			if (endptr == NULL || *endptr != '\0' ||
483 			    svcport == 0 || svcport >= IPPORT_MAX)
484 				usage();
485 			svcport_str = strdup(optarg);
486 			break;
487 		case 'h':
488 			++nhosts;
489 			hosts_bak = hosts;
490 			hosts_bak = realloc(hosts, nhosts * sizeof(char *));
491 			if (hosts_bak == NULL) {
492 				if (hosts != NULL) {
493 					for (k = 0; k < nhosts; k++)
494 						free(hosts[k]);
495 					free(hosts);
496 					out_of_mem();
497 				}
498 			}
499 			hosts = hosts_bak;
500 			hosts[nhosts - 1] = strdup(optarg);
501 			if (hosts[nhosts - 1] == NULL) {
502 				for (k = 0; k < (nhosts - 1); k++)
503 					free(hosts[k]);
504 				free(hosts);
505 				out_of_mem();
506 			}
507 			break;
508 		case 'S':
509 			suspend_nfsd = 1;
510 			break;
511 		default:
512 			usage();
513 		}
514 	if (enable_rpcbind == 0) {
515 		if (svcport_str != NULL) {
516 			warnx("-p option not compatible with -R, ignored");
517 			free(svcport_str);
518 			svcport_str = NULL;
519 		}
520 		if (nhosts > 0) {
521 			warnx("-h option not compatible with -R, ignored");
522 			for (k = 0; k < nhosts; k++)
523 				free(hosts[k]);
524 			free(hosts);
525 			hosts = NULL;
526 			nhosts = 0;
527 		}
528 	}
529 
530 	if (modfind("nfsd") < 0) {
531 		/* Not present in kernel, try loading it */
532 		if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
533 			errx(1, "NFS server is not available");
534 	}
535 
536 	argc -= optind;
537 	argv += optind;
538 	if (argc > 0)
539 		exnames = argv;
540 	else
541 		exnames = exnames_default;
542 	openlog("mountd", LOG_PID, LOG_DAEMON);
543 	if (debug)
544 		warnx("getting export list");
545 	get_exportlist(0);
546 	if (debug)
547 		warnx("getting mount list");
548 	get_mountlist();
549 	if (debug)
550 		warnx("here we go");
551 	if (debug == 0) {
552 		daemon(0, 0);
553 		signal(SIGINT, SIG_IGN);
554 		signal(SIGQUIT, SIG_IGN);
555 	}
556 	signal(SIGHUP, huphandler);
557 	signal(SIGTERM, terminate);
558 	signal(SIGPIPE, SIG_IGN);
559 
560 	pidfile_write(pfh);
561 
562 	if (enable_rpcbind != 0) {
563 		rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
564 		rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
565 		rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
566 
567 		if (!resvport_only) {
568 			if (sysctlbyname("vfs.nfsd.nfs_privport", NULL, NULL,
569 			    &resvport_only, sizeof(resvport_only)) != 0 &&
570 			    errno != ENOENT) {
571 				syslog(LOG_ERR, "sysctl: %m");
572 				exit(1);
573 			}
574 		}
575 
576 		/*
577 		 * If no hosts were specified, add a wildcard entry to bind to
578 		 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added
579 		 * to the list.
580 		 */
581 		if (nhosts == 0) {
582 			hosts = malloc(sizeof(char *));
583 			if (hosts == NULL)
584 				out_of_mem();
585 			hosts[0] = "*";
586 			nhosts = 1;
587 		} else {
588 			hosts_bak = hosts;
589 			if (have_v6) {
590 				hosts_bak = realloc(hosts, (nhosts + 2) *
591 				    sizeof(char *));
592 				if (hosts_bak == NULL) {
593 					for (k = 0; k < nhosts; k++)
594 						free(hosts[k]);
595 			    		free(hosts);
596 			    		out_of_mem();
597 				} else
598 					hosts = hosts_bak;
599 				nhosts += 2;
600 				hosts[nhosts - 2] = "::1";
601 			} else {
602 				hosts_bak = realloc(hosts, (nhosts + 1) *
603 				    sizeof(char *));
604 				if (hosts_bak == NULL) {
605 					for (k = 0; k < nhosts; k++)
606 						free(hosts[k]);
607 					free(hosts);
608 					out_of_mem();
609 				} else {
610 					nhosts += 1;
611 					hosts = hosts_bak;
612 				}
613 			}
614 
615 			hosts[nhosts - 1] = "127.0.0.1";
616 		}
617 	}
618 
619 	attempt_cnt = 1;
620 	sock_fdcnt = 0;
621 	sock_fd = NULL;
622 	port_list = NULL;
623 	port_len = 0;
624 	if (enable_rpcbind != 0) {
625 		nc_handle = setnetconfig();
626 		while ((nconf = getnetconfig(nc_handle))) {
627 			if (nconf->nc_flag & NC_VISIBLE) {
628 				if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
629 				    "inet6") == 0) {
630 					/* DO NOTHING */
631 				} else {
632 					ret = create_service(nconf);
633 					if (ret == 1)
634 						/* Ignore this call */
635 						continue;
636 					if (ret < 0) {
637 						/*
638 						 * Failed to bind port, so close
639 						 * off all sockets created and
640 						 * try again if the port# was
641 						 * dynamically assigned via
642 						 * bind(2).
643 						 */
644 						clearout_service();
645 						if (mallocd_svcport != 0 &&
646 						    attempt_cnt <
647 						    GETPORT_MAXTRY) {
648 							free(svcport_str);
649 							svcport_str = NULL;
650 							mallocd_svcport = 0;
651 						} else {
652 							errno = EADDRINUSE;
653 							syslog(LOG_ERR,
654 							    "bindresvport_sa:"
655 							    " %m");
656 							exit(1);
657 						}
658 
659 						/*
660 						 * Start over at the first
661 						 * service.
662 						 */
663 						free(sock_fd);
664 						sock_fdcnt = 0;
665 						sock_fd = NULL;
666 						nc_handle = setnetconfig();
667 						attempt_cnt++;
668 					} else if (mallocd_svcport != 0 &&
669 					    attempt_cnt == GETPORT_MAXTRY) {
670 						/*
671 						 * For the last attempt, allow
672 						 * different port #s for each
673 						 * nconf by saving the
674 						 * svcport_str setting it back
675 						 * to NULL.
676 						 */
677 						port_list = realloc(port_list,
678 						    (port_len + 1) *
679 						    sizeof(char *));
680 						if (port_list == NULL)
681 							out_of_mem();
682 						port_list[port_len++] =
683 						    svcport_str;
684 						svcport_str = NULL;
685 						mallocd_svcport = 0;
686 					}
687 				}
688 			}
689 		}
690 
691 		/*
692 		 * Successfully bound the ports, so call complete_service() to
693 		 * do the rest of the setup on the service(s).
694 		 */
695 		sock_fdpos = 0;
696 		port_pos = 0;
697 		nc_handle = setnetconfig();
698 		while ((nconf = getnetconfig(nc_handle))) {
699 			if (nconf->nc_flag & NC_VISIBLE) {
700 				if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
701 				    "inet6") == 0) {
702 					/* DO NOTHING */
703 				} else if (port_list != NULL) {
704 					if (port_pos >= port_len) {
705 						syslog(LOG_ERR, "too many"
706 						    " port#s");
707 						exit(1);
708 					}
709 					complete_service(nconf,
710 					    port_list[port_pos++]);
711 				} else
712 					complete_service(nconf, svcport_str);
713 			}
714 		}
715 		endnetconfig(nc_handle);
716 		free(sock_fd);
717 		if (port_list != NULL) {
718 			for (port_pos = 0; port_pos < port_len; port_pos++)
719 				free(port_list[port_pos]);
720 			free(port_list);
721 		}
722 
723 		if (xcreated == 0) {
724 			syslog(LOG_ERR, "could not create any services");
725 			exit(1);
726 		}
727 	}
728 
729 	/* Expand svc_run() here so that we can call get_exportlist(). */
730 	curtime = nexttime = 0;
731 	sigemptyset(&sighup_mask);
732 	sigaddset(&sighup_mask, SIGHUP);
733 	for (;;) {
734 		clock_gettime(CLOCK_MONOTONIC, &tp);
735 		curtime = tp.tv_sec;
736 		curtime = curtime * 1000000 + tp.tv_nsec / 1000;
737 		sigprocmask(SIG_BLOCK, &sighup_mask, &sig_mask);
738 		if (got_sighup && curtime >= nexttime) {
739 			got_sighup = 0;
740 			sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
741 			get_exportlist(1);
742 			clock_gettime(CLOCK_MONOTONIC, &tp);
743 			nexttime = tp.tv_sec;
744 			nexttime = nexttime * 1000000 + tp.tv_nsec / 1000 +
745 			    RELOADDELAY;
746 		} else
747 			sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
748 
749 		/*
750 		 * If a reload is pending, poll for received request(s),
751 		 * otherwise set a RELOADDELAY timeout, since a SIGHUP
752 		 * could be processed between the got_sighup test and
753 		 * the select() system call.
754 		 */
755 		tv.tv_sec = 0;
756 		if (got_sighup)
757 			tv.tv_usec = 0;
758 		else
759 			tv.tv_usec = RELOADDELAY;
760 		if (enable_rpcbind != 0) {
761 			readfds = svc_fdset;
762 			switch (select(svc_maxfd + 1, &readfds, NULL, NULL,
763 			    &tv)) {
764 			case -1:
765 				if (errno == EINTR) {
766 					/* Allow a reload now. */
767 					nexttime = 0;
768 					continue;
769 				}
770 				syslog(LOG_ERR, "mountd died: select: %m");
771 				exit(1);
772 			case 0:
773 				/* Allow a reload now. */
774 				nexttime = 0;
775 				continue;
776 			default:
777 				svc_getreqset(&readfds);
778 			}
779 		} else {
780 			/* Simply wait for a signal. */
781 			sigsuspend(&sig_mask);
782 		}
783 	}
784 }
785 
786 /*
787  * This routine creates and binds sockets on the appropriate
788  * addresses. It gets called one time for each transport.
789  * It returns 0 upon success, 1 for ignore the call and -1 to indicate
790  * bind failed with EADDRINUSE.
791  * Any file descriptors that have been created are stored in sock_fd and
792  * the total count of them is maintained in sock_fdcnt.
793  */
794 static int
create_service(struct netconfig * nconf)795 create_service(struct netconfig *nconf)
796 {
797 	struct addrinfo hints, *res = NULL;
798 	struct sockaddr_in *sin;
799 	struct sockaddr_in6 *sin6;
800 	struct __rpc_sockinfo si;
801 	int aicode;
802 	int fd;
803 	int nhostsbak;
804 	int one = 1;
805 	int r;
806 	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
807 	int mallocd_res;
808 
809 	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
810 	    (nconf->nc_semantics != NC_TPI_COTS) &&
811 	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
812 		return (1);	/* not my type */
813 
814 	/*
815 	 * XXX - using RPC library internal functions.
816 	 */
817 	if (!__rpc_nconf2sockinfo(nconf, &si)) {
818 		syslog(LOG_ERR, "cannot get information for %s",
819 		    nconf->nc_netid);
820 		return (1);
821 	}
822 
823 	/* Get mountd's address on this transport */
824 	memset(&hints, 0, sizeof hints);
825 	hints.ai_family = si.si_af;
826 	hints.ai_socktype = si.si_socktype;
827 	hints.ai_protocol = si.si_proto;
828 
829 	/*
830 	 * Bind to specific IPs if asked to
831 	 */
832 	nhostsbak = nhosts;
833 	while (nhostsbak > 0) {
834 		--nhostsbak;
835 		sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
836 		if (sock_fd == NULL)
837 			out_of_mem();
838 		sock_fd[sock_fdcnt++] = -1;	/* Set invalid for now. */
839 		mallocd_res = 0;
840 
841 		hints.ai_flags = AI_PASSIVE;
842 
843 		/*
844 		 * XXX - using RPC library internal functions.
845 		 */
846 		if ((fd = __rpc_nconf2fd(nconf)) < 0) {
847 			int non_fatal = 0;
848 	    		if (errno == EAFNOSUPPORT &&
849 			    nconf->nc_semantics != NC_TPI_CLTS)
850 				non_fatal = 1;
851 
852 			syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
853 			    "cannot create socket for %s", nconf->nc_netid);
854 			if (non_fatal != 0)
855 				continue;
856 			exit(1);
857 		}
858 
859 		switch (hints.ai_family) {
860 		case AF_INET:
861 			if (inet_pton(AF_INET, hosts[nhostsbak],
862 			    host_addr) == 1) {
863 				hints.ai_flags |= AI_NUMERICHOST;
864 			} else {
865 				/*
866 				 * Skip if we have an AF_INET6 address.
867 				 */
868 				if (inet_pton(AF_INET6, hosts[nhostsbak],
869 				    host_addr) == 1) {
870 					close(fd);
871 					continue;
872 				}
873 			}
874 			break;
875 		case AF_INET6:
876 			if (inet_pton(AF_INET6, hosts[nhostsbak],
877 			    host_addr) == 1) {
878 				hints.ai_flags |= AI_NUMERICHOST;
879 			} else {
880 				/*
881 				 * Skip if we have an AF_INET address.
882 				 */
883 				if (inet_pton(AF_INET, hosts[nhostsbak],
884 				    host_addr) == 1) {
885 					close(fd);
886 					continue;
887 				}
888 			}
889 
890 			/*
891 			 * We're doing host-based access checks here, so don't
892 			 * allow v4-in-v6 to confuse things. The kernel will
893 			 * disable it by default on NFS sockets too.
894 			 */
895 			if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
896 			    sizeof one) < 0) {
897 				syslog(LOG_ERR,
898 				    "can't disable v4-in-v6 on IPv6 socket");
899 				exit(1);
900 			}
901 			break;
902 		default:
903 			break;
904 		}
905 
906 		/*
907 		 * If no hosts were specified, just bind to INADDR_ANY
908 		 */
909 		if (strcmp("*", hosts[nhostsbak]) == 0) {
910 			if (svcport_str == NULL) {
911 				res = malloc(sizeof(struct addrinfo));
912 				if (res == NULL)
913 					out_of_mem();
914 				mallocd_res = 1;
915 				res->ai_flags = hints.ai_flags;
916 				res->ai_family = hints.ai_family;
917 				res->ai_protocol = hints.ai_protocol;
918 				switch (res->ai_family) {
919 				case AF_INET:
920 					sin = malloc(sizeof(struct sockaddr_in));
921 					if (sin == NULL)
922 						out_of_mem();
923 					sin->sin_family = AF_INET;
924 					sin->sin_port = htons(0);
925 					sin->sin_addr.s_addr = htonl(INADDR_ANY);
926 					res->ai_addr = (struct sockaddr*) sin;
927 					res->ai_addrlen = (socklen_t)
928 					    sizeof(struct sockaddr_in);
929 					break;
930 				case AF_INET6:
931 					sin6 = malloc(sizeof(struct sockaddr_in6));
932 					if (sin6 == NULL)
933 						out_of_mem();
934 					sin6->sin6_family = AF_INET6;
935 					sin6->sin6_port = htons(0);
936 					sin6->sin6_addr = in6addr_any;
937 					res->ai_addr = (struct sockaddr*) sin6;
938 					res->ai_addrlen = (socklen_t)
939 					    sizeof(struct sockaddr_in6);
940 					break;
941 				default:
942 					syslog(LOG_ERR, "bad addr fam %d",
943 					    res->ai_family);
944 					exit(1);
945 				}
946 			} else {
947 				if ((aicode = getaddrinfo(NULL, svcport_str,
948 				    &hints, &res)) != 0) {
949 					syslog(LOG_ERR,
950 					    "cannot get local address for %s: %s",
951 					    nconf->nc_netid,
952 					    gai_strerror(aicode));
953 					close(fd);
954 					continue;
955 				}
956 			}
957 		} else {
958 			if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
959 			    &hints, &res)) != 0) {
960 				syslog(LOG_ERR,
961 				    "cannot get local address for %s: %s",
962 				    nconf->nc_netid, gai_strerror(aicode));
963 				close(fd);
964 				continue;
965 			}
966 		}
967 
968 		/* Store the fd. */
969 		sock_fd[sock_fdcnt - 1] = fd;
970 
971 		/* Now, attempt the bind. */
972 		r = bindresvport_sa(fd, res->ai_addr);
973 		if (r != 0) {
974 			if (errno == EADDRINUSE && mallocd_svcport != 0) {
975 				if (mallocd_res != 0) {
976 					free(res->ai_addr);
977 					free(res);
978 				} else
979 					freeaddrinfo(res);
980 				return (-1);
981 			}
982 			syslog(LOG_ERR, "bindresvport_sa: %m");
983 			exit(1);
984 		}
985 
986 		if (svcport_str == NULL) {
987 			svcport_str = malloc(NI_MAXSERV * sizeof(char));
988 			if (svcport_str == NULL)
989 				out_of_mem();
990 			mallocd_svcport = 1;
991 
992 			if (getnameinfo(res->ai_addr,
993 			    res->ai_addr->sa_len, NULL, NI_MAXHOST,
994 			    svcport_str, NI_MAXSERV * sizeof(char),
995 			    NI_NUMERICHOST | NI_NUMERICSERV))
996 				errx(1, "Cannot get port number");
997 		}
998 		if (mallocd_res != 0) {
999 			free(res->ai_addr);
1000 			free(res);
1001 		} else
1002 			freeaddrinfo(res);
1003 		res = NULL;
1004 	}
1005 	return (0);
1006 }
1007 
1008 /*
1009  * Called after all the create_service() calls have succeeded, to complete
1010  * the setup and registration.
1011  */
1012 static void
complete_service(struct netconfig * nconf,char * port_str)1013 complete_service(struct netconfig *nconf, char *port_str)
1014 {
1015 	struct addrinfo hints, *res = NULL;
1016 	struct __rpc_sockinfo si;
1017 	struct netbuf servaddr;
1018 	SVCXPRT	*transp = NULL;
1019 	int aicode, fd, nhostsbak;
1020 	int registered = 0;
1021 
1022 	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
1023 	    (nconf->nc_semantics != NC_TPI_COTS) &&
1024 	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
1025 		return;	/* not my type */
1026 
1027 	/*
1028 	 * XXX - using RPC library internal functions.
1029 	 */
1030 	if (!__rpc_nconf2sockinfo(nconf, &si)) {
1031 		syslog(LOG_ERR, "cannot get information for %s",
1032 		    nconf->nc_netid);
1033 		return;
1034 	}
1035 
1036 	nhostsbak = nhosts;
1037 	while (nhostsbak > 0) {
1038 		--nhostsbak;
1039 		if (sock_fdpos >= sock_fdcnt) {
1040 			/* Should never happen. */
1041 			syslog(LOG_ERR, "Ran out of socket fd's");
1042 			return;
1043 		}
1044 		fd = sock_fd[sock_fdpos++];
1045 		if (fd < 0)
1046 			continue;
1047 
1048 		/*
1049 		 * Using -1 tells listen(2) to use
1050 		 * kern.ipc.soacceptqueue for the backlog.
1051 		 */
1052 		if (nconf->nc_semantics != NC_TPI_CLTS)
1053 			listen(fd, -1);
1054 
1055 		if (nconf->nc_semantics == NC_TPI_CLTS )
1056 			transp = svc_dg_create(fd, 0, 0);
1057 		else
1058 			transp = svc_vc_create(fd, RPC_MAXDATASIZE,
1059 			    RPC_MAXDATASIZE);
1060 
1061 		if (transp != (SVCXPRT *) NULL) {
1062 			if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv,
1063 			    NULL))
1064 				syslog(LOG_ERR,
1065 				    "can't register %s MOUNTVERS service",
1066 				    nconf->nc_netid);
1067 			if (!force_v2) {
1068 				if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3,
1069 				    mntsrv, NULL))
1070 					syslog(LOG_ERR,
1071 					    "can't register %s MOUNTVERS3 service",
1072 					    nconf->nc_netid);
1073 			}
1074 		} else
1075 			syslog(LOG_WARNING, "can't create %s services",
1076 			    nconf->nc_netid);
1077 
1078 		if (registered == 0) {
1079 			registered = 1;
1080 			memset(&hints, 0, sizeof hints);
1081 			hints.ai_flags = AI_PASSIVE;
1082 			hints.ai_family = si.si_af;
1083 			hints.ai_socktype = si.si_socktype;
1084 			hints.ai_protocol = si.si_proto;
1085 
1086 			if ((aicode = getaddrinfo(NULL, port_str, &hints,
1087 			    &res)) != 0) {
1088 				syslog(LOG_ERR, "cannot get local address: %s",
1089 				    gai_strerror(aicode));
1090 				exit(1);
1091 			}
1092 
1093 			servaddr.buf = malloc(res->ai_addrlen);
1094 			memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
1095 			servaddr.len = res->ai_addrlen;
1096 
1097 			rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr);
1098 			rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr);
1099 
1100 			xcreated++;
1101 			freeaddrinfo(res);
1102 		}
1103 	} /* end while */
1104 }
1105 
1106 /*
1107  * Clear out sockets after a failure to bind one of them, so that the
1108  * cycle of socket creation/binding can start anew.
1109  */
1110 static void
clearout_service(void)1111 clearout_service(void)
1112 {
1113 	int i;
1114 
1115 	for (i = 0; i < sock_fdcnt; i++) {
1116 		if (sock_fd[i] >= 0) {
1117 			shutdown(sock_fd[i], SHUT_RDWR);
1118 			close(sock_fd[i]);
1119 		}
1120 	}
1121 }
1122 
1123 static void
usage(void)1124 usage(void)
1125 {
1126 	fprintf(stderr,
1127 		"usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
1128 		"[-S] [-h <bindip>] [export_file ...]\n");
1129 	exit(1);
1130 }
1131 
1132 /*
1133  * The mount rpc service
1134  */
1135 void
mntsrv(struct svc_req * rqstp,SVCXPRT * transp)1136 mntsrv(struct svc_req *rqstp, SVCXPRT *transp)
1137 {
1138 	struct exportlist *ep;
1139 	struct dirlist *dp;
1140 	struct fhreturn fhr;
1141 	struct stat stb;
1142 	struct statfs fsb;
1143 	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
1144 	int lookup_failed = 1;
1145 	struct sockaddr *saddr;
1146 	u_short sport;
1147 	char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
1148 	int defset, hostset;
1149 	long bad = 0;
1150 	sigset_t sighup_mask;
1151 	int numsecflavors, *secflavorsp;
1152 
1153 	sigemptyset(&sighup_mask);
1154 	sigaddset(&sighup_mask, SIGHUP);
1155 	saddr = svc_getrpccaller(transp)->buf;
1156 	switch (saddr->sa_family) {
1157 	case AF_INET6:
1158 		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
1159 		break;
1160 	case AF_INET:
1161 		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
1162 		break;
1163 	default:
1164 		syslog(LOG_ERR, "request from unknown address family");
1165 		return;
1166 	}
1167 	switch (rqstp->rq_proc) {
1168 	case MOUNTPROC_MNT:
1169 	case MOUNTPROC_UMNT:
1170 	case MOUNTPROC_UMNTALL:
1171 		lookup_failed = getnameinfo(saddr, saddr->sa_len, host,
1172 		    sizeof host, NULL, 0, 0);
1173 	}
1174 	getnameinfo(saddr, saddr->sa_len, numerichost,
1175 	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
1176 	switch (rqstp->rq_proc) {
1177 	case NULLPROC:
1178 		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
1179 			syslog(LOG_ERR, "can't send reply");
1180 		return;
1181 	case MOUNTPROC_MNT:
1182 		if (sport >= IPPORT_RESERVED && resvport_only) {
1183 			syslog(LOG_NOTICE,
1184 			    "mount request from %s from unprivileged port",
1185 			    numerichost);
1186 			svcerr_weakauth(transp);
1187 			return;
1188 		}
1189 		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1190 			syslog(LOG_NOTICE, "undecodable mount request from %s",
1191 			    numerichost);
1192 			svcerr_decode(transp);
1193 			return;
1194 		}
1195 
1196 		/*
1197 		 * Get the real pathname and make sure it is a directory
1198 		 * or a regular file if the -r option was specified
1199 		 * and it exists.
1200 		 */
1201 		if (realpath(rpcpath, dirpath) == NULL ||
1202 		    stat(dirpath, &stb) < 0 ||
1203 		    statfs(dirpath, &fsb) < 0) {
1204 			chdir("/");	/* Just in case realpath doesn't */
1205 			syslog(LOG_NOTICE,
1206 			    "mount request from %s for non existent path %s",
1207 			    numerichost, dirpath);
1208 			if (debug)
1209 				warnx("stat failed on %s", dirpath);
1210 			bad = ENOENT;	/* We will send error reply later */
1211 		}
1212 		if (!bad &&
1213 		    !S_ISDIR(stb.st_mode) &&
1214 		    (dir_only || !S_ISREG(stb.st_mode))) {
1215 			syslog(LOG_NOTICE,
1216 			    "mount request from %s for non-directory path %s",
1217 			    numerichost, dirpath);
1218 			if (debug)
1219 				warnx("mounting non-directory %s", dirpath);
1220 			bad = ENOTDIR;	/* We will send error reply later */
1221 		}
1222 
1223 		/* Check in the exports list */
1224 		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1225 		if (bad)
1226 			ep = NULL;
1227 		else
1228 			ep = ex_search(&fsb.f_fsid, exphead);
1229 		hostset = defset = 0;
1230 		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset,
1231 		    &numsecflavors, &secflavorsp) ||
1232 		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
1233 		      chk_host(dp, saddr, &defset, &hostset, &numsecflavors,
1234 		       &secflavorsp)) ||
1235 		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
1236 		     scan_tree(ep->ex_dirl, saddr) == 0))) {
1237 			if (bad) {
1238 				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1239 				    (caddr_t)&bad))
1240 					syslog(LOG_ERR, "can't send reply");
1241 				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1242 				return;
1243 			}
1244 			if (hostset & DP_HOSTSET) {
1245 				fhr.fhr_flag = hostset;
1246 				fhr.fhr_numsecflavors = numsecflavors;
1247 				fhr.fhr_secflavors = secflavorsp;
1248 			} else {
1249 				fhr.fhr_flag = defset;
1250 				fhr.fhr_numsecflavors = ep->ex_defnumsecflavors;
1251 				fhr.fhr_secflavors = ep->ex_defsecflavors;
1252 			}
1253 			fhr.fhr_vers = rqstp->rq_vers;
1254 			/* Get the file handle */
1255 			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
1256 			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
1257 				bad = errno;
1258 				syslog(LOG_ERR, "can't get fh for %s", dirpath);
1259 				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1260 				    (caddr_t)&bad))
1261 					syslog(LOG_ERR, "can't send reply");
1262 				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1263 				return;
1264 			}
1265 			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
1266 			    (caddr_t)&fhr))
1267 				syslog(LOG_ERR, "can't send reply");
1268 			if (!lookup_failed)
1269 				add_mlist(host, dirpath);
1270 			else
1271 				add_mlist(numerichost, dirpath);
1272 			if (debug)
1273 				warnx("mount successful");
1274 			if (dolog)
1275 				syslog(LOG_NOTICE,
1276 				    "mount request succeeded from %s for %s",
1277 				    numerichost, dirpath);
1278 		} else {
1279 			if (!bad)
1280 				bad = EACCES;
1281 			syslog(LOG_NOTICE,
1282 			    "mount request denied from %s for %s",
1283 			    numerichost, dirpath);
1284 		}
1285 
1286 		if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
1287 		    (caddr_t)&bad))
1288 			syslog(LOG_ERR, "can't send reply");
1289 		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1290 		return;
1291 	case MOUNTPROC_DUMP:
1292 		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
1293 			syslog(LOG_ERR, "can't send reply");
1294 		else if (dolog)
1295 			syslog(LOG_NOTICE,
1296 			    "dump request succeeded from %s",
1297 			    numerichost);
1298 		return;
1299 	case MOUNTPROC_UMNT:
1300 		if (sport >= IPPORT_RESERVED && resvport_only) {
1301 			syslog(LOG_NOTICE,
1302 			    "umount request from %s from unprivileged port",
1303 			    numerichost);
1304 			svcerr_weakauth(transp);
1305 			return;
1306 		}
1307 		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1308 			syslog(LOG_NOTICE, "undecodable umount request from %s",
1309 			    numerichost);
1310 			svcerr_decode(transp);
1311 			return;
1312 		}
1313 		if (realpath(rpcpath, dirpath) == NULL) {
1314 			syslog(LOG_NOTICE, "umount request from %s "
1315 			    "for non existent path %s",
1316 			    numerichost, dirpath);
1317 		}
1318 		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1319 			syslog(LOG_ERR, "can't send reply");
1320 		if (!lookup_failed)
1321 			del_mlist(host, dirpath);
1322 		del_mlist(numerichost, dirpath);
1323 		if (dolog)
1324 			syslog(LOG_NOTICE,
1325 			    "umount request succeeded from %s for %s",
1326 			    numerichost, dirpath);
1327 		return;
1328 	case MOUNTPROC_UMNTALL:
1329 		if (sport >= IPPORT_RESERVED && resvport_only) {
1330 			syslog(LOG_NOTICE,
1331 			    "umountall request from %s from unprivileged port",
1332 			    numerichost);
1333 			svcerr_weakauth(transp);
1334 			return;
1335 		}
1336 		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1337 			syslog(LOG_ERR, "can't send reply");
1338 		if (!lookup_failed)
1339 			del_mlist(host, NULL);
1340 		del_mlist(numerichost, NULL);
1341 		if (dolog)
1342 			syslog(LOG_NOTICE,
1343 			    "umountall request succeeded from %s",
1344 			    numerichost);
1345 		return;
1346 	case MOUNTPROC_EXPORT:
1347 		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
1348 			if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
1349 			    (caddr_t)NULL))
1350 				syslog(LOG_ERR, "can't send reply");
1351 		if (dolog)
1352 			syslog(LOG_NOTICE,
1353 			    "export request succeeded from %s",
1354 			    numerichost);
1355 		return;
1356 	default:
1357 		svcerr_noproc(transp);
1358 		return;
1359 	}
1360 }
1361 
1362 /*
1363  * Xdr conversion for a dirpath string
1364  */
1365 static int
xdr_dir(XDR * xdrsp,char * dirp)1366 xdr_dir(XDR *xdrsp, char *dirp)
1367 {
1368 	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
1369 }
1370 
1371 /*
1372  * Xdr routine to generate file handle reply
1373  */
1374 static int
xdr_fhs(XDR * xdrsp,caddr_t cp)1375 xdr_fhs(XDR *xdrsp, caddr_t cp)
1376 {
1377 	struct fhreturn *fhrp = (struct fhreturn *)cp;
1378 	u_long ok = 0, len, auth;
1379 	int i;
1380 
1381 	if (!xdr_long(xdrsp, &ok))
1382 		return (0);
1383 	switch (fhrp->fhr_vers) {
1384 	case 1:
1385 		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
1386 	case 3:
1387 		len = NFSX_V3FH;
1388 		if (!xdr_long(xdrsp, &len))
1389 			return (0);
1390 		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
1391 			return (0);
1392 		if (fhrp->fhr_numsecflavors) {
1393 			if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
1394 				return (0);
1395 			for (i = 0; i < fhrp->fhr_numsecflavors; i++)
1396 				if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
1397 					return (0);
1398 			return (1);
1399 		} else {
1400 			auth = AUTH_SYS;
1401 			len = 1;
1402 			if (!xdr_long(xdrsp, &len))
1403 				return (0);
1404 			return (xdr_long(xdrsp, &auth));
1405 		}
1406 	}
1407 	return (0);
1408 }
1409 
1410 static int
xdr_mlist(XDR * xdrsp,caddr_t cp __unused)1411 xdr_mlist(XDR *xdrsp, caddr_t cp __unused)
1412 {
1413 	struct mountlist *mlp;
1414 	int true = 1;
1415 	int false = 0;
1416 	char *strp;
1417 
1418 	SLIST_FOREACH(mlp, &mlhead, next) {
1419 		if (!xdr_bool(xdrsp, &true))
1420 			return (0);
1421 		strp = &mlp->ml_host[0];
1422 		if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
1423 			return (0);
1424 		strp = &mlp->ml_dirp[0];
1425 		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1426 			return (0);
1427 	}
1428 	if (!xdr_bool(xdrsp, &false))
1429 		return (0);
1430 	return (1);
1431 }
1432 
1433 /*
1434  * Xdr conversion for export list
1435  */
1436 static int
xdr_explist_common(XDR * xdrsp,caddr_t cp __unused,int brief)1437 xdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
1438 {
1439 	struct exportlist *ep;
1440 	int false = 0;
1441 	int putdef;
1442 	sigset_t sighup_mask;
1443 	int i;
1444 
1445 	sigemptyset(&sighup_mask);
1446 	sigaddset(&sighup_mask, SIGHUP);
1447 	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1448 
1449 	for (i = 0; i < exphashsize; i++)
1450 		SLIST_FOREACH(ep, &exphead[i], entries) {
1451 			putdef = 0;
1452 			if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1453 				       &putdef, brief))
1454 				goto errout;
1455 			if (ep->ex_defdir && putdef == 0 &&
1456 				put_exlist(ep->ex_defdir, xdrsp, NULL,
1457 				&putdef, brief))
1458 				goto errout;
1459 		}
1460 	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1461 	if (!xdr_bool(xdrsp, &false))
1462 		return (0);
1463 	return (1);
1464 errout:
1465 	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1466 	return (0);
1467 }
1468 
1469 /*
1470  * Called from xdr_explist() to traverse the tree and export the
1471  * directory paths.
1472  */
1473 static int
put_exlist(struct dirlist * dp,XDR * xdrsp,struct dirlist * adp,int * putdefp,int brief)1474 put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp,
1475 	int brief)
1476 {
1477 	struct grouplist *grp;
1478 	struct hostlist *hp;
1479 	int true = 1;
1480 	int false = 0;
1481 	int gotalldir = 0;
1482 	char *strp;
1483 
1484 	if (dp) {
1485 		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
1486 			return (1);
1487 		if (!xdr_bool(xdrsp, &true))
1488 			return (1);
1489 		strp = dp->dp_dirp;
1490 		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1491 			return (1);
1492 		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
1493 			gotalldir = 1;
1494 			*putdefp = 1;
1495 		}
1496 		if (brief) {
1497 			if (!xdr_bool(xdrsp, &true))
1498 				return (1);
1499 			strp = "(...)";
1500 			if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1501 				return (1);
1502 		} else if ((dp->dp_flag & DP_DEFSET) == 0 &&
1503 		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
1504 			hp = dp->dp_hosts;
1505 			while (hp) {
1506 				grp = hp->ht_grp;
1507 				if (grp->gr_type == GT_HOST) {
1508 					if (!xdr_bool(xdrsp, &true))
1509 						return (1);
1510 					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
1511 					if (!xdr_string(xdrsp, &strp,
1512 					    MNTNAMLEN))
1513 						return (1);
1514 				} else if (grp->gr_type == GT_NET) {
1515 					if (!xdr_bool(xdrsp, &true))
1516 						return (1);
1517 					strp = grp->gr_ptr.gt_net.nt_name;
1518 					if (!xdr_string(xdrsp, &strp,
1519 					    MNTNAMLEN))
1520 						return (1);
1521 				}
1522 				hp = hp->ht_next;
1523 				if (gotalldir && hp == (struct hostlist *)NULL) {
1524 					hp = adp->dp_hosts;
1525 					gotalldir = 0;
1526 				}
1527 			}
1528 		}
1529 		if (!xdr_bool(xdrsp, &false))
1530 			return (1);
1531 		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
1532 			return (1);
1533 	}
1534 	return (0);
1535 }
1536 
1537 static int
xdr_explist(XDR * xdrsp,caddr_t cp)1538 xdr_explist(XDR *xdrsp, caddr_t cp)
1539 {
1540 
1541 	return xdr_explist_common(xdrsp, cp, 0);
1542 }
1543 
1544 static int
xdr_explist_brief(XDR * xdrsp,caddr_t cp)1545 xdr_explist_brief(XDR *xdrsp, caddr_t cp)
1546 {
1547 
1548 	return xdr_explist_common(xdrsp, cp, 1);
1549 }
1550 
1551 static char *line;
1552 static size_t linesize;
1553 static FILE *exp_file;
1554 
1555 /*
1556  * Get the export list from one, currently open file
1557  */
1558 static void
get_exportlist_one(int passno)1559 get_exportlist_one(int passno)
1560 {
1561 	struct exportlist *ep;
1562 	struct grouplist *grp, *tgrp, *savgrp;
1563 	struct dirlist *dirhead;
1564 	struct statfs fsb;
1565 	struct expcred anon;
1566 	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1567 	char *err_msg = NULL;
1568 	int len, has_host, got_nondir, dirplen, netgrp;
1569 	uint64_t exflags;
1570 	char unvis_dir[PATH_MAX + 1];
1571 	int unvis_len;
1572 
1573 	v4root_phase = 0;
1574 	anon.cr_groups = NULL;
1575 	dirhead = (struct dirlist *)NULL;
1576 	unvis_dir[0] = '\0';
1577 	while (get_line()) {
1578 		if (debug)
1579 			warnx("got line %s", line);
1580 		cp = line;
1581 		nextfield(&cp, &endcp);
1582 		if (*cp == '#')
1583 			goto nextline;
1584 
1585 		/*
1586 		 * Set defaults.
1587 		 */
1588 		has_host = FALSE;
1589 		anon.cr_groups = anon.cr_smallgrps;
1590 		anon.cr_uid = UID_NOBODY;
1591 		anon.cr_ngroups = 1;
1592 		anon.cr_groups[0] = GID_NOGROUP;
1593 		exflags = MNT_EXPORTED;
1594 		got_nondir = 0;
1595 		opt_flags = 0;
1596 		ep = (struct exportlist *)NULL;
1597 		dirp = NULL;
1598 
1599 		/*
1600 		 * Handle the V4 root dir.
1601 		 */
1602 		if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1603 			/*
1604 			 * V4: just indicates that it is the v4 root point,
1605 			 * so skip over that and set v4root_phase.
1606 			 */
1607 			if (v4root_phase > 0) {
1608 				syslog(LOG_ERR, "V4:duplicate line, ignored");
1609 				goto nextline;
1610 			}
1611 			v4root_phase = 1;
1612 			cp += 3;
1613 			nextfield(&cp, &endcp);
1614 		}
1615 
1616 		/*
1617 		 * Create new exports list entry
1618 		 */
1619 		len = endcp-cp;
1620 		tgrp = grp = get_grp();
1621 		while (len > 0) {
1622 			if (len > MNTNAMLEN) {
1623 			    getexp_err(ep, tgrp, "mountpoint too long");
1624 			    goto nextline;
1625 			}
1626 			if (*cp == '-') {
1627 			    if (ep == (struct exportlist *)NULL) {
1628 				getexp_err(ep, tgrp,
1629 				    "flag before export path definition");
1630 				goto nextline;
1631 			    }
1632 			    if (debug)
1633 				warnx("doing opt %s", cp);
1634 			    got_nondir = 1;
1635 			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
1636 				&exflags, &anon)) {
1637 				getexp_err(ep, tgrp, NULL);
1638 				goto nextline;
1639 			    }
1640 			} else if (*cp == '/') {
1641 			    savedc = *endcp;
1642 			    *endcp = '\0';
1643 			    unvis_len = strnunvis(unvis_dir, sizeof(unvis_dir),
1644 				cp);
1645 			    if (unvis_len <= 0) {
1646 				getexp_err(ep, tgrp, "Cannot strunvis "
1647 				    "decode dir");
1648 				goto nextline;
1649 			    }
1650 			    if (v4root_phase > 1) {
1651 				    if (dirp != NULL) {
1652 					getexp_err(ep, tgrp, "Multiple V4 dirs");
1653 					goto nextline;
1654 				    }
1655 			    }
1656 			    if (check_dirpath(unvis_dir, &err_msg) &&
1657 				check_statfs(unvis_dir, &fsb, &err_msg)) {
1658 				if ((fsb.f_flags & MNT_AUTOMOUNTED) != 0)
1659 				    syslog(LOG_ERR, "Warning: exporting of "
1660 					"automounted fs %s not supported",
1661 					unvis_dir);
1662 				if (got_nondir) {
1663 				    getexp_err(ep, tgrp, "dirs must be first");
1664 				    goto nextline;
1665 				}
1666 				if (v4root_phase == 1) {
1667 				    if (dirp != NULL) {
1668 					getexp_err(ep, tgrp, "Multiple V4 dirs");
1669 					goto nextline;
1670 				    }
1671 				    if (strlen(v4root_dirpath) == 0) {
1672 					strlcpy(v4root_dirpath, unvis_dir,
1673 					    sizeof (v4root_dirpath));
1674 				    } else if (strcmp(v4root_dirpath, unvis_dir)
1675 					!= 0) {
1676 					syslog(LOG_ERR,
1677 					    "different V4 dirpath %s",
1678 					    unvis_dir);
1679 					getexp_err(ep, tgrp, NULL);
1680 					goto nextline;
1681 				    }
1682 				    dirp = unvis_dir;
1683 				    v4root_phase = 2;
1684 				    got_nondir = 1;
1685 				    ep = get_exp();
1686 				} else {
1687 				    if (ep) {
1688 					if (fsidcmp(&ep->ex_fs, &fsb.f_fsid)
1689 					    != 0) {
1690 						getexp_err(ep, tgrp,
1691 						    "fsid mismatch");
1692 						goto nextline;
1693 					}
1694 				    } else {
1695 					/*
1696 					 * See if this directory is already
1697 					 * in the list.
1698 					 */
1699 					ep = ex_search(&fsb.f_fsid, exphead);
1700 					if (ep == (struct exportlist *)NULL) {
1701 					    ep = get_exp();
1702 					    ep->ex_fs = fsb.f_fsid;
1703 					    ep->ex_fsdir = strdup(fsb.f_mntonname);
1704 					    if (ep->ex_fsdir == NULL)
1705 						out_of_mem();
1706 					    if (debug)
1707 						warnx(
1708 						  "making new ep fs=0x%x,0x%x",
1709 						  fsb.f_fsid.val[0],
1710 						  fsb.f_fsid.val[1]);
1711 					} else if (debug)
1712 					    warnx("found ep fs=0x%x,0x%x",
1713 						fsb.f_fsid.val[0],
1714 						fsb.f_fsid.val[1]);
1715 				    }
1716 
1717 				    if (warn_admin != 0 &&
1718 					(ep->ex_flag & EX_ADMINWARN) == 0 &&
1719 					strcmp(unvis_dir, fsb.f_mntonname) !=
1720 					0) {
1721 					if (debug)
1722 					    warnx("exporting %s exports entire "
1723 						"%s file system", unvis_dir,
1724 						    fsb.f_mntonname);
1725 					syslog(LOG_ERR, "Warning: exporting %s "
1726 					    "exports entire %s file system",
1727 					    unvis_dir, fsb.f_mntonname);
1728 					ep->ex_flag |= EX_ADMINWARN;
1729 				    }
1730 
1731 				    /*
1732 				     * Add dirpath to export mount point.
1733 				     */
1734 				    dirp = add_expdir(&dirhead, unvis_dir,
1735 					unvis_len);
1736 				    dirplen = unvis_len;
1737 				}
1738 			    } else {
1739 				if (err_msg != NULL) {
1740 					getexp_err(ep, tgrp, err_msg);
1741 					free(err_msg);
1742 					err_msg = NULL;
1743 				} else {
1744 					getexp_err(ep, tgrp,
1745 					    "symbolic link in export path or "
1746 					    "statfs failed");
1747 				}
1748 				goto nextline;
1749 			    }
1750 			    *endcp = savedc;
1751 			} else {
1752 			    savedc = *endcp;
1753 			    *endcp = '\0';
1754 			    got_nondir = 1;
1755 			    if (ep == (struct exportlist *)NULL) {
1756 				getexp_err(ep, tgrp,
1757 				    "host(s) before export path definition");
1758 				goto nextline;
1759 			    }
1760 
1761 			    /*
1762 			     * Get the host or netgroup.
1763 			     */
1764 			    setnetgrent(cp);
1765 			    netgrp = getnetgrent(&hst, &usr, &dom);
1766 			    do {
1767 				if (has_host) {
1768 				    grp->gr_next = get_grp();
1769 				    grp = grp->gr_next;
1770 				}
1771 				if (netgrp) {
1772 				    if (hst == 0) {
1773 					syslog(LOG_ERR,
1774 				"null hostname in netgroup %s, skipping", cp);
1775 					grp->gr_type = GT_IGNORE;
1776 				    } else if (get_host(hst, grp, tgrp)) {
1777 					syslog(LOG_ERR,
1778 			"bad host %s in netgroup %s, skipping", hst, cp);
1779 					grp->gr_type = GT_IGNORE;
1780 				    }
1781 				} else if (get_host(cp, grp, tgrp)) {
1782 				    syslog(LOG_ERR, "bad host %s, skipping", cp);
1783 				    grp->gr_type = GT_IGNORE;
1784 				}
1785 				has_host = TRUE;
1786 			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
1787 			    endnetgrent();
1788 			    *endcp = savedc;
1789 			}
1790 			cp = endcp;
1791 			nextfield(&cp, &endcp);
1792 			len = endcp - cp;
1793 		}
1794 		if (opt_flags & OP_CLASSMASK)
1795 			syslog(LOG_WARNING,
1796 			    "WARNING: No mask specified for %s, "
1797 			    "using out-of-date default",
1798 			    (&grp->gr_ptr.gt_net)->nt_name);
1799 		if (check_options(dirhead)) {
1800 			getexp_err(ep, tgrp, NULL);
1801 			goto nextline;
1802 		}
1803 		if (!has_host) {
1804 			grp->gr_type = GT_DEFAULT;
1805 			if (debug)
1806 				warnx("adding a default entry");
1807 
1808 		/*
1809 		 * Don't allow a network export coincide with a list of
1810 		 * host(s) on the same line.
1811 		 */
1812 		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1813 			getexp_err(ep, tgrp, "network/host conflict");
1814 			goto nextline;
1815 
1816 		/*
1817 		 * If an export list was specified on this line, make sure
1818 		 * that we have at least one valid entry, otherwise skip it.
1819 		 */
1820 		} else {
1821 			grp = tgrp;
1822 			while (grp && grp->gr_type == GT_IGNORE)
1823 				grp = grp->gr_next;
1824 			if (! grp) {
1825 			    getexp_err(ep, tgrp, "no valid entries");
1826 			    goto nextline;
1827 			}
1828 		}
1829 
1830 		if (v4root_phase == 1) {
1831 			getexp_err(ep, tgrp, "V4:root, no dirp, ignored");
1832 			goto nextline;
1833 		}
1834 
1835 		/*
1836 		 * Loop through hosts, pushing the exports into the kernel.
1837 		 * After loop, tgrp points to the start of the list and
1838 		 * grp points to the last entry in the list.
1839 		 * Do not do the do_mount() for passno == 1, since the
1840 		 * second pass will do it, as required.
1841 		 */
1842 		grp = tgrp;
1843 		do {
1844 			grp->gr_exflags = exflags;
1845 			cp_cred(&grp->gr_anon, &anon);
1846 			if (v4root_phase == 2 && passno == 0)
1847 				LOGDEBUG("do_mount v4root");
1848 			if (passno == 0 && do_mount(ep, grp, exflags, &anon,
1849 			    dirp, dirplen, &fsb, ep->ex_numsecflavors,
1850 			    ep->ex_secflavors)) {
1851 				getexp_err(ep, tgrp, NULL);
1852 				goto nextline;
1853 			}
1854 		} while (grp->gr_next && (grp = grp->gr_next));
1855 
1856 		/*
1857 		 * For V4: don't enter in mount lists.
1858 		 */
1859 		if (v4root_phase > 0 && v4root_phase <= 2) {
1860 			/*
1861 			 * These structures are used for the reload,
1862 			 * so save them for that case.  Otherwise, just
1863 			 * free them up now.
1864 			 */
1865 			if (passno == 1 && ep != NULL) {
1866 				savgrp = tgrp;
1867 				while (tgrp != NULL) {
1868 					/*
1869 					 * Save the security flavors and exflags
1870 					 * for this host set in the groups.
1871 					 */
1872 					tgrp->gr_numsecflavors =
1873 					    ep->ex_numsecflavors;
1874 					if (ep->ex_numsecflavors > 0)
1875 						memcpy(tgrp->gr_secflavors,
1876 						    ep->ex_secflavors,
1877 						    sizeof(ep->ex_secflavors));
1878 					tgrp = tgrp->gr_next;
1879 				}
1880 				if (v4root_ep == NULL) {
1881 					v4root_ep = ep;
1882 					ep = NULL;	/* Don't free below. */
1883 				}
1884 				grp->gr_next = v4root_ep->ex_grphead;
1885 				v4root_ep->ex_grphead = savgrp;
1886 			}
1887 			if (ep != NULL)
1888 				free_exp(ep);
1889 			while (tgrp != NULL) {
1890 				grp = tgrp;
1891 				tgrp = tgrp->gr_next;
1892 				free_grp(grp);
1893 			}
1894 			goto nextline;
1895 		}
1896 
1897 		/*
1898 		 * Success. Update the data structures.
1899 		 */
1900 		if (has_host) {
1901 			hang_dirp(dirhead, tgrp, ep, opt_flags, &anon, exflags);
1902 			grp->gr_next = ep->ex_grphead;
1903 			ep->ex_grphead = tgrp;
1904 		} else {
1905 			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1906 				opt_flags, &anon, exflags);
1907 			free_grp(grp);
1908 		}
1909 		dirhead = (struct dirlist *)NULL;
1910 		if ((ep->ex_flag & EX_LINKED) == 0) {
1911 			insert_exports(ep, exphead);
1912 
1913 			ep->ex_flag |= EX_LINKED;
1914 		}
1915 nextline:
1916 		v4root_phase = 0;
1917 		if (dirhead) {
1918 			free_dir(dirhead);
1919 			dirhead = (struct dirlist *)NULL;
1920 		}
1921 		if (anon.cr_groups != anon.cr_smallgrps) {
1922 			free(anon.cr_groups);
1923 			anon.cr_groups = NULL;
1924 		}
1925 	}
1926 }
1927 
1928 /*
1929  * Get the export list from all specified files
1930  */
1931 static void
get_exportlist(int passno)1932 get_exportlist(int passno)
1933 {
1934 	struct export_args export;
1935 	struct iovec *iov;
1936 	struct statfs *mntbufp;
1937 	char errmsg[255];
1938 	int error, i, nfs_maxvers, num;
1939 	int iovlen;
1940 	struct nfsex_args eargs;
1941 	FILE *debug_file;
1942 	size_t nfs_maxvers_size;
1943 
1944 	if ((debug_file = fopen(_PATH_MOUNTDDEBUG, "r")) != NULL) {
1945 		fclose(debug_file);
1946 		logdebug = 1;
1947 	} else
1948 		logdebug = 0;
1949 	LOGDEBUG("passno=%d", passno);
1950 	v4root_dirpath[0] = '\0';
1951 	free_v4rootexp();
1952 	if (passno == 1) {
1953 		/*
1954 		 * Save the current lists as old ones, so that the new lists
1955 		 * can be compared with the old ones in the 2nd pass.
1956 		 */
1957 		for (i = 0; i < exphashsize; i++) {
1958 			SLIST_FIRST(&oldexphead[i]) = SLIST_FIRST(&exphead[i]);
1959 			SLIST_INIT(&exphead[i]);
1960 		}
1961 
1962 		/* Note that the public fh has not yet been set. */
1963 		has_set_publicfh = 0;
1964 
1965 		/* Read the export file(s) and process them */
1966 		read_exportfile(passno);
1967 	} else {
1968 		/*
1969 		 * Just make the old lists empty.
1970 		 * exphashsize == 0 for the first call, before oldexphead
1971 		 * has been initialized-->loop won't be executed.
1972 		 */
1973 		for (i = 0; i < exphashsize; i++)
1974 			SLIST_INIT(&oldexphead[i]);
1975 	}
1976 
1977 	bzero(&export, sizeof(export));
1978 	export.ex_flags = MNT_DELEXPORT;
1979 	iov = NULL;
1980 	iovlen = 0;
1981 	bzero(errmsg, sizeof(errmsg));
1982 
1983 	if (suspend_nfsd != 0)
1984 		(void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
1985 	/*
1986 	 * Delete the old V4 root dir.
1987 	 */
1988 	bzero(&eargs, sizeof (eargs));
1989 	eargs.export.ex_flags = MNT_DELEXPORT;
1990 	if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT, (caddr_t)&eargs) < 0 &&
1991 	    errno != ENOENT)
1992 		syslog(LOG_ERR, "Can't delete exports for V4:");
1993 
1994 	build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1995 	build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1996 	build_iovec(&iov, &iovlen, "from", NULL, 0);
1997 	build_iovec(&iov, &iovlen, "update", NULL, 0);
1998 	build_iovec(&iov, &iovlen, "export", &export,
1999 	    sizeof(export));
2000 	build_iovec(&iov, &iovlen, "errmsg", errmsg,
2001 	    sizeof(errmsg));
2002 
2003 	/*
2004 	 * For passno == 1, compare the old and new lists updating the kernel
2005 	 * exports for any cases that have changed.
2006 	 * This call is doing the second pass through the lists.
2007 	 * If it fails, fall back on the bulk reload.
2008 	 */
2009 	if (passno == 1 && compare_nmount_exportlist(iov, iovlen, errmsg) ==
2010 	    0) {
2011 		LOGDEBUG("compareok");
2012 		/* Free up the old lists. */
2013 		free_exports(oldexphead);
2014 	} else {
2015 		LOGDEBUG("doing passno=0");
2016 		/*
2017 		 * Clear flag that notes if a public fh has been exported.
2018 		 * It is set by do_mount() if MNT_EXPUBLIC is set for the entry.
2019 		 */
2020 		has_publicfh = 0;
2021 
2022 		/* exphead == NULL if not yet allocated (first call). */
2023 		if (exphead != NULL) {
2024 			/*
2025 			 * First, get rid of the old lists.
2026 			 */
2027 			free_exports(exphead);
2028 			free_exports(oldexphead);
2029 		}
2030 
2031 		/*
2032 		 * And delete exports that are in the kernel for all local
2033 		 * filesystems.
2034 		 * XXX: Should know how to handle all local exportable
2035 		 * filesystems.
2036 		 */
2037 		num = getmntinfo(&mntbufp, MNT_NOWAIT);
2038 
2039 		/* Allocate hash tables, for first call. */
2040 		if (exphead == NULL) {
2041 			/* Target an average linked list length of 10. */
2042 			exphashsize = num / 10;
2043 			if (exphashsize < 1)
2044 				exphashsize = 1;
2045 			else if (exphashsize > 100000)
2046 				exphashsize = 100000;
2047 			exphead = malloc(exphashsize * sizeof(*exphead));
2048 			oldexphead = malloc(exphashsize * sizeof(*oldexphead));
2049 			if (exphead == NULL || oldexphead == NULL)
2050 				errx(1, "Can't malloc hash tables");
2051 
2052 			for (i = 0; i < exphashsize; i++) {
2053 				SLIST_INIT(&exphead[i]);
2054 				SLIST_INIT(&oldexphead[i]);
2055 			}
2056 		}
2057 
2058 		for (i = 0; i < num; i++)
2059 			delete_export(iov, iovlen, &mntbufp[i], errmsg);
2060 
2061 
2062 		/* Read the export file(s) and process them */
2063 		read_exportfile(0);
2064 	}
2065 
2066 	if (strlen(v4root_dirpath) == 0) {
2067 		/* Check to see if a V4: line is needed. */
2068 		nfs_maxvers_size = sizeof(nfs_maxvers);
2069 		error = sysctlbyname("vfs.nfsd.server_max_nfsvers",
2070 		    &nfs_maxvers, &nfs_maxvers_size, NULL, 0);
2071 		if (error != 0 || nfs_maxvers < NFS_VER2 || nfs_maxvers >
2072 		    NFS_VER4) {
2073 			syslog(LOG_ERR, "sysctlbyname(vfs.nfsd."
2074 			    "server_max_nfsvers) failed, defaulting to NFSv3");
2075 			nfs_maxvers = NFS_VER3;
2076 		}
2077 		if (nfs_maxvers == NFS_VER4)
2078 			syslog(LOG_ERR, "NFSv4 requires at least one V4: line");
2079 	}
2080 
2081 	if (iov != NULL) {
2082 		/* Free strings allocated by strdup() in getmntopts.c */
2083 		free(iov[0].iov_base); /* fstype */
2084 		free(iov[2].iov_base); /* fspath */
2085 		free(iov[4].iov_base); /* from */
2086 		free(iov[6].iov_base); /* update */
2087 		free(iov[8].iov_base); /* export */
2088 		free(iov[10].iov_base); /* errmsg */
2089 
2090 		/* free iov, allocated by realloc() */
2091 		free(iov);
2092 		iovlen = 0;
2093 	}
2094 
2095 	/*
2096 	 * If there was no public fh, clear any previous one set.
2097 	 */
2098 	if (has_publicfh == 0) {
2099 		LOGDEBUG("clear public fh");
2100 		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
2101 	}
2102 
2103 	/* Resume the nfsd. If they weren't suspended, this is harmless. */
2104 	(void)nfssvc(NFSSVC_RESUMENFSD, NULL);
2105 	LOGDEBUG("eo get_exportlist");
2106 }
2107 
2108 /*
2109  * Insert an export entry in the appropriate list.
2110  */
2111 static void
insert_exports(struct exportlist * ep,struct exportlisthead * exhp)2112 insert_exports(struct exportlist *ep, struct exportlisthead *exhp)
2113 {
2114 	uint32_t i;
2115 
2116 	i = EXPHASH(&ep->ex_fs);
2117 	LOGDEBUG("fs=%s hash=%i", ep->ex_fsdir, i);
2118 	SLIST_INSERT_HEAD(&exhp[i], ep, entries);
2119 }
2120 
2121 /*
2122  * Free up the exports lists passed in as arguments.
2123  */
2124 static void
free_exports(struct exportlisthead * exhp)2125 free_exports(struct exportlisthead *exhp)
2126 {
2127 	struct exportlist *ep, *ep2;
2128 	int i;
2129 
2130 	for (i = 0; i < exphashsize; i++) {
2131 		SLIST_FOREACH_SAFE(ep, &exhp[i], entries, ep2) {
2132 			SLIST_REMOVE(&exhp[i], ep, exportlist, entries);
2133 			free_exp(ep);
2134 		}
2135 		SLIST_INIT(&exhp[i]);
2136 	}
2137 }
2138 
2139 /*
2140  * Read the exports file(s) and call get_exportlist_one() for each line.
2141  */
2142 static void
read_exportfile(int passno)2143 read_exportfile(int passno)
2144 {
2145 	int done, i;
2146 
2147 	/*
2148 	 * Read in the exports file and build the list, calling
2149 	 * nmount() as we go along to push the export rules into the kernel.
2150 	 */
2151 	done = 0;
2152 	for (i = 0; exnames[i] != NULL; i++) {
2153 		if (debug)
2154 			warnx("reading exports from %s", exnames[i]);
2155 		if ((exp_file = fopen(exnames[i], "r")) == NULL) {
2156 			syslog(LOG_WARNING, "can't open %s", exnames[i]);
2157 			continue;
2158 		}
2159 		get_exportlist_one(passno);
2160 		fclose(exp_file);
2161 		done++;
2162 	}
2163 	if (done == 0) {
2164 		syslog(LOG_ERR, "can't open any exports file");
2165 		exit(2);
2166 	}
2167 }
2168 
2169 /*
2170  * Compare the export lists against the old ones and do nmount() operations
2171  * for any cases that have changed.  This avoids doing nmount() for entries
2172  * that have not changed.
2173  * Return 0 upon success, 1 otherwise.
2174  */
2175 static int
compare_nmount_exportlist(struct iovec * iov,int iovlen,char * errmsg)2176 compare_nmount_exportlist(struct iovec *iov, int iovlen, char *errmsg)
2177 {
2178 	struct exportlist *ep, *oep;
2179 	struct grouplist *grp;
2180 	struct statfs fs, ofs;
2181 	int i, ret;
2182 
2183 	/*
2184 	 * Loop through the current list and look for an entry in the old
2185 	 * list.
2186 	 * If found, check to see if it the same.
2187 	 *        If it is not the same, delete and re-export.
2188 	 *        Then mark it done on the old list.
2189 	 * else (not found)
2190 	 *        export it.
2191 	 * Any entries left in the old list after processing must have their
2192 	 * exports deleted.
2193 	 */
2194 	for (i = 0; i < exphashsize; i++)
2195 		SLIST_FOREACH(ep, &exphead[i], entries) {
2196 			LOGDEBUG("foreach ep=%s", ep->ex_fsdir);
2197 			oep = ex_search(&ep->ex_fs, oldexphead);
2198 			if (oep != NULL) {
2199 				/*
2200 				 * Check the mount paths are the same.
2201 				 * If not, return 1 so that the reload of the
2202 				 * exports will be done in bulk, the
2203 				 * passno == 0 way.
2204 				 */
2205 				LOGDEBUG("found old exp");
2206 				if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0)
2207 					return (1);
2208 				LOGDEBUG("same fsdir");
2209 				/*
2210 				 * Test to see if the entry is the same.
2211 				 * If not the same delete exports and
2212 				 * re-export.
2213 				 */
2214 				if (compare_export(ep, oep) != 0) {
2215 					/*
2216 					 * Clear has_publicfh if if was set
2217 					 * in the old exports, but only if it
2218 					 * has not been set during processing of
2219 					 * the exports for this pass, as
2220 					 * indicated by has_set_publicfh.
2221 					 */
2222 					if (has_set_publicfh == 0 &&
2223 					    (oep->ex_flag & EX_PUBLICFH) != 0)
2224 						has_publicfh = 0;
2225 
2226 					/* Delete and re-export. */
2227 					if (statfs(ep->ex_fsdir, &fs) < 0)
2228 						return (1);
2229 					delete_export(iov, iovlen, &fs, errmsg);
2230 					ret = do_export_mount(ep, &fs);
2231 					if (ret != 0)
2232 						return (ret);
2233 				}
2234 				oep->ex_flag |= EX_DONE;
2235 				LOGDEBUG("exdone");
2236 			} else {
2237 				LOGDEBUG("not found so export");
2238 				/* Not found, so do export. */
2239 				if (statfs(ep->ex_fsdir, &fs) < 0)
2240 					return (1);
2241 				ret = do_export_mount(ep, &fs);
2242 				if (ret != 0)
2243 					return (ret);
2244 			}
2245 		}
2246 
2247 	/* Delete exports not done. */
2248 	for (i = 0; i < exphashsize; i++)
2249 		SLIST_FOREACH(oep, &oldexphead[i], entries) {
2250 			if ((oep->ex_flag & EX_DONE) == 0) {
2251 				LOGDEBUG("not done delete=%s", oep->ex_fsdir);
2252 				if (statfs(oep->ex_fsdir, &ofs) >= 0 &&
2253 				    fsidcmp(&oep->ex_fs, &ofs.f_fsid) == 0) {
2254 					LOGDEBUG("do delete");
2255 					/*
2256 					 * Clear has_publicfh if if was set
2257 					 * in the old exports, but only if it
2258 					 * has not been set during processing of
2259 					 * the exports for this pass, as
2260 					 * indicated by has_set_publicfh.
2261 					 */
2262 					if (has_set_publicfh == 0 &&
2263 					    (oep->ex_flag & EX_PUBLICFH) != 0)
2264 						has_publicfh = 0;
2265 
2266 					delete_export(iov, iovlen, &ofs,
2267 					    errmsg);
2268 				}
2269 			}
2270 		}
2271 
2272 	/* Do the V4 root exports, as required. */
2273 	grp = NULL;
2274 	if (v4root_ep != NULL)
2275 		grp = v4root_ep->ex_grphead;
2276 	v4root_phase = 2;
2277 	while (v4root_ep != NULL && grp != NULL) {
2278 		LOGDEBUG("v4root expath=%s", v4root_dirpath);
2279 		ret = do_mount(v4root_ep, grp, grp->gr_exflags, &grp->gr_anon,
2280 		    v4root_dirpath, strlen(v4root_dirpath), &fs,
2281 		    grp->gr_numsecflavors, grp->gr_secflavors);
2282 		if (ret != 0) {
2283 			v4root_phase = 0;
2284 			return (ret);
2285 		}
2286 		grp = grp->gr_next;
2287 	}
2288 	v4root_phase = 0;
2289 	free_v4rootexp();
2290 	return (0);
2291 }
2292 
2293 /*
2294  * Compare old and current exportlist entries for the fsid and return 0
2295  * if they are the same, 1 otherwise.
2296  */
2297 static int
compare_export(struct exportlist * ep,struct exportlist * oep)2298 compare_export(struct exportlist *ep, struct exportlist *oep)
2299 {
2300 	struct grouplist *grp, *ogrp;
2301 
2302 	if (strcmp(ep->ex_fsdir, oep->ex_fsdir) != 0)
2303 		return (1);
2304 	if ((ep->ex_flag & EX_DEFSET) != (oep->ex_flag & EX_DEFSET))
2305 		return (1);
2306 	if ((ep->ex_defdir != NULL && oep->ex_defdir == NULL) ||
2307 	    (ep->ex_defdir == NULL && oep->ex_defdir != NULL))
2308 		return (1);
2309 	if (ep->ex_defdir != NULL && (ep->ex_defdir->dp_flag & DP_DEFSET) !=
2310 	    (oep->ex_defdir->dp_flag & DP_DEFSET))
2311 		return (1);
2312 	if ((ep->ex_flag & EX_DEFSET) != 0 && (ep->ex_defnumsecflavors !=
2313 	    oep->ex_defnumsecflavors || ep->ex_defexflags !=
2314 	    oep->ex_defexflags || compare_cred(&ep->ex_defanon,
2315 	    &oep->ex_defanon) != 0 || compare_secflavor(ep->ex_defsecflavors,
2316 	    oep->ex_defsecflavors, ep->ex_defnumsecflavors) != 0))
2317 		return (1);
2318 
2319 	/* Now, check all the groups. */
2320 	for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
2321 		ogrp->gr_flag = 0;
2322 	for (grp = ep->ex_grphead; grp != NULL; grp = grp->gr_next) {
2323 		for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp =
2324 		    ogrp->gr_next)
2325 			if ((ogrp->gr_flag & GR_FND) == 0 &&
2326 			    grp->gr_numsecflavors == ogrp->gr_numsecflavors &&
2327 			    grp->gr_exflags == ogrp->gr_exflags &&
2328 			    compare_cred(&grp->gr_anon, &ogrp->gr_anon) == 0 &&
2329 			    compare_secflavor(grp->gr_secflavors,
2330 			    ogrp->gr_secflavors, grp->gr_numsecflavors) == 0)
2331 				break;
2332 		if (ogrp != NULL)
2333 			ogrp->gr_flag |= GR_FND;
2334 		else
2335 			return (1);
2336 	}
2337 	for (ogrp = oep->ex_grphead; ogrp != NULL; ogrp = ogrp->gr_next)
2338 		if ((ogrp->gr_flag & GR_FND) == 0)
2339 			return (1);
2340 	return (0);
2341 }
2342 
2343 /*
2344  * This algorithm compares two arrays of "n" items. It returns 0 if they are
2345  * the "same" and 1 otherwise.  Although suboptimal, it is always safe to
2346  * return 1, which makes compare_nmount_export() reload the exports entry.
2347  * "same" refers to having the same set of values in the two arrays.
2348  * The arrays are in no particular order and duplicates (multiple entries
2349  * in an array with the same value) is allowed.
2350  * The algorithm is inefficient, but the common case of identical arrays is
2351  * handled first and "n" is normally fairly small.
2352  * Since the two functions need the same algorithm but for arrays of
2353  * different types (gid_t vs int), this is done as a macro.
2354  */
2355 #define	COMPARE_ARRAYS(a1, a2, n)					\
2356 	do {								\
2357 		int fnd, fndarray[(n)], i, j;				\
2358 		/* Handle common case of identical arrays. */		\
2359 		for (i = 0; i < (n); i++)				\
2360 			if ((a1)[i] != (a2)[i])				\
2361 				break;					\
2362 		if (i == (n))						\
2363 			return (0);					\
2364 		for (i = 0; i < (n); i++)				\
2365 			fndarray[i] = 0;				\
2366 		for (i = 0; i < (n); i++) {				\
2367 			fnd = 0;					\
2368 			for (j = 0; j < (n); j++) {			\
2369 				if ((a1)[i] == (a2)[j]) {		\
2370 					fndarray[j] = 1;		\
2371 					fnd = 1;			\
2372 				}					\
2373 			}						\
2374 			if (fnd == 0)					\
2375 				return (1);				\
2376 		}							\
2377 		for (i = 0; i < (n); i++)				\
2378 			if (fndarray[i] == 0)				\
2379 				return (1);				\
2380 		return (0);						\
2381 	} while (0)
2382 
2383 /*
2384  * Compare two struct expcred's.  Return 0 if the same and 1 otherwise.
2385  */
2386 static int
compare_cred(struct expcred * cr0,struct expcred * cr1)2387 compare_cred(struct expcred *cr0, struct expcred *cr1)
2388 {
2389 
2390 	if (cr0->cr_uid != cr1->cr_uid || cr0->cr_ngroups != cr1->cr_ngroups)
2391 		return (1);
2392 
2393 	COMPARE_ARRAYS(cr0->cr_groups, cr1->cr_groups, cr0->cr_ngroups);
2394 }
2395 
2396 /*
2397  * Compare two lists of security flavors.  Return 0 if the same and 1 otherwise.
2398  */
2399 static int
compare_secflavor(int * sec1,int * sec2,int nsec)2400 compare_secflavor(int *sec1, int *sec2, int nsec)
2401 {
2402 
2403 	COMPARE_ARRAYS(sec1, sec2, nsec);
2404 }
2405 
2406 /*
2407  * Delete an exports entry.
2408  */
2409 static void
delete_export(struct iovec * iov,int iovlen,struct statfs * fsp,char * errmsg)2410 delete_export(struct iovec *iov, int iovlen, struct statfs *fsp, char *errmsg)
2411 {
2412 	struct xvfsconf vfc;
2413 
2414 	if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
2415 		syslog(LOG_ERR, "getvfsbyname() failed for %s",
2416 		    fsp->f_fstypename);
2417 		return;
2418 	}
2419 
2420 	/*
2421 	 * We do not need to delete "export" flag from
2422 	 * filesystems that do not have it set.
2423 	 */
2424 	if (!(fsp->f_flags & MNT_EXPORTED))
2425 		return;
2426 	/*
2427 	 * Do not delete export for network filesystem by
2428 	 * passing "export" arg to nmount().
2429 	 * It only makes sense to do this for local filesystems.
2430 	 */
2431 	if (vfc.vfc_flags & VFCF_NETWORK)
2432 		return;
2433 
2434 	iov[1].iov_base = fsp->f_fstypename;
2435 	iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
2436 	iov[3].iov_base = fsp->f_mntonname;
2437 	iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
2438 	iov[5].iov_base = fsp->f_mntfromname;
2439 	iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
2440 	errmsg[0] = '\0';
2441 
2442 	/*
2443 	 * EXDEV is returned when path exists but is not a
2444 	 * mount point.  May happens if raced with unmount.
2445 	 */
2446 	if (nmount(iov, iovlen, fsp->f_flags) < 0 && errno != ENOENT &&
2447 	    errno != ENOTSUP && errno != EXDEV) {
2448 		syslog(LOG_ERR,
2449 		    "can't delete exports for %s: %m %s",
2450 		    fsp->f_mntonname, errmsg);
2451 	}
2452 }
2453 
2454 /*
2455  * Allocate an export list element
2456  */
2457 static struct exportlist *
get_exp(void)2458 get_exp(void)
2459 {
2460 	struct exportlist *ep;
2461 
2462 	ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
2463 	if (ep == (struct exportlist *)NULL)
2464 		out_of_mem();
2465 	return (ep);
2466 }
2467 
2468 /*
2469  * Allocate a group list element
2470  */
2471 static struct grouplist *
get_grp(void)2472 get_grp(void)
2473 {
2474 	struct grouplist *gp;
2475 
2476 	gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
2477 	if (gp == (struct grouplist *)NULL)
2478 		out_of_mem();
2479 	return (gp);
2480 }
2481 
2482 /*
2483  * Clean up upon an error in get_exportlist().
2484  */
2485 static void
getexp_err(struct exportlist * ep,struct grouplist * grp,const char * reason)2486 getexp_err(struct exportlist *ep, struct grouplist *grp, const char *reason)
2487 {
2488 	struct grouplist *tgrp;
2489 
2490 	if (!(opt_flags & OP_QUIET)) {
2491 		if (reason != NULL)
2492 			syslog(LOG_ERR, "bad exports list line '%s': %s", line,
2493 			    reason);
2494 		else
2495 			syslog(LOG_ERR, "bad exports list line '%s'", line);
2496 	}
2497 	if (ep && (ep->ex_flag & EX_LINKED) == 0)
2498 		free_exp(ep);
2499 	while (grp) {
2500 		tgrp = grp;
2501 		grp = grp->gr_next;
2502 		free_grp(tgrp);
2503 	}
2504 }
2505 
2506 /*
2507  * Search the export list for a matching fs.
2508  */
2509 static struct exportlist *
ex_search(fsid_t * fsid,struct exportlisthead * exhp)2510 ex_search(fsid_t *fsid, struct exportlisthead *exhp)
2511 {
2512 	struct exportlist *ep;
2513 	uint32_t i;
2514 
2515 	i = EXPHASH(fsid);
2516 	SLIST_FOREACH(ep, &exhp[i], entries) {
2517 		if (fsidcmp(&ep->ex_fs, fsid) == 0)
2518 			return (ep);
2519 	}
2520 
2521 	return (ep);
2522 }
2523 
2524 /*
2525  * Add a directory path to the list.
2526  */
2527 static char *
add_expdir(struct dirlist ** dpp,char * cp,int len)2528 add_expdir(struct dirlist **dpp, char *cp, int len)
2529 {
2530 	struct dirlist *dp;
2531 
2532 	dp = malloc(sizeof (struct dirlist));
2533 	if (dp == (struct dirlist *)NULL)
2534 		out_of_mem();
2535 	dp->dp_left = *dpp;
2536 	dp->dp_right = (struct dirlist *)NULL;
2537 	dp->dp_flag = 0;
2538 	dp->dp_hosts = (struct hostlist *)NULL;
2539 	dp->dp_dirp = strndup(cp, len);
2540 	if (dp->dp_dirp == NULL)
2541 		out_of_mem();
2542 	*dpp = dp;
2543 	return (dp->dp_dirp);
2544 }
2545 
2546 /*
2547  * Hang the dir list element off the dirpath binary tree as required
2548  * and update the entry for host.
2549  */
2550 static void
hang_dirp(struct dirlist * dp,struct grouplist * grp,struct exportlist * ep,int flags,struct expcred * anoncrp,uint64_t exflags)2551 hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
2552 	int flags, struct expcred *anoncrp, uint64_t exflags)
2553 {
2554 	struct hostlist *hp;
2555 	struct dirlist *dp2;
2556 
2557 	if (flags & OP_ALLDIRS) {
2558 		if (ep->ex_defdir)
2559 			free((caddr_t)dp);
2560 		else
2561 			ep->ex_defdir = dp;
2562 		if (grp == (struct grouplist *)NULL) {
2563 			ep->ex_flag |= EX_DEFSET;
2564 			ep->ex_defdir->dp_flag |= DP_DEFSET;
2565 			/* Save the default security flavors list. */
2566 			ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2567 			if (ep->ex_numsecflavors > 0)
2568 				memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2569 				    sizeof(ep->ex_secflavors));
2570 			cp_cred(&ep->ex_defanon, anoncrp);
2571 			ep->ex_defexflags = exflags;
2572 		} else while (grp) {
2573 			hp = get_ht();
2574 			hp->ht_grp = grp;
2575 			hp->ht_next = ep->ex_defdir->dp_hosts;
2576 			ep->ex_defdir->dp_hosts = hp;
2577 			/* Save the security flavors list for this host set. */
2578 			grp->gr_numsecflavors = ep->ex_numsecflavors;
2579 			if (ep->ex_numsecflavors > 0)
2580 				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2581 				    sizeof(ep->ex_secflavors));
2582 			grp = grp->gr_next;
2583 		}
2584 	} else {
2585 
2586 		/*
2587 		 * Loop through the directories adding them to the tree.
2588 		 */
2589 		while (dp) {
2590 			dp2 = dp->dp_left;
2591 			add_dlist(&ep->ex_dirl, dp, grp, flags, ep, anoncrp,
2592 			    exflags);
2593 			dp = dp2;
2594 		}
2595 	}
2596 }
2597 
2598 /*
2599  * Traverse the binary tree either updating a node that is already there
2600  * for the new directory or adding the new node.
2601  */
2602 static void
add_dlist(struct dirlist ** dpp,struct dirlist * newdp,struct grouplist * grp,int flags,struct exportlist * ep,struct expcred * anoncrp,uint64_t exflags)2603 add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
2604 	int flags, struct exportlist *ep, struct expcred *anoncrp,
2605 	uint64_t exflags)
2606 {
2607 	struct dirlist *dp;
2608 	struct hostlist *hp;
2609 	int cmp;
2610 
2611 	dp = *dpp;
2612 	if (dp) {
2613 		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
2614 		if (cmp > 0) {
2615 			add_dlist(&dp->dp_left, newdp, grp, flags, ep, anoncrp,
2616 			    exflags);
2617 			return;
2618 		} else if (cmp < 0) {
2619 			add_dlist(&dp->dp_right, newdp, grp, flags, ep, anoncrp,
2620 			    exflags);
2621 			return;
2622 		} else
2623 			free((caddr_t)newdp);
2624 	} else {
2625 		dp = newdp;
2626 		dp->dp_left = (struct dirlist *)NULL;
2627 		*dpp = dp;
2628 	}
2629 	if (grp) {
2630 
2631 		/*
2632 		 * Hang all of the host(s) off of the directory point.
2633 		 */
2634 		do {
2635 			hp = get_ht();
2636 			hp->ht_grp = grp;
2637 			hp->ht_next = dp->dp_hosts;
2638 			dp->dp_hosts = hp;
2639 			/* Save the security flavors list for this host set. */
2640 			grp->gr_numsecflavors = ep->ex_numsecflavors;
2641 			if (ep->ex_numsecflavors > 0)
2642 				memcpy(grp->gr_secflavors, ep->ex_secflavors,
2643 				    sizeof(ep->ex_secflavors));
2644 			grp = grp->gr_next;
2645 		} while (grp);
2646 	} else {
2647 		ep->ex_flag |= EX_DEFSET;
2648 		dp->dp_flag |= DP_DEFSET;
2649 		/* Save the default security flavors list. */
2650 		ep->ex_defnumsecflavors = ep->ex_numsecflavors;
2651 		if (ep->ex_numsecflavors > 0)
2652 			memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
2653 			    sizeof(ep->ex_secflavors));
2654 		cp_cred(&ep->ex_defanon, anoncrp);
2655 		ep->ex_defexflags = exflags;
2656 	}
2657 }
2658 
2659 /*
2660  * Search for a dirpath on the export point.
2661  */
2662 static struct dirlist *
dirp_search(struct dirlist * dp,char * dirp)2663 dirp_search(struct dirlist *dp, char *dirp)
2664 {
2665 	int cmp;
2666 
2667 	if (dp) {
2668 		cmp = strcmp(dp->dp_dirp, dirp);
2669 		if (cmp > 0)
2670 			return (dirp_search(dp->dp_left, dirp));
2671 		else if (cmp < 0)
2672 			return (dirp_search(dp->dp_right, dirp));
2673 		else
2674 			return (dp);
2675 	}
2676 	return (dp);
2677 }
2678 
2679 /*
2680  * Scan for a host match in a directory tree.
2681  */
2682 static int
chk_host(struct dirlist * dp,struct sockaddr * saddr,int * defsetp,int * hostsetp,int * numsecflavors,int ** secflavorsp)2683 chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2684 	int *hostsetp, int *numsecflavors, int **secflavorsp)
2685 {
2686 	struct hostlist *hp;
2687 	struct grouplist *grp;
2688 	struct addrinfo *ai;
2689 
2690 	if (dp) {
2691 		if (dp->dp_flag & DP_DEFSET)
2692 			*defsetp = dp->dp_flag;
2693 		hp = dp->dp_hosts;
2694 		while (hp) {
2695 			grp = hp->ht_grp;
2696 			switch (grp->gr_type) {
2697 			case GT_HOST:
2698 				ai = grp->gr_ptr.gt_addrinfo;
2699 				for (; ai; ai = ai->ai_next) {
2700 					if (!sacmp(ai->ai_addr, saddr, NULL)) {
2701 						*hostsetp =
2702 						    (hp->ht_flag | DP_HOSTSET);
2703 						if (numsecflavors != NULL) {
2704 							*numsecflavors =
2705 							    grp->gr_numsecflavors;
2706 							*secflavorsp =
2707 							    grp->gr_secflavors;
2708 						}
2709 						return (1);
2710 					}
2711 				}
2712 				break;
2713 			case GT_NET:
2714 				if (!sacmp(saddr, (struct sockaddr *)
2715 				    &grp->gr_ptr.gt_net.nt_net,
2716 				    (struct sockaddr *)
2717 				    &grp->gr_ptr.gt_net.nt_mask)) {
2718 					*hostsetp = (hp->ht_flag | DP_HOSTSET);
2719 					if (numsecflavors != NULL) {
2720 						*numsecflavors =
2721 						    grp->gr_numsecflavors;
2722 						*secflavorsp =
2723 						    grp->gr_secflavors;
2724 					}
2725 					return (1);
2726 				}
2727 				break;
2728 			}
2729 			hp = hp->ht_next;
2730 		}
2731 	}
2732 	return (0);
2733 }
2734 
2735 /*
2736  * Scan tree for a host that matches the address.
2737  */
2738 static int
scan_tree(struct dirlist * dp,struct sockaddr * saddr)2739 scan_tree(struct dirlist *dp, struct sockaddr *saddr)
2740 {
2741 	int defset, hostset;
2742 
2743 	if (dp) {
2744 		if (scan_tree(dp->dp_left, saddr))
2745 			return (1);
2746 		if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
2747 			return (1);
2748 		if (scan_tree(dp->dp_right, saddr))
2749 			return (1);
2750 	}
2751 	return (0);
2752 }
2753 
2754 /*
2755  * Traverse the dirlist tree and free it up.
2756  */
2757 static void
free_dir(struct dirlist * dp)2758 free_dir(struct dirlist *dp)
2759 {
2760 
2761 	if (dp) {
2762 		free_dir(dp->dp_left);
2763 		free_dir(dp->dp_right);
2764 		free_host(dp->dp_hosts);
2765 		free(dp->dp_dirp);
2766 		free(dp);
2767 	}
2768 }
2769 
2770 /*
2771  * Parse a colon separated list of security flavors
2772  */
2773 static int
parsesec(char * seclist,struct exportlist * ep)2774 parsesec(char *seclist, struct exportlist *ep)
2775 {
2776 	char *cp, savedc;
2777 	int flavor;
2778 
2779 	ep->ex_numsecflavors = 0;
2780 	for (;;) {
2781 		cp = strchr(seclist, ':');
2782 		if (cp) {
2783 			savedc = *cp;
2784 			*cp = '\0';
2785 		}
2786 
2787 		if (!strcmp(seclist, "sys"))
2788 			flavor = AUTH_SYS;
2789 		else if (!strcmp(seclist, "krb5"))
2790 			flavor = RPCSEC_GSS_KRB5;
2791 		else if (!strcmp(seclist, "krb5i"))
2792 			flavor = RPCSEC_GSS_KRB5I;
2793 		else if (!strcmp(seclist, "krb5p"))
2794 			flavor = RPCSEC_GSS_KRB5P;
2795 		else {
2796 			if (cp)
2797 				*cp = savedc;
2798 			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2799 			return (1);
2800 		}
2801 		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2802 			if (cp)
2803 				*cp = savedc;
2804 			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2805 			return (1);
2806 		}
2807 		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2808 		ep->ex_numsecflavors++;
2809 		if (cp) {
2810 			*cp = savedc;
2811 			seclist = cp + 1;
2812 		} else {
2813 			break;
2814 		}
2815 	}
2816 	return (0);
2817 }
2818 
2819 /*
2820  * Parse the option string and update fields.
2821  * Option arguments may either be -<option>=<value> or
2822  * -<option> <value>
2823  */
2824 static int
do_opt(char ** cpp,char ** endcpp,struct exportlist * ep,struct grouplist * grp,int * has_hostp,uint64_t * exflagsp,struct expcred * cr)2825 do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2826 	int *has_hostp, uint64_t *exflagsp, struct expcred *cr)
2827 {
2828 	char *cpoptarg, *cpoptend;
2829 	char *cp, *endcp, *cpopt, savedc, savedc2;
2830 	int allflag, usedarg, fnd_equal;
2831 
2832 	savedc2 = '\0';
2833 	cpopt = *cpp;
2834 	cpopt++;
2835 	cp = *endcpp;
2836 	savedc = *cp;
2837 	*cp = '\0';
2838 	while (cpopt && *cpopt) {
2839 		allflag = 1;
2840 		usedarg = -2;
2841 		fnd_equal = 0;
2842 		if ((cpoptend = strchr(cpopt, ','))) {
2843 			*cpoptend++ = '\0';
2844 			if ((cpoptarg = strchr(cpopt, '='))) {
2845 				*cpoptarg++ = '\0';
2846 				fnd_equal = 1;
2847 			}
2848 		} else {
2849 			if ((cpoptarg = strchr(cpopt, '='))) {
2850 				*cpoptarg++ = '\0';
2851 				fnd_equal = 1;
2852 			} else {
2853 				*cp = savedc;
2854 				nextfield(&cp, &endcp);
2855 				**endcpp = '\0';
2856 				if (endcp > cp && *cp != '-') {
2857 					cpoptarg = cp;
2858 					savedc2 = *endcp;
2859 					*endcp = '\0';
2860 					usedarg = 0;
2861 				}
2862 			}
2863 		}
2864 		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
2865 			if (fnd_equal == 1) {
2866 				syslog(LOG_ERR, "= after op: %s", cpopt);
2867 				return (1);
2868 			}
2869 			*exflagsp |= MNT_EXRDONLY;
2870 		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
2871 		    !(allflag = strcmp(cpopt, "mapall")) ||
2872 		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
2873 			usedarg++;
2874 			parsecred(cpoptarg, cr);
2875 			if (allflag == 0) {
2876 				*exflagsp |= MNT_EXPORTANON;
2877 				opt_flags |= OP_MAPALL;
2878 			} else
2879 				opt_flags |= OP_MAPROOT;
2880 		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
2881 		    !strcmp(cpopt, "m"))) {
2882 			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
2883 				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
2884 				return (1);
2885 			}
2886 			usedarg++;
2887 			opt_flags |= OP_MASK;
2888 		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
2889 			!strcmp(cpopt, "n"))) {
2890 			if (strchr(cpoptarg, '/') != NULL) {
2891 				if (debug)
2892 					fprintf(stderr, "setting OP_MASKLEN\n");
2893 				opt_flags |= OP_MASKLEN;
2894 			}
2895 			if (grp->gr_type != GT_NULL) {
2896 				syslog(LOG_ERR, "network/host conflict");
2897 				return (1);
2898 			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
2899 				syslog(LOG_ERR, "bad net: %s", cpoptarg);
2900 				return (1);
2901 			}
2902 			grp->gr_type = GT_NET;
2903 			*has_hostp = 1;
2904 			usedarg++;
2905 			opt_flags |= OP_NET;
2906 		} else if (!strcmp(cpopt, "alldirs")) {
2907 			if (fnd_equal == 1) {
2908 				syslog(LOG_ERR, "= after op: %s", cpopt);
2909 				return (1);
2910 			}
2911 			opt_flags |= OP_ALLDIRS;
2912 		} else if (!strcmp(cpopt, "public")) {
2913 			if (fnd_equal == 1) {
2914 				syslog(LOG_ERR, "= after op: %s", cpopt);
2915 				return (1);
2916 			}
2917 			*exflagsp |= MNT_EXPUBLIC;
2918 		} else if (!strcmp(cpopt, "webnfs")) {
2919 			if (fnd_equal == 1) {
2920 				syslog(LOG_ERR, "= after op: %s", cpopt);
2921 				return (1);
2922 			}
2923 			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
2924 			opt_flags |= OP_MAPALL;
2925 		} else if (cpoptarg && !strcmp(cpopt, "index")) {
2926 			ep->ex_indexfile = strdup(cpoptarg);
2927 		} else if (!strcmp(cpopt, "quiet")) {
2928 			if (fnd_equal == 1) {
2929 				syslog(LOG_ERR, "= after op: %s", cpopt);
2930 				return (1);
2931 			}
2932 			opt_flags |= OP_QUIET;
2933 		} else if (cpoptarg && !strcmp(cpopt, "sec")) {
2934 			if (parsesec(cpoptarg, ep))
2935 				return (1);
2936 			opt_flags |= OP_SEC;
2937 			usedarg++;
2938 		} else if (!strcmp(cpopt, "tls")) {
2939 			if (fnd_equal == 1) {
2940 				syslog(LOG_ERR, "= after op: %s", cpopt);
2941 				return (1);
2942 			}
2943 			*exflagsp |= MNT_EXTLS;
2944 		} else if (!strcmp(cpopt, "tlscert")) {
2945 			if (fnd_equal == 1) {
2946 				syslog(LOG_ERR, "= after op: %s", cpopt);
2947 				return (1);
2948 			}
2949 			*exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT);
2950 		} else if (!strcmp(cpopt, "tlscertuser")) {
2951 			if (fnd_equal == 1) {
2952 				syslog(LOG_ERR, "= after op: %s", cpopt);
2953 				return (1);
2954 			}
2955 			*exflagsp |= (MNT_EXTLS | MNT_EXTLSCERT |
2956 			    MNT_EXTLSCERTUSER);
2957 		} else {
2958 			syslog(LOG_ERR, "bad opt %s", cpopt);
2959 			return (1);
2960 		}
2961 		if (usedarg >= 0) {
2962 			*endcp = savedc2;
2963 			**endcpp = savedc;
2964 			if (usedarg > 0) {
2965 				*cpp = cp;
2966 				*endcpp = endcp;
2967 			}
2968 			return (0);
2969 		}
2970 		cpopt = cpoptend;
2971 	}
2972 	**endcpp = savedc;
2973 	return (0);
2974 }
2975 
2976 /*
2977  * Translate a character string to the corresponding list of network
2978  * addresses for a hostname.
2979  */
2980 static int
get_host(char * cp,struct grouplist * grp,struct grouplist * tgrp)2981 get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
2982 {
2983 	struct grouplist *checkgrp;
2984 	struct addrinfo *ai, *tai, hints;
2985 	int ecode;
2986 	char host[NI_MAXHOST];
2987 
2988 	if (grp->gr_type != GT_NULL) {
2989 		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
2990 		return (1);
2991 	}
2992 	memset(&hints, 0, sizeof hints);
2993 	hints.ai_flags = AI_CANONNAME;
2994 	hints.ai_protocol = IPPROTO_UDP;
2995 	ecode = getaddrinfo(cp, NULL, &hints, &ai);
2996 	if (ecode != 0) {
2997 		syslog(LOG_ERR,"can't get address info for host %s", cp);
2998 		return 1;
2999 	}
3000 	grp->gr_ptr.gt_addrinfo = ai;
3001 	while (ai != NULL) {
3002 		if (ai->ai_canonname == NULL) {
3003 			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
3004 			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
3005 				strlcpy(host, "?", sizeof(host));
3006 			ai->ai_canonname = strdup(host);
3007 			ai->ai_flags |= AI_CANONNAME;
3008 		}
3009 		if (debug)
3010 			fprintf(stderr, "got host %s\n", ai->ai_canonname);
3011 		/*
3012 		 * Sanity check: make sure we don't already have an entry
3013 		 * for this host in the grouplist.
3014 		 */
3015 		for (checkgrp = tgrp; checkgrp != NULL;
3016 		    checkgrp = checkgrp->gr_next) {
3017 			if (checkgrp->gr_type != GT_HOST)
3018 				continue;
3019 			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
3020 			    tai = tai->ai_next) {
3021 				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
3022 					continue;
3023 				if (debug)
3024 					fprintf(stderr,
3025 					    "ignoring duplicate host %s\n",
3026 					    ai->ai_canonname);
3027 				grp->gr_type = GT_IGNORE;
3028 				return (0);
3029 			}
3030 		}
3031 		ai = ai->ai_next;
3032 	}
3033 	grp->gr_type = GT_HOST;
3034 	return (0);
3035 }
3036 
3037 /*
3038  * Free up an exports list component
3039  */
3040 static void
free_exp(struct exportlist * ep)3041 free_exp(struct exportlist *ep)
3042 {
3043 	struct grouplist *grp, *tgrp;
3044 
3045 	if (ep->ex_defdir) {
3046 		free_host(ep->ex_defdir->dp_hosts);
3047 		free((caddr_t)ep->ex_defdir);
3048 	}
3049 	if (ep->ex_fsdir)
3050 		free(ep->ex_fsdir);
3051 	if (ep->ex_indexfile)
3052 		free(ep->ex_indexfile);
3053 	free_dir(ep->ex_dirl);
3054 	grp = ep->ex_grphead;
3055 	while (grp) {
3056 		tgrp = grp;
3057 		grp = grp->gr_next;
3058 		free_grp(tgrp);
3059 	}
3060 	if (ep->ex_defanon.cr_groups != ep->ex_defanon.cr_smallgrps)
3061 		free(ep->ex_defanon.cr_groups);
3062 	free((caddr_t)ep);
3063 }
3064 
3065 /*
3066  * Free up the v4root exports.
3067  */
3068 static void
free_v4rootexp(void)3069 free_v4rootexp(void)
3070 {
3071 
3072 	if (v4root_ep != NULL) {
3073 		free_exp(v4root_ep);
3074 		v4root_ep = NULL;
3075 	}
3076 }
3077 
3078 /*
3079  * Free hosts.
3080  */
3081 static void
free_host(struct hostlist * hp)3082 free_host(struct hostlist *hp)
3083 {
3084 	struct hostlist *hp2;
3085 
3086 	while (hp) {
3087 		hp2 = hp;
3088 		hp = hp->ht_next;
3089 		free((caddr_t)hp2);
3090 	}
3091 }
3092 
3093 static struct hostlist *
get_ht(void)3094 get_ht(void)
3095 {
3096 	struct hostlist *hp;
3097 
3098 	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
3099 	if (hp == (struct hostlist *)NULL)
3100 		out_of_mem();
3101 	hp->ht_next = (struct hostlist *)NULL;
3102 	hp->ht_flag = 0;
3103 	return (hp);
3104 }
3105 
3106 /*
3107  * Out of memory, fatal
3108  */
3109 static void
out_of_mem(void)3110 out_of_mem(void)
3111 {
3112 
3113 	syslog(LOG_ERR, "out of memory");
3114 	exit(2);
3115 }
3116 
3117 /*
3118  * Call do_mount() from the struct exportlist, for each case needed.
3119  */
3120 static int
do_export_mount(struct exportlist * ep,struct statfs * fsp)3121 do_export_mount(struct exportlist *ep, struct statfs *fsp)
3122 {
3123 	struct grouplist *grp, defgrp;
3124 	int ret;
3125 	size_t dirlen;
3126 
3127 	LOGDEBUG("do_export_mount=%s", ep->ex_fsdir);
3128 	dirlen = strlen(ep->ex_fsdir);
3129 	if ((ep->ex_flag & EX_DEFSET) != 0) {
3130 		defgrp.gr_type = GT_DEFAULT;
3131 		defgrp.gr_next = NULL;
3132 		/* We have an entry for all other hosts/nets. */
3133 		LOGDEBUG("ex_defexflags=0x%jx", (uintmax_t)ep->ex_defexflags);
3134 		ret = do_mount(ep, &defgrp, ep->ex_defexflags, &ep->ex_defanon,
3135 		    ep->ex_fsdir, dirlen, fsp, ep->ex_defnumsecflavors,
3136 		    ep->ex_defsecflavors);
3137 		if (ret != 0)
3138 			return (ret);
3139 	}
3140 
3141 	/* Do a mount for each group. */
3142 	grp = ep->ex_grphead;
3143 	while (grp != NULL) {
3144 		LOGDEBUG("do mount gr_type=0x%x gr_exflags=0x%jx",
3145 		    grp->gr_type, (uintmax_t)grp->gr_exflags);
3146 		ret = do_mount(ep, grp, grp->gr_exflags, &grp->gr_anon,
3147 		    ep->ex_fsdir, dirlen, fsp, grp->gr_numsecflavors,
3148 		    grp->gr_secflavors);
3149 		if (ret != 0)
3150 			return (ret);
3151 		grp = grp->gr_next;
3152 	}
3153 	return (0);
3154 }
3155 
3156 /*
3157  * Do the nmount() syscall with the update flag to push the export info into
3158  * the kernel.
3159  */
3160 static int
do_mount(struct exportlist * ep,struct grouplist * grp,uint64_t exflags,struct expcred * anoncrp,char * dirp,int dirplen,struct statfs * fsb,int numsecflavors,int * secflavors)3161 do_mount(struct exportlist *ep, struct grouplist *grp, uint64_t exflags,
3162     struct expcred *anoncrp, char *dirp, int dirplen, struct statfs *fsb,
3163     int numsecflavors, int *secflavors)
3164 {
3165 	struct statfs fsb1;
3166 	struct addrinfo *ai;
3167 	struct export_args *eap;
3168 	char errmsg[255];
3169 	char *cp;
3170 	int done;
3171 	char savedc;
3172 	struct iovec *iov;
3173 	int i, iovlen;
3174 	int ret;
3175 	struct nfsex_args nfsea;
3176 
3177 	eap = &nfsea.export;
3178 
3179 	cp = NULL;
3180 	savedc = '\0';
3181 	iov = NULL;
3182 	iovlen = 0;
3183 	ret = 0;
3184 
3185 	bzero(eap, sizeof (struct export_args));
3186 	bzero(errmsg, sizeof(errmsg));
3187 	eap->ex_flags = exflags;
3188 	eap->ex_uid = anoncrp->cr_uid;
3189 	eap->ex_ngroups = anoncrp->cr_ngroups;
3190 	if (eap->ex_ngroups > 0) {
3191 		eap->ex_groups = malloc(eap->ex_ngroups * sizeof(gid_t));
3192 		memcpy(eap->ex_groups, anoncrp->cr_groups, eap->ex_ngroups *
3193 		    sizeof(gid_t));
3194 	}
3195 	LOGDEBUG("do_mount exflags=0x%jx", (uintmax_t)exflags);
3196 	eap->ex_indexfile = ep->ex_indexfile;
3197 	if (grp->gr_type == GT_HOST)
3198 		ai = grp->gr_ptr.gt_addrinfo;
3199 	else
3200 		ai = NULL;
3201 	eap->ex_numsecflavors = numsecflavors;
3202 	LOGDEBUG("do_mount numsec=%d", numsecflavors);
3203 	for (i = 0; i < eap->ex_numsecflavors; i++)
3204 		eap->ex_secflavors[i] = secflavors[i];
3205 	if (eap->ex_numsecflavors == 0) {
3206 		eap->ex_numsecflavors = 1;
3207 		eap->ex_secflavors[0] = AUTH_SYS;
3208 	}
3209 	done = FALSE;
3210 
3211 	if (v4root_phase == 0) {
3212 		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
3213 		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
3214 		build_iovec(&iov, &iovlen, "from", NULL, 0);
3215 		build_iovec(&iov, &iovlen, "update", NULL, 0);
3216 		build_iovec(&iov, &iovlen, "export", eap,
3217 		    sizeof (struct export_args));
3218 		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
3219 	}
3220 
3221 	while (!done) {
3222 		switch (grp->gr_type) {
3223 		case GT_HOST:
3224 			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
3225 				goto skip;
3226 			eap->ex_addr = ai->ai_addr;
3227 			eap->ex_addrlen = ai->ai_addrlen;
3228 			eap->ex_masklen = 0;
3229 			break;
3230 		case GT_NET:
3231 			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
3232 			    have_v6 == 0)
3233 				goto skip;
3234 			eap->ex_addr =
3235 			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
3236 			eap->ex_addrlen =
3237 			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
3238 			eap->ex_mask =
3239 			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
3240 			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
3241 			break;
3242 		case GT_DEFAULT:
3243 			eap->ex_addr = NULL;
3244 			eap->ex_addrlen = 0;
3245 			eap->ex_mask = NULL;
3246 			eap->ex_masklen = 0;
3247 			break;
3248 		case GT_IGNORE:
3249 			ret = 0;
3250 			goto error_exit;
3251 			break;
3252 		default:
3253 			syslog(LOG_ERR, "bad grouptype");
3254 			if (cp)
3255 				*cp = savedc;
3256 			ret = 1;
3257 			goto error_exit;
3258 		}
3259 
3260 		/*
3261 		 * For V4:, use the nfssvc() syscall, instead of mount().
3262 		 */
3263 		if (v4root_phase == 2) {
3264 			nfsea.fspec = v4root_dirpath;
3265 			if (nfssvc(NFSSVC_V4ROOTEXPORT | NFSSVC_NEWSTRUCT,
3266 			    (caddr_t)&nfsea) < 0) {
3267 				syslog(LOG_ERR, "Exporting V4: failed");
3268 				ret = 2;
3269 				goto error_exit;
3270 			}
3271 		} else {
3272 			/*
3273 			 * XXX:
3274 			 * Maybe I should just use the fsb->f_mntonname path
3275 			 * instead of looping back up the dirp to the mount
3276 			 * point??
3277 			 * Also, needs to know how to export all types of local
3278 			 * exportable filesystems and not just "ufs".
3279 			 */
3280 			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
3281 			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
3282 			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
3283 			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
3284 			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
3285 			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
3286 			errmsg[0] = '\0';
3287 
3288 			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
3289 				if (cp)
3290 					*cp-- = savedc;
3291 				else
3292 					cp = dirp + dirplen - 1;
3293 				if (opt_flags & OP_QUIET) {
3294 					ret = 1;
3295 					goto error_exit;
3296 				}
3297 				if (errno == EPERM) {
3298 					if (debug)
3299 						warnx("can't change attributes for %s: %s",
3300 						    dirp, errmsg);
3301 					syslog(LOG_ERR,
3302 					   "can't change attributes for %s: %s",
3303 					    dirp, errmsg);
3304 					ret = 1;
3305 					goto error_exit;
3306 				}
3307 				if (opt_flags & OP_ALLDIRS) {
3308 					if (errno == EINVAL)
3309 						syslog(LOG_ERR,
3310 		"-alldirs requested but %s is not a filesystem mountpoint",
3311 						    dirp);
3312 					else
3313 						syslog(LOG_ERR,
3314 						    "could not remount %s: %m",
3315 						    dirp);
3316 					ret = 1;
3317 					goto error_exit;
3318 				}
3319 				/* back up over the last component */
3320 				while (cp > dirp && *cp == '/')
3321 					cp--;
3322 				while (cp > dirp && *(cp - 1) != '/')
3323 					cp--;
3324 				if (cp == dirp) {
3325 					if (debug)
3326 						warnx("mnt unsucc");
3327 					syslog(LOG_ERR, "can't export %s %s",
3328 					    dirp, errmsg);
3329 					ret = 1;
3330 					goto error_exit;
3331 				}
3332 				savedc = *cp;
3333 				*cp = '\0';
3334 				/*
3335 				 * Check that we're still on the same
3336 				 * filesystem.
3337 				 */
3338 				if (statfs(dirp, &fsb1) != 0 ||
3339 				    fsidcmp(&fsb1.f_fsid, &fsb->f_fsid) != 0) {
3340 					*cp = savedc;
3341 					syslog(LOG_ERR,
3342 					    "can't export %s %s", dirp,
3343 					    errmsg);
3344 					ret = 1;
3345 					goto error_exit;
3346 				}
3347 			}
3348 		}
3349 
3350 		/*
3351 		 * For the experimental server:
3352 		 * If this is the public directory, get the file handle
3353 		 * and load it into the kernel via the nfssvc() syscall.
3354 		 */
3355 		if ((exflags & MNT_EXPUBLIC) != 0) {
3356 			fhandle_t fh;
3357 			char *public_name;
3358 
3359 			if (eap->ex_indexfile != NULL)
3360 				public_name = eap->ex_indexfile;
3361 			else
3362 				public_name = dirp;
3363 			if (getfh(public_name, &fh) < 0)
3364 				syslog(LOG_ERR,
3365 				    "Can't get public fh for %s", public_name);
3366 			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
3367 				syslog(LOG_ERR,
3368 				    "Can't set public fh for %s", public_name);
3369 			else {
3370 				has_publicfh = 1;
3371 				has_set_publicfh = 1;
3372 				ep->ex_flag |= EX_PUBLICFH;
3373 			}
3374 		}
3375 skip:
3376 		if (ai != NULL)
3377 			ai = ai->ai_next;
3378 		if (ai == NULL)
3379 			done = TRUE;
3380 	}
3381 	if (cp)
3382 		*cp = savedc;
3383 error_exit:
3384 	free(eap->ex_groups);
3385 	/* free strings allocated by strdup() in getmntopts.c */
3386 	if (iov != NULL) {
3387 		free(iov[0].iov_base); /* fstype */
3388 		free(iov[2].iov_base); /* fspath */
3389 		free(iov[4].iov_base); /* from */
3390 		free(iov[6].iov_base); /* update */
3391 		free(iov[8].iov_base); /* export */
3392 		free(iov[10].iov_base); /* errmsg */
3393 
3394 		/* free iov, allocated by realloc() */
3395 		free(iov);
3396 	}
3397 	return (ret);
3398 }
3399 
3400 /*
3401  * Translate a net address.
3402  *
3403  * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
3404  */
3405 static int
get_net(char * cp,struct netmsk * net,int maskflg)3406 get_net(char *cp, struct netmsk *net, int maskflg)
3407 {
3408 	struct netent *np = NULL;
3409 	char *name, *p, *prefp;
3410 	struct sockaddr_in sin;
3411 	struct sockaddr *sa = NULL;
3412 	struct addrinfo hints, *ai = NULL;
3413 	char netname[NI_MAXHOST];
3414 	long preflen;
3415 
3416 	p = prefp = NULL;
3417 	if ((opt_flags & OP_MASKLEN) && !maskflg) {
3418 		p = strchr(cp, '/');
3419 		*p = '\0';
3420 		prefp = p + 1;
3421 	}
3422 
3423 	/*
3424 	 * Check for a numeric address first. We wish to avoid
3425 	 * possible DNS lookups in getnetbyname().
3426 	 */
3427 	if (isxdigit(*cp) || *cp == ':') {
3428 		memset(&hints, 0, sizeof hints);
3429 		/* Ensure the mask and the network have the same family. */
3430 		if (maskflg && (opt_flags & OP_NET))
3431 			hints.ai_family = net->nt_net.ss_family;
3432 		else if (!maskflg && (opt_flags & OP_HAVEMASK))
3433 			hints.ai_family = net->nt_mask.ss_family;
3434 		else
3435 			hints.ai_family = AF_UNSPEC;
3436 		hints.ai_flags = AI_NUMERICHOST;
3437 		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
3438 			sa = ai->ai_addr;
3439 		if (sa != NULL && ai->ai_family == AF_INET) {
3440 			/*
3441 			 * The address in `cp' is really a network address, so
3442 			 * use inet_network() to re-interpret this correctly.
3443 			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
3444 			 */
3445 			bzero(&sin, sizeof sin);
3446 			sin.sin_family = AF_INET;
3447 			sin.sin_len = sizeof sin;
3448 			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
3449 			if (debug)
3450 				fprintf(stderr, "get_net: v4 addr %s\n",
3451 				    inet_ntoa(sin.sin_addr));
3452 			sa = (struct sockaddr *)&sin;
3453 		}
3454 	}
3455 	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
3456 		bzero(&sin, sizeof sin);
3457 		sin.sin_family = AF_INET;
3458 		sin.sin_len = sizeof sin;
3459 		sin.sin_addr = inet_makeaddr(np->n_net, 0);
3460 		sa = (struct sockaddr *)&sin;
3461 	}
3462 	if (sa == NULL)
3463 		goto fail;
3464 
3465 	if (maskflg) {
3466 		/* The specified sockaddr is a mask. */
3467 		if (checkmask(sa) != 0)
3468 			goto fail;
3469 		bcopy(sa, &net->nt_mask, sa->sa_len);
3470 		opt_flags |= OP_HAVEMASK;
3471 		opt_flags &= ~OP_CLASSMASK;
3472 	} else {
3473 		/* The specified sockaddr is a network address. */
3474 		bcopy(sa, &net->nt_net, sa->sa_len);
3475 
3476 		/* Get a network name for the export list. */
3477 		if (np) {
3478 			name = np->n_name;
3479 		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
3480 		   NULL, 0, NI_NUMERICHOST) == 0) {
3481 			name = netname;
3482 		} else {
3483 			goto fail;
3484 		}
3485 		if ((net->nt_name = strdup(name)) == NULL)
3486 			out_of_mem();
3487 
3488 		/*
3489 		 * Extract a mask from either a "/<masklen>" suffix, or
3490 		 * from the class of an IPv4 address.
3491 		 */
3492 		if (opt_flags & OP_MASKLEN) {
3493 			preflen = strtol(prefp, NULL, 10);
3494 			if (preflen < 0L || preflen == LONG_MAX)
3495 				goto fail;
3496 			bcopy(sa, &net->nt_mask, sa->sa_len);
3497 			if (makemask(&net->nt_mask, (int)preflen) != 0)
3498 				goto fail;
3499 			opt_flags |= OP_HAVEMASK;
3500 			*p = '/';
3501 		} else if (sa->sa_family == AF_INET &&
3502 		    (opt_flags & OP_MASK) == 0) {
3503 			in_addr_t addr;
3504 
3505 			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
3506 			if (IN_CLASSA(addr))
3507 				preflen = 8;
3508 			else if (IN_CLASSB(addr))
3509 				preflen = 16;
3510 			else if (IN_CLASSC(addr))
3511 				preflen = 24;
3512 			else if (IN_CLASSD(addr))	/* XXX Multicast??? */
3513 				preflen = 28;
3514 			else
3515 				preflen = 32;	/* XXX */
3516 
3517 			bcopy(sa, &net->nt_mask, sa->sa_len);
3518 			makemask(&net->nt_mask, (int)preflen);
3519 			opt_flags |= OP_HAVEMASK | OP_CLASSMASK;
3520 		}
3521 	}
3522 
3523 	if (ai)
3524 		freeaddrinfo(ai);
3525 	return 0;
3526 
3527 fail:
3528 	if (ai)
3529 		freeaddrinfo(ai);
3530 	return 1;
3531 }
3532 
3533 /*
3534  * Parse out the next white space separated field
3535  */
3536 static void
nextfield(char ** cp,char ** endcp)3537 nextfield(char **cp, char **endcp)
3538 {
3539 	char *p;
3540 	char quot = 0;
3541 
3542 	p = *cp;
3543 	while (*p == ' ' || *p == '\t')
3544 		p++;
3545 	*cp = p;
3546 	while (*p != '\0') {
3547 		if (quot) {
3548 			if (*p == quot)
3549 				quot = 0;
3550 		} else {
3551 			if (*p == '\\' && *(p + 1) != '\0')
3552 				p++;
3553 			else if (*p == '\'' || *p == '"')
3554 				quot = *p;
3555 			else if (*p == ' ' || *p == '\t')
3556 				break;
3557 		}
3558 		p++;
3559 	};
3560 	*endcp = p;
3561 }
3562 
3563 /*
3564  * Get an exports file line. Skip over blank lines and handle line
3565  * continuations.
3566  */
3567 static int
get_line(void)3568 get_line(void)
3569 {
3570 	char *p, *cp;
3571 	size_t len;
3572 	int totlen, cont_line;
3573 
3574 	/*
3575 	 * Loop around ignoring blank lines and getting all continuation lines.
3576 	 */
3577 	p = line;
3578 	totlen = 0;
3579 	do {
3580 		if ((p = fgetln(exp_file, &len)) == NULL)
3581 			return (0);
3582 		cp = p + len - 1;
3583 		cont_line = 0;
3584 		while (cp >= p &&
3585 		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
3586 			if (*cp == '\\')
3587 				cont_line = 1;
3588 			cp--;
3589 			len--;
3590 		}
3591 		if (cont_line) {
3592 			*++cp = ' ';
3593 			len++;
3594 		}
3595 		if (linesize < len + totlen + 1) {
3596 			linesize = len + totlen + 1;
3597 			line = realloc(line, linesize);
3598 			if (line == NULL)
3599 				out_of_mem();
3600 		}
3601 		memcpy(line + totlen, p, len);
3602 		totlen += len;
3603 		line[totlen] = '\0';
3604 	} while (totlen == 0 || cont_line);
3605 	return (1);
3606 }
3607 
3608 /*
3609  * Parse a description of a credential.
3610  */
3611 static void
parsecred(char * namelist,struct expcred * cr)3612 parsecred(char *namelist, struct expcred *cr)
3613 {
3614 	char *name;
3615 	int inpos;
3616 	char *names;
3617 	struct passwd *pw;
3618 	struct group *gr;
3619 	gid_t groups[NGROUPS_MAX + 1];
3620 	int ngroups;
3621 	unsigned long name_ul;
3622 	char *end = NULL;
3623 
3624 	/*
3625 	 * Set up the unprivileged user.
3626 	 */
3627 	cr->cr_groups = cr->cr_smallgrps;
3628 	cr->cr_uid = UID_NOBODY;
3629 	cr->cr_groups[0] = GID_NOGROUP;
3630 	cr->cr_ngroups = 1;
3631 	/*
3632 	 * Get the user's password table entry.
3633 	 */
3634 	names = namelist;
3635 	name = strsep_quote(&names, ":");
3636 	/* Bug?  name could be NULL here */
3637 	name_ul = strtoul(name, &end, 10);
3638 	if (*end != '\0' || end == name)
3639 		pw = getpwnam(name);
3640 	else
3641 		pw = getpwuid((uid_t)name_ul);
3642 	/*
3643 	 * Credentials specified as those of a user.
3644 	 */
3645 	if (names == NULL) {
3646 		if (pw == NULL) {
3647 			syslog(LOG_ERR, "unknown user: %s", name);
3648 			return;
3649 		}
3650 		cr->cr_uid = pw->pw_uid;
3651 		ngroups = NGROUPS_MAX + 1;
3652 		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups)) {
3653 			syslog(LOG_ERR, "too many groups");
3654 			ngroups = NGROUPS_MAX + 1;
3655 		}
3656 
3657 		/*
3658 		 * Compress out duplicate.
3659 		 */
3660 		if (ngroups > 1 && groups[0] == groups[1]) {
3661 			ngroups--;
3662 			inpos = 2;
3663 		} else {
3664 			inpos = 1;
3665 		}
3666 		if (ngroups > NGROUPS_MAX)
3667 			ngroups = NGROUPS_MAX;
3668 		if (ngroups > SMALLNGROUPS)
3669 			cr->cr_groups = malloc(ngroups * sizeof(gid_t));
3670 		cr->cr_ngroups = ngroups;
3671 		cr->cr_groups[0] = groups[0];
3672 		memcpy(&cr->cr_groups[1], &groups[inpos], (ngroups - 1) *
3673 		    sizeof(gid_t));
3674 		return;
3675 	}
3676 	/*
3677 	 * Explicit credential specified as a colon separated list:
3678 	 *	uid:gid:gid:...
3679 	 */
3680 	if (pw != NULL) {
3681 		cr->cr_uid = pw->pw_uid;
3682 	} else if (*end != '\0' || end == name) {
3683 		syslog(LOG_ERR, "unknown user: %s", name);
3684 		return;
3685 	} else {
3686 		cr->cr_uid = name_ul;
3687 	}
3688 	cr->cr_ngroups = 0;
3689 	while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS_MAX) {
3690 		name = strsep_quote(&names, ":");
3691 		name_ul = strtoul(name, &end, 10);
3692 		if (*end != '\0' || end == name) {
3693 			if ((gr = getgrnam(name)) == NULL) {
3694 				syslog(LOG_ERR, "unknown group: %s", name);
3695 				continue;
3696 			}
3697 			groups[cr->cr_ngroups++] = gr->gr_gid;
3698 		} else {
3699 			groups[cr->cr_ngroups++] = name_ul;
3700 		}
3701 	}
3702 	if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS_MAX)
3703 		syslog(LOG_ERR, "too many groups");
3704 	if (cr->cr_ngroups > SMALLNGROUPS)
3705 		cr->cr_groups = malloc(cr->cr_ngroups * sizeof(gid_t));
3706 	memcpy(cr->cr_groups, groups, cr->cr_ngroups * sizeof(gid_t));
3707 }
3708 
3709 #define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
3710 /*
3711  * Routines that maintain the remote mounttab
3712  */
3713 static void
get_mountlist(void)3714 get_mountlist(void)
3715 {
3716 	struct mountlist *mlp;
3717 	char *host, *dirp, *cp;
3718 	char str[STRSIZ];
3719 	FILE *mlfile;
3720 
3721 	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
3722 		if (errno == ENOENT)
3723 			return;
3724 		else {
3725 			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
3726 			return;
3727 		}
3728 	}
3729 	while (fgets(str, STRSIZ, mlfile) != NULL) {
3730 		cp = str;
3731 		host = strsep(&cp, " \t\n");
3732 		dirp = strsep(&cp, " \t\n");
3733 		if (host == NULL || dirp == NULL)
3734 			continue;
3735 		mlp = (struct mountlist *)malloc(sizeof (*mlp));
3736 		if (mlp == (struct mountlist *)NULL)
3737 			out_of_mem();
3738 		strncpy(mlp->ml_host, host, MNTNAMLEN);
3739 		mlp->ml_host[MNTNAMLEN] = '\0';
3740 		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3741 		mlp->ml_dirp[MNTPATHLEN] = '\0';
3742 
3743 		SLIST_INSERT_HEAD(&mlhead, mlp, next);
3744 	}
3745 	fclose(mlfile);
3746 }
3747 
3748 static void
del_mlist(char * hostp,char * dirp)3749 del_mlist(char *hostp, char *dirp)
3750 {
3751 	struct mountlist *mlp, *mlp2;
3752 	FILE *mlfile;
3753 	int fnd = 0;
3754 
3755 	SLIST_FOREACH_SAFE(mlp, &mlhead, next, mlp2) {
3756 		if (!strcmp(mlp->ml_host, hostp) &&
3757 		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
3758 			fnd = 1;
3759 			SLIST_REMOVE(&mlhead, mlp, mountlist, next);
3760 			free((caddr_t)mlp);
3761 		}
3762 	}
3763 	if (fnd) {
3764 		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
3765 			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
3766 			return;
3767 		}
3768 		SLIST_FOREACH(mlp, &mlhead, next) {
3769 			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3770 		}
3771 		fclose(mlfile);
3772 	}
3773 }
3774 
3775 static void
add_mlist(char * hostp,char * dirp)3776 add_mlist(char *hostp, char *dirp)
3777 {
3778 	struct mountlist *mlp;
3779 	FILE *mlfile;
3780 
3781 	SLIST_FOREACH(mlp, &mlhead, next) {
3782 		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
3783 			return;
3784 	}
3785 
3786 	mlp = (struct mountlist *)malloc(sizeof (*mlp));
3787 	if (mlp == (struct mountlist *)NULL)
3788 		out_of_mem();
3789 	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
3790 	mlp->ml_host[MNTNAMLEN] = '\0';
3791 	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
3792 	mlp->ml_dirp[MNTPATHLEN] = '\0';
3793 	SLIST_INSERT_HEAD(&mlhead, mlp, next);
3794 	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
3795 		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
3796 		return;
3797 	}
3798 	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3799 	fclose(mlfile);
3800 }
3801 
3802 /*
3803  * Free up a group list.
3804  */
3805 static void
free_grp(struct grouplist * grp)3806 free_grp(struct grouplist *grp)
3807 {
3808 	if (grp->gr_type == GT_HOST) {
3809 		if (grp->gr_ptr.gt_addrinfo != NULL)
3810 			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
3811 	} else if (grp->gr_type == GT_NET) {
3812 		if (grp->gr_ptr.gt_net.nt_name)
3813 			free(grp->gr_ptr.gt_net.nt_name);
3814 	}
3815 	if (grp->gr_anon.cr_groups != grp->gr_anon.cr_smallgrps)
3816 		free(grp->gr_anon.cr_groups);
3817 	free((caddr_t)grp);
3818 }
3819 
3820 #ifdef DEBUG
3821 static void
SYSLOG(int pri,const char * fmt,...)3822 SYSLOG(int pri, const char *fmt, ...)
3823 {
3824 	va_list ap;
3825 
3826 	va_start(ap, fmt);
3827 	vfprintf(stderr, fmt, ap);
3828 	va_end(ap);
3829 }
3830 #endif /* DEBUG */
3831 
3832 /*
3833  * Check options for consistency.
3834  */
3835 static int
check_options(struct dirlist * dp)3836 check_options(struct dirlist *dp)
3837 {
3838 
3839 	if (v4root_phase == 0 && dp == NULL)
3840 	    return (1);
3841 	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
3842 	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
3843 	    return (1);
3844 	}
3845 	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
3846 		syslog(LOG_ERR, "-mask requires -network");
3847 		return (1);
3848 	}
3849 	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
3850 		syslog(LOG_ERR, "-network requires mask specification");
3851 		return (1);
3852 	}
3853 	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
3854 		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
3855 		return (1);
3856 	}
3857 	if (v4root_phase > 0 &&
3858 	    (opt_flags &
3859 	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3860 	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3861 	    return (1);
3862 	}
3863 	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3864 	    syslog(LOG_ERR, "-alldirs has multiple directories");
3865 	    return (1);
3866 	}
3867 	return (0);
3868 }
3869 
3870 static int
check_path_component(const char * path,char ** err)3871 check_path_component(const char *path, char **err)
3872 {
3873 	struct stat sb;
3874 
3875 	if (lstat(path, &sb)) {
3876 		asprintf(err, "%s: lstat() failed: %s.\n",
3877 		    path, strerror(errno));
3878 		return (0);
3879 	}
3880 
3881 	switch (sb.st_mode & S_IFMT) {
3882 	case S_IFDIR:
3883 		return (1);
3884 	case S_IFLNK:
3885 		asprintf(err, "%s: path is a symbolic link.\n", path);
3886 		break;
3887 	case S_IFREG:
3888 		asprintf(err, "%s: path is a file rather than a directory.\n",
3889 		    path);
3890 		break;
3891 	default:
3892 		asprintf(err, "%s: path is not a directory.\n", path);
3893 	}
3894 
3895 	return (0);
3896 }
3897 
3898 /*
3899  * Check each path component for the presence of symbolic links. Return true
3900  */
3901 static int
check_dirpath(char * dirp,char ** err)3902 check_dirpath(char *dirp, char **err)
3903 {
3904 	char *cp;
3905 
3906 	cp = dirp + 1;
3907 	while (*cp) {
3908 		if (*cp == '/') {
3909 			*cp = '\0';
3910 
3911 			if (!check_path_component(dirp, err)) {
3912 				*cp = '/';
3913 				return (0);
3914 			}
3915 
3916 			*cp = '/';
3917 		}
3918 		cp++;
3919 	}
3920 
3921 	if (!check_path_component(dirp, err))
3922 		return (0);
3923 
3924 	return (1);
3925 }
3926 
3927 /*
3928  * Populate statfs information. Return true on success.
3929  */
3930 static int
check_statfs(const char * dirp,struct statfs * fsb,char ** err)3931 check_statfs(const char *dirp, struct statfs *fsb, char **err)
3932 {
3933 	if (statfs(dirp, fsb)) {
3934 		asprintf(err, "%s: statfs() failed: %s\n", dirp,
3935 		    strerror(errno));
3936 		return (0);
3937 	}
3938 
3939 	return (1);
3940 }
3941 
3942 /*
3943  * Make a netmask according to the specified prefix length. The ss_family
3944  * and other non-address fields must be initialised before calling this.
3945  */
3946 static int
makemask(struct sockaddr_storage * ssp,int bitlen)3947 makemask(struct sockaddr_storage *ssp, int bitlen)
3948 {
3949 	u_char *p;
3950 	int bits, i, len;
3951 
3952 	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
3953 		return (-1);
3954 	if (bitlen > len * CHAR_BIT)
3955 		return (-1);
3956 
3957 	for (i = 0; i < len; i++) {
3958 		bits = MIN(CHAR_BIT, bitlen);
3959 		*p++ = (u_char)~0 << (CHAR_BIT - bits);
3960 		bitlen -= bits;
3961 	}
3962 	return 0;
3963 }
3964 
3965 /*
3966  * Check that the sockaddr is a valid netmask. Returns 0 if the mask
3967  * is acceptable (i.e. of the form 1...10....0).
3968  */
3969 static int
checkmask(struct sockaddr * sa)3970 checkmask(struct sockaddr *sa)
3971 {
3972 	u_char *mask;
3973 	int i, len;
3974 
3975 	if ((mask = sa_rawaddr(sa, &len)) == NULL)
3976 		return (-1);
3977 
3978 	for (i = 0; i < len; i++)
3979 		if (mask[i] != 0xff)
3980 			break;
3981 	if (i < len) {
3982 		if (~mask[i] & (u_char)(~mask[i] + 1))
3983 			return (-1);
3984 		i++;
3985 	}
3986 	for (; i < len; i++)
3987 		if (mask[i] != 0)
3988 			return (-1);
3989 	return (0);
3990 }
3991 
3992 /*
3993  * Compare two sockaddrs according to a specified mask. Return zero if
3994  * `sa1' matches `sa2' when filtered by the netmask in `samask'.
3995  * If samask is NULL, perform a full comparison.
3996  */
3997 static int
sacmp(struct sockaddr * sa1,struct sockaddr * sa2,struct sockaddr * samask)3998 sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
3999 {
4000 	unsigned char *p1, *p2, *mask;
4001 	int len, i;
4002 
4003 	if (sa1->sa_family != sa2->sa_family ||
4004 	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
4005 	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
4006 		return (1);
4007 
4008 	switch (sa1->sa_family) {
4009 	case AF_INET6:
4010 		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
4011 		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
4012 			return (1);
4013 		break;
4014 	}
4015 
4016 	/* Simple binary comparison if no mask specified. */
4017 	if (samask == NULL)
4018 		return (memcmp(p1, p2, len));
4019 
4020 	/* Set up the mask, and do a mask-based comparison. */
4021 	if (sa1->sa_family != samask->sa_family ||
4022 	    (mask = sa_rawaddr(samask, NULL)) == NULL)
4023 		return (1);
4024 
4025 	for (i = 0; i < len; i++)
4026 		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
4027 			return (1);
4028 	return (0);
4029 }
4030 
4031 /*
4032  * Return a pointer to the part of the sockaddr that contains the
4033  * raw address, and set *nbytes to its length in bytes. Returns
4034  * NULL if the address family is unknown.
4035  */
4036 static void *
sa_rawaddr(struct sockaddr * sa,int * nbytes)4037 sa_rawaddr(struct sockaddr *sa, int *nbytes) {
4038 	void *p;
4039 	int len;
4040 
4041 	switch (sa->sa_family) {
4042 	case AF_INET:
4043 		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
4044 		p = &((struct sockaddr_in *)sa)->sin_addr;
4045 		break;
4046 	case AF_INET6:
4047 		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
4048 		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
4049 		break;
4050 	default:
4051 		p = NULL;
4052 		len = 0;
4053 	}
4054 
4055 	if (nbytes != NULL)
4056 		*nbytes = len;
4057 	return (p);
4058 }
4059 
4060 static void
huphandler(int sig __unused)4061 huphandler(int sig __unused)
4062 {
4063 
4064 	got_sighup = 1;
4065 }
4066 
4067 static void
terminate(int sig __unused)4068 terminate(int sig __unused)
4069 {
4070 	pidfile_remove(pfh);
4071 	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
4072 	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
4073 	exit (0);
4074 }
4075 
4076 static void
cp_cred(struct expcred * outcr,struct expcred * incr)4077 cp_cred(struct expcred *outcr, struct expcred *incr)
4078 {
4079 
4080 	outcr->cr_uid = incr->cr_uid;
4081 	outcr->cr_ngroups = incr->cr_ngroups;
4082 	if (outcr->cr_ngroups > SMALLNGROUPS)
4083 		outcr->cr_groups = malloc(outcr->cr_ngroups * sizeof(gid_t));
4084 	else
4085 		outcr->cr_groups = outcr->cr_smallgrps;
4086 	memcpy(outcr->cr_groups, incr->cr_groups, incr->cr_ngroups *
4087 	    sizeof(gid_t));
4088 }
4089