Sync usage with man page.
[netbsd-mini2440.git] / usr.sbin / mountd / mountd.c
blob91ffa380296d5fd4b2eb6253f0df4974837e4c81
1 /* $NetBSD: mountd.c,v 1.119 2009/04/17 13:56:33 lukem Exp $ */
3 /*
4 * Copyright (c) 1989, 1993
5 * The Regents of the University of California. All rights reserved.
7 * This code is derived from software contributed to Berkeley by
8 * Herb Hasler and Rick Macklem at The University of Guelph.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
35 #include <sys/cdefs.h>
36 #ifndef lint
37 __COPYRIGHT("@(#) Copyright (c) 1989, 1993\
38 The Regents of the University of California. All rights reserved.");
39 #endif /* not lint */
41 #ifndef lint
42 #if 0
43 static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95";
44 #else
45 __RCSID("$NetBSD: mountd.c,v 1.119 2009/04/17 13:56:33 lukem Exp $");
46 #endif
47 #endif /* not lint */
49 #include <sys/param.h>
50 #include <sys/file.h>
51 #include <sys/ioctl.h>
52 #include <sys/mount.h>
53 #include <sys/socket.h>
54 #include <sys/stat.h>
55 #include <syslog.h>
56 #include <sys/ucred.h>
58 #include <rpc/rpc.h>
59 #include <rpc/pmap_clnt.h>
60 #include <rpc/pmap_prot.h>
61 #include <rpcsvc/mount.h>
62 #include <nfs/rpcv2.h>
63 #include <nfs/nfsproto.h>
64 #include <nfs/nfs.h>
65 #include <nfs/nfsmount.h>
67 #include <arpa/inet.h>
69 #include <ctype.h>
70 #include <errno.h>
71 #include <grp.h>
72 #include <netdb.h>
73 #include <pwd.h>
74 #include <netgroup.h>
75 #include <signal.h>
76 #include <stdio.h>
77 #include <stdlib.h>
78 #include <string.h>
79 #include <unistd.h>
80 #include <err.h>
81 #include <util.h>
82 #include "pathnames.h"
84 #ifdef IPSEC
85 #include <netinet6/ipsec.h>
86 #ifndef IPSEC_POLICY_IPSEC /* no ipsec support on old ipsec */
87 #undef IPSEC
88 #endif
89 #include "ipsec.h"
90 #endif
92 #include <stdarg.h>
95 * Structures for keeping the mount list and export list
97 struct mountlist {
98 struct mountlist *ml_next;
99 char ml_host[RPCMNT_NAMELEN + 1];
100 char ml_dirp[RPCMNT_PATHLEN + 1];
101 int ml_flag;/* XXX more flags (same as dp_flag) */
104 struct dirlist {
105 struct dirlist *dp_left;
106 struct dirlist *dp_right;
107 int dp_flag;
108 struct hostlist *dp_hosts; /* List of hosts this dir exported to */
109 char dp_dirp[1]; /* Actually malloc'd to size of dir */
111 /* dp_flag bits */
112 #define DP_DEFSET 0x1
113 #define DP_HOSTSET 0x2
114 #define DP_KERB 0x4
115 #define DP_NORESMNT 0x8
117 struct exportlist {
118 struct exportlist *ex_next;
119 struct dirlist *ex_dirl;
120 struct dirlist *ex_defdir;
121 int ex_flag;
122 fsid_t ex_fs;
123 char *ex_fsdir;
124 char *ex_indexfile;
126 /* ex_flag bits */
127 #define EX_LINKED 0x1
129 struct netmsk {
130 struct sockaddr_storage nt_net;
131 int nt_len;
132 char *nt_name;
135 union grouptypes {
136 struct addrinfo *gt_addrinfo;
137 struct netmsk gt_net;
140 struct grouplist {
141 int gr_type;
142 union grouptypes gr_ptr;
143 struct grouplist *gr_next;
145 /* Group types */
146 #define GT_NULL 0x0
147 #define GT_HOST 0x1
148 #define GT_NET 0x2
150 struct hostlist {
151 int ht_flag;/* Uses DP_xx bits */
152 struct grouplist *ht_grp;
153 struct hostlist *ht_next;
156 struct fhreturn {
157 int fhr_flag;
158 int fhr_vers;
159 size_t fhr_fhsize;
160 union {
161 uint8_t v2[NFSX_V2FH];
162 uint8_t v3[NFSX_V3FHMAX];
163 } fhr_fh;
166 /* Global defs */
167 static char *add_expdir __P((struct dirlist **, char *, int));
168 static void add_dlist __P((struct dirlist **, struct dirlist *,
169 struct grouplist *, int));
170 static void add_mlist __P((char *, char *, int));
171 static int check_dirpath __P((const char *, size_t, char *));
172 static int check_options __P((const char *, size_t, struct dirlist *));
173 static int chk_host __P((struct dirlist *, struct sockaddr *, int *, int *));
174 static int del_mlist __P((char *, char *, struct sockaddr *));
175 static struct dirlist *dirp_search __P((struct dirlist *, char *));
176 static int do_nfssvc __P((const char *, size_t, struct exportlist *,
177 struct grouplist *, int, struct uucred *, char *, int, struct statvfs *));
178 static int do_opt __P((const char *, size_t, char **, char **,
179 struct exportlist *, struct grouplist *, int *, int *, struct uucred *));
180 static struct exportlist *ex_search __P((fsid_t *));
181 static int parse_directory __P((const char *, size_t, struct grouplist *,
182 int, char *, struct exportlist **, struct statvfs *));
183 static int parse_host_netgroup __P((const char *, size_t, struct exportlist *,
184 struct grouplist *, char *, int *, struct grouplist **));
185 static struct exportlist *get_exp __P((void));
186 static void free_dir __P((struct dirlist *));
187 static void free_exp __P((struct exportlist *));
188 static void free_grp __P((struct grouplist *));
189 static void free_host __P((struct hostlist *));
190 static void get_exportlist __P((int));
191 static int get_host __P((const char *, size_t, const char *,
192 struct grouplist *));
193 static struct hostlist *get_ht __P((void));
194 static void get_mountlist __P((void));
195 static int get_net __P((char *, struct netmsk *, int));
196 static void free_exp_grp __P((struct exportlist *, struct grouplist *));
197 static struct grouplist *get_grp __P((void));
198 static void hang_dirp __P((struct dirlist *, struct grouplist *,
199 struct exportlist *, int));
200 static void mntsrv __P((struct svc_req *, SVCXPRT *));
201 static void nextfield __P((char **, char **));
202 static void parsecred __P((char *, struct uucred *));
203 static int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
204 static int scan_tree __P((struct dirlist *, struct sockaddr *));
205 static void send_umntall __P((int));
206 static int umntall_each __P((caddr_t, struct sockaddr_in *));
207 static int xdr_dir __P((XDR *, char *));
208 static int xdr_explist __P((XDR *, caddr_t));
209 static int xdr_fhs __P((XDR *, caddr_t));
210 static int xdr_mlist __P((XDR *, caddr_t));
211 static int bitcmp __P((void *, void *, int));
212 static int netpartcmp __P((struct sockaddr *, struct sockaddr *, int));
213 static int sacmp __P((struct sockaddr *, struct sockaddr *));
214 static int allones __P((struct sockaddr_storage *, int));
215 static int countones __P((struct sockaddr *));
216 static void bind_resv_port __P((int, sa_family_t, in_port_t));
217 static void no_nfs(int);
218 static struct exportlist *exphead;
219 static struct mountlist *mlhead;
220 static struct grouplist *grphead;
221 static const char *exname;
222 static struct uucred def_anon = {
224 (uid_t) -2,
225 (gid_t) -2,
227 { 0 }
230 static int opt_flags;
231 static int have_v6 = 1;
232 static const int ninumeric = NI_NUMERICHOST;
234 /* Bits for above */
235 #define OP_MAPROOT 0x001
236 #define OP_MAPALL 0x002
237 #define OP_KERB 0x004
238 #define OP_MASK 0x008
239 #define OP_NET 0x010
240 #define OP_ALLDIRS 0x040
241 #define OP_NORESPORT 0x080
242 #define OP_NORESMNT 0x100
243 #define OP_MASKLEN 0x200
245 static int debug = 0;
246 #if 0
247 static void SYSLOG __P((int, const char *,...));
248 #endif
249 int main __P((int, char *[]));
252 * If this is non-zero, -noresvport and -noresvmnt are implied for
253 * each export.
255 static int noprivports;
258 * Mountd server for NFS mount protocol as described in:
259 * NFS: Network File System Protocol Specification, RFC1094, Appendix A
260 * The optional arguments are the exports file name
261 * default: _PATH_EXPORTS
262 * "-d" to enable debugging
263 * and "-n" to allow nonroot mount.
266 main(argc, argv)
267 int argc;
268 char **argv;
270 SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp;
271 struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
272 int udpsock, tcpsock, udp6sock, tcp6sock;
273 int xcreated = 0, s;
274 int c, one = 1;
275 int maxrec = RPC_MAXDATASIZE;
276 in_port_t forcedport = 0;
277 #ifdef IPSEC
278 char *policy = NULL;
279 #define ADDOPTS "P:"
280 #else
281 #define ADDOPTS
282 #endif
284 while ((c = getopt(argc, argv, "dNnrp:" ADDOPTS)) != -1)
285 switch (c) {
286 #ifdef IPSEC
287 case 'P':
288 if (ipsecsetup_test(policy = optarg))
289 errx(1, "Invalid ipsec policy `%s'", policy);
290 break;
291 #endif
292 case 'p':
293 /* A forced port "0" will dynamically allocate a port */
294 forcedport = atoi(optarg);
295 break;
296 case 'd':
297 debug = 1;
298 break;
299 case 'N':
300 noprivports = 1;
301 break;
302 /* Compatibility */
303 case 'n':
304 case 'r':
305 break;
306 default:
307 fprintf(stderr, "usage: %s [-dNn]"
308 #ifdef IPSEC
309 " [-P policy]"
310 #endif
311 " [-p port] [exportsfile]\n", getprogname());
312 exit(1);
314 argc -= optind;
315 argv += optind;
316 grphead = NULL;
317 exphead = NULL;
318 mlhead = NULL;
319 if (argc == 1)
320 exname = *argv;
321 else
322 exname = _PATH_EXPORTS;
323 openlog("mountd", LOG_PID | (debug ? LOG_PERROR : 0), LOG_DAEMON);
324 (void)signal(SIGSYS, no_nfs);
326 s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
327 if (s < 0)
328 have_v6 = 0;
329 else
330 close(s);
332 if (debug)
333 (void)fprintf(stderr, "Getting export list.\n");
334 get_exportlist(0);
335 if (debug)
336 (void)fprintf(stderr, "Getting mount list.\n");
337 get_mountlist();
338 if (debug)
339 (void)fprintf(stderr, "Here we go.\n");
340 if (debug == 0) {
341 daemon(0, 0);
342 (void)signal(SIGINT, SIG_IGN);
343 (void)signal(SIGQUIT, SIG_IGN);
345 (void)signal(SIGHUP, get_exportlist);
346 (void)signal(SIGTERM, send_umntall);
347 pidfile(NULL);
349 rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
350 rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
352 udpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
353 tcpsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
354 udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
355 tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
358 * We're doing host-based access checks here, so don't allow
359 * v4-in-v6 to confuse things. The kernel will disable it
360 * by default on NFS sockets too.
362 if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6,
363 IPV6_V6ONLY, &one, sizeof one) < 0){
364 syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
365 exit(1);
367 if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6,
368 IPV6_V6ONLY, &one, sizeof one) < 0){
369 syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
370 exit(1);
373 udpconf = getnetconfigent("udp");
374 tcpconf = getnetconfigent("tcp");
375 udp6conf = getnetconfigent("udp6");
376 tcp6conf = getnetconfigent("tcp6");
378 rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
380 if (udpsock != -1 && udpconf != NULL) {
381 bind_resv_port(udpsock, AF_INET, forcedport);
382 #ifdef IPSEC
383 if (policy)
384 ipsecsetup(AF_INET, udpsock, policy);
385 #endif
386 udptransp = svc_dg_create(udpsock, 0, 0);
387 if (udptransp != NULL) {
388 if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1,
389 mntsrv, udpconf) ||
390 !svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3,
391 mntsrv, udpconf))
392 syslog(LOG_WARNING, "can't register UDP service");
393 else
394 xcreated++;
395 } else
396 syslog(LOG_WARNING, "can't create UDP service");
400 if (tcpsock != -1 && tcpconf != NULL) {
401 bind_resv_port(tcpsock, AF_INET, forcedport);
402 #ifdef IPSEC
403 if (policy)
404 ipsecsetup(AF_INET, tcpsock, policy);
405 #endif
406 listen(tcpsock, SOMAXCONN);
407 tcptransp = svc_vc_create(tcpsock, RPC_MAXDATASIZE,
408 RPC_MAXDATASIZE);
409 if (tcptransp != NULL) {
410 if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1,
411 mntsrv, tcpconf) ||
412 !svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3,
413 mntsrv, tcpconf))
414 syslog(LOG_WARNING, "can't register TCP service");
415 else
416 xcreated++;
417 } else
418 syslog(LOG_WARNING, "can't create TCP service");
422 if (udp6sock != -1 && udp6conf != NULL) {
423 bind_resv_port(udp6sock, AF_INET6, forcedport);
424 #ifdef IPSEC
425 if (policy)
426 ipsecsetup(AF_INET6, tcpsock, policy);
427 #endif
428 udp6transp = svc_dg_create(udp6sock, 0, 0);
429 if (udp6transp != NULL) {
430 if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1,
431 mntsrv, udp6conf) ||
432 !svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3,
433 mntsrv, udp6conf))
434 syslog(LOG_WARNING, "can't register UDP6 service");
435 else
436 xcreated++;
437 } else
438 syslog(LOG_WARNING, "can't create UDP6 service");
442 if (tcp6sock != -1 && tcp6conf != NULL) {
443 bind_resv_port(tcp6sock, AF_INET6, forcedport);
444 #ifdef IPSEC
445 if (policy)
446 ipsecsetup(AF_INET6, tcpsock, policy);
447 #endif
448 listen(tcp6sock, SOMAXCONN);
449 tcp6transp = svc_vc_create(tcp6sock, RPC_MAXDATASIZE,
450 RPC_MAXDATASIZE);
451 if (tcp6transp != NULL) {
452 if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1,
453 mntsrv, tcp6conf) ||
454 !svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3,
455 mntsrv, tcp6conf))
456 syslog(LOG_WARNING, "can't register TCP6 service");
457 else
458 xcreated++;
459 } else
460 syslog(LOG_WARNING, "can't create TCP6 service");
464 if (xcreated == 0) {
465 syslog(LOG_ERR, "could not create any services");
466 exit(1);
469 svc_run();
470 syslog(LOG_ERR, "Mountd died");
471 exit(1);
475 * The mount rpc service
477 void
478 mntsrv(rqstp, transp)
479 struct svc_req *rqstp;
480 SVCXPRT *transp;
482 struct exportlist *ep;
483 struct dirlist *dp;
484 struct fhreturn fhr;
485 struct stat stb;
486 struct statvfs fsb;
487 struct addrinfo *ai;
488 char host[NI_MAXHOST], numerichost[NI_MAXHOST];
489 int lookup_failed = 1;
490 struct sockaddr *saddr;
491 u_short sport;
492 char rpcpath[RPCMNT_PATHLEN + 1], rdirpath[MAXPATHLEN];
493 long bad = EACCES;
494 int defset, hostset, ret;
495 sigset_t sighup_mask;
496 struct sockaddr_in6 *sin6;
497 struct sockaddr_in *sin;
498 size_t fh_size;
500 (void)sigemptyset(&sighup_mask);
501 (void)sigaddset(&sighup_mask, SIGHUP);
502 saddr = svc_getrpccaller(transp)->buf;
503 switch (saddr->sa_family) {
504 case AF_INET6:
505 sin6 = (struct sockaddr_in6 *)saddr;
506 sport = ntohs(sin6->sin6_port);
507 break;
508 case AF_INET:
509 sin = (struct sockaddr_in *)saddr;
510 sport = ntohs(sin->sin_port);
511 break;
512 default:
513 syslog(LOG_ERR, "request from unknown address family");
514 return;
516 lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
517 NULL, 0, 0);
518 if (getnameinfo(saddr, saddr->sa_len, numerichost,
519 sizeof numerichost, NULL, 0, ninumeric) != 0)
520 strlcpy(numerichost, "?", sizeof(numerichost));
521 ai = NULL;
522 ret = 0;
523 switch (rqstp->rq_proc) {
524 case NULLPROC:
525 if (!svc_sendreply(transp, xdr_void, NULL))
526 syslog(LOG_ERR, "Can't send reply");
527 return;
528 case MOUNTPROC_MNT:
529 if (debug)
530 fprintf(stderr,
531 "got mount request from %s\n", numerichost);
532 if (!svc_getargs(transp, xdr_dir, rpcpath)) {
533 if (debug)
534 fprintf(stderr, "-> garbage args\n");
535 svcerr_decode(transp);
536 return;
538 if (debug)
539 fprintf(stderr,
540 "-> rpcpath: %s\n", rpcpath);
542 * Get the real pathname and make sure it is a file or
543 * directory that exists.
545 if (realpath(rpcpath, rdirpath) == 0 ||
546 stat(rdirpath, &stb) < 0 ||
547 (!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) ||
548 statvfs(rdirpath, &fsb) < 0) {
549 (void)chdir("/"); /* Just in case realpath doesn't */
550 if (debug)
551 (void)fprintf(stderr, "-> stat failed on %s\n",
552 rdirpath);
553 if (!svc_sendreply(transp, xdr_long, (caddr_t) &bad))
554 syslog(LOG_ERR, "Can't send reply");
555 return;
557 if (debug)
558 fprintf(stderr,
559 "-> dirpath: %s\n", rdirpath);
560 /* Check in the exports list */
561 (void)sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
562 ep = ex_search(&fsb.f_fsidx);
563 hostset = defset = 0;
564 if (ep && (chk_host(ep->ex_defdir, saddr, &defset,
565 &hostset) || ((dp = dirp_search(ep->ex_dirl, rdirpath)) &&
566 chk_host(dp, saddr, &defset, &hostset)) ||
567 (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
568 scan_tree(ep->ex_dirl, saddr) == 0))) {
569 if ((hostset & DP_HOSTSET) == 0) {
570 hostset = defset;
572 if (sport >= IPPORT_RESERVED &&
573 !(hostset & DP_NORESMNT)) {
574 syslog(LOG_NOTICE,
575 "Refused mount RPC from host %s port %d",
576 numerichost, sport);
577 svcerr_weakauth(transp);
578 goto out;
580 fhr.fhr_flag = hostset;
581 fhr.fhr_vers = rqstp->rq_vers;
582 /* Get the file handle */
583 memset(&fhr.fhr_fh, 0, sizeof(fhr.fhr_fh)); /* for v2 */
584 fh_size = sizeof(fhr.fhr_fh);
585 if (getfh(rdirpath, &fhr.fhr_fh, &fh_size) < 0) {
586 bad = errno;
587 syslog(LOG_ERR, "Can't get fh for %s", rdirpath);
588 if (!svc_sendreply(transp, xdr_long,
589 (char *)&bad))
590 syslog(LOG_ERR, "Can't send reply");
591 goto out;
593 if ((fhr.fhr_vers == 1 && fh_size > NFSX_V2FH) ||
594 fh_size > NFSX_V3FHMAX) {
595 bad = EINVAL; /* XXX */
596 if (!svc_sendreply(transp, xdr_long,
597 (char *)&bad))
598 syslog(LOG_ERR, "Can't send reply");
599 goto out;
601 fhr.fhr_fhsize = fh_size;
602 if (!svc_sendreply(transp, xdr_fhs, (char *) &fhr))
603 syslog(LOG_ERR, "Can't send reply");
604 if (!lookup_failed)
605 add_mlist(host, rdirpath, hostset);
606 else
607 add_mlist(numerichost, rdirpath, hostset);
608 if (debug)
609 (void)fprintf(stderr, "Mount successful.\n");
610 } else {
611 if (!svc_sendreply(transp, xdr_long, (caddr_t) &bad))
612 syslog(LOG_ERR, "Can't send reply");
614 out:
615 (void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
616 return;
617 case MOUNTPROC_DUMP:
618 if (!svc_sendreply(transp, xdr_mlist, NULL))
619 syslog(LOG_ERR, "Can't send reply");
620 return;
621 case MOUNTPROC_UMNT:
622 if (!svc_getargs(transp, xdr_dir, rdirpath)) {
623 svcerr_decode(transp);
624 return;
626 if (!lookup_failed)
627 ret = del_mlist(host, rdirpath, saddr);
628 ret |= del_mlist(numerichost, rdirpath, saddr);
629 if (ret) {
630 svcerr_weakauth(transp);
631 return;
633 if (!svc_sendreply(transp, xdr_void, NULL))
634 syslog(LOG_ERR, "Can't send reply");
635 return;
636 case MOUNTPROC_UMNTALL:
637 if (!lookup_failed)
638 ret = del_mlist(host, NULL, saddr);
639 ret |= del_mlist(numerichost, NULL, saddr);
640 if (ret) {
641 svcerr_weakauth(transp);
642 return;
644 if (!svc_sendreply(transp, xdr_void, NULL))
645 syslog(LOG_ERR, "Can't send reply");
646 return;
647 case MOUNTPROC_EXPORT:
648 case MOUNTPROC_EXPORTALL:
649 if (!svc_sendreply(transp, xdr_explist, NULL))
650 syslog(LOG_ERR, "Can't send reply");
651 return;
654 default:
655 svcerr_noproc(transp);
656 return;
661 * Xdr conversion for a dirpath string
663 static int
664 xdr_dir(xdrsp, dirp)
665 XDR *xdrsp;
666 char *dirp;
669 return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
673 * Xdr routine to generate file handle reply
675 static int
676 xdr_fhs(xdrsp, cp)
677 XDR *xdrsp;
678 caddr_t cp;
680 struct fhreturn *fhrp = (struct fhreturn *) cp;
681 long ok = 0, len, auth;
683 if (!xdr_long(xdrsp, &ok))
684 return (0);
685 switch (fhrp->fhr_vers) {
686 case 1:
687 return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
688 case 3:
689 len = fhrp->fhr_fhsize;
690 if (!xdr_long(xdrsp, &len))
691 return (0);
692 if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
693 return (0);
694 if (fhrp->fhr_flag & DP_KERB)
695 auth = RPCAUTH_KERB4;
696 else
697 auth = RPCAUTH_UNIX;
698 len = 1;
699 if (!xdr_long(xdrsp, &len))
700 return (0);
701 return (xdr_long(xdrsp, &auth));
703 return (0);
707 xdr_mlist(xdrsp, cp)
708 XDR *xdrsp;
709 caddr_t cp;
711 struct mountlist *mlp;
712 int trueval = 1;
713 int falseval = 0;
714 char *strp;
716 mlp = mlhead;
717 while (mlp) {
718 if (!xdr_bool(xdrsp, &trueval))
719 return (0);
720 strp = &mlp->ml_host[0];
721 if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
722 return (0);
723 strp = &mlp->ml_dirp[0];
724 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
725 return (0);
726 mlp = mlp->ml_next;
728 if (!xdr_bool(xdrsp, &falseval))
729 return (0);
730 return (1);
734 * Xdr conversion for export list
737 xdr_explist(xdrsp, cp)
738 XDR *xdrsp;
739 caddr_t cp;
741 struct exportlist *ep;
742 int falseval = 0;
743 int putdef;
744 sigset_t sighup_mask;
746 (void)sigemptyset(&sighup_mask);
747 (void)sigaddset(&sighup_mask, SIGHUP);
748 (void)sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
749 ep = exphead;
750 while (ep) {
751 putdef = 0;
752 if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
753 goto errout;
754 if (ep->ex_defdir && putdef == 0 &&
755 put_exlist(ep->ex_defdir, xdrsp, NULL, &putdef))
756 goto errout;
757 ep = ep->ex_next;
759 (void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
760 if (!xdr_bool(xdrsp, &falseval))
761 return (0);
762 return (1);
763 errout:
764 (void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
765 return (0);
769 * Called from xdr_explist() to traverse the tree and export the
770 * directory paths. Assumes SIGHUP has already been masked.
773 put_exlist(dp, xdrsp, adp, putdefp)
774 struct dirlist *dp;
775 XDR *xdrsp;
776 struct dirlist *adp;
777 int *putdefp;
779 struct grouplist *grp;
780 struct hostlist *hp;
781 int trueval = 1;
782 int falseval = 0;
783 int gotalldir = 0;
784 char *strp;
786 if (dp) {
787 if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
788 return (1);
789 if (!xdr_bool(xdrsp, &trueval))
790 return (1);
791 strp = dp->dp_dirp;
792 if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
793 return (1);
794 if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
795 gotalldir = 1;
796 *putdefp = 1;
798 if ((dp->dp_flag & DP_DEFSET) == 0 &&
799 (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
800 hp = dp->dp_hosts;
801 while (hp) {
802 grp = hp->ht_grp;
803 if (grp->gr_type == GT_HOST) {
804 if (!xdr_bool(xdrsp, &trueval))
805 return (1);
806 strp =
807 grp->gr_ptr.gt_addrinfo->ai_canonname;
808 if (!xdr_string(xdrsp, &strp,
809 RPCMNT_NAMELEN))
810 return (1);
811 } else if (grp->gr_type == GT_NET) {
812 if (!xdr_bool(xdrsp, &trueval))
813 return (1);
814 strp = grp->gr_ptr.gt_net.nt_name;
815 if (!xdr_string(xdrsp, &strp,
816 RPCMNT_NAMELEN))
817 return (1);
819 hp = hp->ht_next;
820 if (gotalldir && hp == NULL) {
821 hp = adp->dp_hosts;
822 gotalldir = 0;
826 if (!xdr_bool(xdrsp, &falseval))
827 return (1);
828 if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
829 return (1);
831 return (0);
834 static int
835 parse_host_netgroup(line, lineno, ep, tgrp, cp, has_host, grp)
836 const char *line;
837 size_t lineno;
838 struct exportlist *ep;
839 struct grouplist *tgrp;
840 char *cp;
841 int *has_host;
842 struct grouplist **grp;
844 const char *hst, *usr, *dom;
845 int netgrp;
847 if (ep == NULL) {
848 syslog(LOG_ERR, "\"%s\", line %ld: No current export",
849 line, (unsigned long)lineno);
850 return 0;
852 setnetgrent(cp);
853 netgrp = getnetgrent(&hst, &usr, &dom);
854 do {
855 if (*has_host) {
856 (*grp)->gr_next = get_grp();
857 *grp = (*grp)->gr_next;
859 if (netgrp) {
860 if (hst == NULL) {
861 syslog(LOG_ERR,
862 "\"%s\", line %ld: No host in netgroup %s",
863 line, (unsigned long)lineno, cp);
864 goto bad;
866 if (get_host(line, lineno, hst, *grp))
867 goto bad;
868 } else if (get_host(line, lineno, cp, *grp))
869 goto bad;
870 *has_host = TRUE;
871 } while (netgrp && getnetgrent(&hst, &usr, &dom));
873 endnetgrent();
874 return 1;
875 bad:
876 endnetgrent();
877 return 0;
881 static int
882 parse_directory(line, lineno, tgrp, got_nondir, cp, ep, fsp)
883 const char *line;
884 size_t lineno;
885 struct grouplist *tgrp;
886 int got_nondir;
887 char *cp;
888 struct exportlist **ep;
889 struct statvfs *fsp;
891 if (!check_dirpath(line, lineno, cp))
892 return 0;
894 if (statvfs(cp, fsp) == -1) {
895 syslog(LOG_ERR, "\"%s\", line %ld: statvfs for `%s' failed: %m",
896 line, (unsigned long)lineno, cp);
897 return 0;
900 if (got_nondir) {
901 syslog(LOG_ERR,
902 "\"%s\", line %ld: Directories must precede files",
903 line, (unsigned long)lineno);
904 return 0;
906 if (*ep) {
907 if ((*ep)->ex_fs.__fsid_val[0] != fsp->f_fsidx.__fsid_val[0] ||
908 (*ep)->ex_fs.__fsid_val[1] != fsp->f_fsidx.__fsid_val[1]) {
909 syslog(LOG_ERR,
910 "\"%s\", line %ld: filesystem ids disagree",
911 line, (unsigned long)lineno);
912 return 0;
914 } else {
916 * See if this directory is already
917 * in the list.
919 *ep = ex_search(&fsp->f_fsidx);
920 if (*ep == NULL) {
921 *ep = get_exp();
922 (*ep)->ex_fs = fsp->f_fsidx;
923 (*ep)->ex_fsdir = estrdup(fsp->f_mntonname);
924 if (debug)
925 (void)fprintf(stderr,
926 "Making new ep fs=0x%x,0x%x\n",
927 fsp->f_fsidx.__fsid_val[0], fsp->f_fsidx.__fsid_val[1]);
928 } else {
929 if (debug)
930 (void)fprintf(stderr,
931 "Found ep fs=0x%x,0x%x\n",
932 fsp->f_fsidx.__fsid_val[0], fsp->f_fsidx.__fsid_val[1]);
936 return 1;
941 * Get the export list
943 /* ARGSUSED */
944 void
945 get_exportlist(n)
946 int n;
948 struct exportlist *ep, *ep2;
949 struct grouplist *grp, *tgrp;
950 struct exportlist **epp;
951 struct dirlist *dirhead;
952 struct statvfs fsb, *fsp;
953 struct addrinfo *ai;
954 struct uucred anon;
955 char *cp, *endcp, *dirp, savedc;
956 int has_host, exflags, got_nondir, dirplen, num, i;
957 FILE *exp_file;
958 char *line;
959 size_t lineno = 0, len;
963 * First, get rid of the old list
965 ep = exphead;
966 while (ep) {
967 ep2 = ep;
968 ep = ep->ex_next;
969 free_exp(ep2);
971 exphead = NULL;
973 dirp = NULL;
974 dirplen = 0;
975 grp = grphead;
976 while (grp) {
977 tgrp = grp;
978 grp = grp->gr_next;
979 free_grp(tgrp);
981 grphead = NULL;
984 * And delete exports that are in the kernel for all local
985 * file systems.
987 num = getmntinfo(&fsp, MNT_NOWAIT);
988 for (i = 0; i < num; i++) {
989 struct mountd_exports_list mel;
991 /* Delete all entries from the export list. */
992 mel.mel_path = fsp->f_mntonname;
993 mel.mel_nexports = 0;
994 if (nfssvc(NFSSVC_SETEXPORTSLIST, &mel) == -1 &&
995 errno != EOPNOTSUPP)
996 syslog(LOG_ERR, "Can't delete exports for %s (%m)",
997 fsp->f_mntonname);
999 fsp++;
1003 * Read in the exports file and build the list, calling
1004 * mount() as we go along to push the export rules into the kernel.
1006 if ((exp_file = fopen(exname, "r")) == NULL) {
1008 * Don't exit here; we can still reload the config
1009 * after a SIGHUP.
1011 if (debug)
1012 (void)fprintf(stderr, "Can't open %s: %s\n", exname,
1013 strerror(errno));
1014 return;
1016 dirhead = NULL;
1017 while ((line = fparseln(exp_file, &len, &lineno, NULL, 0)) != NULL) {
1018 if (debug)
1019 (void)fprintf(stderr, "Got line %s\n", line);
1020 cp = line;
1021 nextfield(&cp, &endcp);
1022 if (cp == endcp)
1023 goto nextline; /* skip empty line */
1025 * Set defaults.
1027 has_host = FALSE;
1028 anon = def_anon;
1029 exflags = MNT_EXPORTED;
1030 got_nondir = 0;
1031 opt_flags = 0;
1032 ep = NULL;
1034 if (noprivports) {
1035 opt_flags |= OP_NORESMNT | OP_NORESPORT;
1036 exflags |= MNT_EXNORESPORT;
1040 * Create new exports list entry
1042 len = endcp - cp;
1043 tgrp = grp = get_grp();
1044 while (len > 0) {
1045 if (len > RPCMNT_NAMELEN) {
1046 *endcp = '\0';
1047 syslog(LOG_ERR,
1048 "\"%s\", line %ld: name `%s' is too long",
1049 line, (unsigned long)lineno, cp);
1050 goto badline;
1052 switch (*cp) {
1053 case '-':
1055 * Option
1057 if (ep == NULL) {
1058 syslog(LOG_ERR,
1059 "\"%s\", line %ld: No current export list",
1060 line, (unsigned long)lineno);
1061 goto badline;
1063 if (debug)
1064 (void)fprintf(stderr, "doing opt %s\n",
1065 cp);
1066 got_nondir = 1;
1067 if (do_opt(line, lineno, &cp, &endcp, ep, grp,
1068 &has_host, &exflags, &anon))
1069 goto badline;
1070 break;
1072 case '/':
1074 * Directory
1076 savedc = *endcp;
1077 *endcp = '\0';
1079 if (!parse_directory(line, lineno, tgrp,
1080 got_nondir, cp, &ep, &fsb))
1081 goto badline;
1083 * Add dirpath to export mount point.
1085 dirp = add_expdir(&dirhead, cp, len);
1086 dirplen = len;
1088 *endcp = savedc;
1089 break;
1091 default:
1093 * Host or netgroup.
1095 savedc = *endcp;
1096 *endcp = '\0';
1098 if (!parse_host_netgroup(line, lineno, ep,
1099 tgrp, cp, &has_host, &grp))
1100 goto badline;
1102 got_nondir = 1;
1104 *endcp = savedc;
1105 break;
1108 cp = endcp;
1109 nextfield(&cp, &endcp);
1110 len = endcp - cp;
1112 if (check_options(line, lineno, dirhead))
1113 goto badline;
1115 if (!has_host) {
1116 grp->gr_type = GT_HOST;
1117 if (debug)
1118 (void)fprintf(stderr,
1119 "Adding a default entry\n");
1120 /* add a default group and make the grp list NULL */
1121 ai = emalloc(sizeof(struct addrinfo));
1122 ai->ai_flags = 0;
1123 ai->ai_family = AF_INET; /* XXXX */
1124 ai->ai_socktype = SOCK_DGRAM;
1125 /* setting the length to 0 will match anything */
1126 ai->ai_addrlen = 0;
1127 ai->ai_flags = AI_CANONNAME;
1128 ai->ai_canonname = estrdup("Default");
1129 ai->ai_addr = NULL;
1130 ai->ai_next = NULL;
1131 grp->gr_ptr.gt_addrinfo = ai;
1133 } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
1135 * Don't allow a network export coincide with a list of
1136 * host(s) on the same line.
1138 syslog(LOG_ERR,
1139 "\"%s\", line %ld: Mixed exporting of networks and hosts is disallowed",
1140 line, (unsigned long)lineno);
1141 goto badline;
1144 * Loop through hosts, pushing the exports into the kernel.
1145 * After loop, tgrp points to the start of the list and
1146 * grp points to the last entry in the list.
1148 grp = tgrp;
1149 do {
1150 if (do_nfssvc(line, lineno, ep, grp, exflags, &anon,
1151 dirp, dirplen, &fsb))
1152 goto badline;
1153 } while (grp->gr_next && (grp = grp->gr_next));
1156 * Success. Update the data structures.
1158 if (has_host) {
1159 hang_dirp(dirhead, tgrp, ep, opt_flags);
1160 grp->gr_next = grphead;
1161 grphead = tgrp;
1162 } else {
1163 hang_dirp(dirhead, NULL, ep, opt_flags);
1164 free_grp(tgrp);
1166 tgrp = NULL;
1167 dirhead = NULL;
1168 if ((ep->ex_flag & EX_LINKED) == 0) {
1169 ep2 = exphead;
1170 epp = &exphead;
1173 * Insert in the list in alphabetical order.
1175 while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
1176 epp = &ep2->ex_next;
1177 ep2 = ep2->ex_next;
1179 if (ep2)
1180 ep->ex_next = ep2;
1181 *epp = ep;
1182 ep->ex_flag |= EX_LINKED;
1184 goto nextline;
1185 badline:
1186 free_exp_grp(ep, grp);
1187 nextline:
1188 if (dirhead) {
1189 free_dir(dirhead);
1190 dirhead = NULL;
1192 free(line);
1194 (void)fclose(exp_file);
1198 * Allocate an export list element
1200 static struct exportlist *
1201 get_exp()
1203 struct exportlist *ep;
1205 ep = emalloc(sizeof(struct exportlist));
1206 (void)memset(ep, 0, sizeof(struct exportlist));
1207 return (ep);
1211 * Allocate a group list element
1213 static struct grouplist *
1214 get_grp()
1216 struct grouplist *gp;
1218 gp = emalloc(sizeof(struct grouplist));
1219 (void)memset(gp, 0, sizeof(struct grouplist));
1220 return (gp);
1224 * Clean up upon an error in get_exportlist().
1226 static void
1227 free_exp_grp(ep, grp)
1228 struct exportlist *ep;
1229 struct grouplist *grp;
1231 struct grouplist *tgrp;
1233 if (ep && (ep->ex_flag & EX_LINKED) == 0)
1234 free_exp(ep);
1235 while (grp) {
1236 tgrp = grp;
1237 grp = grp->gr_next;
1238 free_grp(tgrp);
1243 * Search the export list for a matching fs.
1245 static struct exportlist *
1246 ex_search(fsid)
1247 fsid_t *fsid;
1249 struct exportlist *ep;
1251 ep = exphead;
1252 while (ep) {
1253 if (ep->ex_fs.__fsid_val[0] == fsid->__fsid_val[0] &&
1254 ep->ex_fs.__fsid_val[1] == fsid->__fsid_val[1])
1255 return (ep);
1256 ep = ep->ex_next;
1258 return (ep);
1262 * Add a directory path to the list.
1264 static char *
1265 add_expdir(dpp, cp, len)
1266 struct dirlist **dpp;
1267 char *cp;
1268 int len;
1270 struct dirlist *dp;
1272 dp = emalloc(sizeof(struct dirlist) + len);
1273 dp->dp_left = *dpp;
1274 dp->dp_right = NULL;
1275 dp->dp_flag = 0;
1276 dp->dp_hosts = NULL;
1277 (void)strcpy(dp->dp_dirp, cp);
1278 *dpp = dp;
1279 return (dp->dp_dirp);
1283 * Hang the dir list element off the dirpath binary tree as required
1284 * and update the entry for host.
1286 void
1287 hang_dirp(dp, grp, ep, flags)
1288 struct dirlist *dp;
1289 struct grouplist *grp;
1290 struct exportlist *ep;
1291 int flags;
1293 struct hostlist *hp;
1294 struct dirlist *dp2;
1296 if (flags & OP_ALLDIRS) {
1297 if (ep->ex_defdir)
1298 free(dp);
1299 else
1300 ep->ex_defdir = dp;
1301 if (grp == NULL) {
1302 ep->ex_defdir->dp_flag |= DP_DEFSET;
1303 if (flags & OP_KERB)
1304 ep->ex_defdir->dp_flag |= DP_KERB;
1305 if (flags & OP_NORESMNT)
1306 ep->ex_defdir->dp_flag |= DP_NORESMNT;
1307 } else
1308 while (grp) {
1309 hp = get_ht();
1310 if (flags & OP_KERB)
1311 hp->ht_flag |= DP_KERB;
1312 if (flags & OP_NORESMNT)
1313 hp->ht_flag |= DP_NORESMNT;
1314 hp->ht_grp = grp;
1315 hp->ht_next = ep->ex_defdir->dp_hosts;
1316 ep->ex_defdir->dp_hosts = hp;
1317 grp = grp->gr_next;
1319 } else {
1322 * Loop through the directories adding them to the tree.
1324 while (dp) {
1325 dp2 = dp->dp_left;
1326 add_dlist(&ep->ex_dirl, dp, grp, flags);
1327 dp = dp2;
1333 * Traverse the binary tree either updating a node that is already there
1334 * for the new directory or adding the new node.
1336 static void
1337 add_dlist(dpp, newdp, grp, flags)
1338 struct dirlist **dpp;
1339 struct dirlist *newdp;
1340 struct grouplist *grp;
1341 int flags;
1343 struct dirlist *dp;
1344 struct hostlist *hp;
1345 int cmp;
1347 dp = *dpp;
1348 if (dp) {
1349 cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
1350 if (cmp > 0) {
1351 add_dlist(&dp->dp_left, newdp, grp, flags);
1352 return;
1353 } else if (cmp < 0) {
1354 add_dlist(&dp->dp_right, newdp, grp, flags);
1355 return;
1356 } else
1357 free(newdp);
1358 } else {
1359 dp = newdp;
1360 dp->dp_left = NULL;
1361 *dpp = dp;
1363 if (grp) {
1366 * Hang all of the host(s) off of the directory point.
1368 do {
1369 hp = get_ht();
1370 if (flags & OP_KERB)
1371 hp->ht_flag |= DP_KERB;
1372 if (flags & OP_NORESMNT)
1373 hp->ht_flag |= DP_NORESMNT;
1374 hp->ht_grp = grp;
1375 hp->ht_next = dp->dp_hosts;
1376 dp->dp_hosts = hp;
1377 grp = grp->gr_next;
1378 } while (grp);
1379 } else {
1380 dp->dp_flag |= DP_DEFSET;
1381 if (flags & OP_KERB)
1382 dp->dp_flag |= DP_KERB;
1383 if (flags & OP_NORESMNT)
1384 dp->dp_flag |= DP_NORESMNT;
1389 * Search for a dirpath on the export point.
1391 static struct dirlist *
1392 dirp_search(dp, dirp)
1393 struct dirlist *dp;
1394 char *dirp;
1396 int cmp;
1398 if (dp) {
1399 cmp = strcmp(dp->dp_dirp, dirp);
1400 if (cmp > 0)
1401 return (dirp_search(dp->dp_left, dirp));
1402 else if (cmp < 0)
1403 return (dirp_search(dp->dp_right, dirp));
1404 else
1405 return (dp);
1407 return (dp);
1411 * Some helper functions for netmasks. They all assume masks in network
1412 * order (big endian).
1414 static int
1415 bitcmp(void *dst, void *src, int bitlen)
1417 int i;
1418 u_int8_t *p1 = dst, *p2 = src;
1419 u_int8_t bitmask;
1420 int bytelen, bitsleft;
1422 bytelen = bitlen / 8;
1423 bitsleft = bitlen % 8;
1425 if (debug) {
1426 printf("comparing:\n");
1427 for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++)
1428 printf("%02x", p1[i]);
1429 printf("\n");
1430 for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++)
1431 printf("%02x", p2[i]);
1432 printf("\n");
1435 for (i = 0; i < bytelen; i++) {
1436 if (*p1 != *p2)
1437 return 1;
1438 p1++;
1439 p2++;
1442 for (i = 0; i < bitsleft; i++) {
1443 bitmask = 1 << (7 - i);
1444 if ((*p1 & bitmask) != (*p2 & bitmask))
1445 return 1;
1448 return 0;
1451 static int
1452 netpartcmp(struct sockaddr *s1, struct sockaddr *s2, int bitlen)
1454 void *src, *dst;
1456 if (s1->sa_family != s2->sa_family)
1457 return 1;
1459 switch (s1->sa_family) {
1460 case AF_INET:
1461 src = &((struct sockaddr_in *)s1)->sin_addr;
1462 dst = &((struct sockaddr_in *)s2)->sin_addr;
1463 if (bitlen > (int)sizeof(((struct sockaddr_in *)s1)->sin_addr) * 8)
1464 return 1;
1465 break;
1466 case AF_INET6:
1467 src = &((struct sockaddr_in6 *)s1)->sin6_addr;
1468 dst = &((struct sockaddr_in6 *)s2)->sin6_addr;
1469 if (((struct sockaddr_in6 *)s1)->sin6_scope_id !=
1470 ((struct sockaddr_in6 *)s2)->sin6_scope_id)
1471 return 1;
1472 if (bitlen > (int)sizeof(((struct sockaddr_in6 *)s1)->sin6_addr) * 8)
1473 return 1;
1474 break;
1475 default:
1476 return 1;
1479 return bitcmp(src, dst, bitlen);
1482 static int
1483 allones(struct sockaddr_storage *ssp, int bitlen)
1485 u_int8_t *p;
1486 int bytelen, bitsleft, i;
1487 int zerolen;
1489 switch (ssp->ss_family) {
1490 case AF_INET:
1491 p = (u_int8_t *)&((struct sockaddr_in *)ssp)->sin_addr;
1492 zerolen = sizeof (((struct sockaddr_in *)ssp)->sin_addr);
1493 break;
1494 case AF_INET6:
1495 p = (u_int8_t *)&((struct sockaddr_in6 *)ssp)->sin6_addr;
1496 zerolen = sizeof (((struct sockaddr_in6 *)ssp)->sin6_addr);
1497 break;
1498 default:
1499 return -1;
1502 memset(p, 0, zerolen);
1504 bytelen = bitlen / 8;
1505 bitsleft = bitlen % 8;
1507 if (bytelen > zerolen)
1508 return -1;
1510 for (i = 0; i < bytelen; i++)
1511 *p++ = 0xff;
1513 for (i = 0; i < bitsleft; i++)
1514 *p |= 1 << (7 - i);
1516 return 0;
1519 static int
1520 countones(struct sockaddr *sa)
1522 void *mask;
1523 int i, bits = 0, bytelen;
1524 u_int8_t *p;
1526 switch (sa->sa_family) {
1527 case AF_INET:
1528 mask = (u_int8_t *)&((struct sockaddr_in *)sa)->sin_addr;
1529 bytelen = 4;
1530 break;
1531 case AF_INET6:
1532 mask = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_addr;
1533 bytelen = 16;
1534 break;
1535 default:
1536 return 0;
1539 p = mask;
1541 for (i = 0; i < bytelen; i++, p++) {
1542 if (*p != 0xff) {
1543 for (bits = 0; bits < 8; bits++) {
1544 if (!(*p & (1 << (7 - bits))))
1545 break;
1547 break;
1551 return (i * 8 + bits);
1554 static int
1555 sacmp(struct sockaddr *sa1, struct sockaddr *sa2)
1557 void *p1, *p2;
1558 int len;
1560 if (sa1->sa_family != sa2->sa_family)
1561 return 1;
1563 switch (sa1->sa_family) {
1564 case AF_INET:
1565 p1 = &((struct sockaddr_in *)sa1)->sin_addr;
1566 p2 = &((struct sockaddr_in *)sa2)->sin_addr;
1567 len = 4;
1568 break;
1569 case AF_INET6:
1570 p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
1571 p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
1572 len = 16;
1573 if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
1574 ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
1575 return 1;
1576 break;
1577 default:
1578 return 1;
1581 return memcmp(p1, p2, len);
1585 * Scan for a host match in a directory tree.
1587 static int
1588 chk_host(dp, saddr, defsetp, hostsetp)
1589 struct dirlist *dp;
1590 struct sockaddr *saddr;
1591 int *defsetp;
1592 int *hostsetp;
1594 struct hostlist *hp;
1595 struct grouplist *grp;
1596 struct addrinfo *ai;
1598 if (dp) {
1599 if (dp->dp_flag & DP_DEFSET)
1600 *defsetp = dp->dp_flag;
1601 hp = dp->dp_hosts;
1602 while (hp) {
1603 grp = hp->ht_grp;
1604 switch (grp->gr_type) {
1605 case GT_HOST:
1606 ai = grp->gr_ptr.gt_addrinfo;
1607 for (; ai; ai = ai->ai_next) {
1608 if (!sacmp(ai->ai_addr, saddr)) {
1609 *hostsetp =
1610 (hp->ht_flag | DP_HOSTSET);
1611 return (1);
1614 break;
1615 case GT_NET:
1616 if (!netpartcmp(saddr,
1617 (struct sockaddr *)
1618 &grp->gr_ptr.gt_net.nt_net,
1619 grp->gr_ptr.gt_net.nt_len)) {
1620 *hostsetp = (hp->ht_flag | DP_HOSTSET);
1621 return (1);
1623 break;
1625 hp = hp->ht_next;
1628 return (0);
1632 * Scan tree for a host that matches the address.
1634 static int
1635 scan_tree(dp, saddr)
1636 struct dirlist *dp;
1637 struct sockaddr *saddr;
1639 int defset, hostset;
1641 if (dp) {
1642 if (scan_tree(dp->dp_left, saddr))
1643 return (1);
1644 if (chk_host(dp, saddr, &defset, &hostset))
1645 return (1);
1646 if (scan_tree(dp->dp_right, saddr))
1647 return (1);
1649 return (0);
1653 * Traverse the dirlist tree and free it up.
1655 static void
1656 free_dir(dp)
1657 struct dirlist *dp;
1660 if (dp) {
1661 free_dir(dp->dp_left);
1662 free_dir(dp->dp_right);
1663 free_host(dp->dp_hosts);
1664 free(dp);
1669 * Parse the option string and update fields.
1670 * Option arguments may either be -<option>=<value> or
1671 * -<option> <value>
1673 static int
1674 do_opt(line, lineno, cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
1675 const char *line;
1676 size_t lineno;
1677 char **cpp, **endcpp;
1678 struct exportlist *ep;
1679 struct grouplist *grp;
1680 int *has_hostp;
1681 int *exflagsp;
1682 struct uucred *cr;
1684 char *cpoptarg, *cpoptend;
1685 char *cp, *cpopt, savedc, savedc2;
1686 char *endcp = NULL; /* XXX: GCC */
1687 int allflag, usedarg;
1689 cpopt = *cpp;
1690 cpopt++;
1691 cp = *endcpp;
1692 savedc = *cp;
1693 *cp = '\0';
1694 while (cpopt && *cpopt) {
1695 allflag = 1;
1696 usedarg = -2;
1697 savedc2 = '\0';
1698 if ((cpoptend = strchr(cpopt, ',')) != NULL) {
1699 *cpoptend++ = '\0';
1700 if ((cpoptarg = strchr(cpopt, '=')) != NULL)
1701 *cpoptarg++ = '\0';
1702 } else {
1703 if ((cpoptarg = strchr(cpopt, '=')) != NULL)
1704 *cpoptarg++ = '\0';
1705 else {
1706 *cp = savedc;
1707 nextfield(&cp, &endcp);
1708 **endcpp = '\0';
1709 if (endcp > cp && *cp != '-') {
1710 cpoptarg = cp;
1711 savedc2 = *endcp;
1712 *endcp = '\0';
1713 usedarg = 0;
1717 if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
1718 *exflagsp |= MNT_EXRDONLY;
1719 } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
1720 !(allflag = strcmp(cpopt, "mapall")) ||
1721 !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
1722 usedarg++;
1723 parsecred(cpoptarg, cr);
1724 if (allflag == 0) {
1725 *exflagsp |= MNT_EXPORTANON;
1726 opt_flags |= OP_MAPALL;
1727 } else
1728 opt_flags |= OP_MAPROOT;
1729 } else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
1730 *exflagsp |= MNT_EXKERB;
1731 opt_flags |= OP_KERB;
1732 } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
1733 !strcmp(cpopt, "m"))) {
1734 if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
1735 syslog(LOG_ERR,
1736 "\"%s\", line %ld: Bad mask: %s",
1737 line, (unsigned long)lineno, cpoptarg);
1738 return (1);
1740 usedarg++;
1741 opt_flags |= OP_MASK;
1742 } else if (cpoptarg && (!strcmp(cpopt, "network") ||
1743 !strcmp(cpopt, "n"))) {
1744 if (strchr(cpoptarg, '/') != NULL) {
1745 if (debug)
1746 fprintf(stderr, "setting OP_MASKLEN\n");
1747 opt_flags |= OP_MASKLEN;
1749 if (grp->gr_type != GT_NULL) {
1750 syslog(LOG_ERR,
1751 "\"%s\", line %ld: Network/host conflict",
1752 line, (unsigned long)lineno);
1753 return (1);
1754 } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
1755 syslog(LOG_ERR,
1756 "\"%s\", line %ld: Bad net: %s",
1757 line, (unsigned long)lineno, cpoptarg);
1758 return (1);
1760 grp->gr_type = GT_NET;
1761 *has_hostp = 1;
1762 usedarg++;
1763 opt_flags |= OP_NET;
1764 } else if (!strcmp(cpopt, "alldirs")) {
1765 opt_flags |= OP_ALLDIRS;
1766 } else if (!strcmp(cpopt, "noresvmnt")) {
1767 opt_flags |= OP_NORESMNT;
1768 } else if (!strcmp(cpopt, "noresvport")) {
1769 opt_flags |= OP_NORESPORT;
1770 *exflagsp |= MNT_EXNORESPORT;
1771 } else if (!strcmp(cpopt, "public")) {
1772 *exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC);
1773 opt_flags |= OP_NORESPORT;
1774 } else if (!strcmp(cpopt, "webnfs")) {
1775 *exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC |
1776 MNT_EXRDONLY | MNT_EXPORTANON);
1777 opt_flags |= (OP_MAPALL | OP_NORESPORT);
1778 } else if (cpoptarg && !strcmp(cpopt, "index")) {
1779 ep->ex_indexfile = strdup(cpoptarg);
1780 } else {
1781 syslog(LOG_ERR,
1782 "\"%s\", line %ld: Bad opt %s",
1783 line, (unsigned long)lineno, cpopt);
1784 return (1);
1786 if (usedarg >= 0) {
1787 *endcp = savedc2;
1788 **endcpp = savedc;
1789 if (usedarg > 0) {
1790 *cpp = cp;
1791 *endcpp = endcp;
1793 return (0);
1795 cpopt = cpoptend;
1797 **endcpp = savedc;
1798 return (0);
1802 * Translate a character string to the corresponding list of network
1803 * addresses for a hostname.
1805 static int
1806 get_host(line, lineno, cp, grp)
1807 const char *line;
1808 size_t lineno;
1809 const char *cp;
1810 struct grouplist *grp;
1812 struct addrinfo *ai, hints;
1813 int ecode;
1814 char host[NI_MAXHOST];
1816 if (grp->gr_type != GT_NULL) {
1817 syslog(LOG_ERR,
1818 "\"%s\", line %ld: Bad netgroup type for ip host %s",
1819 line, (unsigned long)lineno, cp);
1820 return (1);
1822 memset(&hints, 0, sizeof hints);
1823 hints.ai_flags = AI_CANONNAME;
1824 hints.ai_protocol = IPPROTO_UDP;
1825 ecode = getaddrinfo(cp, NULL, &hints, &ai);
1826 if (ecode != 0) {
1827 syslog(LOG_ERR, "\"%s\", line %ld: can't get address info for "
1828 "host %s",
1829 line, (long)lineno, cp);
1830 return 1;
1832 grp->gr_type = GT_HOST;
1833 grp->gr_ptr.gt_addrinfo = ai;
1834 while (ai != NULL) {
1835 if (ai->ai_canonname == NULL) {
1836 if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
1837 sizeof host, NULL, 0, ninumeric) != 0)
1838 strlcpy(host, "?", sizeof(host));
1839 ai->ai_canonname = estrdup(host);
1840 ai->ai_flags |= AI_CANONNAME;
1841 } else
1842 ai->ai_flags &= ~AI_CANONNAME;
1843 if (debug)
1844 (void)fprintf(stderr, "got host %s\n", ai->ai_canonname);
1845 ai = ai->ai_next;
1847 return (0);
1851 * Free up an exports list component
1853 static void
1854 free_exp(ep)
1855 struct exportlist *ep;
1858 if (ep->ex_defdir) {
1859 free_host(ep->ex_defdir->dp_hosts);
1860 free(ep->ex_defdir);
1862 if (ep->ex_fsdir)
1863 free(ep->ex_fsdir);
1864 if (ep->ex_indexfile)
1865 free(ep->ex_indexfile);
1866 free_dir(ep->ex_dirl);
1867 free(ep);
1871 * Free hosts.
1873 static void
1874 free_host(hp)
1875 struct hostlist *hp;
1877 struct hostlist *hp2;
1879 while (hp) {
1880 hp2 = hp;
1881 hp = hp->ht_next;
1882 free(hp2);
1886 static struct hostlist *
1887 get_ht()
1889 struct hostlist *hp;
1891 hp = emalloc(sizeof(struct hostlist));
1892 hp->ht_next = NULL;
1893 hp->ht_flag = 0;
1894 return (hp);
1898 * Do the nfssvc syscall to push the export info into the kernel.
1900 static int
1901 do_nfssvc(line, lineno, ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
1902 const char *line;
1903 size_t lineno;
1904 struct exportlist *ep;
1905 struct grouplist *grp;
1906 int exflags;
1907 struct uucred *anoncrp;
1908 char *dirp;
1909 int dirplen;
1910 struct statvfs *fsb;
1912 struct sockaddr *addrp;
1913 struct sockaddr_storage ss;
1914 struct addrinfo *ai;
1915 int addrlen;
1916 int done;
1917 struct export_args export;
1919 export.ex_flags = exflags;
1920 export.ex_anon = *anoncrp;
1921 export.ex_indexfile = ep->ex_indexfile;
1922 if (grp->gr_type == GT_HOST) {
1923 ai = grp->gr_ptr.gt_addrinfo;
1924 addrp = ai->ai_addr;
1925 addrlen = ai->ai_addrlen;
1926 } else {
1927 addrp = NULL;
1928 ai = NULL; /* XXXGCC -Wuninitialized */
1929 addrlen = 0; /* XXXGCC -Wuninitialized */
1931 done = FALSE;
1932 while (!done) {
1933 struct mountd_exports_list mel;
1935 switch (grp->gr_type) {
1936 case GT_HOST:
1937 if (addrp != NULL && addrp->sa_family == AF_INET6 &&
1938 have_v6 == 0)
1939 goto skip;
1940 export.ex_addr = addrp;
1941 export.ex_addrlen = addrlen;
1942 export.ex_masklen = 0;
1943 break;
1944 case GT_NET:
1945 export.ex_addr = (struct sockaddr *)
1946 &grp->gr_ptr.gt_net.nt_net;
1947 if (export.ex_addr->sa_family == AF_INET6 &&
1948 have_v6 == 0)
1949 goto skip;
1950 export.ex_addrlen = export.ex_addr->sa_len;
1951 memset(&ss, 0, sizeof ss);
1952 ss.ss_family = export.ex_addr->sa_family;
1953 ss.ss_len = export.ex_addr->sa_len;
1954 if (allones(&ss, grp->gr_ptr.gt_net.nt_len) != 0) {
1955 syslog(LOG_ERR,
1956 "\"%s\", line %ld: Bad network flag",
1957 line, (unsigned long)lineno);
1958 return (1);
1960 export.ex_mask = (struct sockaddr *)&ss;
1961 export.ex_masklen = ss.ss_len;
1962 break;
1963 default:
1964 syslog(LOG_ERR, "\"%s\", line %ld: Bad netgroup type",
1965 line, (unsigned long)lineno);
1966 return (1);
1970 * XXX:
1971 * Maybe I should just use the fsb->f_mntonname path?
1974 mel.mel_path = dirp;
1975 mel.mel_nexports = 1;
1976 mel.mel_exports = &export;
1978 if (nfssvc(NFSSVC_SETEXPORTSLIST, &mel) != 0) {
1979 syslog(LOG_ERR,
1980 "\"%s\", line %ld: Can't change attributes for %s to %s: %m",
1981 line, (unsigned long)lineno,
1982 dirp, (grp->gr_type == GT_HOST) ?
1983 grp->gr_ptr.gt_addrinfo->ai_canonname :
1984 (grp->gr_type == GT_NET) ?
1985 grp->gr_ptr.gt_net.nt_name :
1986 "Unknown");
1987 return (1);
1989 skip:
1990 if (addrp) {
1991 ai = ai->ai_next;
1992 if (ai == NULL)
1993 done = TRUE;
1994 else {
1995 addrp = ai->ai_addr;
1996 addrlen = ai->ai_addrlen;
1998 } else
1999 done = TRUE;
2001 return (0);
2005 * Translate a net address.
2007 static int
2008 get_net(cp, net, maskflg)
2009 char *cp;
2010 struct netmsk *net;
2011 int maskflg;
2013 struct netent *np;
2014 char *nname, *p, *prefp;
2015 struct sockaddr_in sin, *sinp;
2016 struct sockaddr *sa;
2017 struct addrinfo hints, *ai = NULL;
2018 char netname[NI_MAXHOST];
2019 long preflen;
2020 int ecode;
2022 (void)memset(&sin, 0, sizeof(sin));
2023 if ((opt_flags & OP_MASKLEN) && !maskflg) {
2024 p = strchr(cp, '/');
2025 *p = '\0';
2026 prefp = p + 1;
2027 } else {
2028 p = NULL; /* XXXGCC -Wuninitialized */
2029 prefp = NULL; /* XXXGCC -Wuninitialized */
2032 if ((np = getnetbyname(cp)) != NULL) {
2033 sin.sin_family = AF_INET;
2034 sin.sin_len = sizeof sin;
2035 sin.sin_addr = inet_makeaddr(np->n_net, 0);
2036 sa = (struct sockaddr *)&sin;
2037 } else if (isdigit((unsigned char)*cp)) {
2038 memset(&hints, 0, sizeof hints);
2039 hints.ai_family = AF_UNSPEC;
2040 hints.ai_flags = AI_NUMERICHOST;
2041 if (getaddrinfo(cp, NULL, &hints, &ai) != 0) {
2043 * If getaddrinfo() failed, try the inet4 network
2044 * notation with less than 3 dots.
2046 sin.sin_family = AF_INET;
2047 sin.sin_len = sizeof sin;
2048 sin.sin_addr = inet_makeaddr(inet_network(cp),0);
2049 if (debug)
2050 fprintf(stderr, "get_net: v4 addr %x\n",
2051 sin.sin_addr.s_addr);
2052 sa = (struct sockaddr *)&sin;
2053 } else
2054 sa = ai->ai_addr;
2055 } else if (isxdigit((unsigned char)*cp) || *cp == ':') {
2056 memset(&hints, 0, sizeof hints);
2057 hints.ai_family = AF_UNSPEC;
2058 hints.ai_flags = AI_NUMERICHOST;
2059 if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
2060 sa = ai->ai_addr;
2061 else
2062 goto fail;
2063 } else
2064 goto fail;
2067 * Only allow /pref notation for v6 addresses.
2069 if (sa->sa_family == AF_INET6 && (!(opt_flags & OP_MASKLEN) || maskflg))
2070 return 1;
2072 ecode = getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2073 NULL, 0, ninumeric);
2074 if (ecode != 0)
2075 goto fail;
2077 if (maskflg)
2078 net->nt_len = countones(sa);
2079 else {
2080 if (opt_flags & OP_MASKLEN) {
2081 errno = 0;
2082 preflen = strtol(prefp, NULL, 10);
2083 if (preflen == LONG_MIN && errno == ERANGE)
2084 goto fail;
2085 net->nt_len = (int)preflen;
2086 *p = '/';
2089 if (np)
2090 nname = np->n_name;
2091 else {
2092 if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
2093 NULL, 0, ninumeric) != 0)
2094 strlcpy(netname, "?", sizeof(netname));
2095 nname = netname;
2097 net->nt_name = estrdup(nname);
2098 memcpy(&net->nt_net, sa, sa->sa_len);
2101 if (!maskflg && sa->sa_family == AF_INET &&
2102 !(opt_flags & (OP_MASK|OP_MASKLEN))) {
2103 sinp = (struct sockaddr_in *)sa;
2104 if (IN_CLASSA(sinp->sin_addr.s_addr))
2105 net->nt_len = 8;
2106 else if (IN_CLASSB(sinp->sin_addr.s_addr))
2107 net->nt_len = 16;
2108 else if (IN_CLASSC(sinp->sin_addr.s_addr))
2109 net->nt_len = 24;
2110 else if (IN_CLASSD(sinp->sin_addr.s_addr))
2111 net->nt_len = 28;
2112 else
2113 net->nt_len = 32; /* XXX */
2116 if (ai)
2117 freeaddrinfo(ai);
2118 return 0;
2120 fail:
2121 if (ai)
2122 freeaddrinfo(ai);
2123 return 1;
2127 * Parse out the next white space separated field
2129 static void
2130 nextfield(cp, endcp)
2131 char **cp;
2132 char **endcp;
2134 char *p;
2136 p = *cp;
2137 while (*p == ' ' || *p == '\t')
2138 p++;
2139 if (*p == '\n' || *p == '\0')
2140 *cp = *endcp = p;
2141 else {
2142 *cp = p++;
2143 while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
2144 p++;
2145 *endcp = p;
2150 * Parse a description of a credential.
2152 static void
2153 parsecred(namelist, cr)
2154 char *namelist;
2155 struct uucred *cr;
2157 char *username;
2158 int cnt;
2159 char *names;
2160 struct passwd *pw;
2161 struct group *gr;
2162 int ngroups;
2163 gid_t usergroups[NGROUPS + 1];
2166 * Set up the unprivileged user.
2168 *cr = def_anon;
2170 * Get the user's password table entry.
2172 names = strsep(&namelist, " \t\n");
2173 username = strsep(&names, ":");
2174 if (isdigit((unsigned char)*username) || *username == '-')
2175 pw = getpwuid(atoi(username));
2176 else
2177 pw = getpwnam(username);
2179 * Credentials specified as those of a user.
2181 if (names == NULL) {
2182 if (pw == NULL) {
2183 syslog(LOG_ERR, "Unknown user: %s", username);
2184 return;
2186 cr->cr_uid = pw->pw_uid;
2187 ngroups = NGROUPS + 1;
2188 if (getgrouplist(pw->pw_name, pw->pw_gid, usergroups, &ngroups))
2189 syslog(LOG_ERR, "Too many groups for user %s", username);
2191 * Convert from int's to gid_t's and compress out duplicate
2193 cr->cr_ngroups = ngroups - 1;
2194 cr->cr_gid = usergroups[0];
2195 for (cnt = 1; cnt < ngroups; cnt++)
2196 cr->cr_groups[cnt - 1] = usergroups[cnt];
2197 return;
2200 * Explicit credential specified as a colon separated list:
2201 * uid:gid:gid:...
2203 if (pw != NULL)
2204 cr->cr_uid = pw->pw_uid;
2205 else if (isdigit((unsigned char)*username) || *username == '-')
2206 cr->cr_uid = atoi(username);
2207 else {
2208 syslog(LOG_ERR, "Unknown user: %s", username);
2209 return;
2211 cr->cr_ngroups = 0;
2212 while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
2213 username = strsep(&names, ":");
2214 if (isdigit((unsigned char)*username) || *username == '-') {
2215 cr->cr_groups[cr->cr_ngroups++] = atoi(username);
2216 } else {
2217 if ((gr = getgrnam(username)) == NULL) {
2218 syslog(LOG_ERR, "Unknown group: %s", username);
2219 continue;
2221 cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
2224 if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
2225 syslog(LOG_ERR, "Too many groups");
2228 #define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
2230 * Routines that maintain the remote mounttab
2232 static void
2233 get_mountlist()
2235 struct mountlist *mlp, **mlpp;
2236 char *host, *dirp, *cp;
2237 char str[STRSIZ];
2238 FILE *mlfile;
2240 if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
2241 syslog(LOG_ERR, "Can't open %s: %m", _PATH_RMOUNTLIST);
2242 return;
2244 mlpp = &mlhead;
2245 while (fgets(str, STRSIZ, mlfile) != NULL) {
2246 cp = str;
2247 host = strsep(&cp, " \t\n");
2248 dirp = strsep(&cp, " \t\n");
2249 if (host == NULL || dirp == NULL)
2250 continue;
2251 mlp = emalloc(sizeof(*mlp));
2252 (void)strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
2253 mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2254 (void)strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2255 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2256 mlp->ml_next = NULL;
2257 *mlpp = mlp;
2258 mlpp = &mlp->ml_next;
2260 (void)fclose(mlfile);
2263 static int
2264 del_mlist(hostp, dirp, saddr)
2265 char *hostp, *dirp;
2266 struct sockaddr *saddr;
2268 struct mountlist *mlp, **mlpp;
2269 struct mountlist *mlp2;
2270 u_short sport;
2271 FILE *mlfile;
2272 int fnd = 0, ret = 0;
2273 char host[NI_MAXHOST];
2275 switch (saddr->sa_family) {
2276 case AF_INET6:
2277 sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
2278 break;
2279 case AF_INET:
2280 sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
2281 break;
2282 default:
2283 return -1;
2285 mlpp = &mlhead;
2286 mlp = mlhead;
2287 while (mlp) {
2288 if (!strcmp(mlp->ml_host, hostp) &&
2289 (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
2290 if (!(mlp->ml_flag & DP_NORESMNT) &&
2291 sport >= IPPORT_RESERVED) {
2292 if (getnameinfo(saddr, saddr->sa_len, host,
2293 sizeof host, NULL, 0, ninumeric) != 0)
2294 strlcpy(host, "?", sizeof(host));
2295 syslog(LOG_NOTICE,
2296 "Umount request for %s:%s from %s refused\n",
2297 mlp->ml_host, mlp->ml_dirp, host);
2298 ret = -1;
2299 goto cont;
2301 fnd = 1;
2302 mlp2 = mlp;
2303 *mlpp = mlp = mlp->ml_next;
2304 free(mlp2);
2305 } else {
2306 cont:
2307 mlpp = &mlp->ml_next;
2308 mlp = mlp->ml_next;
2311 if (fnd) {
2312 if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
2313 syslog(LOG_ERR, "Can't update %s: %m",
2314 _PATH_RMOUNTLIST);
2315 return ret;
2317 mlp = mlhead;
2318 while (mlp) {
2319 (void)fprintf(mlfile, "%s %s\n", mlp->ml_host,
2320 mlp->ml_dirp);
2321 mlp = mlp->ml_next;
2323 (void)fclose(mlfile);
2325 return ret;
2328 static void
2329 add_mlist(hostp, dirp, flags)
2330 char *hostp, *dirp;
2331 int flags;
2333 struct mountlist *mlp, **mlpp;
2334 FILE *mlfile;
2336 mlpp = &mlhead;
2337 mlp = mlhead;
2338 while (mlp) {
2339 if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
2340 return;
2341 mlpp = &mlp->ml_next;
2342 mlp = mlp->ml_next;
2344 mlp = emalloc(sizeof(*mlp));
2345 strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
2346 mlp->ml_host[RPCMNT_NAMELEN] = '\0';
2347 strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
2348 mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
2349 mlp->ml_flag = flags;
2350 mlp->ml_next = NULL;
2351 *mlpp = mlp;
2352 if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
2353 syslog(LOG_ERR, "Can't update %s: %m", _PATH_RMOUNTLIST);
2354 return;
2356 (void)fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
2357 (void)fclose(mlfile);
2361 * This function is called via. SIGTERM when the system is going down.
2362 * It sends a broadcast RPCMNT_UMNTALL.
2364 /* ARGSUSED */
2365 static void
2366 send_umntall(n)
2367 int n;
2369 (void)clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
2370 xdr_void, NULL, xdr_void, NULL, (resultproc_t)umntall_each);
2371 exit(0);
2374 static int
2375 umntall_each(resultsp, raddr)
2376 caddr_t resultsp;
2377 struct sockaddr_in *raddr;
2379 return (1);
2383 * Free up a group list.
2385 static void
2386 free_grp(grp)
2387 struct grouplist *grp;
2390 if (grp->gr_type == GT_HOST) {
2391 if (grp->gr_ptr.gt_addrinfo != NULL)
2392 freeaddrinfo(grp->gr_ptr.gt_addrinfo);
2393 } else if (grp->gr_type == GT_NET) {
2394 if (grp->gr_ptr.gt_net.nt_name)
2395 free(grp->gr_ptr.gt_net.nt_name);
2397 free(grp);
2400 #if 0
2401 static void
2402 SYSLOG(int pri, const char *fmt,...)
2404 va_list ap;
2406 va_start(ap, fmt);
2408 if (debug)
2409 vfprintf(stderr, fmt, ap);
2410 else
2411 vsyslog(pri, fmt, ap);
2413 va_end(ap);
2415 #endif
2418 * Check options for consistency.
2420 static int
2421 check_options(line, lineno, dp)
2422 const char *line;
2423 size_t lineno;
2424 struct dirlist *dp;
2427 if (dp == NULL) {
2428 syslog(LOG_ERR,
2429 "\"%s\", line %ld: missing directory list",
2430 line, (unsigned long)lineno);
2431 return (1);
2433 if ((opt_flags & (OP_MAPROOT|OP_MAPALL)) == (OP_MAPROOT|OP_MAPALL) ||
2434 (opt_flags & (OP_MAPROOT|OP_KERB)) == (OP_MAPROOT|OP_KERB) ||
2435 (opt_flags & (OP_MAPALL|OP_KERB)) == (OP_MAPALL|OP_KERB)) {
2436 syslog(LOG_ERR,
2437 "\"%s\", line %ld: -mapall, -maproot and -kerb mutually exclusive",
2438 line, (unsigned long)lineno);
2439 return (1);
2441 if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
2442 syslog(LOG_ERR, "\"%s\", line %ld: -mask requires -net",
2443 line, (unsigned long)lineno);
2444 return (1);
2446 if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN) != 0) {
2447 syslog(LOG_ERR, "\"%s\", line %ld: /pref and -mask mutually"
2448 " exclusive",
2449 line, (unsigned long)lineno);
2450 return (1);
2452 if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
2453 syslog(LOG_ERR,
2454 "\"%s\", line %ld: -alldirs has multiple directories",
2455 line, (unsigned long)lineno);
2456 return (1);
2458 return (0);
2462 * Check an absolute directory path for any symbolic links. Return true
2463 * if no symbolic links are found.
2465 static int
2466 check_dirpath(line, lineno, dirp)
2467 const char *line;
2468 size_t lineno;
2469 char *dirp;
2471 char *cp;
2472 struct stat sb;
2473 const char *file = "";
2475 for (cp = dirp + 1; *cp; cp++) {
2476 if (*cp == '/') {
2477 *cp = '\0';
2478 if (lstat(dirp, &sb) == -1)
2479 goto bad;
2480 if (!S_ISDIR(sb.st_mode))
2481 goto bad1;
2482 *cp = '/';
2486 cp = NULL;
2487 if (lstat(dirp, &sb) == -1)
2488 goto bad;
2490 if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) {
2491 file = " file or a";
2492 goto bad1;
2495 return 1;
2497 bad:
2498 syslog(LOG_ERR,
2499 "\"%s\", line %ld: lstat for `%s' failed: %m",
2500 line, (unsigned long)lineno, dirp);
2501 if (cp)
2502 *cp = '/';
2503 return 0;
2505 bad1:
2506 syslog(LOG_ERR,
2507 "\"%s\", line %ld: `%s' is not a%s directory",
2508 line, (unsigned long)lineno, dirp, file);
2509 if (cp)
2510 *cp = '/';
2511 return 0;
2514 static void
2515 bind_resv_port(int sock, sa_family_t family, in_port_t port)
2517 struct sockaddr *sa;
2518 struct sockaddr_in sasin;
2519 struct sockaddr_in6 sasin6;
2521 switch (family) {
2522 case AF_INET:
2523 (void)memset(&sasin, 0, sizeof(sasin));
2524 sasin.sin_len = sizeof(sasin);
2525 sasin.sin_family = family;
2526 sasin.sin_port = htons(port);
2527 sa = (struct sockaddr *)(void *)&sasin;
2528 break;
2529 case AF_INET6:
2530 (void)memset(&sasin6, 0, sizeof(sasin6));
2531 sasin6.sin6_len = sizeof(sasin6);
2532 sasin6.sin6_family = family;
2533 sasin6.sin6_port = htons(port);
2534 sa = (struct sockaddr *)(void *)&sasin6;
2535 break;
2536 default:
2537 syslog(LOG_ERR, "Unsupported address family %d", family);
2538 return;
2540 if (bindresvport_sa(sock, sa) == -1)
2541 syslog(LOG_ERR, "Cannot bind to reserved port %d (%m)", port);
2544 /* ARGSUSED */
2545 static void
2546 no_nfs(int sig)
2548 syslog(LOG_ERR, "kernel NFS support not present; exiting");
2549 exit(1);