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