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