mountd.c revision 279224
1284345Ssjg/*
2284345Ssjg * Copyright (c) 1989, 1993
3284345Ssjg *	The Regents of the University of California.  All rights reserved.
4284345Ssjg *
5284345Ssjg * This code is derived from software contributed to Berkeley by
6284345Ssjg * Herb Hasler and Rick Macklem at The University of Guelph.
7284345Ssjg *
8284345Ssjg * Redistribution and use in source and binary forms, with or without
9284345Ssjg * modification, are permitted provided that the following conditions
10284345Ssjg * are met:
11284345Ssjg * 1. Redistributions of source code must retain the above copyright
12284345Ssjg *    notice, this list of conditions and the following disclaimer.
13284345Ssjg * 2. Redistributions in binary form must reproduce the above copyright
14284345Ssjg *    notice, this list of conditions and the following disclaimer in the
15284345Ssjg *    documentation and/or other materials provided with the distribution.
16284345Ssjg * 4. Neither the name of the University nor the names of its contributors
17284345Ssjg *    may be used to endorse or promote products derived from this software
18284345Ssjg *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#ifndef lint
34static const char copyright[] =
35"@(#) Copyright (c) 1989, 1993\n\
36	The Regents of the University of California.  All rights reserved.\n";
37#endif /*not lint*/
38
39#if 0
40#ifndef lint
41static char sccsid[] = "@(#)mountd.c	8.15 (Berkeley) 5/1/95";
42#endif /*not lint*/
43#endif
44
45#include <sys/cdefs.h>
46__FBSDID("$FreeBSD: stable/10/usr.sbin/mountd/mountd.c 279224 2015-02-24 01:46:43Z kib $");
47
48#include <sys/param.h>
49#include <sys/fcntl.h>
50#include <sys/linker.h>
51#include <sys/module.h>
52#include <sys/mount.h>
53#include <sys/stat.h>
54#include <sys/sysctl.h>
55#include <sys/syslog.h>
56
57#include <rpc/rpc.h>
58#include <rpc/rpc_com.h>
59#include <rpc/pmap_clnt.h>
60#include <rpc/pmap_prot.h>
61#include <rpcsvc/mount.h>
62#include <nfs/nfsproto.h>
63#include <nfs/nfssvc.h>
64#include <nfsserver/nfs.h>
65
66#include <fs/nfs/nfsport.h>
67
68#include <arpa/inet.h>
69
70#include <ctype.h>
71#include <err.h>
72#include <errno.h>
73#include <grp.h>
74#include <libutil.h>
75#include <limits.h>
76#include <netdb.h>
77#include <pwd.h>
78#include <signal.h>
79#include <stdio.h>
80#include <stdlib.h>
81#include <string.h>
82#include <unistd.h>
83#include "pathnames.h"
84#include "mntopts.h"
85
86#ifdef DEBUG
87#include <stdarg.h>
88#endif
89
90/*
91 * Structures for keeping the mount list and export list
92 */
93struct mountlist {
94	struct mountlist *ml_next;
95	char	ml_host[MNTNAMLEN+1];
96	char	ml_dirp[MNTPATHLEN+1];
97};
98
99struct dirlist {
100	struct dirlist	*dp_left;
101	struct dirlist	*dp_right;
102	int		dp_flag;
103	struct hostlist	*dp_hosts;	/* List of hosts this dir exported to */
104	char		dp_dirp[1];	/* Actually malloc'd to size of dir */
105};
106/* dp_flag bits */
107#define	DP_DEFSET	0x1
108#define DP_HOSTSET	0x2
109
110struct exportlist {
111	struct exportlist *ex_next;
112	struct dirlist	*ex_dirl;
113	struct dirlist	*ex_defdir;
114	int		ex_flag;
115	fsid_t		ex_fs;
116	char		*ex_fsdir;
117	char		*ex_indexfile;
118	int		ex_numsecflavors;
119	int		ex_secflavors[MAXSECFLAVORS];
120	int		ex_defnumsecflavors;
121	int		ex_defsecflavors[MAXSECFLAVORS];
122};
123/* ex_flag bits */
124#define	EX_LINKED	0x1
125
126struct netmsk {
127	struct sockaddr_storage nt_net;
128	struct sockaddr_storage nt_mask;
129	char		*nt_name;
130};
131
132union grouptypes {
133	struct addrinfo *gt_addrinfo;
134	struct netmsk	gt_net;
135};
136
137struct grouplist {
138	int gr_type;
139	union grouptypes gr_ptr;
140	struct grouplist *gr_next;
141	int gr_numsecflavors;
142	int gr_secflavors[MAXSECFLAVORS];
143};
144/* Group types */
145#define	GT_NULL		0x0
146#define	GT_HOST		0x1
147#define	GT_NET		0x2
148#define	GT_DEFAULT	0x3
149#define GT_IGNORE	0x5
150
151struct hostlist {
152	int		 ht_flag;	/* Uses DP_xx bits */
153	struct grouplist *ht_grp;
154	struct hostlist	 *ht_next;
155};
156
157struct fhreturn {
158	int	fhr_flag;
159	int	fhr_vers;
160	nfsfh_t	fhr_fh;
161	int	fhr_numsecflavors;
162	int	*fhr_secflavors;
163};
164
165#define	GETPORT_MAXTRY	20	/* Max tries to get a port # */
166
167/* Global defs */
168char	*add_expdir(struct dirlist **, char *, int);
169void	add_dlist(struct dirlist **, struct dirlist *,
170				struct grouplist *, int, struct exportlist *);
171void	add_mlist(char *, char *);
172int	check_dirpath(char *);
173int	check_options(struct dirlist *);
174int	checkmask(struct sockaddr *sa);
175int	chk_host(struct dirlist *, struct sockaddr *, int *, int *, int *,
176				 int **);
177static int	create_service(struct netconfig *nconf);
178static void	complete_service(struct netconfig *nconf, char *port_str);
179static void	clearout_service(void);
180void	del_mlist(char *hostp, char *dirp);
181struct dirlist *dirp_search(struct dirlist *, char *);
182int	do_mount(struct exportlist *, struct grouplist *, int,
183		struct xucred *, char *, int, struct statfs *);
184int	do_opt(char **, char **, struct exportlist *, struct grouplist *,
185				int *, int *, struct xucred *);
186struct	exportlist *ex_search(fsid_t *);
187struct	exportlist *get_exp(void);
188void	free_dir(struct dirlist *);
189void	free_exp(struct exportlist *);
190void	free_grp(struct grouplist *);
191void	free_host(struct hostlist *);
192void	get_exportlist(void);
193int	get_host(char *, struct grouplist *, struct grouplist *);
194struct hostlist *get_ht(void);
195int	get_line(void);
196void	get_mountlist(void);
197int	get_net(char *, struct netmsk *, int);
198void	getexp_err(struct exportlist *, struct grouplist *);
199struct grouplist *get_grp(void);
200void	hang_dirp(struct dirlist *, struct grouplist *,
201				struct exportlist *, int);
202void	huphandler(int sig);
203int	makemask(struct sockaddr_storage *ssp, int bitlen);
204void	mntsrv(struct svc_req *, SVCXPRT *);
205void	nextfield(char **, char **);
206void	out_of_mem(void);
207void	parsecred(char *, struct xucred *);
208int	parsesec(char *, struct exportlist *);
209int	put_exlist(struct dirlist *, XDR *, struct dirlist *, int *, int);
210void	*sa_rawaddr(struct sockaddr *sa, int *nbytes);
211int	sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
212    struct sockaddr *samask);
213int	scan_tree(struct dirlist *, struct sockaddr *);
214static void usage(void);
215int	xdr_dir(XDR *, char *);
216int	xdr_explist(XDR *, caddr_t);
217int	xdr_explist_brief(XDR *, caddr_t);
218int	xdr_explist_common(XDR *, caddr_t, int);
219int	xdr_fhs(XDR *, caddr_t);
220int	xdr_mlist(XDR *, caddr_t);
221void	terminate(int);
222
223struct exportlist *exphead;
224struct mountlist *mlhead;
225struct grouplist *grphead;
226char *exnames_default[2] = { _PATH_EXPORTS, NULL };
227char **exnames;
228char **hosts = NULL;
229struct xucred def_anon = {
230	XUCRED_VERSION,
231	(uid_t)-2,
232	1,
233	{ (gid_t)-2 },
234	NULL
235};
236int force_v2 = 0;
237int resvport_only = 1;
238int nhosts = 0;
239int dir_only = 1;
240int dolog = 0;
241int got_sighup = 0;
242int xcreated = 0;
243
244char *svcport_str = NULL;
245static int	mallocd_svcport = 0;
246static int	*sock_fd;
247static int	sock_fdcnt;
248static int	sock_fdpos;
249static int	suspend_nfsd = 0;
250
251int opt_flags;
252static int have_v6 = 1;
253
254int v4root_phase = 0;
255char v4root_dirpath[PATH_MAX + 1];
256int run_v4server = 1;
257int has_publicfh = 0;
258
259struct pidfh *pfh = NULL;
260/* Bits for opt_flags above */
261#define	OP_MAPROOT	0x01
262#define	OP_MAPALL	0x02
263/* 0x4 free */
264#define	OP_MASK		0x08
265#define	OP_NET		0x10
266#define	OP_ALLDIRS	0x40
267#define	OP_HAVEMASK	0x80	/* A mask was specified or inferred. */
268#define	OP_QUIET	0x100
269#define OP_MASKLEN	0x200
270#define OP_SEC		0x400
271
272#ifdef DEBUG
273int debug = 1;
274void	SYSLOG(int, const char *, ...) __printflike(2, 3);
275#define syslog SYSLOG
276#else
277int debug = 0;
278#endif
279
280/*
281 * Mountd server for NFS mount protocol as described in:
282 * NFS: Network File System Protocol Specification, RFC1094, Appendix A
283 * The optional arguments are the exports file name
284 * default: _PATH_EXPORTS
285 * and "-n" to allow nonroot mount.
286 */
287int
288main(int argc, char **argv)
289{
290	fd_set readfds;
291	struct netconfig *nconf;
292	char *endptr, **hosts_bak;
293	void *nc_handle;
294	pid_t otherpid;
295	in_port_t svcport;
296	int c, k, s;
297	int maxrec = RPC_MAXDATASIZE;
298	int attempt_cnt, port_len, port_pos, ret;
299	char **port_list;
300
301	/* Check that another mountd isn't already running. */
302	pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
303	if (pfh == NULL) {
304		if (errno == EEXIST)
305			errx(1, "mountd already running, pid: %d.", otherpid);
306		warn("cannot open or create pidfile");
307	}
308
309	s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
310	if (s < 0)
311		have_v6 = 0;
312	else
313		close(s);
314
315	while ((c = getopt(argc, argv, "2deh:lnop:rS")) != -1)
316		switch (c) {
317		case '2':
318			force_v2 = 1;
319			break;
320		case 'e':
321			/* now a no-op, since this is the default */
322			break;
323		case 'n':
324			resvport_only = 0;
325			break;
326		case 'r':
327			dir_only = 0;
328			break;
329		case 'd':
330			debug = debug ? 0 : 1;
331			break;
332		case 'l':
333			dolog = 1;
334			break;
335		case 'o':
336			run_v4server = 0;
337			break;
338		case 'p':
339			endptr = NULL;
340			svcport = (in_port_t)strtoul(optarg, &endptr, 10);
341			if (endptr == NULL || *endptr != '\0' ||
342			    svcport == 0 || svcport >= IPPORT_MAX)
343				usage();
344			svcport_str = strdup(optarg);
345			break;
346		case 'h':
347			++nhosts;
348			hosts_bak = hosts;
349			hosts_bak = realloc(hosts, nhosts * sizeof(char *));
350			if (hosts_bak == NULL) {
351				if (hosts != NULL) {
352					for (k = 0; k < nhosts; k++)
353						free(hosts[k]);
354					free(hosts);
355					out_of_mem();
356				}
357			}
358			hosts = hosts_bak;
359			hosts[nhosts - 1] = strdup(optarg);
360			if (hosts[nhosts - 1] == NULL) {
361				for (k = 0; k < (nhosts - 1); k++)
362					free(hosts[k]);
363				free(hosts);
364				out_of_mem();
365			}
366			break;
367		case 'S':
368			suspend_nfsd = 1;
369			break;
370		default:
371			usage();
372		};
373
374	/*
375	 * Unless the "-o" option was specified, try and run "nfsd".
376	 * If "-o" was specified, try and run "nfsserver".
377	 */
378	if (run_v4server > 0) {
379		if (modfind("nfsd") < 0) {
380			/* Not present in kernel, try loading it */
381			if (kldload("nfsd") < 0 || modfind("nfsd") < 0)
382				errx(1, "NFS server is not available");
383		}
384	} else if (modfind("nfsserver") < 0) {
385		/* Not present in kernel, try loading it */
386		if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
387			errx(1, "NFS server is not available");
388	}
389
390	argc -= optind;
391	argv += optind;
392	grphead = (struct grouplist *)NULL;
393	exphead = (struct exportlist *)NULL;
394	mlhead = (struct mountlist *)NULL;
395	if (argc > 0)
396		exnames = argv;
397	else
398		exnames = exnames_default;
399	openlog("mountd", LOG_PID, LOG_DAEMON);
400	if (debug)
401		warnx("getting export list");
402	get_exportlist();
403	if (debug)
404		warnx("getting mount list");
405	get_mountlist();
406	if (debug)
407		warnx("here we go");
408	if (debug == 0) {
409		daemon(0, 0);
410		signal(SIGINT, SIG_IGN);
411		signal(SIGQUIT, SIG_IGN);
412	}
413	signal(SIGHUP, huphandler);
414	signal(SIGTERM, terminate);
415	signal(SIGPIPE, SIG_IGN);
416
417	pidfile_write(pfh);
418
419	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
420	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
421	rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
422
423	if (!resvport_only) {
424		if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL,
425		    &resvport_only, sizeof(resvport_only)) != 0 &&
426		    errno != ENOENT) {
427			syslog(LOG_ERR, "sysctl: %m");
428			exit(1);
429		}
430	}
431
432	/*
433	 * If no hosts were specified, add a wildcard entry to bind to
434	 * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
435	 * list.
436	 */
437	if (nhosts == 0) {
438		hosts = malloc(sizeof(char**));
439		if (hosts == NULL)
440			out_of_mem();
441		hosts[0] = "*";
442		nhosts = 1;
443	} else {
444		hosts_bak = hosts;
445		if (have_v6) {
446			hosts_bak = realloc(hosts, (nhosts + 2) *
447			    sizeof(char *));
448			if (hosts_bak == NULL) {
449				for (k = 0; k < nhosts; k++)
450					free(hosts[k]);
451		    		free(hosts);
452		    		out_of_mem();
453			} else
454				hosts = hosts_bak;
455			nhosts += 2;
456			hosts[nhosts - 2] = "::1";
457		} else {
458			hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
459			if (hosts_bak == NULL) {
460				for (k = 0; k < nhosts; k++)
461					free(hosts[k]);
462				free(hosts);
463				out_of_mem();
464			} else {
465				nhosts += 1;
466				hosts = hosts_bak;
467			}
468		}
469
470		hosts[nhosts - 1] = "127.0.0.1";
471	}
472
473	attempt_cnt = 1;
474	sock_fdcnt = 0;
475	sock_fd = NULL;
476	port_list = NULL;
477	port_len = 0;
478	nc_handle = setnetconfig();
479	while ((nconf = getnetconfig(nc_handle))) {
480		if (nconf->nc_flag & NC_VISIBLE) {
481			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
482			    "inet6") == 0) {
483				/* DO NOTHING */
484			} else {
485				ret = create_service(nconf);
486				if (ret == 1)
487					/* Ignore this call */
488					continue;
489				if (ret < 0) {
490					/*
491					 * Failed to bind port, so close off
492					 * all sockets created and try again
493					 * if the port# was dynamically
494					 * assigned via bind(2).
495					 */
496					clearout_service();
497					if (mallocd_svcport != 0 &&
498					    attempt_cnt < GETPORT_MAXTRY) {
499						free(svcport_str);
500						svcport_str = NULL;
501						mallocd_svcport = 0;
502					} else {
503						errno = EADDRINUSE;
504						syslog(LOG_ERR,
505						    "bindresvport_sa: %m");
506						exit(1);
507					}
508
509					/* Start over at the first service. */
510					free(sock_fd);
511					sock_fdcnt = 0;
512					sock_fd = NULL;
513					nc_handle = setnetconfig();
514					attempt_cnt++;
515				} else if (mallocd_svcport != 0 &&
516				    attempt_cnt == GETPORT_MAXTRY) {
517					/*
518					 * For the last attempt, allow
519					 * different port #s for each nconf
520					 * by saving the svcport_str and
521					 * setting it back to NULL.
522					 */
523					port_list = realloc(port_list,
524					    (port_len + 1) * sizeof(char *));
525					if (port_list == NULL)
526						out_of_mem();
527					port_list[port_len++] = svcport_str;
528					svcport_str = NULL;
529					mallocd_svcport = 0;
530				}
531			}
532		}
533	}
534
535	/*
536	 * Successfully bound the ports, so call complete_service() to
537	 * do the rest of the setup on the service(s).
538	 */
539	sock_fdpos = 0;
540	port_pos = 0;
541	nc_handle = setnetconfig();
542	while ((nconf = getnetconfig(nc_handle))) {
543		if (nconf->nc_flag & NC_VISIBLE) {
544			if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
545			    "inet6") == 0) {
546				/* DO NOTHING */
547			} else if (port_list != NULL) {
548				if (port_pos >= port_len) {
549					syslog(LOG_ERR, "too many port#s");
550					exit(1);
551				}
552				complete_service(nconf, port_list[port_pos++]);
553			} else
554				complete_service(nconf, svcport_str);
555		}
556	}
557	endnetconfig(nc_handle);
558	free(sock_fd);
559	if (port_list != NULL) {
560		for (port_pos = 0; port_pos < port_len; port_pos++)
561			free(port_list[port_pos]);
562		free(port_list);
563	}
564
565	if (xcreated == 0) {
566		syslog(LOG_ERR, "could not create any services");
567		exit(1);
568	}
569
570	/* Expand svc_run() here so that we can call get_exportlist(). */
571	for (;;) {
572		if (got_sighup) {
573			get_exportlist();
574			got_sighup = 0;
575		}
576		readfds = svc_fdset;
577		switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
578		case -1:
579			if (errno == EINTR)
580                                continue;
581			syslog(LOG_ERR, "mountd died: select: %m");
582			exit(1);
583		case 0:
584			continue;
585		default:
586			svc_getreqset(&readfds);
587		}
588	}
589}
590
591/*
592 * This routine creates and binds sockets on the appropriate
593 * addresses. It gets called one time for each transport.
594 * It returns 0 upon success, 1 for ingore the call and -1 to indicate
595 * bind failed with EADDRINUSE.
596 * Any file descriptors that have been created are stored in sock_fd and
597 * the total count of them is maintained in sock_fdcnt.
598 */
599static int
600create_service(struct netconfig *nconf)
601{
602	struct addrinfo hints, *res = NULL;
603	struct sockaddr_in *sin;
604	struct sockaddr_in6 *sin6;
605	struct __rpc_sockinfo si;
606	int aicode;
607	int fd;
608	int nhostsbak;
609	int one = 1;
610	int r;
611	u_int32_t host_addr[4];  /* IPv4 or IPv6 */
612	int mallocd_res;
613
614	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
615	    (nconf->nc_semantics != NC_TPI_COTS) &&
616	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
617		return (1);	/* not my type */
618
619	/*
620	 * XXX - using RPC library internal functions.
621	 */
622	if (!__rpc_nconf2sockinfo(nconf, &si)) {
623		syslog(LOG_ERR, "cannot get information for %s",
624		    nconf->nc_netid);
625		return (1);
626	}
627
628	/* Get mountd's address on this transport */
629	memset(&hints, 0, sizeof hints);
630	hints.ai_family = si.si_af;
631	hints.ai_socktype = si.si_socktype;
632	hints.ai_protocol = si.si_proto;
633
634	/*
635	 * Bind to specific IPs if asked to
636	 */
637	nhostsbak = nhosts;
638	while (nhostsbak > 0) {
639		--nhostsbak;
640		sock_fd = realloc(sock_fd, (sock_fdcnt + 1) * sizeof(int));
641		if (sock_fd == NULL)
642			out_of_mem();
643		sock_fd[sock_fdcnt++] = -1;	/* Set invalid for now. */
644		mallocd_res = 0;
645
646		hints.ai_flags = AI_PASSIVE;
647
648		/*
649		 * XXX - using RPC library internal functions.
650		 */
651		if ((fd = __rpc_nconf2fd(nconf)) < 0) {
652			int non_fatal = 0;
653	    		if (errno == EAFNOSUPPORT &&
654			    nconf->nc_semantics != NC_TPI_CLTS)
655				non_fatal = 1;
656
657			syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
658			    "cannot create socket for %s", nconf->nc_netid);
659			if (non_fatal != 0)
660				continue;
661			exit(1);
662		}
663
664		switch (hints.ai_family) {
665		case AF_INET:
666			if (inet_pton(AF_INET, hosts[nhostsbak],
667			    host_addr) == 1) {
668				hints.ai_flags |= AI_NUMERICHOST;
669			} else {
670				/*
671				 * Skip if we have an AF_INET6 address.
672				 */
673				if (inet_pton(AF_INET6, hosts[nhostsbak],
674				    host_addr) == 1) {
675					close(fd);
676					continue;
677				}
678			}
679			break;
680		case AF_INET6:
681			if (inet_pton(AF_INET6, hosts[nhostsbak],
682			    host_addr) == 1) {
683				hints.ai_flags |= AI_NUMERICHOST;
684			} else {
685				/*
686				 * Skip if we have an AF_INET address.
687				 */
688				if (inet_pton(AF_INET, hosts[nhostsbak],
689				    host_addr) == 1) {
690					close(fd);
691					continue;
692				}
693			}
694
695			/*
696			 * We're doing host-based access checks here, so don't
697			 * allow v4-in-v6 to confuse things. The kernel will
698			 * disable it by default on NFS sockets too.
699			 */
700			if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
701			    sizeof one) < 0) {
702				syslog(LOG_ERR,
703				    "can't disable v4-in-v6 on IPv6 socket");
704				exit(1);
705			}
706			break;
707		default:
708			break;
709		}
710
711		/*
712		 * If no hosts were specified, just bind to INADDR_ANY
713		 */
714		if (strcmp("*", hosts[nhostsbak]) == 0) {
715			if (svcport_str == NULL) {
716				res = malloc(sizeof(struct addrinfo));
717				if (res == NULL)
718					out_of_mem();
719				mallocd_res = 1;
720				res->ai_flags = hints.ai_flags;
721				res->ai_family = hints.ai_family;
722				res->ai_protocol = hints.ai_protocol;
723				switch (res->ai_family) {
724				case AF_INET:
725					sin = malloc(sizeof(struct sockaddr_in));
726					if (sin == NULL)
727						out_of_mem();
728					sin->sin_family = AF_INET;
729					sin->sin_port = htons(0);
730					sin->sin_addr.s_addr = htonl(INADDR_ANY);
731					res->ai_addr = (struct sockaddr*) sin;
732					res->ai_addrlen = (socklen_t)
733					    sizeof(struct sockaddr_in);
734					break;
735				case AF_INET6:
736					sin6 = malloc(sizeof(struct sockaddr_in6));
737					if (sin6 == NULL)
738						out_of_mem();
739					sin6->sin6_family = AF_INET6;
740					sin6->sin6_port = htons(0);
741					sin6->sin6_addr = in6addr_any;
742					res->ai_addr = (struct sockaddr*) sin6;
743					res->ai_addrlen = (socklen_t)
744					    sizeof(struct sockaddr_in6);
745					break;
746				default:
747					syslog(LOG_ERR, "bad addr fam %d",
748					    res->ai_family);
749					exit(1);
750				}
751			} else {
752				if ((aicode = getaddrinfo(NULL, svcport_str,
753				    &hints, &res)) != 0) {
754					syslog(LOG_ERR,
755					    "cannot get local address for %s: %s",
756					    nconf->nc_netid,
757					    gai_strerror(aicode));
758					close(fd);
759					continue;
760				}
761			}
762		} else {
763			if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
764			    &hints, &res)) != 0) {
765				syslog(LOG_ERR,
766				    "cannot get local address for %s: %s",
767				    nconf->nc_netid, gai_strerror(aicode));
768				close(fd);
769				continue;
770			}
771		}
772
773		/* Store the fd. */
774		sock_fd[sock_fdcnt - 1] = fd;
775
776		/* Now, attempt the bind. */
777		r = bindresvport_sa(fd, res->ai_addr);
778		if (r != 0) {
779			if (errno == EADDRINUSE && mallocd_svcport != 0) {
780				if (mallocd_res != 0) {
781					free(res->ai_addr);
782					free(res);
783				} else
784					freeaddrinfo(res);
785				return (-1);
786			}
787			syslog(LOG_ERR, "bindresvport_sa: %m");
788			exit(1);
789		}
790
791		if (svcport_str == NULL) {
792			svcport_str = malloc(NI_MAXSERV * sizeof(char));
793			if (svcport_str == NULL)
794				out_of_mem();
795			mallocd_svcport = 1;
796
797			if (getnameinfo(res->ai_addr,
798			    res->ai_addr->sa_len, NULL, NI_MAXHOST,
799			    svcport_str, NI_MAXSERV * sizeof(char),
800			    NI_NUMERICHOST | NI_NUMERICSERV))
801				errx(1, "Cannot get port number");
802		}
803		if (mallocd_res != 0) {
804			free(res->ai_addr);
805			free(res);
806		} else
807			freeaddrinfo(res);
808		res = NULL;
809	}
810	return (0);
811}
812
813/*
814 * Called after all the create_service() calls have succeeded, to complete
815 * the setup and registration.
816 */
817static void
818complete_service(struct netconfig *nconf, char *port_str)
819{
820	struct addrinfo hints, *res = NULL;
821	struct __rpc_sockinfo si;
822	struct netbuf servaddr;
823	SVCXPRT	*transp = NULL;
824	int aicode, fd, nhostsbak;
825	int registered = 0;
826
827	if ((nconf->nc_semantics != NC_TPI_CLTS) &&
828	    (nconf->nc_semantics != NC_TPI_COTS) &&
829	    (nconf->nc_semantics != NC_TPI_COTS_ORD))
830		return;	/* not my type */
831
832	/*
833	 * XXX - using RPC library internal functions.
834	 */
835	if (!__rpc_nconf2sockinfo(nconf, &si)) {
836		syslog(LOG_ERR, "cannot get information for %s",
837		    nconf->nc_netid);
838		return;
839	}
840
841	nhostsbak = nhosts;
842	while (nhostsbak > 0) {
843		--nhostsbak;
844		if (sock_fdpos >= sock_fdcnt) {
845			/* Should never happen. */
846			syslog(LOG_ERR, "Ran out of socket fd's");
847			return;
848		}
849		fd = sock_fd[sock_fdpos++];
850		if (fd < 0)
851			continue;
852
853		if (nconf->nc_semantics != NC_TPI_CLTS)
854			listen(fd, SOMAXCONN);
855
856		if (nconf->nc_semantics == NC_TPI_CLTS )
857			transp = svc_dg_create(fd, 0, 0);
858		else
859			transp = svc_vc_create(fd, RPC_MAXDATASIZE,
860			    RPC_MAXDATASIZE);
861
862		if (transp != (SVCXPRT *) NULL) {
863			if (!svc_reg(transp, MOUNTPROG, MOUNTVERS, mntsrv,
864			    NULL))
865				syslog(LOG_ERR,
866				    "can't register %s MOUNTVERS service",
867				    nconf->nc_netid);
868			if (!force_v2) {
869				if (!svc_reg(transp, MOUNTPROG, MOUNTVERS3,
870				    mntsrv, NULL))
871					syslog(LOG_ERR,
872					    "can't register %s MOUNTVERS3 service",
873					    nconf->nc_netid);
874			}
875		} else
876			syslog(LOG_WARNING, "can't create %s services",
877			    nconf->nc_netid);
878
879		if (registered == 0) {
880			registered = 1;
881			memset(&hints, 0, sizeof hints);
882			hints.ai_flags = AI_PASSIVE;
883			hints.ai_family = si.si_af;
884			hints.ai_socktype = si.si_socktype;
885			hints.ai_protocol = si.si_proto;
886
887			if ((aicode = getaddrinfo(NULL, port_str, &hints,
888			    &res)) != 0) {
889				syslog(LOG_ERR, "cannot get local address: %s",
890				    gai_strerror(aicode));
891				exit(1);
892			}
893
894			servaddr.buf = malloc(res->ai_addrlen);
895			memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
896			servaddr.len = res->ai_addrlen;
897
898			rpcb_set(MOUNTPROG, MOUNTVERS, nconf, &servaddr);
899			rpcb_set(MOUNTPROG, MOUNTVERS3, nconf, &servaddr);
900
901			xcreated++;
902			freeaddrinfo(res);
903		}
904	} /* end while */
905}
906
907/*
908 * Clear out sockets after a failure to bind one of them, so that the
909 * cycle of socket creation/binding can start anew.
910 */
911static void
912clearout_service(void)
913{
914	int i;
915
916	for (i = 0; i < sock_fdcnt; i++) {
917		if (sock_fd[i] >= 0) {
918			shutdown(sock_fd[i], SHUT_RDWR);
919			close(sock_fd[i]);
920		}
921	}
922}
923
924static void
925usage(void)
926{
927	fprintf(stderr,
928		"usage: mountd [-2] [-d] [-e] [-l] [-n] [-p <port>] [-r] "
929		"[-S] [-h <bindip>] [export_file ...]\n");
930	exit(1);
931}
932
933/*
934 * The mount rpc service
935 */
936void
937mntsrv(struct svc_req *rqstp, SVCXPRT *transp)
938{
939	struct exportlist *ep;
940	struct dirlist *dp;
941	struct fhreturn fhr;
942	struct stat stb;
943	struct statfs fsb;
944	char host[NI_MAXHOST], numerichost[NI_MAXHOST];
945	int lookup_failed = 1;
946	struct sockaddr *saddr;
947	u_short sport;
948	char rpcpath[MNTPATHLEN + 1], dirpath[MAXPATHLEN];
949	int bad = 0, defset, hostset;
950	sigset_t sighup_mask;
951	int numsecflavors, *secflavorsp;
952
953	sigemptyset(&sighup_mask);
954	sigaddset(&sighup_mask, SIGHUP);
955	saddr = svc_getrpccaller(transp)->buf;
956	switch (saddr->sa_family) {
957	case AF_INET6:
958		sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
959		break;
960	case AF_INET:
961		sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
962		break;
963	default:
964		syslog(LOG_ERR, "request from unknown address family");
965		return;
966	}
967	lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
968	    NULL, 0, 0);
969	getnameinfo(saddr, saddr->sa_len, numerichost,
970	    sizeof numerichost, NULL, 0, NI_NUMERICHOST);
971	switch (rqstp->rq_proc) {
972	case NULLPROC:
973		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
974			syslog(LOG_ERR, "can't send reply");
975		return;
976	case MOUNTPROC_MNT:
977		if (sport >= IPPORT_RESERVED && resvport_only) {
978			syslog(LOG_NOTICE,
979			    "mount request from %s from unprivileged port",
980			    numerichost);
981			svcerr_weakauth(transp);
982			return;
983		}
984		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
985			syslog(LOG_NOTICE, "undecodable mount request from %s",
986			    numerichost);
987			svcerr_decode(transp);
988			return;
989		}
990
991		/*
992		 * Get the real pathname and make sure it is a directory
993		 * or a regular file if the -r option was specified
994		 * and it exists.
995		 */
996		if (realpath(rpcpath, dirpath) == NULL ||
997		    stat(dirpath, &stb) < 0 ||
998		    (!S_ISDIR(stb.st_mode) &&
999		    (dir_only || !S_ISREG(stb.st_mode))) ||
1000		    statfs(dirpath, &fsb) < 0) {
1001			chdir("/");	/* Just in case realpath doesn't */
1002			syslog(LOG_NOTICE,
1003			    "mount request from %s for non existent path %s",
1004			    numerichost, dirpath);
1005			if (debug)
1006				warnx("stat failed on %s", dirpath);
1007			bad = ENOENT;	/* We will send error reply later */
1008		}
1009
1010		/* Check in the exports list */
1011		sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1012		ep = ex_search(&fsb.f_fsid);
1013		hostset = defset = 0;
1014		if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset,
1015		    &numsecflavors, &secflavorsp) ||
1016		    ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
1017		      chk_host(dp, saddr, &defset, &hostset, &numsecflavors,
1018		       &secflavorsp)) ||
1019		    (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
1020		     scan_tree(ep->ex_dirl, saddr) == 0))) {
1021			if (bad) {
1022				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1023				    (caddr_t)&bad))
1024					syslog(LOG_ERR, "can't send reply");
1025				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1026				return;
1027			}
1028			if (hostset & DP_HOSTSET) {
1029				fhr.fhr_flag = hostset;
1030				fhr.fhr_numsecflavors = numsecflavors;
1031				fhr.fhr_secflavors = secflavorsp;
1032			} else {
1033				fhr.fhr_flag = defset;
1034				fhr.fhr_numsecflavors = ep->ex_defnumsecflavors;
1035				fhr.fhr_secflavors = ep->ex_defsecflavors;
1036			}
1037			fhr.fhr_vers = rqstp->rq_vers;
1038			/* Get the file handle */
1039			memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
1040			if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
1041				bad = errno;
1042				syslog(LOG_ERR, "can't get fh for %s", dirpath);
1043				if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
1044				    (caddr_t)&bad))
1045					syslog(LOG_ERR, "can't send reply");
1046				sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1047				return;
1048			}
1049			if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
1050			    (caddr_t)&fhr))
1051				syslog(LOG_ERR, "can't send reply");
1052			if (!lookup_failed)
1053				add_mlist(host, dirpath);
1054			else
1055				add_mlist(numerichost, dirpath);
1056			if (debug)
1057				warnx("mount successful");
1058			if (dolog)
1059				syslog(LOG_NOTICE,
1060				    "mount request succeeded from %s for %s",
1061				    numerichost, dirpath);
1062		} else {
1063			bad = EACCES;
1064			syslog(LOG_NOTICE,
1065			    "mount request denied from %s for %s",
1066			    numerichost, dirpath);
1067		}
1068
1069		if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
1070		    (caddr_t)&bad))
1071			syslog(LOG_ERR, "can't send reply");
1072		sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1073		return;
1074	case MOUNTPROC_DUMP:
1075		if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
1076			syslog(LOG_ERR, "can't send reply");
1077		else if (dolog)
1078			syslog(LOG_NOTICE,
1079			    "dump request succeeded from %s",
1080			    numerichost);
1081		return;
1082	case MOUNTPROC_UMNT:
1083		if (sport >= IPPORT_RESERVED && resvport_only) {
1084			syslog(LOG_NOTICE,
1085			    "umount request from %s from unprivileged port",
1086			    numerichost);
1087			svcerr_weakauth(transp);
1088			return;
1089		}
1090		if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
1091			syslog(LOG_NOTICE, "undecodable umount request from %s",
1092			    numerichost);
1093			svcerr_decode(transp);
1094			return;
1095		}
1096		if (realpath(rpcpath, dirpath) == NULL) {
1097			syslog(LOG_NOTICE, "umount request from %s "
1098			    "for non existent path %s",
1099			    numerichost, dirpath);
1100		}
1101		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1102			syslog(LOG_ERR, "can't send reply");
1103		if (!lookup_failed)
1104			del_mlist(host, dirpath);
1105		del_mlist(numerichost, dirpath);
1106		if (dolog)
1107			syslog(LOG_NOTICE,
1108			    "umount request succeeded from %s for %s",
1109			    numerichost, dirpath);
1110		return;
1111	case MOUNTPROC_UMNTALL:
1112		if (sport >= IPPORT_RESERVED && resvport_only) {
1113			syslog(LOG_NOTICE,
1114			    "umountall request from %s from unprivileged port",
1115			    numerichost);
1116			svcerr_weakauth(transp);
1117			return;
1118		}
1119		if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
1120			syslog(LOG_ERR, "can't send reply");
1121		if (!lookup_failed)
1122			del_mlist(host, NULL);
1123		del_mlist(numerichost, NULL);
1124		if (dolog)
1125			syslog(LOG_NOTICE,
1126			    "umountall request succeeded from %s",
1127			    numerichost);
1128		return;
1129	case MOUNTPROC_EXPORT:
1130		if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
1131			if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
1132			    (caddr_t)NULL))
1133				syslog(LOG_ERR, "can't send reply");
1134		if (dolog)
1135			syslog(LOG_NOTICE,
1136			    "export request succeeded from %s",
1137			    numerichost);
1138		return;
1139	default:
1140		svcerr_noproc(transp);
1141		return;
1142	}
1143}
1144
1145/*
1146 * Xdr conversion for a dirpath string
1147 */
1148int
1149xdr_dir(XDR *xdrsp, char *dirp)
1150{
1151	return (xdr_string(xdrsp, &dirp, MNTPATHLEN));
1152}
1153
1154/*
1155 * Xdr routine to generate file handle reply
1156 */
1157int
1158xdr_fhs(XDR *xdrsp, caddr_t cp)
1159{
1160	struct fhreturn *fhrp = (struct fhreturn *)cp;
1161	u_long ok = 0, len, auth;
1162	int i;
1163
1164	if (!xdr_long(xdrsp, &ok))
1165		return (0);
1166	switch (fhrp->fhr_vers) {
1167	case 1:
1168		return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
1169	case 3:
1170		len = NFSX_V3FH;
1171		if (!xdr_long(xdrsp, &len))
1172			return (0);
1173		if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
1174			return (0);
1175		if (fhrp->fhr_numsecflavors) {
1176			if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
1177				return (0);
1178			for (i = 0; i < fhrp->fhr_numsecflavors; i++)
1179				if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
1180					return (0);
1181			return (1);
1182		} else {
1183			auth = AUTH_SYS;
1184			len = 1;
1185			if (!xdr_long(xdrsp, &len))
1186				return (0);
1187			return (xdr_long(xdrsp, &auth));
1188		}
1189	};
1190	return (0);
1191}
1192
1193int
1194xdr_mlist(XDR *xdrsp, caddr_t cp __unused)
1195{
1196	struct mountlist *mlp;
1197	int true = 1;
1198	int false = 0;
1199	char *strp;
1200
1201	mlp = mlhead;
1202	while (mlp) {
1203		if (!xdr_bool(xdrsp, &true))
1204			return (0);
1205		strp = &mlp->ml_host[0];
1206		if (!xdr_string(xdrsp, &strp, MNTNAMLEN))
1207			return (0);
1208		strp = &mlp->ml_dirp[0];
1209		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1210			return (0);
1211		mlp = mlp->ml_next;
1212	}
1213	if (!xdr_bool(xdrsp, &false))
1214		return (0);
1215	return (1);
1216}
1217
1218/*
1219 * Xdr conversion for export list
1220 */
1221int
1222xdr_explist_common(XDR *xdrsp, caddr_t cp __unused, int brief)
1223{
1224	struct exportlist *ep;
1225	int false = 0;
1226	int putdef;
1227	sigset_t sighup_mask;
1228
1229	sigemptyset(&sighup_mask);
1230	sigaddset(&sighup_mask, SIGHUP);
1231	sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
1232	ep = exphead;
1233	while (ep) {
1234		putdef = 0;
1235		if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
1236			       &putdef, brief))
1237			goto errout;
1238		if (ep->ex_defdir && putdef == 0 &&
1239			put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
1240			&putdef, brief))
1241			goto errout;
1242		ep = ep->ex_next;
1243	}
1244	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1245	if (!xdr_bool(xdrsp, &false))
1246		return (0);
1247	return (1);
1248errout:
1249	sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
1250	return (0);
1251}
1252
1253/*
1254 * Called from xdr_explist() to traverse the tree and export the
1255 * directory paths.
1256 */
1257int
1258put_exlist(struct dirlist *dp, XDR *xdrsp, struct dirlist *adp, int *putdefp,
1259	int brief)
1260{
1261	struct grouplist *grp;
1262	struct hostlist *hp;
1263	int true = 1;
1264	int false = 0;
1265	int gotalldir = 0;
1266	char *strp;
1267
1268	if (dp) {
1269		if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
1270			return (1);
1271		if (!xdr_bool(xdrsp, &true))
1272			return (1);
1273		strp = dp->dp_dirp;
1274		if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1275			return (1);
1276		if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
1277			gotalldir = 1;
1278			*putdefp = 1;
1279		}
1280		if (brief) {
1281			if (!xdr_bool(xdrsp, &true))
1282				return (1);
1283			strp = "(...)";
1284			if (!xdr_string(xdrsp, &strp, MNTPATHLEN))
1285				return (1);
1286		} else if ((dp->dp_flag & DP_DEFSET) == 0 &&
1287		    (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
1288			hp = dp->dp_hosts;
1289			while (hp) {
1290				grp = hp->ht_grp;
1291				if (grp->gr_type == GT_HOST) {
1292					if (!xdr_bool(xdrsp, &true))
1293						return (1);
1294					strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
1295					if (!xdr_string(xdrsp, &strp,
1296					    MNTNAMLEN))
1297						return (1);
1298				} else if (grp->gr_type == GT_NET) {
1299					if (!xdr_bool(xdrsp, &true))
1300						return (1);
1301					strp = grp->gr_ptr.gt_net.nt_name;
1302					if (!xdr_string(xdrsp, &strp,
1303					    MNTNAMLEN))
1304						return (1);
1305				}
1306				hp = hp->ht_next;
1307				if (gotalldir && hp == (struct hostlist *)NULL) {
1308					hp = adp->dp_hosts;
1309					gotalldir = 0;
1310				}
1311			}
1312		}
1313		if (!xdr_bool(xdrsp, &false))
1314			return (1);
1315		if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
1316			return (1);
1317	}
1318	return (0);
1319}
1320
1321int
1322xdr_explist(XDR *xdrsp, caddr_t cp)
1323{
1324
1325	return xdr_explist_common(xdrsp, cp, 0);
1326}
1327
1328int
1329xdr_explist_brief(XDR *xdrsp, caddr_t cp)
1330{
1331
1332	return xdr_explist_common(xdrsp, cp, 1);
1333}
1334
1335char *line;
1336int linesize;
1337FILE *exp_file;
1338
1339/*
1340 * Get the export list from one, currently open file
1341 */
1342static void
1343get_exportlist_one(void)
1344{
1345	struct exportlist *ep, *ep2;
1346	struct grouplist *grp, *tgrp;
1347	struct exportlist **epp;
1348	struct dirlist *dirhead;
1349	struct statfs fsb;
1350	struct xucred anon;
1351	char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
1352	int len, has_host, exflags, got_nondir, dirplen, netgrp;
1353
1354	v4root_phase = 0;
1355	dirhead = (struct dirlist *)NULL;
1356	while (get_line()) {
1357		if (debug)
1358			warnx("got line %s", line);
1359		cp = line;
1360		nextfield(&cp, &endcp);
1361		if (*cp == '#')
1362			goto nextline;
1363
1364		/*
1365		 * Set defaults.
1366		 */
1367		has_host = FALSE;
1368		anon = def_anon;
1369		exflags = MNT_EXPORTED;
1370		got_nondir = 0;
1371		opt_flags = 0;
1372		ep = (struct exportlist *)NULL;
1373		dirp = NULL;
1374
1375		/*
1376		 * Handle the V4 root dir.
1377		 */
1378		if (*cp == 'V' && *(cp + 1) == '4' && *(cp + 2) == ':') {
1379			/*
1380			 * V4: just indicates that it is the v4 root point,
1381			 * so skip over that and set v4root_phase.
1382			 */
1383			if (v4root_phase > 0) {
1384				syslog(LOG_ERR, "V4:duplicate line, ignored");
1385				goto nextline;
1386			}
1387			v4root_phase = 1;
1388			cp += 3;
1389			nextfield(&cp, &endcp);
1390		}
1391
1392		/*
1393		 * Create new exports list entry
1394		 */
1395		len = endcp-cp;
1396		tgrp = grp = get_grp();
1397		while (len > 0) {
1398			if (len > MNTNAMLEN) {
1399			    getexp_err(ep, tgrp);
1400			    goto nextline;
1401			}
1402			if (*cp == '-') {
1403			    if (ep == (struct exportlist *)NULL) {
1404				getexp_err(ep, tgrp);
1405				goto nextline;
1406			    }
1407			    if (debug)
1408				warnx("doing opt %s", cp);
1409			    got_nondir = 1;
1410			    if (do_opt(&cp, &endcp, ep, grp, &has_host,
1411				&exflags, &anon)) {
1412				getexp_err(ep, tgrp);
1413				goto nextline;
1414			    }
1415			} else if (*cp == '/') {
1416			    savedc = *endcp;
1417			    *endcp = '\0';
1418			    if (v4root_phase > 1) {
1419				    if (dirp != NULL) {
1420					syslog(LOG_ERR, "Multiple V4 dirs");
1421					getexp_err(ep, tgrp);
1422					goto nextline;
1423				    }
1424			    }
1425			    if (check_dirpath(cp) &&
1426				statfs(cp, &fsb) >= 0) {
1427				if (got_nondir) {
1428				    syslog(LOG_ERR, "dirs must be first");
1429				    getexp_err(ep, tgrp);
1430				    goto nextline;
1431				}
1432				if (v4root_phase == 1) {
1433				    if (dirp != NULL) {
1434					syslog(LOG_ERR, "Multiple V4 dirs");
1435					getexp_err(ep, tgrp);
1436					goto nextline;
1437				    }
1438				    if (strlen(v4root_dirpath) == 0) {
1439					strlcpy(v4root_dirpath, cp,
1440					    sizeof (v4root_dirpath));
1441				    } else if (strcmp(v4root_dirpath, cp)
1442					!= 0) {
1443					syslog(LOG_ERR,
1444					    "different V4 dirpath %s", cp);
1445					getexp_err(ep, tgrp);
1446					goto nextline;
1447				    }
1448				    dirp = cp;
1449				    v4root_phase = 2;
1450				    got_nondir = 1;
1451				    ep = get_exp();
1452				} else {
1453				    if (ep) {
1454					if (ep->ex_fs.val[0] !=
1455					    fsb.f_fsid.val[0] ||
1456					    ep->ex_fs.val[1] !=
1457					    fsb.f_fsid.val[1]) {
1458						getexp_err(ep, tgrp);
1459						goto nextline;
1460					}
1461				    } else {
1462					/*
1463					 * See if this directory is already
1464					 * in the list.
1465					 */
1466					ep = ex_search(&fsb.f_fsid);
1467					if (ep == (struct exportlist *)NULL) {
1468					    ep = get_exp();
1469					    ep->ex_fs = fsb.f_fsid;
1470					    ep->ex_fsdir = (char *)malloc
1471					        (strlen(fsb.f_mntonname) + 1);
1472					    if (ep->ex_fsdir)
1473						strcpy(ep->ex_fsdir,
1474						    fsb.f_mntonname);
1475					    else
1476						out_of_mem();
1477					    if (debug)
1478						warnx(
1479						  "making new ep fs=0x%x,0x%x",
1480						  fsb.f_fsid.val[0],
1481						  fsb.f_fsid.val[1]);
1482					} else if (debug)
1483					    warnx("found ep fs=0x%x,0x%x",
1484						fsb.f_fsid.val[0],
1485						fsb.f_fsid.val[1]);
1486				    }
1487
1488				    /*
1489				     * Add dirpath to export mount point.
1490				     */
1491				    dirp = add_expdir(&dirhead, cp, len);
1492				    dirplen = len;
1493				}
1494			    } else {
1495				getexp_err(ep, tgrp);
1496				goto nextline;
1497			    }
1498			    *endcp = savedc;
1499			} else {
1500			    savedc = *endcp;
1501			    *endcp = '\0';
1502			    got_nondir = 1;
1503			    if (ep == (struct exportlist *)NULL) {
1504				getexp_err(ep, tgrp);
1505				goto nextline;
1506			    }
1507
1508			    /*
1509			     * Get the host or netgroup.
1510			     */
1511			    setnetgrent(cp);
1512			    netgrp = getnetgrent(&hst, &usr, &dom);
1513			    do {
1514				if (has_host) {
1515				    grp->gr_next = get_grp();
1516				    grp = grp->gr_next;
1517				}
1518				if (netgrp) {
1519				    if (hst == 0) {
1520					syslog(LOG_ERR,
1521				"null hostname in netgroup %s, skipping", cp);
1522					grp->gr_type = GT_IGNORE;
1523				    } else if (get_host(hst, grp, tgrp)) {
1524					syslog(LOG_ERR,
1525			"bad host %s in netgroup %s, skipping", hst, cp);
1526					grp->gr_type = GT_IGNORE;
1527				    }
1528				} else if (get_host(cp, grp, tgrp)) {
1529				    syslog(LOG_ERR, "bad host %s, skipping", cp);
1530				    grp->gr_type = GT_IGNORE;
1531				}
1532				has_host = TRUE;
1533			    } while (netgrp && getnetgrent(&hst, &usr, &dom));
1534			    endnetgrent();
1535			    *endcp = savedc;
1536			}
1537			cp = endcp;
1538			nextfield(&cp, &endcp);
1539			len = endcp - cp;
1540		}
1541		if (check_options(dirhead)) {
1542			getexp_err(ep, tgrp);
1543			goto nextline;
1544		}
1545		if (!has_host) {
1546			grp->gr_type = GT_DEFAULT;
1547			if (debug)
1548				warnx("adding a default entry");
1549
1550		/*
1551		 * Don't allow a network export coincide with a list of
1552		 * host(s) on the same line.
1553		 */
1554		} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1555			syslog(LOG_ERR, "network/host conflict");
1556			getexp_err(ep, tgrp);
1557			goto nextline;
1558
1559		/*
1560		 * If an export list was specified on this line, make sure
1561		 * that we have at least one valid entry, otherwise skip it.
1562		 */
1563		} else {
1564			grp = tgrp;
1565			while (grp && grp->gr_type == GT_IGNORE)
1566				grp = grp->gr_next;
1567			if (! grp) {
1568			    getexp_err(ep, tgrp);
1569			    goto nextline;
1570			}
1571		}
1572
1573		if (v4root_phase == 1) {
1574			syslog(LOG_ERR, "V4:root, no dirp, ignored");
1575			getexp_err(ep, tgrp);
1576			goto nextline;
1577		}
1578
1579		/*
1580		 * Loop through hosts, pushing the exports into the kernel.
1581		 * After loop, tgrp points to the start of the list and
1582		 * grp points to the last entry in the list.
1583		 */
1584		grp = tgrp;
1585		do {
1586			if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
1587			    &fsb)) {
1588				getexp_err(ep, tgrp);
1589				goto nextline;
1590			}
1591		} while (grp->gr_next && (grp = grp->gr_next));
1592
1593		/*
1594		 * For V4: don't enter in mount lists.
1595		 */
1596		if (v4root_phase > 0 && v4root_phase <= 2) {
1597			/*
1598			 * Since these structures aren't used by mountd,
1599			 * free them up now.
1600			 */
1601			if (ep != NULL)
1602				free_exp(ep);
1603			while (tgrp != NULL) {
1604				grp = tgrp;
1605				tgrp = tgrp->gr_next;
1606				free_grp(grp);
1607			}
1608			goto nextline;
1609		}
1610
1611		/*
1612		 * Success. Update the data structures.
1613		 */
1614		if (has_host) {
1615			hang_dirp(dirhead, tgrp, ep, opt_flags);
1616			grp->gr_next = grphead;
1617			grphead = tgrp;
1618		} else {
1619			hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1620				opt_flags);
1621			free_grp(grp);
1622		}
1623		dirhead = (struct dirlist *)NULL;
1624		if ((ep->ex_flag & EX_LINKED) == 0) {
1625			ep2 = exphead;
1626			epp = &exphead;
1627
1628			/*
1629			 * Insert in the list in alphabetical order.
1630			 */
1631			while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1632				epp = &ep2->ex_next;
1633				ep2 = ep2->ex_next;
1634			}
1635			if (ep2)
1636				ep->ex_next = ep2;
1637			*epp = ep;
1638			ep->ex_flag |= EX_LINKED;
1639		}
1640nextline:
1641		v4root_phase = 0;
1642		if (dirhead) {
1643			free_dir(dirhead);
1644			dirhead = (struct dirlist *)NULL;
1645		}
1646	}
1647}
1648
1649/*
1650 * Get the export list from all specified files
1651 */
1652void
1653get_exportlist(void)
1654{
1655	struct exportlist *ep, *ep2;
1656	struct grouplist *grp, *tgrp;
1657	struct export_args export;
1658	struct iovec *iov;
1659	struct statfs *fsp, *mntbufp;
1660	struct xvfsconf vfc;
1661	char errmsg[255];
1662	int num, i;
1663	int iovlen;
1664	int done;
1665	struct nfsex_args eargs;
1666
1667	if (suspend_nfsd != 0)
1668		(void)nfssvc(NFSSVC_SUSPENDNFSD, NULL);
1669	v4root_dirpath[0] = '\0';
1670	bzero(&export, sizeof(export));
1671	export.ex_flags = MNT_DELEXPORT;
1672	iov = NULL;
1673	iovlen = 0;
1674	bzero(errmsg, sizeof(errmsg));
1675
1676	/*
1677	 * First, get rid of the old list
1678	 */
1679	ep = exphead;
1680	while (ep) {
1681		ep2 = ep;
1682		ep = ep->ex_next;
1683		free_exp(ep2);
1684	}
1685	exphead = (struct exportlist *)NULL;
1686
1687	grp = grphead;
1688	while (grp) {
1689		tgrp = grp;
1690		grp = grp->gr_next;
1691		free_grp(tgrp);
1692	}
1693	grphead = (struct grouplist *)NULL;
1694
1695	/*
1696	 * and the old V4 root dir.
1697	 */
1698	bzero(&eargs, sizeof (eargs));
1699	eargs.export.ex_flags = MNT_DELEXPORT;
1700	if (run_v4server > 0 &&
1701	    nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&eargs) < 0 &&
1702	    errno != ENOENT)
1703		syslog(LOG_ERR, "Can't delete exports for V4:");
1704
1705	/*
1706	 * and clear flag that notes if a public fh has been exported.
1707	 */
1708	has_publicfh = 0;
1709
1710	/*
1711	 * And delete exports that are in the kernel for all local
1712	 * filesystems.
1713	 * XXX: Should know how to handle all local exportable filesystems.
1714	 */
1715	num = getmntinfo(&mntbufp, MNT_NOWAIT);
1716
1717	if (num > 0) {
1718		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
1719		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
1720		build_iovec(&iov, &iovlen, "from", NULL, 0);
1721		build_iovec(&iov, &iovlen, "update", NULL, 0);
1722		build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
1723		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
1724	}
1725
1726	for (i = 0; i < num; i++) {
1727		fsp = &mntbufp[i];
1728		if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
1729			syslog(LOG_ERR, "getvfsbyname() failed for %s",
1730			    fsp->f_fstypename);
1731			continue;
1732		}
1733
1734		/*
1735		 * Do not delete export for network filesystem by
1736		 * passing "export" arg to nmount().
1737		 * It only makes sense to do this for local filesystems.
1738		 */
1739		if (vfc.vfc_flags & VFCF_NETWORK)
1740			continue;
1741
1742		iov[1].iov_base = fsp->f_fstypename;
1743		iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
1744		iov[3].iov_base = fsp->f_mntonname;
1745		iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
1746		iov[5].iov_base = fsp->f_mntfromname;
1747		iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
1748		errmsg[0] = '\0';
1749
1750		/*
1751		 * EXDEV is returned when path exists but is not a
1752		 * mount point.  May happens if raced with unmount.
1753		 */
1754		if (nmount(iov, iovlen, fsp->f_flags) < 0 &&
1755		    errno != ENOENT && errno != ENOTSUP && errno != EXDEV) {
1756			syslog(LOG_ERR,
1757			    "can't delete exports for %s: %m %s",
1758			    fsp->f_mntonname, errmsg);
1759		}
1760	}
1761
1762	if (iov != NULL) {
1763		/* Free strings allocated by strdup() in getmntopts.c */
1764		free(iov[0].iov_base); /* fstype */
1765		free(iov[2].iov_base); /* fspath */
1766		free(iov[4].iov_base); /* from */
1767		free(iov[6].iov_base); /* update */
1768		free(iov[8].iov_base); /* export */
1769		free(iov[10].iov_base); /* errmsg */
1770
1771		/* free iov, allocated by realloc() */
1772		free(iov);
1773		iovlen = 0;
1774	}
1775
1776	/*
1777	 * Read in the exports file and build the list, calling
1778	 * nmount() as we go along to push the export rules into the kernel.
1779	 */
1780	done = 0;
1781	for (i = 0; exnames[i] != NULL; i++) {
1782		if (debug)
1783			warnx("reading exports from %s", exnames[i]);
1784		if ((exp_file = fopen(exnames[i], "r")) == NULL) {
1785			syslog(LOG_WARNING, "can't open %s", exnames[i]);
1786			continue;
1787		}
1788		get_exportlist_one();
1789		fclose(exp_file);
1790		done++;
1791	}
1792	if (done == 0) {
1793		syslog(LOG_ERR, "can't open any exports file");
1794		exit(2);
1795	}
1796
1797	/*
1798	 * If there was no public fh, clear any previous one set.
1799	 */
1800	if (run_v4server > 0 && has_publicfh == 0)
1801		(void) nfssvc(NFSSVC_NOPUBLICFH, NULL);
1802
1803	/* Resume the nfsd. If they weren't suspended, this is harmless. */
1804	(void)nfssvc(NFSSVC_RESUMENFSD, NULL);
1805}
1806
1807/*
1808 * Allocate an export list element
1809 */
1810struct exportlist *
1811get_exp(void)
1812{
1813	struct exportlist *ep;
1814
1815	ep = (struct exportlist *)calloc(1, sizeof (struct exportlist));
1816	if (ep == (struct exportlist *)NULL)
1817		out_of_mem();
1818	return (ep);
1819}
1820
1821/*
1822 * Allocate a group list element
1823 */
1824struct grouplist *
1825get_grp(void)
1826{
1827	struct grouplist *gp;
1828
1829	gp = (struct grouplist *)calloc(1, sizeof (struct grouplist));
1830	if (gp == (struct grouplist *)NULL)
1831		out_of_mem();
1832	return (gp);
1833}
1834
1835/*
1836 * Clean up upon an error in get_exportlist().
1837 */
1838void
1839getexp_err(struct exportlist *ep, struct grouplist *grp)
1840{
1841	struct grouplist *tgrp;
1842
1843	if (!(opt_flags & OP_QUIET))
1844		syslog(LOG_ERR, "bad exports list line %s", line);
1845	if (ep && (ep->ex_flag & EX_LINKED) == 0)
1846		free_exp(ep);
1847	while (grp) {
1848		tgrp = grp;
1849		grp = grp->gr_next;
1850		free_grp(tgrp);
1851	}
1852}
1853
1854/*
1855 * Search the export list for a matching fs.
1856 */
1857struct exportlist *
1858ex_search(fsid_t *fsid)
1859{
1860	struct exportlist *ep;
1861
1862	ep = exphead;
1863	while (ep) {
1864		if (ep->ex_fs.val[0] == fsid->val[0] &&
1865		    ep->ex_fs.val[1] == fsid->val[1])
1866			return (ep);
1867		ep = ep->ex_next;
1868	}
1869	return (ep);
1870}
1871
1872/*
1873 * Add a directory path to the list.
1874 */
1875char *
1876add_expdir(struct dirlist **dpp, char *cp, int len)
1877{
1878	struct dirlist *dp;
1879
1880	dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1881	if (dp == (struct dirlist *)NULL)
1882		out_of_mem();
1883	dp->dp_left = *dpp;
1884	dp->dp_right = (struct dirlist *)NULL;
1885	dp->dp_flag = 0;
1886	dp->dp_hosts = (struct hostlist *)NULL;
1887	strcpy(dp->dp_dirp, cp);
1888	*dpp = dp;
1889	return (dp->dp_dirp);
1890}
1891
1892/*
1893 * Hang the dir list element off the dirpath binary tree as required
1894 * and update the entry for host.
1895 */
1896void
1897hang_dirp(struct dirlist *dp, struct grouplist *grp, struct exportlist *ep,
1898	int flags)
1899{
1900	struct hostlist *hp;
1901	struct dirlist *dp2;
1902
1903	if (flags & OP_ALLDIRS) {
1904		if (ep->ex_defdir)
1905			free((caddr_t)dp);
1906		else
1907			ep->ex_defdir = dp;
1908		if (grp == (struct grouplist *)NULL) {
1909			ep->ex_defdir->dp_flag |= DP_DEFSET;
1910			/* Save the default security flavors list. */
1911			ep->ex_defnumsecflavors = ep->ex_numsecflavors;
1912			if (ep->ex_numsecflavors > 0)
1913				memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
1914				    sizeof(ep->ex_secflavors));
1915		} else while (grp) {
1916			hp = get_ht();
1917			hp->ht_grp = grp;
1918			hp->ht_next = ep->ex_defdir->dp_hosts;
1919			ep->ex_defdir->dp_hosts = hp;
1920			/* Save the security flavors list for this host set. */
1921			grp->gr_numsecflavors = ep->ex_numsecflavors;
1922			if (ep->ex_numsecflavors > 0)
1923				memcpy(grp->gr_secflavors, ep->ex_secflavors,
1924				    sizeof(ep->ex_secflavors));
1925			grp = grp->gr_next;
1926		}
1927	} else {
1928
1929		/*
1930		 * Loop through the directories adding them to the tree.
1931		 */
1932		while (dp) {
1933			dp2 = dp->dp_left;
1934			add_dlist(&ep->ex_dirl, dp, grp, flags, ep);
1935			dp = dp2;
1936		}
1937	}
1938}
1939
1940/*
1941 * Traverse the binary tree either updating a node that is already there
1942 * for the new directory or adding the new node.
1943 */
1944void
1945add_dlist(struct dirlist **dpp, struct dirlist *newdp, struct grouplist *grp,
1946	int flags, struct exportlist *ep)
1947{
1948	struct dirlist *dp;
1949	struct hostlist *hp;
1950	int cmp;
1951
1952	dp = *dpp;
1953	if (dp) {
1954		cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1955		if (cmp > 0) {
1956			add_dlist(&dp->dp_left, newdp, grp, flags, ep);
1957			return;
1958		} else if (cmp < 0) {
1959			add_dlist(&dp->dp_right, newdp, grp, flags, ep);
1960			return;
1961		} else
1962			free((caddr_t)newdp);
1963	} else {
1964		dp = newdp;
1965		dp->dp_left = (struct dirlist *)NULL;
1966		*dpp = dp;
1967	}
1968	if (grp) {
1969
1970		/*
1971		 * Hang all of the host(s) off of the directory point.
1972		 */
1973		do {
1974			hp = get_ht();
1975			hp->ht_grp = grp;
1976			hp->ht_next = dp->dp_hosts;
1977			dp->dp_hosts = hp;
1978			/* Save the security flavors list for this host set. */
1979			grp->gr_numsecflavors = ep->ex_numsecflavors;
1980			if (ep->ex_numsecflavors > 0)
1981				memcpy(grp->gr_secflavors, ep->ex_secflavors,
1982				    sizeof(ep->ex_secflavors));
1983			grp = grp->gr_next;
1984		} while (grp);
1985	} else {
1986		dp->dp_flag |= DP_DEFSET;
1987		/* Save the default security flavors list. */
1988		ep->ex_defnumsecflavors = ep->ex_numsecflavors;
1989		if (ep->ex_numsecflavors > 0)
1990			memcpy(ep->ex_defsecflavors, ep->ex_secflavors,
1991			    sizeof(ep->ex_secflavors));
1992	}
1993}
1994
1995/*
1996 * Search for a dirpath on the export point.
1997 */
1998struct dirlist *
1999dirp_search(struct dirlist *dp, char *dirp)
2000{
2001	int cmp;
2002
2003	if (dp) {
2004		cmp = strcmp(dp->dp_dirp, dirp);
2005		if (cmp > 0)
2006			return (dirp_search(dp->dp_left, dirp));
2007		else if (cmp < 0)
2008			return (dirp_search(dp->dp_right, dirp));
2009		else
2010			return (dp);
2011	}
2012	return (dp);
2013}
2014
2015/*
2016 * Scan for a host match in a directory tree.
2017 */
2018int
2019chk_host(struct dirlist *dp, struct sockaddr *saddr, int *defsetp,
2020	int *hostsetp, int *numsecflavors, int **secflavorsp)
2021{
2022	struct hostlist *hp;
2023	struct grouplist *grp;
2024	struct addrinfo *ai;
2025
2026	if (dp) {
2027		if (dp->dp_flag & DP_DEFSET)
2028			*defsetp = dp->dp_flag;
2029		hp = dp->dp_hosts;
2030		while (hp) {
2031			grp = hp->ht_grp;
2032			switch (grp->gr_type) {
2033			case GT_HOST:
2034				ai = grp->gr_ptr.gt_addrinfo;
2035				for (; ai; ai = ai->ai_next) {
2036					if (!sacmp(ai->ai_addr, saddr, NULL)) {
2037						*hostsetp =
2038						    (hp->ht_flag | DP_HOSTSET);
2039						if (numsecflavors != NULL) {
2040							*numsecflavors =
2041							    grp->gr_numsecflavors;
2042							*secflavorsp =
2043							    grp->gr_secflavors;
2044						}
2045						return (1);
2046					}
2047				}
2048				break;
2049			case GT_NET:
2050				if (!sacmp(saddr, (struct sockaddr *)
2051				    &grp->gr_ptr.gt_net.nt_net,
2052				    (struct sockaddr *)
2053				    &grp->gr_ptr.gt_net.nt_mask)) {
2054					*hostsetp = (hp->ht_flag | DP_HOSTSET);
2055					if (numsecflavors != NULL) {
2056						*numsecflavors =
2057						    grp->gr_numsecflavors;
2058						*secflavorsp =
2059						    grp->gr_secflavors;
2060					}
2061					return (1);
2062				}
2063				break;
2064			}
2065			hp = hp->ht_next;
2066		}
2067	}
2068	return (0);
2069}
2070
2071/*
2072 * Scan tree for a host that matches the address.
2073 */
2074int
2075scan_tree(struct dirlist *dp, struct sockaddr *saddr)
2076{
2077	int defset, hostset;
2078
2079	if (dp) {
2080		if (scan_tree(dp->dp_left, saddr))
2081			return (1);
2082		if (chk_host(dp, saddr, &defset, &hostset, NULL, NULL))
2083			return (1);
2084		if (scan_tree(dp->dp_right, saddr))
2085			return (1);
2086	}
2087	return (0);
2088}
2089
2090/*
2091 * Traverse the dirlist tree and free it up.
2092 */
2093void
2094free_dir(struct dirlist *dp)
2095{
2096
2097	if (dp) {
2098		free_dir(dp->dp_left);
2099		free_dir(dp->dp_right);
2100		free_host(dp->dp_hosts);
2101		free((caddr_t)dp);
2102	}
2103}
2104
2105/*
2106 * Parse a colon separated list of security flavors
2107 */
2108int
2109parsesec(char *seclist, struct exportlist *ep)
2110{
2111	char *cp, savedc;
2112	int flavor;
2113
2114	ep->ex_numsecflavors = 0;
2115	for (;;) {
2116		cp = strchr(seclist, ':');
2117		if (cp) {
2118			savedc = *cp;
2119			*cp = '\0';
2120		}
2121
2122		if (!strcmp(seclist, "sys"))
2123			flavor = AUTH_SYS;
2124		else if (!strcmp(seclist, "krb5"))
2125			flavor = RPCSEC_GSS_KRB5;
2126		else if (!strcmp(seclist, "krb5i"))
2127			flavor = RPCSEC_GSS_KRB5I;
2128		else if (!strcmp(seclist, "krb5p"))
2129			flavor = RPCSEC_GSS_KRB5P;
2130		else {
2131			if (cp)
2132				*cp = savedc;
2133			syslog(LOG_ERR, "bad sec flavor: %s", seclist);
2134			return (1);
2135		}
2136		if (ep->ex_numsecflavors == MAXSECFLAVORS) {
2137			if (cp)
2138				*cp = savedc;
2139			syslog(LOG_ERR, "too many sec flavors: %s", seclist);
2140			return (1);
2141		}
2142		ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
2143		ep->ex_numsecflavors++;
2144		if (cp) {
2145			*cp = savedc;
2146			seclist = cp + 1;
2147		} else {
2148			break;
2149		}
2150	}
2151	return (0);
2152}
2153
2154/*
2155 * Parse the option string and update fields.
2156 * Option arguments may either be -<option>=<value> or
2157 * -<option> <value>
2158 */
2159int
2160do_opt(char **cpp, char **endcpp, struct exportlist *ep, struct grouplist *grp,
2161	int *has_hostp, int *exflagsp, struct xucred *cr)
2162{
2163	char *cpoptarg, *cpoptend;
2164	char *cp, *endcp, *cpopt, savedc, savedc2;
2165	int allflag, usedarg;
2166
2167	savedc2 = '\0';
2168	cpopt = *cpp;
2169	cpopt++;
2170	cp = *endcpp;
2171	savedc = *cp;
2172	*cp = '\0';
2173	while (cpopt && *cpopt) {
2174		allflag = 1;
2175		usedarg = -2;
2176		if ((cpoptend = strchr(cpopt, ','))) {
2177			*cpoptend++ = '\0';
2178			if ((cpoptarg = strchr(cpopt, '=')))
2179				*cpoptarg++ = '\0';
2180		} else {
2181			if ((cpoptarg = strchr(cpopt, '=')))
2182				*cpoptarg++ = '\0';
2183			else {
2184				*cp = savedc;
2185				nextfield(&cp, &endcp);
2186				**endcpp = '\0';
2187				if (endcp > cp && *cp != '-') {
2188					cpoptarg = cp;
2189					savedc2 = *endcp;
2190					*endcp = '\0';
2191					usedarg = 0;
2192				}
2193			}
2194		}
2195		if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
2196			*exflagsp |= MNT_EXRDONLY;
2197		} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
2198		    !(allflag = strcmp(cpopt, "mapall")) ||
2199		    !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
2200			usedarg++;
2201			parsecred(cpoptarg, cr);
2202			if (allflag == 0) {
2203				*exflagsp |= MNT_EXPORTANON;
2204				opt_flags |= OP_MAPALL;
2205			} else
2206				opt_flags |= OP_MAPROOT;
2207		} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
2208		    !strcmp(cpopt, "m"))) {
2209			if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
2210				syslog(LOG_ERR, "bad mask: %s", cpoptarg);
2211				return (1);
2212			}
2213			usedarg++;
2214			opt_flags |= OP_MASK;
2215		} else if (cpoptarg && (!strcmp(cpopt, "network") ||
2216			!strcmp(cpopt, "n"))) {
2217			if (strchr(cpoptarg, '/') != NULL) {
2218				if (debug)
2219					fprintf(stderr, "setting OP_MASKLEN\n");
2220				opt_flags |= OP_MASKLEN;
2221			}
2222			if (grp->gr_type != GT_NULL) {
2223				syslog(LOG_ERR, "network/host conflict");
2224				return (1);
2225			} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
2226				syslog(LOG_ERR, "bad net: %s", cpoptarg);
2227				return (1);
2228			}
2229			grp->gr_type = GT_NET;
2230			*has_hostp = 1;
2231			usedarg++;
2232			opt_flags |= OP_NET;
2233		} else if (!strcmp(cpopt, "alldirs")) {
2234			opt_flags |= OP_ALLDIRS;
2235		} else if (!strcmp(cpopt, "public")) {
2236			*exflagsp |= MNT_EXPUBLIC;
2237		} else if (!strcmp(cpopt, "webnfs")) {
2238			*exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
2239			opt_flags |= OP_MAPALL;
2240		} else if (cpoptarg && !strcmp(cpopt, "index")) {
2241			ep->ex_indexfile = strdup(cpoptarg);
2242		} else if (!strcmp(cpopt, "quiet")) {
2243			opt_flags |= OP_QUIET;
2244		} else if (cpoptarg && !strcmp(cpopt, "sec")) {
2245			if (parsesec(cpoptarg, ep))
2246				return (1);
2247			opt_flags |= OP_SEC;
2248			usedarg++;
2249		} else {
2250			syslog(LOG_ERR, "bad opt %s", cpopt);
2251			return (1);
2252		}
2253		if (usedarg >= 0) {
2254			*endcp = savedc2;
2255			**endcpp = savedc;
2256			if (usedarg > 0) {
2257				*cpp = cp;
2258				*endcpp = endcp;
2259			}
2260			return (0);
2261		}
2262		cpopt = cpoptend;
2263	}
2264	**endcpp = savedc;
2265	return (0);
2266}
2267
2268/*
2269 * Translate a character string to the corresponding list of network
2270 * addresses for a hostname.
2271 */
2272int
2273get_host(char *cp, struct grouplist *grp, struct grouplist *tgrp)
2274{
2275	struct grouplist *checkgrp;
2276	struct addrinfo *ai, *tai, hints;
2277	int ecode;
2278	char host[NI_MAXHOST];
2279
2280	if (grp->gr_type != GT_NULL) {
2281		syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
2282		return (1);
2283	}
2284	memset(&hints, 0, sizeof hints);
2285	hints.ai_flags = AI_CANONNAME;
2286	hints.ai_protocol = IPPROTO_UDP;
2287	ecode = getaddrinfo(cp, NULL, &hints, &ai);
2288	if (ecode != 0) {
2289		syslog(LOG_ERR,"can't get address info for host %s", cp);
2290		return 1;
2291	}
2292	grp->gr_ptr.gt_addrinfo = ai;
2293	while (ai != NULL) {
2294		if (ai->ai_canonname == NULL) {
2295			if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
2296			    sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
2297				strlcpy(host, "?", sizeof(host));
2298			ai->ai_canonname = strdup(host);
2299			ai->ai_flags |= AI_CANONNAME;
2300		}
2301		if (debug)
2302			fprintf(stderr, "got host %s\n", ai->ai_canonname);
2303		/*
2304		 * Sanity check: make sure we don't already have an entry
2305		 * for this host in the grouplist.
2306		 */
2307		for (checkgrp = tgrp; checkgrp != NULL;
2308		    checkgrp = checkgrp->gr_next) {
2309			if (checkgrp->gr_type != GT_HOST)
2310				continue;
2311			for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
2312			    tai = tai->ai_next) {
2313				if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
2314					continue;
2315				if (debug)
2316					fprintf(stderr,
2317					    "ignoring duplicate host %s\n",
2318					    ai->ai_canonname);
2319				grp->gr_type = GT_IGNORE;
2320				return (0);
2321			}
2322		}
2323		ai = ai->ai_next;
2324	}
2325	grp->gr_type = GT_HOST;
2326	return (0);
2327}
2328
2329/*
2330 * Free up an exports list component
2331 */
2332void
2333free_exp(struct exportlist *ep)
2334{
2335
2336	if (ep->ex_defdir) {
2337		free_host(ep->ex_defdir->dp_hosts);
2338		free((caddr_t)ep->ex_defdir);
2339	}
2340	if (ep->ex_fsdir)
2341		free(ep->ex_fsdir);
2342	if (ep->ex_indexfile)
2343		free(ep->ex_indexfile);
2344	free_dir(ep->ex_dirl);
2345	free((caddr_t)ep);
2346}
2347
2348/*
2349 * Free hosts.
2350 */
2351void
2352free_host(struct hostlist *hp)
2353{
2354	struct hostlist *hp2;
2355
2356	while (hp) {
2357		hp2 = hp;
2358		hp = hp->ht_next;
2359		free((caddr_t)hp2);
2360	}
2361}
2362
2363struct hostlist *
2364get_ht(void)
2365{
2366	struct hostlist *hp;
2367
2368	hp = (struct hostlist *)malloc(sizeof (struct hostlist));
2369	if (hp == (struct hostlist *)NULL)
2370		out_of_mem();
2371	hp->ht_next = (struct hostlist *)NULL;
2372	hp->ht_flag = 0;
2373	return (hp);
2374}
2375
2376/*
2377 * Out of memory, fatal
2378 */
2379void
2380out_of_mem(void)
2381{
2382
2383	syslog(LOG_ERR, "out of memory");
2384	exit(2);
2385}
2386
2387/*
2388 * Do the nmount() syscall with the update flag to push the export info into
2389 * the kernel.
2390 */
2391int
2392do_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
2393    struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
2394{
2395	struct statfs fsb1;
2396	struct addrinfo *ai;
2397	struct export_args ea, *eap;
2398	char errmsg[255];
2399	char *cp;
2400	int done;
2401	char savedc;
2402	struct iovec *iov;
2403	int i, iovlen;
2404	int ret;
2405	struct nfsex_args nfsea;
2406
2407	if (run_v4server > 0)
2408		eap = &nfsea.export;
2409	else
2410		eap = &ea;
2411
2412	cp = NULL;
2413	savedc = '\0';
2414	iov = NULL;
2415	iovlen = 0;
2416	ret = 0;
2417
2418	bzero(eap, sizeof (struct export_args));
2419	bzero(errmsg, sizeof(errmsg));
2420	eap->ex_flags = exflags;
2421	eap->ex_anon = *anoncrp;
2422	eap->ex_indexfile = ep->ex_indexfile;
2423	if (grp->gr_type == GT_HOST)
2424		ai = grp->gr_ptr.gt_addrinfo;
2425	else
2426		ai = NULL;
2427	eap->ex_numsecflavors = ep->ex_numsecflavors;
2428	for (i = 0; i < eap->ex_numsecflavors; i++)
2429		eap->ex_secflavors[i] = ep->ex_secflavors[i];
2430	if (eap->ex_numsecflavors == 0) {
2431		eap->ex_numsecflavors = 1;
2432		eap->ex_secflavors[0] = AUTH_SYS;
2433	}
2434	done = FALSE;
2435
2436	if (v4root_phase == 0) {
2437		build_iovec(&iov, &iovlen, "fstype", NULL, 0);
2438		build_iovec(&iov, &iovlen, "fspath", NULL, 0);
2439		build_iovec(&iov, &iovlen, "from", NULL, 0);
2440		build_iovec(&iov, &iovlen, "update", NULL, 0);
2441		build_iovec(&iov, &iovlen, "export", eap,
2442		    sizeof (struct export_args));
2443		build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
2444	}
2445
2446	while (!done) {
2447		switch (grp->gr_type) {
2448		case GT_HOST:
2449			if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
2450				goto skip;
2451			eap->ex_addr = ai->ai_addr;
2452			eap->ex_addrlen = ai->ai_addrlen;
2453			eap->ex_masklen = 0;
2454			break;
2455		case GT_NET:
2456			if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
2457			    have_v6 == 0)
2458				goto skip;
2459			eap->ex_addr =
2460			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
2461			eap->ex_addrlen =
2462			    ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
2463			eap->ex_mask =
2464			    (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
2465			eap->ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
2466			break;
2467		case GT_DEFAULT:
2468			eap->ex_addr = NULL;
2469			eap->ex_addrlen = 0;
2470			eap->ex_mask = NULL;
2471			eap->ex_masklen = 0;
2472			break;
2473		case GT_IGNORE:
2474			ret = 0;
2475			goto error_exit;
2476			break;
2477		default:
2478			syslog(LOG_ERR, "bad grouptype");
2479			if (cp)
2480				*cp = savedc;
2481			ret = 1;
2482			goto error_exit;
2483		};
2484
2485		/*
2486		 * For V4:, use the nfssvc() syscall, instead of mount().
2487		 */
2488		if (v4root_phase == 2) {
2489			nfsea.fspec = v4root_dirpath;
2490			if (run_v4server > 0 &&
2491			    nfssvc(NFSSVC_V4ROOTEXPORT, (caddr_t)&nfsea) < 0) {
2492				syslog(LOG_ERR, "Exporting V4: failed");
2493				return (2);
2494			}
2495		} else {
2496			/*
2497			 * XXX:
2498			 * Maybe I should just use the fsb->f_mntonname path
2499			 * instead of looping back up the dirp to the mount
2500			 * point??
2501			 * Also, needs to know how to export all types of local
2502			 * exportable filesystems and not just "ufs".
2503			 */
2504			iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
2505			iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
2506			iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
2507			iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
2508			iov[5].iov_base = fsb->f_mntfromname; /* "from" */
2509			iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
2510			errmsg[0] = '\0';
2511
2512			while (nmount(iov, iovlen, fsb->f_flags) < 0) {
2513				if (cp)
2514					*cp-- = savedc;
2515				else
2516					cp = dirp + dirplen - 1;
2517				if (opt_flags & OP_QUIET) {
2518					ret = 1;
2519					goto error_exit;
2520				}
2521				if (errno == EPERM) {
2522					if (debug)
2523						warnx("can't change attributes for %s: %s",
2524						    dirp, errmsg);
2525					syslog(LOG_ERR,
2526					   "can't change attributes for %s: %s",
2527					    dirp, errmsg);
2528					ret = 1;
2529					goto error_exit;
2530				}
2531				if (opt_flags & OP_ALLDIRS) {
2532					if (errno == EINVAL)
2533						syslog(LOG_ERR,
2534		"-alldirs requested but %s is not a filesystem mountpoint",
2535						    dirp);
2536					else
2537						syslog(LOG_ERR,
2538						    "could not remount %s: %m",
2539						    dirp);
2540					ret = 1;
2541					goto error_exit;
2542				}
2543				/* back up over the last component */
2544				while (*cp == '/' && cp > dirp)
2545					cp--;
2546				while (*(cp - 1) != '/' && cp > dirp)
2547					cp--;
2548				if (cp == dirp) {
2549					if (debug)
2550						warnx("mnt unsucc");
2551					syslog(LOG_ERR, "can't export %s %s",
2552					    dirp, errmsg);
2553					ret = 1;
2554					goto error_exit;
2555				}
2556				savedc = *cp;
2557				*cp = '\0';
2558				/*
2559				 * Check that we're still on the same
2560				 * filesystem.
2561				 */
2562				if (statfs(dirp, &fsb1) != 0 ||
2563				    bcmp(&fsb1.f_fsid, &fsb->f_fsid,
2564				    sizeof (fsb1.f_fsid)) != 0) {
2565					*cp = savedc;
2566					syslog(LOG_ERR,
2567					    "can't export %s %s", dirp,
2568					    errmsg);
2569					ret = 1;
2570					goto error_exit;
2571				}
2572			}
2573		}
2574
2575		/*
2576		 * For the experimental server:
2577		 * If this is the public directory, get the file handle
2578		 * and load it into the kernel via the nfssvc() syscall.
2579		 */
2580		if (run_v4server > 0 && (exflags & MNT_EXPUBLIC) != 0) {
2581			fhandle_t fh;
2582			char *public_name;
2583
2584			if (eap->ex_indexfile != NULL)
2585				public_name = eap->ex_indexfile;
2586			else
2587				public_name = dirp;
2588			if (getfh(public_name, &fh) < 0)
2589				syslog(LOG_ERR,
2590				    "Can't get public fh for %s", public_name);
2591			else if (nfssvc(NFSSVC_PUBLICFH, (caddr_t)&fh) < 0)
2592				syslog(LOG_ERR,
2593				    "Can't set public fh for %s", public_name);
2594			else
2595				has_publicfh = 1;
2596		}
2597skip:
2598		if (ai != NULL)
2599			ai = ai->ai_next;
2600		if (ai == NULL)
2601			done = TRUE;
2602	}
2603	if (cp)
2604		*cp = savedc;
2605error_exit:
2606	/* free strings allocated by strdup() in getmntopts.c */
2607	if (iov != NULL) {
2608		free(iov[0].iov_base); /* fstype */
2609		free(iov[2].iov_base); /* fspath */
2610		free(iov[4].iov_base); /* from */
2611		free(iov[6].iov_base); /* update */
2612		free(iov[8].iov_base); /* export */
2613		free(iov[10].iov_base); /* errmsg */
2614
2615		/* free iov, allocated by realloc() */
2616		free(iov);
2617	}
2618	return (ret);
2619}
2620
2621/*
2622 * Translate a net address.
2623 *
2624 * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
2625 */
2626int
2627get_net(char *cp, struct netmsk *net, int maskflg)
2628{
2629	struct netent *np = NULL;
2630	char *name, *p, *prefp;
2631	struct sockaddr_in sin;
2632	struct sockaddr *sa = NULL;
2633	struct addrinfo hints, *ai = NULL;
2634	char netname[NI_MAXHOST];
2635	long preflen;
2636
2637	p = prefp = NULL;
2638	if ((opt_flags & OP_MASKLEN) && !maskflg) {
2639		p = strchr(cp, '/');
2640		*p = '\0';
2641		prefp = p + 1;
2642	}
2643
2644	/*
2645	 * Check for a numeric address first. We wish to avoid
2646	 * possible DNS lookups in getnetbyname().
2647	 */
2648	if (isxdigit(*cp) || *cp == ':') {
2649		memset(&hints, 0, sizeof hints);
2650		/* Ensure the mask and the network have the same family. */
2651		if (maskflg && (opt_flags & OP_NET))
2652			hints.ai_family = net->nt_net.ss_family;
2653		else if (!maskflg && (opt_flags & OP_HAVEMASK))
2654			hints.ai_family = net->nt_mask.ss_family;
2655		else
2656			hints.ai_family = AF_UNSPEC;
2657		hints.ai_flags = AI_NUMERICHOST;
2658		if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
2659			sa = ai->ai_addr;
2660		if (sa != NULL && ai->ai_family == AF_INET) {
2661			/*
2662			 * The address in `cp' is really a network address, so
2663			 * use inet_network() to re-interpret this correctly.
2664			 * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
2665			 */
2666			bzero(&sin, sizeof sin);
2667			sin.sin_family = AF_INET;
2668			sin.sin_len = sizeof sin;
2669			sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
2670			if (debug)
2671				fprintf(stderr, "get_net: v4 addr %s\n",
2672				    inet_ntoa(sin.sin_addr));
2673			sa = (struct sockaddr *)&sin;
2674		}
2675	}
2676	if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
2677		bzero(&sin, sizeof sin);
2678		sin.sin_family = AF_INET;
2679		sin.sin_len = sizeof sin;
2680		sin.sin_addr = inet_makeaddr(np->n_net, 0);
2681		sa = (struct sockaddr *)&sin;
2682	}
2683	if (sa == NULL)
2684		goto fail;
2685
2686	if (maskflg) {
2687		/* The specified sockaddr is a mask. */
2688		if (checkmask(sa) != 0)
2689			goto fail;
2690		bcopy(sa, &net->nt_mask, sa->sa_len);
2691		opt_flags |= OP_HAVEMASK;
2692	} else {
2693		/* The specified sockaddr is a network address. */
2694		bcopy(sa, &net->nt_net, sa->sa_len);
2695
2696		/* Get a network name for the export list. */
2697		if (np) {
2698			name = np->n_name;
2699		} else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2700		   NULL, 0, NI_NUMERICHOST) == 0) {
2701			name = netname;
2702		} else {
2703			goto fail;
2704		}
2705		if ((net->nt_name = strdup(name)) == NULL)
2706			out_of_mem();
2707
2708		/*
2709		 * Extract a mask from either a "/<masklen>" suffix, or
2710		 * from the class of an IPv4 address.
2711		 */
2712		if (opt_flags & OP_MASKLEN) {
2713			preflen = strtol(prefp, NULL, 10);
2714			if (preflen < 0L || preflen == LONG_MAX)
2715				goto fail;
2716			bcopy(sa, &net->nt_mask, sa->sa_len);
2717			if (makemask(&net->nt_mask, (int)preflen) != 0)
2718				goto fail;
2719			opt_flags |= OP_HAVEMASK;
2720			*p = '/';
2721		} else if (sa->sa_family == AF_INET &&
2722		    (opt_flags & OP_MASK) == 0) {
2723			in_addr_t addr;
2724
2725			addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
2726			if (IN_CLASSA(addr))
2727				preflen = 8;
2728			else if (IN_CLASSB(addr))
2729				preflen = 16;
2730			else if (IN_CLASSC(addr))
2731				preflen = 24;
2732			else if (IN_CLASSD(addr))
2733				preflen = 28;
2734			else
2735				preflen = 32;	/* XXX */
2736
2737			bcopy(sa, &net->nt_mask, sa->sa_len);
2738			makemask(&net->nt_mask, (int)preflen);
2739			opt_flags |= OP_HAVEMASK;
2740		}
2741	}
2742
2743	if (ai)
2744		freeaddrinfo(ai);
2745	return 0;
2746
2747fail:
2748	if (ai)
2749		freeaddrinfo(ai);
2750	return 1;
2751}
2752
2753/*
2754 * Parse out the next white space separated field
2755 */
2756void
2757nextfield(char **cp, char **endcp)
2758{
2759	char *p;
2760
2761	p = *cp;
2762	while (*p == ' ' || *p == '\t')
2763		p++;
2764	if (*p == '\n' || *p == '\0')
2765		*cp = *endcp = p;
2766	else {
2767		*cp = p++;
2768		while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
2769			p++;
2770		*endcp = p;
2771	}
2772}
2773
2774/*
2775 * Get an exports file line. Skip over blank lines and handle line
2776 * continuations.
2777 */
2778int
2779get_line(void)
2780{
2781	char *p, *cp;
2782	size_t len;
2783	int totlen, cont_line;
2784
2785	/*
2786	 * Loop around ignoring blank lines and getting all continuation lines.
2787	 */
2788	p = line;
2789	totlen = 0;
2790	do {
2791		if ((p = fgetln(exp_file, &len)) == NULL)
2792			return (0);
2793		cp = p + len - 1;
2794		cont_line = 0;
2795		while (cp >= p &&
2796		    (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
2797			if (*cp == '\\')
2798				cont_line = 1;
2799			cp--;
2800			len--;
2801		}
2802		if (cont_line) {
2803			*++cp = ' ';
2804			len++;
2805		}
2806		if (linesize < len + totlen + 1) {
2807			linesize = len + totlen + 1;
2808			line = realloc(line, linesize);
2809			if (line == NULL)
2810				out_of_mem();
2811		}
2812		memcpy(line + totlen, p, len);
2813		totlen += len;
2814		line[totlen] = '\0';
2815	} while (totlen == 0 || cont_line);
2816	return (1);
2817}
2818
2819/*
2820 * Parse a description of a credential.
2821 */
2822void
2823parsecred(char *namelist, struct xucred *cr)
2824{
2825	char *name;
2826	int cnt;
2827	char *names;
2828	struct passwd *pw;
2829	struct group *gr;
2830	gid_t groups[XU_NGROUPS + 1];
2831	int ngroups;
2832
2833	cr->cr_version = XUCRED_VERSION;
2834	/*
2835	 * Set up the unprivileged user.
2836	 */
2837	cr->cr_uid = -2;
2838	cr->cr_groups[0] = -2;
2839	cr->cr_ngroups = 1;
2840	/*
2841	 * Get the user's password table entry.
2842	 */
2843	names = strsep(&namelist, " \t\n");
2844	name = strsep(&names, ":");
2845	if (isdigit(*name) || *name == '-')
2846		pw = getpwuid(atoi(name));
2847	else
2848		pw = getpwnam(name);
2849	/*
2850	 * Credentials specified as those of a user.
2851	 */
2852	if (names == NULL) {
2853		if (pw == NULL) {
2854			syslog(LOG_ERR, "unknown user: %s", name);
2855			return;
2856		}
2857		cr->cr_uid = pw->pw_uid;
2858		ngroups = XU_NGROUPS + 1;
2859		if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
2860			syslog(LOG_ERR, "too many groups");
2861		/*
2862		 * Compress out duplicate.
2863		 */
2864		cr->cr_ngroups = ngroups - 1;
2865		cr->cr_groups[0] = groups[0];
2866		for (cnt = 2; cnt < ngroups; cnt++)
2867			cr->cr_groups[cnt - 1] = groups[cnt];
2868		return;
2869	}
2870	/*
2871	 * Explicit credential specified as a colon separated list:
2872	 *	uid:gid:gid:...
2873	 */
2874	if (pw != NULL)
2875		cr->cr_uid = pw->pw_uid;
2876	else if (isdigit(*name) || *name == '-')
2877		cr->cr_uid = atoi(name);
2878	else {
2879		syslog(LOG_ERR, "unknown user: %s", name);
2880		return;
2881	}
2882	cr->cr_ngroups = 0;
2883	while (names != NULL && *names != '\0' && cr->cr_ngroups < XU_NGROUPS) {
2884		name = strsep(&names, ":");
2885		if (isdigit(*name) || *name == '-') {
2886			cr->cr_groups[cr->cr_ngroups++] = atoi(name);
2887		} else {
2888			if ((gr = getgrnam(name)) == NULL) {
2889				syslog(LOG_ERR, "unknown group: %s", name);
2890				continue;
2891			}
2892			cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2893		}
2894	}
2895	if (names != NULL && *names != '\0' && cr->cr_ngroups == XU_NGROUPS)
2896		syslog(LOG_ERR, "too many groups");
2897}
2898
2899#define	STRSIZ	(MNTNAMLEN+MNTPATHLEN+50)
2900/*
2901 * Routines that maintain the remote mounttab
2902 */
2903void
2904get_mountlist(void)
2905{
2906	struct mountlist *mlp, **mlpp;
2907	char *host, *dirp, *cp;
2908	char str[STRSIZ];
2909	FILE *mlfile;
2910
2911	if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2912		if (errno == ENOENT)
2913			return;
2914		else {
2915			syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
2916			return;
2917		}
2918	}
2919	mlpp = &mlhead;
2920	while (fgets(str, STRSIZ, mlfile) != NULL) {
2921		cp = str;
2922		host = strsep(&cp, " \t\n");
2923		dirp = strsep(&cp, " \t\n");
2924		if (host == NULL || dirp == NULL)
2925			continue;
2926		mlp = (struct mountlist *)malloc(sizeof (*mlp));
2927		if (mlp == (struct mountlist *)NULL)
2928			out_of_mem();
2929		strncpy(mlp->ml_host, host, MNTNAMLEN);
2930		mlp->ml_host[MNTNAMLEN] = '\0';
2931		strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
2932		mlp->ml_dirp[MNTPATHLEN] = '\0';
2933		mlp->ml_next = (struct mountlist *)NULL;
2934		*mlpp = mlp;
2935		mlpp = &mlp->ml_next;
2936	}
2937	fclose(mlfile);
2938}
2939
2940void
2941del_mlist(char *hostp, char *dirp)
2942{
2943	struct mountlist *mlp, **mlpp;
2944	struct mountlist *mlp2;
2945	FILE *mlfile;
2946	int fnd = 0;
2947
2948	mlpp = &mlhead;
2949	mlp = mlhead;
2950	while (mlp) {
2951		if (!strcmp(mlp->ml_host, hostp) &&
2952		    (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2953			fnd = 1;
2954			mlp2 = mlp;
2955			*mlpp = mlp = mlp->ml_next;
2956			free((caddr_t)mlp2);
2957		} else {
2958			mlpp = &mlp->ml_next;
2959			mlp = mlp->ml_next;
2960		}
2961	}
2962	if (fnd) {
2963		if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
2964			syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
2965			return;
2966		}
2967		mlp = mlhead;
2968		while (mlp) {
2969			fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2970			mlp = mlp->ml_next;
2971		}
2972		fclose(mlfile);
2973	}
2974}
2975
2976void
2977add_mlist(char *hostp, char *dirp)
2978{
2979	struct mountlist *mlp, **mlpp;
2980	FILE *mlfile;
2981
2982	mlpp = &mlhead;
2983	mlp = mlhead;
2984	while (mlp) {
2985		if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2986			return;
2987		mlpp = &mlp->ml_next;
2988		mlp = mlp->ml_next;
2989	}
2990	mlp = (struct mountlist *)malloc(sizeof (*mlp));
2991	if (mlp == (struct mountlist *)NULL)
2992		out_of_mem();
2993	strncpy(mlp->ml_host, hostp, MNTNAMLEN);
2994	mlp->ml_host[MNTNAMLEN] = '\0';
2995	strncpy(mlp->ml_dirp, dirp, MNTPATHLEN);
2996	mlp->ml_dirp[MNTPATHLEN] = '\0';
2997	mlp->ml_next = (struct mountlist *)NULL;
2998	*mlpp = mlp;
2999	if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
3000		syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
3001		return;
3002	}
3003	fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
3004	fclose(mlfile);
3005}
3006
3007/*
3008 * Free up a group list.
3009 */
3010void
3011free_grp(struct grouplist *grp)
3012{
3013	if (grp->gr_type == GT_HOST) {
3014		if (grp->gr_ptr.gt_addrinfo != NULL)
3015			freeaddrinfo(grp->gr_ptr.gt_addrinfo);
3016	} else if (grp->gr_type == GT_NET) {
3017		if (grp->gr_ptr.gt_net.nt_name)
3018			free(grp->gr_ptr.gt_net.nt_name);
3019	}
3020	free((caddr_t)grp);
3021}
3022
3023#ifdef DEBUG
3024void
3025SYSLOG(int pri, const char *fmt, ...)
3026{
3027	va_list ap;
3028
3029	va_start(ap, fmt);
3030	vfprintf(stderr, fmt, ap);
3031	va_end(ap);
3032}
3033#endif /* DEBUG */
3034
3035/*
3036 * Check options for consistency.
3037 */
3038int
3039check_options(struct dirlist *dp)
3040{
3041
3042	if (v4root_phase == 0 && dp == NULL)
3043	    return (1);
3044	if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
3045	    syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
3046	    return (1);
3047	}
3048	if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
3049		syslog(LOG_ERR, "-mask requires -network");
3050		return (1);
3051	}
3052	if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
3053		syslog(LOG_ERR, "-network requires mask specification");
3054		return (1);
3055	}
3056	if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
3057		syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
3058		return (1);
3059	}
3060	if (v4root_phase > 0 &&
3061	    (opt_flags &
3062	     ~(OP_SEC | OP_MASK | OP_NET | OP_HAVEMASK | OP_MASKLEN)) != 0) {
3063	    syslog(LOG_ERR,"only -sec,-net,-mask options allowed on V4:");
3064	    return (1);
3065	}
3066	if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
3067	    syslog(LOG_ERR, "-alldirs has multiple directories");
3068	    return (1);
3069	}
3070	return (0);
3071}
3072
3073/*
3074 * Check an absolute directory path for any symbolic links. Return true
3075 */
3076int
3077check_dirpath(char *dirp)
3078{
3079	char *cp;
3080	int ret = 1;
3081	struct stat sb;
3082
3083	cp = dirp + 1;
3084	while (*cp && ret) {
3085		if (*cp == '/') {
3086			*cp = '\0';
3087			if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3088				ret = 0;
3089			*cp = '/';
3090		}
3091		cp++;
3092	}
3093	if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
3094		ret = 0;
3095	return (ret);
3096}
3097
3098/*
3099 * Make a netmask according to the specified prefix length. The ss_family
3100 * and other non-address fields must be initialised before calling this.
3101 */
3102int
3103makemask(struct sockaddr_storage *ssp, int bitlen)
3104{
3105	u_char *p;
3106	int bits, i, len;
3107
3108	if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
3109		return (-1);
3110	if (bitlen > len * CHAR_BIT)
3111		return (-1);
3112
3113	for (i = 0; i < len; i++) {
3114		bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen;
3115		*p++ = (u_char)~0 << (CHAR_BIT - bits);
3116		bitlen -= bits;
3117	}
3118	return 0;
3119}
3120
3121/*
3122 * Check that the sockaddr is a valid netmask. Returns 0 if the mask
3123 * is acceptable (i.e. of the form 1...10....0).
3124 */
3125int
3126checkmask(struct sockaddr *sa)
3127{
3128	u_char *mask;
3129	int i, len;
3130
3131	if ((mask = sa_rawaddr(sa, &len)) == NULL)
3132		return (-1);
3133
3134	for (i = 0; i < len; i++)
3135		if (mask[i] != 0xff)
3136			break;
3137	if (i < len) {
3138		if (~mask[i] & (u_char)(~mask[i] + 1))
3139			return (-1);
3140		i++;
3141	}
3142	for (; i < len; i++)
3143		if (mask[i] != 0)
3144			return (-1);
3145	return (0);
3146}
3147
3148/*
3149 * Compare two sockaddrs according to a specified mask. Return zero if
3150 * `sa1' matches `sa2' when filtered by the netmask in `samask'.
3151 * If samask is NULL, perform a full comparison.
3152 */
3153int
3154sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
3155{
3156	unsigned char *p1, *p2, *mask;
3157	int len, i;
3158
3159	if (sa1->sa_family != sa2->sa_family ||
3160	    (p1 = sa_rawaddr(sa1, &len)) == NULL ||
3161	    (p2 = sa_rawaddr(sa2, NULL)) == NULL)
3162		return (1);
3163
3164	switch (sa1->sa_family) {
3165	case AF_INET6:
3166		if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
3167		    ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
3168			return (1);
3169		break;
3170	}
3171
3172	/* Simple binary comparison if no mask specified. */
3173	if (samask == NULL)
3174		return (memcmp(p1, p2, len));
3175
3176	/* Set up the mask, and do a mask-based comparison. */
3177	if (sa1->sa_family != samask->sa_family ||
3178	    (mask = sa_rawaddr(samask, NULL)) == NULL)
3179		return (1);
3180
3181	for (i = 0; i < len; i++)
3182		if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
3183			return (1);
3184	return (0);
3185}
3186
3187/*
3188 * Return a pointer to the part of the sockaddr that contains the
3189 * raw address, and set *nbytes to its length in bytes. Returns
3190 * NULL if the address family is unknown.
3191 */
3192void *
3193sa_rawaddr(struct sockaddr *sa, int *nbytes) {
3194	void *p;
3195	int len;
3196
3197	switch (sa->sa_family) {
3198	case AF_INET:
3199		len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
3200		p = &((struct sockaddr_in *)sa)->sin_addr;
3201		break;
3202	case AF_INET6:
3203		len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
3204		p = &((struct sockaddr_in6 *)sa)->sin6_addr;
3205		break;
3206	default:
3207		p = NULL;
3208		len = 0;
3209	}
3210
3211	if (nbytes != NULL)
3212		*nbytes = len;
3213	return (p);
3214}
3215
3216void
3217huphandler(int sig __unused)
3218{
3219	got_sighup = 1;
3220}
3221
3222void terminate(int sig __unused)
3223{
3224	pidfile_remove(pfh);
3225	rpcb_unset(MOUNTPROG, MOUNTVERS, NULL);
3226	rpcb_unset(MOUNTPROG, MOUNTVERS3, NULL);
3227	exit (0);
3228}
3229
3230