]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/mountd/mountd.c
WARNS=4, de-__P()
[FreeBSD/FreeBSD.git] / usr.sbin / mountd / mountd.c
1 /*
2  * Copyright (c) 1989, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software contributed to Berkeley by
6  * Herb Hasler and Rick Macklem at The University of Guelph.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. All advertising materials mentioning features or use of this software
17  *    must display the following acknowledgement:
18  *      This product includes software developed by the University of
19  *      California, Berkeley and its contributors.
20  * 4. Neither the name of the University nor the names of its contributors
21  *    may be used to endorse or promote products derived from this software
22  *    without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36
37 #ifndef lint
38 static const char copyright[] =
39 "@(#) Copyright (c) 1989, 1993\n\
40         The Regents of the University of California.  All rights reserved.\n";
41 #endif /*not lint*/
42
43 #ifndef lint
44 #if 0
45 static char sccsid[] = "@(#)mountd.c    8.15 (Berkeley) 5/1/95";
46 #endif
47 static const char rcsid[] =
48   "$FreeBSD$";
49 #endif /*not lint*/
50
51 #include <sys/param.h>
52 #include <sys/mount.h>
53 #include <sys/fcntl.h>
54 #include <sys/stat.h>
55 #include <sys/syslog.h>
56 #include <sys/sysctl.h>
57 #include <sys/linker.h>
58 #include <sys/module.h>
59
60 #include <rpc/rpc.h>
61 #include <rpc/pmap_clnt.h>
62 #include <rpc/pmap_prot.h>
63 #include <rpcsvc/mount.h>
64 #include <nfs/rpcv2.h>
65 #include <nfs/nfsproto.h>
66 #include <nfsserver/nfs.h>
67 #include <ufs/ufs/ufsmount.h>
68 #include <fs/msdosfs/msdosfsmount.h>
69 #include <fs/ntfs/ntfsmount.h>
70 #include <isofs/cd9660/cd9660_mount.h>  /* XXX need isofs in include */
71
72 #include <arpa/inet.h>
73
74 #include <ctype.h>
75 #include <err.h>
76 #include <errno.h>
77 #include <grp.h>
78 #include <netdb.h>
79 #include <pwd.h>
80 #include <signal.h>
81 #include <stdio.h>
82 #include <stdlib.h>
83 #include <string.h>
84 #include <unistd.h>
85 #include "pathnames.h"
86
87 #ifdef DEBUG
88 #include <stdarg.h>
89 #endif
90
91 #ifndef MOUNTDLOCK
92 #define MOUNTDLOCK "/var/run/mountd.lock"
93 #endif
94
95 /*
96  * Structures for keeping the mount list and export list
97  */
98 struct mountlist {
99         struct mountlist *ml_next;
100         char    ml_host[RPCMNT_NAMELEN+1];
101         char    ml_dirp[RPCMNT_PATHLEN+1];
102 };
103
104 struct dirlist {
105         struct dirlist  *dp_left;
106         struct dirlist  *dp_right;
107         int             dp_flag;
108         struct hostlist *dp_hosts;      /* List of hosts this dir exported to */
109         char            dp_dirp[1];     /* Actually malloc'd to size of dir */
110 };
111 /* dp_flag bits */
112 #define DP_DEFSET       0x1
113 #define DP_HOSTSET      0x2
114
115 struct exportlist {
116         struct exportlist *ex_next;
117         struct dirlist  *ex_dirl;
118         struct dirlist  *ex_defdir;
119         int             ex_flag;
120         fsid_t          ex_fs;
121         char            *ex_fsdir;
122         char            *ex_indexfile;
123 };
124 /* ex_flag bits */
125 #define EX_LINKED       0x1
126
127 struct netmsk {
128         struct sockaddr_storage nt_net;
129         struct sockaddr_storage nt_mask;
130         char            *nt_name;
131 };
132
133 union grouptypes {
134         struct addrinfo *gt_addrinfo;
135         struct netmsk   gt_net;
136 };
137
138 struct grouplist {
139         int gr_type;
140         union grouptypes gr_ptr;
141         struct grouplist *gr_next;
142 };
143 /* Group types */
144 #define GT_NULL         0x0
145 #define GT_HOST         0x1
146 #define GT_NET          0x2
147 #define GT_DEFAULT      0x3
148 #define GT_IGNORE       0x5
149
150 struct hostlist {
151         int              ht_flag;       /* Uses DP_xx bits */
152         struct grouplist *ht_grp;
153         struct hostlist  *ht_next;
154 };
155
156 struct fhreturn {
157         int     fhr_flag;
158         int     fhr_vers;
159         nfsfh_t fhr_fh;
160 };
161
162 /* Global defs */
163 char    *add_expdir(struct dirlist **, char *, int);
164 void    add_dlist(struct dirlist **, struct dirlist *,
165                                 struct grouplist *, int);
166 void    add_mlist(char *, char *);
167 int     check_dirpath(char *);
168 int     check_options(struct dirlist *);
169 int     checkmask(struct sockaddr *sa);
170 int     chk_host(struct dirlist *, struct sockaddr *, int *, int *);
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 *);
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_fhs(XDR *, caddr_t);
208 int     xdr_mlist(XDR *, caddr_t);
209 void    terminate(int);
210
211 struct exportlist *exphead;
212 struct mountlist *mlhead;
213 struct grouplist *grphead;
214 char exname[MAXPATHLEN];
215 struct xucred def_anon = {
216         XUCRED_VERSION,
217         (uid_t)-2,
218         1,
219         { (gid_t)-2 },
220         NULL
221 };
222 int force_v2 = 0;
223 int resvport_only = 1;
224 int dir_only = 1;
225 int log = 0;
226 int got_sighup = 0;
227
228 int opt_flags;
229 static int have_v6 = 1;
230 #ifdef NI_WITHSCOPEID
231 static const int ninumeric = NI_NUMERICHOST | NI_WITHSCOPEID;
232 #else
233 static const int ninumeric = NI_NUMERICHOST;
234 #endif
235
236 int mountdlockfd;
237 /* Bits for opt_flags above */
238 #define OP_MAPROOT      0x01
239 #define OP_MAPALL       0x02
240 /* 0x4 free */
241 #define OP_MASK         0x08
242 #define OP_NET          0x10
243 #define OP_ALLDIRS      0x40
244 #define OP_HAVEMASK     0x80    /* A mask was specified or inferred. */
245 #define OP_MASKLEN      0x200
246
247 #ifdef DEBUG
248 int debug = 1;
249 void    SYSLOG(int, const char *, ...) __printflike(2, 3);
250 #define syslog SYSLOG
251 #else
252 int debug = 0;
253 #endif
254
255 /*
256  * Mountd server for NFS mount protocol as described in:
257  * NFS: Network File System Protocol Specification, RFC1094, Appendix A
258  * The optional arguments are the exports file name
259  * default: _PATH_EXPORTS
260  * and "-n" to allow nonroot mount.
261  */
262 int
263 main(argc, argv)
264         int argc;
265         char **argv;
266 {
267         fd_set readfds;
268         SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp;
269         struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
270         int udpsock, tcpsock, udp6sock, tcp6sock;
271         int xcreated = 0, s;
272         int one = 1;
273         int c;
274
275         udp6conf = tcp6conf = NULL;
276         udp6sock = tcp6sock = NULL;
277
278         /* Check that another mountd isn't already running. */
279         if ((mountdlockfd = (open(MOUNTDLOCK, O_RDONLY|O_CREAT, 0444))) == -1)
280                 err(1, "%s", MOUNTDLOCK);
281
282         if(flock(mountdlockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK)
283                 errx(1, "another rpc.mountd is already running. Aborting");
284         s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
285         if (s < 0)
286                 have_v6 = 0;
287         else
288                 close(s);
289         if (modfind("nfsserver") < 0) {
290                 /* Not present in kernel, try loading it */
291                 if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
292                         errx(1, "NFS server is not available or loadable");
293         }
294
295         while ((c = getopt(argc, argv, "2dlnr")) != -1)
296                 switch (c) {
297                 case '2':
298                         force_v2 = 1;
299                         break;
300                 case 'n':
301                         resvport_only = 0;
302                         break;
303                 case 'r':
304                         dir_only = 0;
305                         break;
306                 case 'd':
307                         debug = debug ? 0 : 1;
308                         break;
309                 case 'l':
310                         log = 1;
311                         break;
312                 default:
313                         usage();
314                 };
315         argc -= optind;
316         argv += optind;
317         grphead = (struct grouplist *)NULL;
318         exphead = (struct exportlist *)NULL;
319         mlhead = (struct mountlist *)NULL;
320         if (argc == 1) {
321                 strncpy(exname, *argv, MAXPATHLEN-1);
322                 exname[MAXPATHLEN-1] = '\0';
323         } else
324                 strcpy(exname, _PATH_EXPORTS);
325         openlog("mountd", LOG_PID, LOG_DAEMON);
326         if (debug)
327                 warnx("getting export list");
328         get_exportlist();
329         if (debug)
330                 warnx("getting mount list");
331         get_mountlist();
332         if (debug)
333                 warnx("here we go");
334         if (debug == 0) {
335                 daemon(0, 0);
336                 signal(SIGINT, SIG_IGN);
337                 signal(SIGQUIT, SIG_IGN);
338         }
339         signal(SIGHUP, huphandler);
340         signal(SIGTERM, terminate);
341         { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
342           if (pidfile != NULL) {
343                 fprintf(pidfile, "%d\n", getpid());
344                 fclose(pidfile);
345           }
346         }
347         rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
348         rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
349         udpsock  = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
350         tcpsock  = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
351         udpconf  = getnetconfigent("udp");
352         tcpconf  = getnetconfigent("tcp");
353         if (!have_v6)
354                 goto skip_v6;
355         udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
356         tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
357         /*
358          * We're doing host-based access checks here, so don't allow
359          * v4-in-v6 to confuse things. The kernel will disable it
360          * by default on NFS sockets too.
361          */
362         if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6,
363                 IPV6_BINDV6ONLY, &one, sizeof one) < 0){
364                 syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
365                 exit(1);
366         }
367         if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6,
368                 IPV6_BINDV6ONLY, &one, sizeof one) < 0){
369                 syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
370                 exit(1);
371         }
372         udp6conf = getnetconfigent("udp6");
373         tcp6conf = getnetconfigent("tcp6");
374
375 skip_v6:
376         if (!resvport_only) {
377                 if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL,
378                     &resvport_only, sizeof(resvport_only)) != 0 &&
379                     errno != ENOENT) {
380                         syslog(LOG_ERR, "sysctl: %m");
381                         exit(1);
382                 }
383         }
384         if (udpsock != -1 && udpconf != NULL) {
385                 bindresvport(udpsock, NULL);
386                 udptransp = svc_dg_create(udpsock, 0, 0);
387                 if (udptransp != NULL) {
388                         if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1,
389                             mntsrv, udpconf))
390                                 syslog(LOG_WARNING, "can't register UDP RPCMNT_VER1 service");
391                         else
392                                 xcreated++;
393                         if (!force_v2) {
394                                 if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3,
395                                     mntsrv, udpconf))
396                                         syslog(LOG_WARNING, "can't register UDP RPCMNT_VER3 service");
397                                 else
398                                         xcreated++;
399                         }
400                 } else
401                         syslog(LOG_WARNING, "can't create UDP services");
402
403         }
404         if (tcpsock != -1 && tcpconf != NULL) {
405                 bindresvport(tcpsock, NULL);
406                 listen(tcpsock, SOMAXCONN);
407                 tcptransp = svc_vc_create(tcpsock, 0, 0);
408                 if (tcptransp != NULL) {
409                         if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1,
410                             mntsrv, tcpconf))
411                                 syslog(LOG_WARNING, "can't register TCP RPCMNT_VER1 service");
412                         else
413                                 xcreated++;
414                         if (!force_v2) {
415                                 if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3,
416                                     mntsrv, tcpconf))
417                                         syslog(LOG_WARNING, "can't register TCP RPCMNT_VER3 service");
418                                 else
419                                         xcreated++;
420                         }
421                 } else
422                         syslog(LOG_WARNING, "can't create TCP service");
423
424         }
425         if (have_v6 && udp6sock != -1 && udp6conf != NULL) {
426                 bindresvport(udp6sock, NULL);
427                 udp6transp = svc_dg_create(udp6sock, 0, 0);
428                 if (udp6transp != NULL) {
429                         if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1,
430                             mntsrv, udp6conf))
431                                 syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER1 service");
432                         else
433                                 xcreated++;
434                         if (!force_v2) {
435                                 if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3,
436                                     mntsrv, udp6conf))
437                                         syslog(LOG_WARNING, "can't register UDP6 RPCMNT_VER3 service");
438                                 else
439                                         xcreated++;
440                         }
441                 } else
442                         syslog(LOG_WARNING, "can't create UDP6 service");
443
444         }
445         if (have_v6 && tcp6sock != -1 && tcp6conf != NULL) {
446                 bindresvport(tcp6sock, NULL);
447                 listen(tcp6sock, SOMAXCONN);
448                 tcp6transp = svc_vc_create(tcp6sock, 0, 0);
449                 if (tcp6transp != NULL) {
450                         if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1,
451                             mntsrv, tcp6conf))
452                                 syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER1 service");
453                         else
454                                 xcreated++;
455                         if (!force_v2) {
456                                 if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3,
457                                     mntsrv, tcp6conf))
458                                         syslog(LOG_WARNING, "can't register TCP6 RPCMNT_VER3 service");
459                                         else
460                                                 xcreated++;
461                                 }
462                 } else
463                         syslog(LOG_WARNING, "can't create TCP6 service");
464
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 static void
493 usage()
494 {
495         fprintf(stderr,
496                 "usage: mountd [-2] [-d] [-l] [-n] [-r] [export_file]\n");
497         exit(1);
498 }
499
500 /*
501  * The mount rpc service
502  */
503 void
504 mntsrv(rqstp, transp)
505         struct svc_req *rqstp;
506         SVCXPRT *transp;
507 {
508         struct exportlist *ep;
509         struct dirlist *dp;
510         struct fhreturn fhr;
511         struct stat stb;
512         struct statfs fsb;
513         struct addrinfo *ai;
514         char host[NI_MAXHOST], numerichost[NI_MAXHOST];
515         int lookup_failed = 1;
516         struct sockaddr *saddr;
517         u_short sport;
518         char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
519         int bad = 0, defset, hostset;
520         sigset_t sighup_mask;
521
522         sigemptyset(&sighup_mask);
523         sigaddset(&sighup_mask, SIGHUP);
524         saddr = svc_getrpccaller(transp)->buf;
525         switch (saddr->sa_family) {
526         case AF_INET6:
527                 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
528                 break;
529         case AF_INET:
530                 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
531                 break;
532         default:
533                 syslog(LOG_ERR, "request from unknown address family");
534                 return;
535         }
536         lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host, 
537             NULL, 0, 0);
538         getnameinfo(saddr, saddr->sa_len, numerichost,
539             sizeof numerichost, NULL, 0, NI_NUMERICHOST);
540         ai = NULL;
541         switch (rqstp->rq_proc) {
542         case NULLPROC:
543                 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
544                         syslog(LOG_ERR, "can't send reply");
545                 return;
546         case RPCMNT_MOUNT:
547                 if (sport >= IPPORT_RESERVED && resvport_only) {
548                         syslog(LOG_NOTICE,
549                             "mount request from %s from unprivileged port",
550                             numerichost);
551                         svcerr_weakauth(transp);
552                         return;
553                 }
554                 if (!svc_getargs(transp, xdr_dir, rpcpath)) {
555                         syslog(LOG_NOTICE, "undecodable mount request from %s",
556                             numerichost);
557                         svcerr_decode(transp);
558                         return;
559                 }
560
561                 /*
562                  * Get the real pathname and make sure it is a directory
563                  * or a regular file if the -r option was specified
564                  * and it exists.
565                  */
566                 if (realpath(rpcpath, dirpath) == NULL ||
567                     stat(dirpath, &stb) < 0 ||
568                     (!S_ISDIR(stb.st_mode) &&
569                     (dir_only || !S_ISREG(stb.st_mode))) ||
570                     statfs(dirpath, &fsb) < 0) {
571                         chdir("/");     /* Just in case realpath doesn't */
572                         syslog(LOG_NOTICE,
573                             "mount request from %s for non existent path %s",
574                             numerichost, dirpath);
575                         if (debug)
576                                 warnx("stat failed on %s", dirpath);
577                         bad = ENOENT;   /* We will send error reply later */
578                 }
579
580                 /* Check in the exports list */
581                 sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
582                 ep = ex_search(&fsb.f_fsid);
583                 hostset = defset = 0;
584                 if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
585                     ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
586                       chk_host(dp, saddr, &defset, &hostset)) ||
587                     (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
588                      scan_tree(ep->ex_dirl, saddr) == 0))) {
589                         if (bad) {
590                                 if (!svc_sendreply(transp, xdr_long,
591                                     (caddr_t)&bad))
592                                         syslog(LOG_ERR, "can't send reply");
593                                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
594                                 return;
595                         }
596                         if (hostset & DP_HOSTSET)
597                                 fhr.fhr_flag = hostset;
598                         else
599                                 fhr.fhr_flag = defset;
600                         fhr.fhr_vers = rqstp->rq_vers;
601                         /* Get the file handle */
602                         memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
603                         if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
604                                 bad = errno;
605                                 syslog(LOG_ERR, "can't get fh for %s", dirpath);
606                                 if (!svc_sendreply(transp, xdr_long,
607                                     (caddr_t)&bad))
608                                         syslog(LOG_ERR, "can't send reply");
609                                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
610                                 return;
611                         }
612                         if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr))
613                                 syslog(LOG_ERR, "can't send reply");
614                         if (!lookup_failed)
615                                 add_mlist(host, dirpath);
616                         else
617                                 add_mlist(numerichost, dirpath);
618                         if (debug)
619                                 warnx("mount successful");
620                         if (log)
621                                 syslog(LOG_NOTICE,
622                                     "mount request succeeded from %s for %s",
623                                     numerichost, dirpath);
624                 } else {
625                         bad = EACCES;
626                         syslog(LOG_NOTICE,
627                             "mount request denied from %s for %s",
628                             numerichost, dirpath);
629                 }
630
631                 if (bad && !svc_sendreply(transp, xdr_long, (caddr_t)&bad))
632                         syslog(LOG_ERR, "can't send reply");
633                 sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
634                 return;
635         case RPCMNT_DUMP:
636                 if (!svc_sendreply(transp, xdr_mlist, (caddr_t)NULL))
637                         syslog(LOG_ERR, "can't send reply");
638                 else if (log)
639                         syslog(LOG_NOTICE,
640                             "dump request succeeded from %s",
641                             numerichost);
642                 return;
643         case RPCMNT_UMOUNT:
644                 if (sport >= IPPORT_RESERVED && resvport_only) {
645                         syslog(LOG_NOTICE,
646                             "umount request from %s from unprivileged port",
647                             numerichost);
648                         svcerr_weakauth(transp);
649                         return;
650                 }
651                 if (!svc_getargs(transp, xdr_dir, rpcpath)) {
652                         syslog(LOG_NOTICE, "undecodable umount request from %s",
653                             numerichost);
654                         svcerr_decode(transp);
655                         return;
656                 }
657                 if (realpath(rpcpath, dirpath) == NULL) {
658                         syslog(LOG_NOTICE, "umount request from %s "
659                             "for non existent path %s",
660                             numerichost, dirpath);
661                 }
662                 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
663                         syslog(LOG_ERR, "can't send reply");
664                 if (!lookup_failed)
665                         del_mlist(host, dirpath);
666                 del_mlist(numerichost, dirpath);
667                 if (log)
668                         syslog(LOG_NOTICE,
669                             "umount request succeeded from %s for %s",
670                             numerichost, dirpath);
671                 return;
672         case RPCMNT_UMNTALL:
673                 if (sport >= IPPORT_RESERVED && resvport_only) {
674                         syslog(LOG_NOTICE,
675                             "umountall request from %s from unprivileged port",
676                             numerichost);
677                         svcerr_weakauth(transp);
678                         return;
679                 }
680                 if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
681                         syslog(LOG_ERR, "can't send reply");
682                 if (!lookup_failed)
683                         del_mlist(host, NULL);
684                 del_mlist(numerichost, NULL);
685                 if (log)
686                         syslog(LOG_NOTICE,
687                             "umountall request succeeded from %s",
688                             numerichost);
689                 return;
690         case RPCMNT_EXPORT:
691                 if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))
692                         syslog(LOG_ERR, "can't send reply");
693                 if (log)
694                         syslog(LOG_NOTICE,
695                             "export request succeeded from %s",
696                             numerichost);
697                 return;
698         default:
699                 svcerr_noproc(transp);
700                 return;
701         }
702 }
703
704 /*
705  * Xdr conversion for a dirpath string
706  */
707 int
708 xdr_dir(xdrsp, dirp)
709         XDR *xdrsp;
710         char *dirp;
711 {
712         return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
713 }
714
715 /*
716  * Xdr routine to generate file handle reply
717  */
718 int
719 xdr_fhs(xdrsp, cp)
720         XDR *xdrsp;
721         caddr_t cp;
722 {
723         struct fhreturn *fhrp = (struct fhreturn *)cp;
724         u_long ok = 0, len, auth;
725
726         if (!xdr_long(xdrsp, &ok))
727                 return (0);
728         switch (fhrp->fhr_vers) {
729         case 1:
730                 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
731         case 3:
732                 len = NFSX_V3FH;
733                 if (!xdr_long(xdrsp, &len))
734                         return (0);
735                 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
736                         return (0);
737                 auth = RPCAUTH_UNIX;
738                 len = 1;
739                 if (!xdr_long(xdrsp, &len))
740                         return (0);
741                 return (xdr_long(xdrsp, &auth));
742         };
743         return (0);
744 }
745
746 int
747 xdr_mlist(xdrsp, cp)
748         XDR *xdrsp;
749         caddr_t cp;
750 {
751         struct mountlist *mlp;
752         int true = 1;
753         int false = 0;
754         char *strp;
755
756         mlp = mlhead;
757         while (mlp) {
758                 if (!xdr_bool(xdrsp, &true))
759                         return (0);
760                 strp = &mlp->ml_host[0];
761                 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
762                         return (0);
763                 strp = &mlp->ml_dirp[0];
764                 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
765                         return (0);
766                 mlp = mlp->ml_next;
767         }
768         if (!xdr_bool(xdrsp, &false))
769                 return (0);
770         return (1);
771 }
772
773 /*
774  * Xdr conversion for export list
775  */
776 int
777 xdr_explist(xdrsp, cp)
778         XDR *xdrsp;
779         caddr_t cp;
780 {
781         struct exportlist *ep;
782         int false = 0;
783         int putdef;
784         sigset_t sighup_mask;
785
786         sigemptyset(&sighup_mask);
787         sigaddset(&sighup_mask, SIGHUP);
788         sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
789         ep = exphead;
790         while (ep) {
791                 putdef = 0;
792                 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
793                         goto errout;
794                 if (ep->ex_defdir && putdef == 0 &&
795                         put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
796                         &putdef))
797                         goto errout;
798                 ep = ep->ex_next;
799         }
800         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
801         if (!xdr_bool(xdrsp, &false))
802                 return (0);
803         return (1);
804 errout:
805         sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
806         return (0);
807 }
808
809 /*
810  * Called from xdr_explist() to traverse the tree and export the
811  * directory paths.
812  */
813 int
814 put_exlist(dp, xdrsp, adp, putdefp)
815         struct dirlist *dp;
816         XDR *xdrsp;
817         struct dirlist *adp;
818         int *putdefp;
819 {
820         struct grouplist *grp;
821         struct hostlist *hp;
822         int true = 1;
823         int false = 0;
824         int gotalldir = 0;
825         char *strp;
826
827         if (dp) {
828                 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
829                         return (1);
830                 if (!xdr_bool(xdrsp, &true))
831                         return (1);
832                 strp = dp->dp_dirp;
833                 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
834                         return (1);
835                 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
836                         gotalldir = 1;
837                         *putdefp = 1;
838                 }
839                 if ((dp->dp_flag & DP_DEFSET) == 0 &&
840                     (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
841                         hp = dp->dp_hosts;
842                         while (hp) {
843                                 grp = hp->ht_grp;
844                                 if (grp->gr_type == GT_HOST) {
845                                         if (!xdr_bool(xdrsp, &true))
846                                                 return (1);
847                                         strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
848                                         if (!xdr_string(xdrsp, &strp,
849                                             RPCMNT_NAMELEN))
850                                                 return (1);
851                                 } else if (grp->gr_type == GT_NET) {
852                                         if (!xdr_bool(xdrsp, &true))
853                                                 return (1);
854                                         strp = grp->gr_ptr.gt_net.nt_name;
855                                         if (!xdr_string(xdrsp, &strp,
856                                             RPCMNT_NAMELEN))
857                                                 return (1);
858                                 }
859                                 hp = hp->ht_next;
860                                 if (gotalldir && hp == (struct hostlist *)NULL) {
861                                         hp = adp->dp_hosts;
862                                         gotalldir = 0;
863                                 }
864                         }
865                 }
866                 if (!xdr_bool(xdrsp, &false))
867                         return (1);
868                 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
869                         return (1);
870         }
871         return (0);
872 }
873
874 char *line;
875 int linesize;
876 FILE *exp_file;
877
878 /*
879  * Get the export list
880  */
881 void
882 get_exportlist()
883 {
884         struct exportlist *ep, *ep2;
885         struct grouplist *grp, *tgrp;
886         struct exportlist **epp;
887         struct dirlist *dirhead;
888         struct statfs fsb, *fsp;
889         struct xucred anon;
890         char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
891         int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
892
893         dirp = NULL;
894         dirplen = 0;
895
896         /*
897          * First, get rid of the old list
898          */
899         ep = exphead;
900         while (ep) {
901                 ep2 = ep;
902                 ep = ep->ex_next;
903                 free_exp(ep2);
904         }
905         exphead = (struct exportlist *)NULL;
906
907         grp = grphead;
908         while (grp) {
909                 tgrp = grp;
910                 grp = grp->gr_next;
911                 free_grp(tgrp);
912         }
913         grphead = (struct grouplist *)NULL;
914
915         /*
916          * And delete exports that are in the kernel for all local
917          * filesystems.
918          * XXX: Should know how to handle all local exportable filesystems
919          *      instead of just "ufs".
920          */
921         num = getmntinfo(&fsp, MNT_NOWAIT);
922         for (i = 0; i < num; i++) {
923                 union {
924                         struct ufs_args ua;
925                         struct iso_args ia;
926                         struct msdosfs_args da;
927                         struct ntfs_args na;
928                 } targs;
929
930                 if (!strcmp(fsp->f_fstypename, "ufs") ||
931                     !strcmp(fsp->f_fstypename, "msdosfs") ||
932                     !strcmp(fsp->f_fstypename, "ntfs") ||
933                     !strcmp(fsp->f_fstypename, "cd9660")) {
934                         targs.ua.fspec = NULL;
935                         targs.ua.export.ex_flags = MNT_DELEXPORT;
936                         if (mount(fsp->f_fstypename, fsp->f_mntonname,
937                             fsp->f_flags | MNT_UPDATE, (caddr_t)&targs) < 0 &&
938                             errno != ENOENT)
939                                 syslog(LOG_ERR,
940                                     "can't delete exports for %s: %m",
941                                     fsp->f_mntonname);
942                 }
943                 fsp++;
944         }
945
946         /*
947          * Read in the exports file and build the list, calling
948          * mount() as we go along to push the export rules into the kernel.
949          */
950         if ((exp_file = fopen(exname, "r")) == NULL) {
951                 syslog(LOG_ERR, "can't open %s", exname);
952                 exit(2);
953         }
954         dirhead = (struct dirlist *)NULL;
955         while (get_line()) {
956                 if (debug)
957                         warnx("got line %s", line);
958                 cp = line;
959                 nextfield(&cp, &endcp);
960                 if (*cp == '#')
961                         goto nextline;
962
963                 /*
964                  * Set defaults.
965                  */
966                 has_host = FALSE;
967                 anon = def_anon;
968                 exflags = MNT_EXPORTED;
969                 got_nondir = 0;
970                 opt_flags = 0;
971                 ep = (struct exportlist *)NULL;
972
973                 /*
974                  * Create new exports list entry
975                  */
976                 len = endcp-cp;
977                 tgrp = grp = get_grp();
978                 while (len > 0) {
979                         if (len > RPCMNT_NAMELEN) {
980                             getexp_err(ep, tgrp);
981                             goto nextline;
982                         }
983                         if (*cp == '-') {
984                             if (ep == (struct exportlist *)NULL) {
985                                 getexp_err(ep, tgrp);
986                                 goto nextline;
987                             }
988                             if (debug)
989                                 warnx("doing opt %s", cp);
990                             got_nondir = 1;
991                             if (do_opt(&cp, &endcp, ep, grp, &has_host,
992                                 &exflags, &anon)) {
993                                 getexp_err(ep, tgrp);
994                                 goto nextline;
995                             }
996                         } else if (*cp == '/') {
997                             savedc = *endcp;
998                             *endcp = '\0';
999                             if (check_dirpath(cp) &&
1000                                 statfs(cp, &fsb) >= 0) {
1001                                 if (got_nondir) {
1002                                     syslog(LOG_ERR, "dirs must be first");
1003                                     getexp_err(ep, tgrp);
1004                                     goto nextline;
1005                                 }
1006                                 if (ep) {
1007                                     if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
1008                                         ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
1009                                         getexp_err(ep, tgrp);
1010                                         goto nextline;
1011                                     }
1012                                 } else {
1013                                     /*
1014                                      * See if this directory is already
1015                                      * in the list.
1016                                      */
1017                                     ep = ex_search(&fsb.f_fsid);
1018                                     if (ep == (struct exportlist *)NULL) {
1019                                         ep = get_exp();
1020                                         ep->ex_fs = fsb.f_fsid;
1021                                         ep->ex_fsdir = (char *)
1022                                             malloc(strlen(fsb.f_mntonname) + 1);
1023                                         if (ep->ex_fsdir)
1024                                             strcpy(ep->ex_fsdir,
1025                                                 fsb.f_mntonname);
1026                                         else
1027                                             out_of_mem();
1028                                         if (debug)
1029                                                 warnx("making new ep fs=0x%x,0x%x",
1030                                                     fsb.f_fsid.val[0],
1031                                                     fsb.f_fsid.val[1]);
1032                                     } else if (debug)
1033                                         warnx("found ep fs=0x%x,0x%x",
1034                                             fsb.f_fsid.val[0],
1035                                             fsb.f_fsid.val[1]);
1036                                 }
1037
1038                                 /*
1039                                  * Add dirpath to export mount point.
1040                                  */
1041                                 dirp = add_expdir(&dirhead, cp, len);
1042                                 dirplen = len;
1043                             } else {
1044                                 getexp_err(ep, tgrp);
1045                                 goto nextline;
1046                             }
1047                             *endcp = savedc;
1048                         } else {
1049                             savedc = *endcp;
1050                             *endcp = '\0';
1051                             got_nondir = 1;
1052                             if (ep == (struct exportlist *)NULL) {
1053                                 getexp_err(ep, tgrp);
1054                                 goto nextline;
1055                             }
1056
1057                             /*
1058                              * Get the host or netgroup.
1059                              */
1060                             setnetgrent(cp);
1061                             netgrp = getnetgrent(&hst, &usr, &dom);
1062                             do {
1063                                 if (has_host) {
1064                                     grp->gr_next = get_grp();
1065                                     grp = grp->gr_next;
1066                                 }
1067                                 if (netgrp) {
1068                                     if (hst == 0) {
1069                                         syslog(LOG_ERR,
1070                                 "null hostname in netgroup %s, skipping", cp);
1071                                         grp->gr_type = GT_IGNORE;
1072                                     } else if (get_host(hst, grp, tgrp)) {
1073                                         syslog(LOG_ERR,
1074                         "bad host %s in netgroup %s, skipping", hst, cp);
1075                                         grp->gr_type = GT_IGNORE;
1076                                     }
1077                                 } else if (get_host(cp, grp, tgrp)) {
1078                                     syslog(LOG_ERR, "bad host %s, skipping", cp);
1079                                     grp->gr_type = GT_IGNORE;
1080                                 }
1081                                 has_host = TRUE;
1082                             } while (netgrp && getnetgrent(&hst, &usr, &dom));
1083                             endnetgrent();
1084                             *endcp = savedc;
1085                         }
1086                         cp = endcp;
1087                         nextfield(&cp, &endcp);
1088                         len = endcp - cp;
1089                 }
1090                 if (check_options(dirhead)) {
1091                         getexp_err(ep, tgrp);
1092                         goto nextline;
1093                 }
1094                 if (!has_host) {
1095                         grp->gr_type = GT_DEFAULT;
1096                         if (debug)
1097                                 warnx("adding a default entry");
1098
1099                 /*
1100                  * Don't allow a network export coincide with a list of
1101                  * host(s) on the same line.
1102                  */
1103                 } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1104                         syslog(LOG_ERR, "network/host conflict");
1105                         getexp_err(ep, tgrp);
1106                         goto nextline;
1107
1108                 /*
1109                  * If an export list was specified on this line, make sure
1110                  * that we have at least one valid entry, otherwise skip it.
1111                  */
1112                 } else {
1113                         grp = tgrp;
1114                         while (grp && grp->gr_type == GT_IGNORE)
1115                                 grp = grp->gr_next;
1116                         if (! grp) {
1117                             getexp_err(ep, tgrp);
1118                             goto nextline;
1119                         }
1120                 }
1121
1122                 /*
1123                  * Loop through hosts, pushing the exports into the kernel.
1124                  * After loop, tgrp points to the start of the list and
1125                  * grp points to the last entry in the list.
1126                  */
1127                 grp = tgrp;
1128                 do {
1129                         if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
1130                             &fsb)) {
1131                                 getexp_err(ep, tgrp);
1132                                 goto nextline;
1133                         }
1134                 } while (grp->gr_next && (grp = grp->gr_next));
1135
1136                 /*
1137                  * Success. Update the data structures.
1138                  */
1139                 if (has_host) {
1140                         hang_dirp(dirhead, tgrp, ep, opt_flags);
1141                         grp->gr_next = grphead;
1142                         grphead = tgrp;
1143                 } else {
1144                         hang_dirp(dirhead, (struct grouplist *)NULL, ep,
1145                                 opt_flags);
1146                         free_grp(grp);
1147                 }
1148                 dirhead = (struct dirlist *)NULL;
1149                 if ((ep->ex_flag & EX_LINKED) == 0) {
1150                         ep2 = exphead;
1151                         epp = &exphead;
1152
1153                         /*
1154                          * Insert in the list in alphabetical order.
1155                          */
1156                         while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1157                                 epp = &ep2->ex_next;
1158                                 ep2 = ep2->ex_next;
1159                         }
1160                         if (ep2)
1161                                 ep->ex_next = ep2;
1162                         *epp = ep;
1163                         ep->ex_flag |= EX_LINKED;
1164                 }
1165 nextline:
1166                 if (dirhead) {
1167                         free_dir(dirhead);
1168                         dirhead = (struct dirlist *)NULL;
1169                 }
1170         }
1171         fclose(exp_file);
1172 }
1173
1174 /*
1175  * Allocate an export list element
1176  */
1177 struct exportlist *
1178 get_exp()
1179 {
1180         struct exportlist *ep;
1181
1182         ep = (struct exportlist *)malloc(sizeof (struct exportlist));
1183         if (ep == (struct exportlist *)NULL)
1184                 out_of_mem();
1185         memset(ep, 0, sizeof(struct exportlist));
1186         return (ep);
1187 }
1188
1189 /*
1190  * Allocate a group list element
1191  */
1192 struct grouplist *
1193 get_grp()
1194 {
1195         struct grouplist *gp;
1196
1197         gp = (struct grouplist *)malloc(sizeof (struct grouplist));
1198         if (gp == (struct grouplist *)NULL)
1199                 out_of_mem();
1200         memset(gp, 0, sizeof(struct grouplist));
1201         return (gp);
1202 }
1203
1204 /*
1205  * Clean up upon an error in get_exportlist().
1206  */
1207 void
1208 getexp_err(ep, grp)
1209         struct exportlist *ep;
1210         struct grouplist *grp;
1211 {
1212         struct grouplist *tgrp;
1213
1214         syslog(LOG_ERR, "bad exports list line %s", line);
1215         if (ep && (ep->ex_flag & EX_LINKED) == 0)
1216                 free_exp(ep);
1217         while (grp) {
1218                 tgrp = grp;
1219                 grp = grp->gr_next;
1220                 free_grp(tgrp);
1221         }
1222 }
1223
1224 /*
1225  * Search the export list for a matching fs.
1226  */
1227 struct exportlist *
1228 ex_search(fsid)
1229         fsid_t *fsid;
1230 {
1231         struct exportlist *ep;
1232
1233         ep = exphead;
1234         while (ep) {
1235                 if (ep->ex_fs.val[0] == fsid->val[0] &&
1236                     ep->ex_fs.val[1] == fsid->val[1])
1237                         return (ep);
1238                 ep = ep->ex_next;
1239         }
1240         return (ep);
1241 }
1242
1243 /*
1244  * Add a directory path to the list.
1245  */
1246 char *
1247 add_expdir(dpp, cp, len)
1248         struct dirlist **dpp;
1249         char *cp;
1250         int len;
1251 {
1252         struct dirlist *dp;
1253
1254         dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
1255         if (dp == (struct dirlist *)NULL)
1256                 out_of_mem();
1257         dp->dp_left = *dpp;
1258         dp->dp_right = (struct dirlist *)NULL;
1259         dp->dp_flag = 0;
1260         dp->dp_hosts = (struct hostlist *)NULL;
1261         strcpy(dp->dp_dirp, cp);
1262         *dpp = dp;
1263         return (dp->dp_dirp);
1264 }
1265
1266 /*
1267  * Hang the dir list element off the dirpath binary tree as required
1268  * and update the entry for host.
1269  */
1270 void
1271 hang_dirp(dp, grp, ep, flags)
1272         struct dirlist *dp;
1273         struct grouplist *grp;
1274         struct exportlist *ep;
1275         int flags;
1276 {
1277         struct hostlist *hp;
1278         struct dirlist *dp2;
1279
1280         if (flags & OP_ALLDIRS) {
1281                 if (ep->ex_defdir)
1282                         free((caddr_t)dp);
1283                 else
1284                         ep->ex_defdir = dp;
1285                 if (grp == (struct grouplist *)NULL) {
1286                         ep->ex_defdir->dp_flag |= DP_DEFSET;
1287                 } else while (grp) {
1288                         hp = get_ht();
1289                         hp->ht_grp = grp;
1290                         hp->ht_next = ep->ex_defdir->dp_hosts;
1291                         ep->ex_defdir->dp_hosts = hp;
1292                         grp = grp->gr_next;
1293                 }
1294         } else {
1295
1296                 /*
1297                  * Loop through the directories adding them to the tree.
1298                  */
1299                 while (dp) {
1300                         dp2 = dp->dp_left;
1301                         add_dlist(&ep->ex_dirl, dp, grp, flags);
1302                         dp = dp2;
1303                 }
1304         }
1305 }
1306
1307 /*
1308  * Traverse the binary tree either updating a node that is already there
1309  * for the new directory or adding the new node.
1310  */
1311 void
1312 add_dlist(dpp, newdp, grp, flags)
1313         struct dirlist **dpp;
1314         struct dirlist *newdp;
1315         struct grouplist *grp;
1316         int flags;
1317 {
1318         struct dirlist *dp;
1319         struct hostlist *hp;
1320         int cmp;
1321
1322         dp = *dpp;
1323         if (dp) {
1324                 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1325                 if (cmp > 0) {
1326                         add_dlist(&dp->dp_left, newdp, grp, flags);
1327                         return;
1328                 } else if (cmp < 0) {
1329                         add_dlist(&dp->dp_right, newdp, grp, flags);
1330                         return;
1331                 } else
1332                         free((caddr_t)newdp);
1333         } else {
1334                 dp = newdp;
1335                 dp->dp_left = (struct dirlist *)NULL;
1336                 *dpp = dp;
1337         }
1338         if (grp) {
1339
1340                 /*
1341                  * Hang all of the host(s) off of the directory point.
1342                  */
1343                 do {
1344                         hp = get_ht();
1345                         hp->ht_grp = grp;
1346                         hp->ht_next = dp->dp_hosts;
1347                         dp->dp_hosts = hp;
1348                         grp = grp->gr_next;
1349                 } while (grp);
1350         } else {
1351                 dp->dp_flag |= DP_DEFSET;
1352         }
1353 }
1354
1355 /*
1356  * Search for a dirpath on the export point.
1357  */
1358 struct dirlist *
1359 dirp_search(dp, dirp)
1360         struct dirlist *dp;
1361         char *dirp;
1362 {
1363         int cmp;
1364
1365         if (dp) {
1366                 cmp = strcmp(dp->dp_dirp, dirp);
1367                 if (cmp > 0)
1368                         return (dirp_search(dp->dp_left, dirp));
1369                 else if (cmp < 0)
1370                         return (dirp_search(dp->dp_right, dirp));
1371                 else
1372                         return (dp);
1373         }
1374         return (dp);
1375 }
1376
1377 /*
1378  * Scan for a host match in a directory tree.
1379  */
1380 int
1381 chk_host(dp, saddr, defsetp, hostsetp)
1382         struct dirlist *dp;
1383         struct sockaddr *saddr;
1384         int *defsetp;
1385         int *hostsetp;
1386 {
1387         struct hostlist *hp;
1388         struct grouplist *grp;
1389         struct addrinfo *ai;
1390
1391         if (dp) {
1392                 if (dp->dp_flag & DP_DEFSET)
1393                         *defsetp = dp->dp_flag;
1394                 hp = dp->dp_hosts;
1395                 while (hp) {
1396                         grp = hp->ht_grp;
1397                         switch (grp->gr_type) {
1398                         case GT_HOST:
1399                                 ai = grp->gr_ptr.gt_addrinfo;
1400                                 for (; ai; ai = ai->ai_next) {
1401                                         if (!sacmp(ai->ai_addr, saddr, NULL)) {
1402                                                 *hostsetp =
1403                                                     (hp->ht_flag | DP_HOSTSET);
1404                                                 return (1);
1405                                         }
1406                                 }
1407                                 break;
1408                         case GT_NET:
1409                                 if (!sacmp(saddr, (struct sockaddr *)
1410                                     &grp->gr_ptr.gt_net.nt_net,
1411                                     (struct sockaddr *)
1412                                     &grp->gr_ptr.gt_net.nt_mask)) {
1413                                         *hostsetp = (hp->ht_flag | DP_HOSTSET);
1414                                         return (1);
1415                                 }
1416                                 break;
1417                         }
1418                         hp = hp->ht_next;
1419                 }
1420         }
1421         return (0);
1422 }
1423
1424 /*
1425  * Scan tree for a host that matches the address.
1426  */
1427 int
1428 scan_tree(dp, saddr)
1429         struct dirlist *dp;
1430         struct sockaddr *saddr;
1431 {
1432         int defset, hostset;
1433
1434         if (dp) {
1435                 if (scan_tree(dp->dp_left, saddr))
1436                         return (1);
1437                 if (chk_host(dp, saddr, &defset, &hostset))
1438                         return (1);
1439                 if (scan_tree(dp->dp_right, saddr))
1440                         return (1);
1441         }
1442         return (0);
1443 }
1444
1445 /*
1446  * Traverse the dirlist tree and free it up.
1447  */
1448 void
1449 free_dir(dp)
1450         struct dirlist *dp;
1451 {
1452
1453         if (dp) {
1454                 free_dir(dp->dp_left);
1455                 free_dir(dp->dp_right);
1456                 free_host(dp->dp_hosts);
1457                 free((caddr_t)dp);
1458         }
1459 }
1460
1461 /*
1462  * Parse the option string and update fields.
1463  * Option arguments may either be -<option>=<value> or
1464  * -<option> <value>
1465  */
1466 int
1467 do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1468         char **cpp, **endcpp;
1469         struct exportlist *ep;
1470         struct grouplist *grp;
1471         int *has_hostp;
1472         int *exflagsp;
1473         struct xucred *cr;
1474 {
1475         char *cpoptarg, *cpoptend;
1476         char *cp, *endcp, *cpopt, savedc, savedc2;
1477         int allflag, usedarg;
1478
1479         savedc2 = '\0';
1480         cpopt = *cpp;
1481         cpopt++;
1482         cp = *endcpp;
1483         savedc = *cp;
1484         *cp = '\0';
1485         while (cpopt && *cpopt) {
1486                 allflag = 1;
1487                 usedarg = -2;
1488                 if ((cpoptend = strchr(cpopt, ','))) {
1489                         *cpoptend++ = '\0';
1490                         if ((cpoptarg = strchr(cpopt, '=')))
1491                                 *cpoptarg++ = '\0';
1492                 } else {
1493                         if ((cpoptarg = strchr(cpopt, '=')))
1494                                 *cpoptarg++ = '\0';
1495                         else {
1496                                 *cp = savedc;
1497                                 nextfield(&cp, &endcp);
1498                                 **endcpp = '\0';
1499                                 if (endcp > cp && *cp != '-') {
1500                                         cpoptarg = cp;
1501                                         savedc2 = *endcp;
1502                                         *endcp = '\0';
1503                                         usedarg = 0;
1504                                 }
1505                         }
1506                 }
1507                 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1508                         *exflagsp |= MNT_EXRDONLY;
1509                 } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1510                     !(allflag = strcmp(cpopt, "mapall")) ||
1511                     !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1512                         usedarg++;
1513                         parsecred(cpoptarg, cr);
1514                         if (allflag == 0) {
1515                                 *exflagsp |= MNT_EXPORTANON;
1516                                 opt_flags |= OP_MAPALL;
1517                         } else
1518                                 opt_flags |= OP_MAPROOT;
1519                 } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1520                     !strcmp(cpopt, "m"))) {
1521                         if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1522                                 syslog(LOG_ERR, "bad mask: %s", cpoptarg);
1523                                 return (1);
1524                         }
1525                         usedarg++;
1526                         opt_flags |= OP_MASK;
1527                 } else if (cpoptarg && (!strcmp(cpopt, "network") ||
1528                         !strcmp(cpopt, "n"))) {
1529                         if (strchr(cpoptarg, '/') != NULL) {
1530                                 if (debug)
1531                                         fprintf(stderr, "setting OP_MASKLEN\n");
1532                                 opt_flags |= OP_MASKLEN;
1533                         }
1534                         if (grp->gr_type != GT_NULL) {
1535                                 syslog(LOG_ERR, "network/host conflict");
1536                                 return (1);
1537                         } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1538                                 syslog(LOG_ERR, "bad net: %s", cpoptarg);
1539                                 return (1);
1540                         }
1541                         grp->gr_type = GT_NET;
1542                         *has_hostp = 1;
1543                         usedarg++;
1544                         opt_flags |= OP_NET;
1545                 } else if (!strcmp(cpopt, "alldirs")) {
1546                         opt_flags |= OP_ALLDIRS;
1547                 } else if (!strcmp(cpopt, "public")) {
1548                         *exflagsp |= MNT_EXPUBLIC;
1549                 } else if (!strcmp(cpopt, "webnfs")) {
1550                         *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
1551                         opt_flags |= OP_MAPALL;
1552                 } else if (cpoptarg && !strcmp(cpopt, "index")) {
1553                         ep->ex_indexfile = strdup(cpoptarg);
1554                 } else {
1555                         syslog(LOG_ERR, "bad opt %s", cpopt);
1556                         return (1);
1557                 }
1558                 if (usedarg >= 0) {
1559                         *endcp = savedc2;
1560                         **endcpp = savedc;
1561                         if (usedarg > 0) {
1562                                 *cpp = cp;
1563                                 *endcpp = endcp;
1564                         }
1565                         return (0);
1566                 }
1567                 cpopt = cpoptend;
1568         }
1569         **endcpp = savedc;
1570         return (0);
1571 }
1572
1573 /*
1574  * Translate a character string to the corresponding list of network
1575  * addresses for a hostname.
1576  */
1577 int
1578 get_host(cp, grp, tgrp)
1579         char *cp;
1580         struct grouplist *grp;
1581         struct grouplist *tgrp;
1582 {
1583         struct grouplist *checkgrp;
1584         struct addrinfo *ai, *tai, hints;
1585         int ecode;
1586         char host[NI_MAXHOST];
1587
1588         if (grp->gr_type != GT_NULL) {
1589                 syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
1590                 return (1);
1591         }
1592         memset(&hints, 0, sizeof hints);
1593         hints.ai_flags = AI_CANONNAME;
1594         hints.ai_protocol = IPPROTO_UDP;
1595         ecode = getaddrinfo(cp, NULL, &hints, &ai);
1596         if (ecode != 0) {
1597                 syslog(LOG_ERR,"can't get address info for host %s", cp);
1598                 return 1;
1599         }
1600         grp->gr_ptr.gt_addrinfo = ai;
1601         while (ai != NULL) {
1602                 if (ai->ai_canonname == NULL) {
1603                         if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
1604                             sizeof host, NULL, 0, ninumeric) != 0)
1605                                 strlcpy(host, "?", sizeof(host));
1606                         ai->ai_canonname = strdup(host);
1607                         ai->ai_flags |= AI_CANONNAME;
1608                 }
1609                 if (debug)
1610                         fprintf(stderr, "got host %s\n", ai->ai_canonname);
1611                 /*
1612                  * Sanity check: make sure we don't already have an entry
1613                  * for this host in the grouplist.
1614                  */
1615                 for (checkgrp = tgrp; checkgrp != NULL;
1616                     checkgrp = checkgrp->gr_next) {
1617                         if (checkgrp->gr_type != GT_HOST)
1618                                 continue;
1619                         for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
1620                             tai = tai->ai_next) {
1621                                 if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
1622                                         continue;
1623                                 if (debug)
1624                                         fprintf(stderr,
1625                                             "ignoring duplicate host %s\n",
1626                                             ai->ai_canonname);
1627                                 grp->gr_type = GT_IGNORE;
1628                                 return (0);
1629                         }
1630                 }
1631                 ai = ai->ai_next;
1632         }
1633         grp->gr_type = GT_HOST;
1634         return (0);
1635 }
1636
1637 /*
1638  * Free up an exports list component
1639  */
1640 void
1641 free_exp(ep)
1642         struct exportlist *ep;
1643 {
1644
1645         if (ep->ex_defdir) {
1646                 free_host(ep->ex_defdir->dp_hosts);
1647                 free((caddr_t)ep->ex_defdir);
1648         }
1649         if (ep->ex_fsdir)
1650                 free(ep->ex_fsdir);
1651         if (ep->ex_indexfile)
1652                 free(ep->ex_indexfile);
1653         free_dir(ep->ex_dirl);
1654         free((caddr_t)ep);
1655 }
1656
1657 /*
1658  * Free hosts.
1659  */
1660 void
1661 free_host(hp)
1662         struct hostlist *hp;
1663 {
1664         struct hostlist *hp2;
1665
1666         while (hp) {
1667                 hp2 = hp;
1668                 hp = hp->ht_next;
1669                 free((caddr_t)hp2);
1670         }
1671 }
1672
1673 struct hostlist *
1674 get_ht()
1675 {
1676         struct hostlist *hp;
1677
1678         hp = (struct hostlist *)malloc(sizeof (struct hostlist));
1679         if (hp == (struct hostlist *)NULL)
1680                 out_of_mem();
1681         hp->ht_next = (struct hostlist *)NULL;
1682         hp->ht_flag = 0;
1683         return (hp);
1684 }
1685
1686 /*
1687  * Out of memory, fatal
1688  */
1689 void
1690 out_of_mem()
1691 {
1692
1693         syslog(LOG_ERR, "out of memory");
1694         exit(2);
1695 }
1696
1697 /*
1698  * Do the mount syscall with the update flag to push the export info into
1699  * the kernel.
1700  */
1701 int
1702 do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
1703         struct exportlist *ep;
1704         struct grouplist *grp;
1705         int exflags;
1706         struct xucred *anoncrp;
1707         char *dirp;
1708         int dirplen;
1709         struct statfs *fsb;
1710 {
1711         struct statfs fsb1;
1712         struct addrinfo *ai;
1713         struct export_args *eap;
1714         char *cp = NULL;
1715         int done;
1716         char savedc = '\0';
1717         union {
1718                 struct ufs_args ua;
1719                 struct iso_args ia;
1720                 struct msdosfs_args da;
1721                 struct ntfs_args na;
1722         } args;
1723
1724         bzero(&args, sizeof args);
1725         /* XXX, we assume that all xx_args look like ufs_args. */
1726         args.ua.fspec = 0;
1727         eap = &args.ua.export;
1728
1729         eap->ex_flags = exflags;
1730         eap->ex_anon = *anoncrp;
1731         eap->ex_indexfile = ep->ex_indexfile;
1732         if (grp->gr_type == GT_HOST)
1733                 ai = grp->gr_ptr.gt_addrinfo;
1734         else
1735                 ai = NULL;
1736         done = FALSE;
1737         while (!done) {
1738                 switch (grp->gr_type) {
1739                 case GT_HOST:
1740                         if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
1741                                 goto skip;
1742                         eap->ex_addr = ai->ai_addr;
1743                         eap->ex_addrlen = ai->ai_addrlen;
1744                         eap->ex_masklen = 0;
1745                         break;
1746                 case GT_NET:
1747                         if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
1748                             have_v6 == 0)
1749                                 goto skip;
1750                         eap->ex_addr =
1751                             (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
1752                         eap->ex_addrlen = args.ua.export.ex_addr->sa_len;
1753                         eap->ex_mask =
1754                             (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
1755                         eap->ex_masklen = args.ua.export.ex_mask->sa_len;
1756                         break;
1757                 case GT_DEFAULT:
1758                         eap->ex_addr = NULL;
1759                         eap->ex_addrlen = 0;
1760                         eap->ex_mask = NULL;
1761                         eap->ex_masklen = 0;
1762                         break;
1763                 case GT_IGNORE:
1764                         return(0);
1765                         break;
1766                 default:
1767                         syslog(LOG_ERR, "bad grouptype");
1768                         if (cp)
1769                                 *cp = savedc;
1770                         return (1);
1771                 };
1772
1773                 /*
1774                  * XXX:
1775                  * Maybe I should just use the fsb->f_mntonname path instead
1776                  * of looping back up the dirp to the mount point??
1777                  * Also, needs to know how to export all types of local
1778                  * exportable filesystems and not just "ufs".
1779                  */
1780                 while (mount(fsb->f_fstypename, dirp,
1781                     fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
1782                         if (cp)
1783                                 *cp-- = savedc;
1784                         else
1785                                 cp = dirp + dirplen - 1;
1786                         if (errno == EPERM) {
1787                                 if (debug)
1788                                         warnx("can't change attributes for %s",
1789                                             dirp);
1790                                 syslog(LOG_ERR,
1791                                    "can't change attributes for %s", dirp);
1792                                 return (1);
1793                         }
1794                         if (opt_flags & OP_ALLDIRS) {
1795                                 syslog(LOG_ERR, "could not remount %s: %m",
1796                                         dirp);
1797                                 return (1);
1798                         }
1799                         /* back up over the last component */
1800                         while (*cp == '/' && cp > dirp)
1801                                 cp--;
1802                         while (*(cp - 1) != '/' && cp > dirp)
1803                                 cp--;
1804                         if (cp == dirp) {
1805                                 if (debug)
1806                                         warnx("mnt unsucc");
1807                                 syslog(LOG_ERR, "can't export %s", dirp);
1808                                 return (1);
1809                         }
1810                         savedc = *cp;
1811                         *cp = '\0';
1812                         /* Check that we're still on the same filesystem. */
1813                         if (statfs(dirp, &fsb1) != 0 || bcmp(&fsb1.f_fsid,
1814                             &fsb->f_fsid, sizeof(fsb1.f_fsid)) != 0) {
1815                                 *cp = savedc;
1816                                 syslog(LOG_ERR, "can't export %s", dirp);
1817                                 return (1);
1818                         }
1819                 }
1820 skip:
1821                 if (ai != NULL)
1822                         ai = ai->ai_next;
1823                 if (ai == NULL)
1824                         done = TRUE;
1825         }
1826         if (cp)
1827                 *cp = savedc;
1828         return (0);
1829 }
1830
1831 /*
1832  * Translate a net address.
1833  *
1834  * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
1835  */
1836 int
1837 get_net(cp, net, maskflg)
1838         char *cp;
1839         struct netmsk *net;
1840         int maskflg;
1841 {
1842         struct netent *np = NULL;
1843         char *name, *p, *prefp;
1844         struct sockaddr_in sin;
1845         struct sockaddr *sa = NULL;
1846         struct addrinfo hints, *ai = NULL;
1847         char netname[NI_MAXHOST];
1848         long preflen;
1849
1850         p = prefp = NULL;
1851         if ((opt_flags & OP_MASKLEN) && !maskflg) {
1852                 p = strchr(cp, '/');
1853                 *p = '\0';
1854                 prefp = p + 1;
1855         }
1856
1857         /*
1858          * Check for a numeric address first. We wish to avoid
1859          * possible DNS lookups in getnetbyname().
1860          */
1861         if (isxdigit(*cp) || *cp == ':') {
1862                 memset(&hints, 0, sizeof hints);
1863                 /* Ensure the mask and the network have the same family. */
1864                 if (maskflg && (opt_flags & OP_NET))
1865                         hints.ai_family = net->nt_net.ss_family;
1866                 else if (!maskflg && (opt_flags & OP_HAVEMASK))
1867                         hints.ai_family = net->nt_mask.ss_family;
1868                 else
1869                         hints.ai_family = AF_UNSPEC;
1870                 hints.ai_flags = AI_NUMERICHOST;
1871                 if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
1872                         sa = ai->ai_addr;
1873                 if (sa != NULL && ai->ai_family == AF_INET) {
1874                         /*
1875                          * The address in `cp' is really a network address, so
1876                          * use inet_network() to re-interpret this correctly.
1877                          * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
1878                          */
1879                         bzero(&sin, sizeof sin);
1880                         sin.sin_family = AF_INET;
1881                         sin.sin_len = sizeof sin;
1882                         sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
1883                         if (debug)
1884                                 fprintf(stderr, "get_net: v4 addr %s\n",
1885                                     inet_ntoa(sin.sin_addr));
1886                         sa = (struct sockaddr *)&sin;
1887                 }
1888         }
1889         if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
1890                 bzero(&sin, sizeof sin);
1891                 sin.sin_family = AF_INET;
1892                 sin.sin_len = sizeof sin;
1893                 sin.sin_addr = inet_makeaddr(np->n_net, 0);
1894                 sa = (struct sockaddr *)&sin;
1895         }
1896         if (sa == NULL)
1897                 goto fail;
1898
1899         if (maskflg) {
1900                 /* The specified sockaddr is a mask. */
1901                 if (checkmask(sa) != 0)
1902                         goto fail;
1903                 bcopy(sa, &net->nt_mask, sa->sa_len);
1904                 opt_flags |= OP_HAVEMASK;
1905         } else {
1906                 /* The specified sockaddr is a network address. */
1907                 bcopy(sa, &net->nt_net, sa->sa_len);
1908
1909                 /* Get a network name for the export list. */
1910                 if (np) {
1911                         name = np->n_name;
1912                 } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
1913                    NULL, 0, ninumeric) == 0) {
1914                         name = netname;
1915                 } else {
1916                         goto fail;
1917                 }
1918                 if ((net->nt_name = strdup(name)) == NULL)
1919                         out_of_mem();
1920
1921                 /*
1922                  * Extract a mask from either a "/<masklen>" suffix, or
1923                  * from the class of an IPv4 address.
1924                  */
1925                 if (opt_flags & OP_MASKLEN) {
1926                         preflen = strtol(prefp, NULL, 10);
1927                         if (preflen < 0L || preflen == LONG_MAX)
1928                                 goto fail;
1929                         bcopy(sa, &net->nt_mask, sa->sa_len);
1930                         if (makemask(&net->nt_mask, (int)preflen) != 0)
1931                                 goto fail;
1932                         opt_flags |= OP_HAVEMASK;
1933                         *p = '/';
1934                 } else if (sa->sa_family == AF_INET &&
1935                     (opt_flags & OP_MASK) == 0) {
1936                         in_addr_t addr;
1937
1938                         addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
1939                         if (IN_CLASSA(addr))
1940                                 preflen = 8;
1941                         else if (IN_CLASSB(addr))
1942                                 preflen = 16;
1943                         else if (IN_CLASSC(addr))
1944                                 preflen = 24;
1945                         else if (IN_CLASSD(addr))
1946                                 preflen = 28;
1947                         else
1948                                 preflen = 32;   /* XXX */
1949
1950                         bcopy(sa, &net->nt_mask, sa->sa_len);
1951                         makemask(&net->nt_mask, (int)preflen);
1952                         opt_flags |= OP_HAVEMASK;
1953                 }
1954         }
1955
1956         if (ai)
1957                 freeaddrinfo(ai);
1958         return 0;
1959
1960 fail:
1961         if (ai)
1962                 freeaddrinfo(ai);
1963         return 1;
1964 }
1965
1966 /*
1967  * Parse out the next white space separated field
1968  */
1969 void
1970 nextfield(cp, endcp)
1971         char **cp;
1972         char **endcp;
1973 {
1974         char *p;
1975
1976         p = *cp;
1977         while (*p == ' ' || *p == '\t')
1978                 p++;
1979         if (*p == '\n' || *p == '\0')
1980                 *cp = *endcp = p;
1981         else {
1982                 *cp = p++;
1983                 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
1984                         p++;
1985                 *endcp = p;
1986         }
1987 }
1988
1989 /*
1990  * Get an exports file line. Skip over blank lines and handle line
1991  * continuations.
1992  */
1993 int
1994 get_line()
1995 {
1996         char *p, *cp;
1997         size_t len;
1998         int totlen, cont_line;
1999
2000         /*
2001          * Loop around ignoring blank lines and getting all continuation lines.
2002          */
2003         p = line;
2004         totlen = 0;
2005         do {
2006                 if ((p = fgetln(exp_file, &len)) == NULL)
2007                         return (0);
2008                 cp = p + len - 1;
2009                 cont_line = 0;
2010                 while (cp >= p &&
2011                     (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
2012                         if (*cp == '\\')
2013                                 cont_line = 1;
2014                         cp--;
2015                         len--;
2016                 }
2017                 if (cont_line) {
2018                         *++cp = ' ';
2019                         len++;
2020                 }
2021                 if (linesize < len + totlen + 1) {
2022                         linesize = len + totlen + 1;
2023                         line = realloc(line, linesize);
2024                         if (line == NULL)
2025                                 out_of_mem();
2026                 }
2027                 memcpy(line + totlen, p, len);
2028                 totlen += len;
2029                 line[totlen] = '\0';
2030         } while (totlen == 0 || cont_line);
2031         return (1);
2032 }
2033
2034 /*
2035  * Parse a description of a credential.
2036  */
2037 void
2038 parsecred(namelist, cr)
2039         char *namelist;
2040         struct xucred *cr;
2041 {
2042         char *name;
2043         int cnt;
2044         char *names;
2045         struct passwd *pw;
2046         struct group *gr;
2047         int ngroups, groups[NGROUPS + 1];
2048
2049         cr->cr_version = XUCRED_VERSION;
2050         /*
2051          * Set up the unprivileged user.
2052          */
2053         cr->cr_uid = -2;
2054         cr->cr_groups[0] = -2;
2055         cr->cr_ngroups = 1;
2056         /*
2057          * Get the user's password table entry.
2058          */
2059         names = strsep(&namelist, " \t\n");
2060         name = strsep(&names, ":");
2061         if (isdigit(*name) || *name == '-')
2062                 pw = getpwuid(atoi(name));
2063         else
2064                 pw = getpwnam(name);
2065         /*
2066          * Credentials specified as those of a user.
2067          */
2068         if (names == NULL) {
2069                 if (pw == NULL) {
2070                         syslog(LOG_ERR, "unknown user: %s", name);
2071                         return;
2072                 }
2073                 cr->cr_uid = pw->pw_uid;
2074                 ngroups = NGROUPS + 1;
2075                 if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
2076                         syslog(LOG_ERR, "too many groups");
2077                 /*
2078                  * Convert from int's to gid_t's and compress out duplicate
2079                  */
2080                 cr->cr_ngroups = ngroups - 1;
2081                 cr->cr_groups[0] = groups[0];
2082                 for (cnt = 2; cnt < ngroups; cnt++)
2083                         cr->cr_groups[cnt - 1] = groups[cnt];
2084                 return;
2085         }
2086         /*
2087          * Explicit credential specified as a colon separated list:
2088          *      uid:gid:gid:...
2089          */
2090         if (pw != NULL)
2091                 cr->cr_uid = pw->pw_uid;
2092         else if (isdigit(*name) || *name == '-')
2093                 cr->cr_uid = atoi(name);
2094         else {
2095                 syslog(LOG_ERR, "unknown user: %s", name);
2096                 return;
2097         }
2098         cr->cr_ngroups = 0;
2099         while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
2100                 name = strsep(&names, ":");
2101                 if (isdigit(*name) || *name == '-') {
2102                         cr->cr_groups[cr->cr_ngroups++] = atoi(name);
2103                 } else {
2104                         if ((gr = getgrnam(name)) == NULL) {
2105                                 syslog(LOG_ERR, "unknown group: %s", name);
2106                                 continue;
2107                         }
2108                         cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2109                 }
2110         }
2111         if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
2112                 syslog(LOG_ERR, "too many groups");
2113 }
2114
2115 #define STRSIZ  (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
2116 /*
2117  * Routines that maintain the remote mounttab
2118  */
2119 void
2120 get_mountlist()
2121 {
2122         struct mountlist *mlp, **mlpp;
2123         char *host, *dirp, *cp;
2124         char str[STRSIZ];
2125         FILE *mlfile;
2126
2127         if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2128                 if (errno == ENOENT)
2129                         return;
2130                 else {
2131                         syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
2132                         return;
2133                 }
2134         }
2135         mlpp = &mlhead;
2136         while (fgets(str, STRSIZ, mlfile) != NULL) {
2137                 cp = str;
2138                 host = strsep(&cp, " \t\n");
2139                 dirp = strsep(&cp, " \t\n");
2140                 if (host == NULL || dirp == NULL)
2141                         continue;
2142                 mlp = (struct mountlist *)malloc(sizeof (*mlp));
2143                 if (mlp == (struct mountlist *)NULL)
2144                         out_of_mem();
2145                 strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
2146                 mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2147                 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2148                 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2149                 mlp->ml_next = (struct mountlist *)NULL;
2150                 *mlpp = mlp;
2151                 mlpp = &mlp->ml_next;
2152         }
2153         fclose(mlfile);
2154 }
2155
2156 void
2157 del_mlist(char *hostp, char *dirp)
2158 {
2159         struct mountlist *mlp, **mlpp;
2160         struct mountlist *mlp2;
2161         FILE *mlfile;
2162         int fnd = 0;
2163
2164         mlpp = &mlhead;
2165         mlp = mlhead;
2166         while (mlp) {
2167                 if (!strcmp(mlp->ml_host, hostp) &&
2168                     (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2169                         fnd = 1;
2170                         mlp2 = mlp;
2171                         *mlpp = mlp = mlp->ml_next;
2172                         free((caddr_t)mlp2);
2173                 } else {
2174                         mlpp = &mlp->ml_next;
2175                         mlp = mlp->ml_next;
2176                 }
2177         }
2178         if (fnd) {
2179                 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
2180                         syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
2181                         return;
2182                 }
2183                 mlp = mlhead;
2184                 while (mlp) {
2185                         fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2186                         mlp = mlp->ml_next;
2187                 }
2188                 fclose(mlfile);
2189         }
2190 }
2191
2192 void
2193 add_mlist(hostp, dirp)
2194         char *hostp, *dirp;
2195 {
2196         struct mountlist *mlp, **mlpp;
2197         FILE *mlfile;
2198
2199         mlpp = &mlhead;
2200         mlp = mlhead;
2201         while (mlp) {
2202                 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2203                         return;
2204                 mlpp = &mlp->ml_next;
2205                 mlp = mlp->ml_next;
2206         }
2207         mlp = (struct mountlist *)malloc(sizeof (*mlp));
2208         if (mlp == (struct mountlist *)NULL)
2209                 out_of_mem();
2210         strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
2211         mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2212         strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2213         mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2214         mlp->ml_next = (struct mountlist *)NULL;
2215         *mlpp = mlp;
2216         if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2217                 syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
2218                 return;
2219         }
2220         fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2221         fclose(mlfile);
2222 }
2223
2224 /*
2225  * Free up a group list.
2226  */
2227 void
2228 free_grp(grp)
2229         struct grouplist *grp;
2230 {
2231         if (grp->gr_type == GT_HOST) {
2232                 if (grp->gr_ptr.gt_addrinfo != NULL)
2233                         freeaddrinfo(grp->gr_ptr.gt_addrinfo);
2234         } else if (grp->gr_type == GT_NET) {
2235                 if (grp->gr_ptr.gt_net.nt_name)
2236                         free(grp->gr_ptr.gt_net.nt_name);
2237         }
2238         free((caddr_t)grp);
2239 }
2240
2241 #ifdef DEBUG
2242 void
2243 SYSLOG(int pri, const char *fmt, ...)
2244 {
2245         va_list ap;
2246
2247         va_start(ap, fmt);
2248         vfprintf(stderr, fmt, ap);
2249         va_end(ap);
2250 }
2251 #endif /* DEBUG */
2252
2253 /*
2254  * Check options for consistency.
2255  */
2256 int
2257 check_options(dp)
2258         struct dirlist *dp;
2259 {
2260
2261         if (dp == (struct dirlist *)NULL)
2262             return (1);
2263         if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
2264             syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
2265             return (1);
2266         }
2267         if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2268                 syslog(LOG_ERR, "-mask requires -network");
2269                 return (1);
2270         }
2271         if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
2272                 syslog(LOG_ERR, "-network requires mask specification");
2273                 return (1);
2274         }
2275         if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
2276                 syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
2277                 return (1);
2278         }
2279         if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2280             syslog(LOG_ERR, "-alldirs has multiple directories");
2281             return (1);
2282         }
2283         return (0);
2284 }
2285
2286 /*
2287  * Check an absolute directory path for any symbolic links. Return true
2288  */
2289 int
2290 check_dirpath(dirp)
2291         char *dirp;
2292 {
2293         char *cp;
2294         int ret = 1;
2295         struct stat sb;
2296
2297         cp = dirp + 1;
2298         while (*cp && ret) {
2299                 if (*cp == '/') {
2300                         *cp = '\0';
2301                         if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2302                                 ret = 0;
2303                         *cp = '/';
2304                 }
2305                 cp++;
2306         }
2307         if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
2308                 ret = 0;
2309         return (ret);
2310 }
2311
2312 /*
2313  * Make a netmask according to the specified prefix length. The ss_family
2314  * and other non-address fields must be initialised before calling this.
2315  */
2316 int
2317 makemask(struct sockaddr_storage *ssp, int bitlen)
2318 {
2319         u_char *p;
2320         int bits, i, len;
2321
2322         if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
2323                 return (-1);
2324         if (bitlen > len * NBBY)
2325                 return (-1);
2326
2327         for (i = 0; i < len; i++) {
2328                 bits = (bitlen > NBBY) ? NBBY : bitlen;
2329                 *p++ = (1 << bits) - 1;
2330                 bitlen -= bits;
2331         }
2332         return 0;
2333 }
2334
2335 /*
2336  * Check that the sockaddr is a valid netmask. Returns 0 if the mask
2337  * is acceptable (i.e. of the form 1...10....0).
2338  */
2339 int
2340 checkmask(struct sockaddr *sa)
2341 {
2342         u_char *mask;
2343         int i, len;
2344
2345         if ((mask = sa_rawaddr(sa, &len)) == NULL)
2346                 return (-1);
2347
2348         for (i = 0; i < len; i++)
2349                 if (mask[i] != 0xff)
2350                         break;
2351         if (i < len) {
2352                 if (~mask[i] & (u_char)(~mask[i] + 1))
2353                         return (-1);
2354                 i++;
2355         }
2356         for (; i < len; i++)
2357                 if (mask[i] != 0)
2358                         return (-1);
2359         return (0);
2360 }
2361
2362 /*
2363  * Compare two sockaddrs according to a specified mask. Return zero if
2364  * `sa1' matches `sa2' when filtered by the netmask in `samask'.
2365  * If samask is NULL, perform a full comparision.
2366  */
2367 int
2368 sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
2369 {
2370         unsigned char *p1, *p2, *mask;
2371         int len, i;
2372
2373         if (sa1->sa_family != sa2->sa_family ||
2374             (p1 = sa_rawaddr(sa1, &len)) == NULL ||
2375             (p2 = sa_rawaddr(sa2, NULL)) == NULL)
2376                 return (1);
2377
2378         switch (sa1->sa_family) {
2379         case AF_INET6:
2380                 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
2381                     ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
2382                         return (1);
2383                 break;
2384         }
2385
2386         /* Simple binary comparison if no mask specified. */
2387         if (samask == NULL)
2388                 return (memcmp(p1, p2, len));
2389
2390         /* Set up the mask, and do a mask-based comparison. */
2391         if (sa1->sa_family != samask->sa_family ||
2392             (mask = sa_rawaddr(samask, NULL)) == NULL)
2393                 return (1);
2394
2395         for (i = 0; i < len; i++)
2396                 if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
2397                         return (1);
2398         return (0);
2399 }
2400
2401 /*
2402  * Return a pointer to the part of the sockaddr that contains the
2403  * raw address, and set *nbytes to its length in bytes. Returns
2404  * NULL if the address family is unknown.
2405  */
2406 void *
2407 sa_rawaddr(struct sockaddr *sa, int *nbytes) {
2408         void *p;
2409         int len;
2410
2411         switch (sa->sa_family) {
2412         case AF_INET:
2413                 len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
2414                 p = &((struct sockaddr_in *)sa)->sin_addr;
2415                 break;
2416         case AF_INET6:
2417                 len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
2418                 p = &((struct sockaddr_in6 *)sa)->sin6_addr;
2419                 break;
2420         default:
2421                 p = NULL;
2422                 len = 0;
2423         }
2424
2425         if (nbytes != NULL)
2426                 *nbytes = len;
2427         return (p);
2428 }
2429
2430 void
2431 huphandler(int sig)
2432 {
2433         got_sighup = 1;
2434 }
2435
2436 void terminate(sig)
2437 int sig;
2438 {
2439         close(mountdlockfd);
2440         unlink(MOUNTDLOCK);
2441         rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
2442         rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
2443         exit (0);
2444 }