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