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