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