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