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