1 /* $NetBSD: mountd.c,v 1.119 2009/04/17 13:56:33 lukem Exp $ */
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
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
35 #include <sys/cdefs.h>
37 __COPYRIGHT("@(#) Copyright (c) 1989, 1993\
38 The Regents of the University of California. All rights reserved.");
43 static char sccsid
[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95";
45 __RCSID("$NetBSD: mountd.c,v 1.119 2009/04/17 13:56:33 lukem Exp $");
49 #include <sys/param.h>
51 #include <sys/ioctl.h>
52 #include <sys/mount.h>
53 #include <sys/socket.h>
56 #include <sys/ucred.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>
65 #include <nfs/nfsmount.h>
67 #include <arpa/inet.h>
82 #include "pathnames.h"
85 #include <netinet6/ipsec.h>
86 #ifndef IPSEC_POLICY_IPSEC /* no ipsec support on old ipsec */
95 * Structures for keeping the mount list and export list
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) */
105 struct dirlist
*dp_left
;
106 struct dirlist
*dp_right
;
108 struct hostlist
*dp_hosts
; /* List of hosts this dir exported to */
109 char dp_dirp
[1]; /* Actually malloc'd to size of dir */
112 #define DP_DEFSET 0x1
113 #define DP_HOSTSET 0x2
115 #define DP_NORESMNT 0x8
118 struct exportlist
*ex_next
;
119 struct dirlist
*ex_dirl
;
120 struct dirlist
*ex_defdir
;
127 #define EX_LINKED 0x1
130 struct sockaddr_storage nt_net
;
136 struct addrinfo
*gt_addrinfo
;
137 struct netmsk gt_net
;
142 union grouptypes gr_ptr
;
143 struct grouplist
*gr_next
;
151 int ht_flag
;/* Uses DP_xx bits */
152 struct grouplist
*ht_grp
;
153 struct hostlist
*ht_next
;
161 uint8_t v2
[NFSX_V2FH
];
162 uint8_t v3
[NFSX_V3FHMAX
];
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
= {
230 static int opt_flags
;
231 static int have_v6
= 1;
232 static const int ninumeric
= NI_NUMERICHOST
;
235 #define OP_MAPROOT 0x001
236 #define OP_MAPALL 0x002
237 #define OP_KERB 0x004
238 #define OP_MASK 0x008
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;
247 static void SYSLOG
__P((int, const char *,...));
249 int main
__P((int, char *[]));
252 * If this is non-zero, -noresvport and -noresvmnt are implied for
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.
270 SVCXPRT
*udptransp
, *tcptransp
, *udp6transp
, *tcp6transp
;
271 struct netconfig
*udpconf
, *tcpconf
, *udp6conf
, *tcp6conf
;
272 int udpsock
, tcpsock
, udp6sock
, tcp6sock
;
275 int maxrec
= RPC_MAXDATASIZE
;
276 in_port_t forcedport
= 0;
284 while ((c
= getopt(argc
, argv
, "dNnrp:" ADDOPTS
)) != -1)
288 if (ipsecsetup_test(policy
= optarg
))
289 errx(1, "Invalid ipsec policy `%s'", policy
);
293 /* A forced port "0" will dynamically allocate a port */
294 forcedport
= atoi(optarg
);
307 fprintf(stderr
, "usage: %s [-dNn]"
311 " [-p port] [exportsfile]\n", getprogname());
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
);
333 (void)fprintf(stderr
, "Getting export list.\n");
336 (void)fprintf(stderr
, "Getting mount list.\n");
339 (void)fprintf(stderr
, "Here we go.\n");
342 (void)signal(SIGINT
, SIG_IGN
);
343 (void)signal(SIGQUIT
, SIG_IGN
);
345 (void)signal(SIGHUP
, get_exportlist
);
346 (void)signal(SIGTERM
, send_umntall
);
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");
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");
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
);
384 ipsecsetup(AF_INET
, udpsock
, policy
);
386 udptransp
= svc_dg_create(udpsock
, 0, 0);
387 if (udptransp
!= NULL
) {
388 if (!svc_reg(udptransp
, RPCPROG_MNT
, RPCMNT_VER1
,
390 !svc_reg(udptransp
, RPCPROG_MNT
, RPCMNT_VER3
,
392 syslog(LOG_WARNING
, "can't register UDP service");
396 syslog(LOG_WARNING
, "can't create UDP service");
400 if (tcpsock
!= -1 && tcpconf
!= NULL
) {
401 bind_resv_port(tcpsock
, AF_INET
, forcedport
);
404 ipsecsetup(AF_INET
, tcpsock
, policy
);
406 listen(tcpsock
, SOMAXCONN
);
407 tcptransp
= svc_vc_create(tcpsock
, RPC_MAXDATASIZE
,
409 if (tcptransp
!= NULL
) {
410 if (!svc_reg(tcptransp
, RPCPROG_MNT
, RPCMNT_VER1
,
412 !svc_reg(tcptransp
, RPCPROG_MNT
, RPCMNT_VER3
,
414 syslog(LOG_WARNING
, "can't register TCP service");
418 syslog(LOG_WARNING
, "can't create TCP service");
422 if (udp6sock
!= -1 && udp6conf
!= NULL
) {
423 bind_resv_port(udp6sock
, AF_INET6
, forcedport
);
426 ipsecsetup(AF_INET6
, tcpsock
, policy
);
428 udp6transp
= svc_dg_create(udp6sock
, 0, 0);
429 if (udp6transp
!= NULL
) {
430 if (!svc_reg(udp6transp
, RPCPROG_MNT
, RPCMNT_VER1
,
432 !svc_reg(udp6transp
, RPCPROG_MNT
, RPCMNT_VER3
,
434 syslog(LOG_WARNING
, "can't register UDP6 service");
438 syslog(LOG_WARNING
, "can't create UDP6 service");
442 if (tcp6sock
!= -1 && tcp6conf
!= NULL
) {
443 bind_resv_port(tcp6sock
, AF_INET6
, forcedport
);
446 ipsecsetup(AF_INET6
, tcpsock
, policy
);
448 listen(tcp6sock
, SOMAXCONN
);
449 tcp6transp
= svc_vc_create(tcp6sock
, RPC_MAXDATASIZE
,
451 if (tcp6transp
!= NULL
) {
452 if (!svc_reg(tcp6transp
, RPCPROG_MNT
, RPCMNT_VER1
,
454 !svc_reg(tcp6transp
, RPCPROG_MNT
, RPCMNT_VER3
,
456 syslog(LOG_WARNING
, "can't register TCP6 service");
460 syslog(LOG_WARNING
, "can't create TCP6 service");
465 syslog(LOG_ERR
, "could not create any services");
470 syslog(LOG_ERR
, "Mountd died");
475 * The mount rpc service
478 mntsrv(rqstp
, transp
)
479 struct svc_req
*rqstp
;
482 struct exportlist
*ep
;
488 char host
[NI_MAXHOST
], numerichost
[NI_MAXHOST
];
489 int lookup_failed
= 1;
490 struct sockaddr
*saddr
;
492 char rpcpath
[RPCMNT_PATHLEN
+ 1], rdirpath
[MAXPATHLEN
];
494 int defset
, hostset
, ret
;
495 sigset_t sighup_mask
;
496 struct sockaddr_in6
*sin6
;
497 struct sockaddr_in
*sin
;
500 (void)sigemptyset(&sighup_mask
);
501 (void)sigaddset(&sighup_mask
, SIGHUP
);
502 saddr
= svc_getrpccaller(transp
)->buf
;
503 switch (saddr
->sa_family
) {
505 sin6
= (struct sockaddr_in6
*)saddr
;
506 sport
= ntohs(sin6
->sin6_port
);
509 sin
= (struct sockaddr_in
*)saddr
;
510 sport
= ntohs(sin
->sin_port
);
513 syslog(LOG_ERR
, "request from unknown address family");
516 lookup_failed
= getnameinfo(saddr
, saddr
->sa_len
, host
, sizeof host
,
518 if (getnameinfo(saddr
, saddr
->sa_len
, numerichost
,
519 sizeof numerichost
, NULL
, 0, ninumeric
) != 0)
520 strlcpy(numerichost
, "?", sizeof(numerichost
));
523 switch (rqstp
->rq_proc
) {
525 if (!svc_sendreply(transp
, xdr_void
, NULL
))
526 syslog(LOG_ERR
, "Can't send reply");
531 "got mount request from %s\n", numerichost
);
532 if (!svc_getargs(transp
, xdr_dir
, rpcpath
)) {
534 fprintf(stderr
, "-> garbage args\n");
535 svcerr_decode(transp
);
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 */
551 (void)fprintf(stderr
, "-> stat failed on %s\n",
553 if (!svc_sendreply(transp
, xdr_long
, (caddr_t
) &bad
))
554 syslog(LOG_ERR
, "Can't send reply");
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) {
572 if (sport
>= IPPORT_RESERVED
&&
573 !(hostset
& DP_NORESMNT
)) {
575 "Refused mount RPC from host %s port %d",
577 svcerr_weakauth(transp
);
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) {
587 syslog(LOG_ERR
, "Can't get fh for %s", rdirpath
);
588 if (!svc_sendreply(transp
, xdr_long
,
590 syslog(LOG_ERR
, "Can't send reply");
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
,
598 syslog(LOG_ERR
, "Can't send reply");
601 fhr
.fhr_fhsize
= fh_size
;
602 if (!svc_sendreply(transp
, xdr_fhs
, (char *) &fhr
))
603 syslog(LOG_ERR
, "Can't send reply");
605 add_mlist(host
, rdirpath
, hostset
);
607 add_mlist(numerichost
, rdirpath
, hostset
);
609 (void)fprintf(stderr
, "Mount successful.\n");
611 if (!svc_sendreply(transp
, xdr_long
, (caddr_t
) &bad
))
612 syslog(LOG_ERR
, "Can't send reply");
615 (void)sigprocmask(SIG_UNBLOCK
, &sighup_mask
, NULL
);
618 if (!svc_sendreply(transp
, xdr_mlist
, NULL
))
619 syslog(LOG_ERR
, "Can't send reply");
622 if (!svc_getargs(transp
, xdr_dir
, rdirpath
)) {
623 svcerr_decode(transp
);
627 ret
= del_mlist(host
, rdirpath
, saddr
);
628 ret
|= del_mlist(numerichost
, rdirpath
, saddr
);
630 svcerr_weakauth(transp
);
633 if (!svc_sendreply(transp
, xdr_void
, NULL
))
634 syslog(LOG_ERR
, "Can't send reply");
636 case MOUNTPROC_UMNTALL
:
638 ret
= del_mlist(host
, NULL
, saddr
);
639 ret
|= del_mlist(numerichost
, NULL
, saddr
);
641 svcerr_weakauth(transp
);
644 if (!svc_sendreply(transp
, xdr_void
, NULL
))
645 syslog(LOG_ERR
, "Can't send reply");
647 case MOUNTPROC_EXPORT
:
648 case MOUNTPROC_EXPORTALL
:
649 if (!svc_sendreply(transp
, xdr_explist
, NULL
))
650 syslog(LOG_ERR
, "Can't send reply");
655 svcerr_noproc(transp
);
661 * Xdr conversion for a dirpath string
669 return (xdr_string(xdrsp
, &dirp
, RPCMNT_PATHLEN
));
673 * Xdr routine to generate file handle reply
680 struct fhreturn
*fhrp
= (struct fhreturn
*) cp
;
681 long ok
= 0, len
, auth
;
683 if (!xdr_long(xdrsp
, &ok
))
685 switch (fhrp
->fhr_vers
) {
687 return (xdr_opaque(xdrsp
, (caddr_t
)&fhrp
->fhr_fh
, NFSX_V2FH
));
689 len
= fhrp
->fhr_fhsize
;
690 if (!xdr_long(xdrsp
, &len
))
692 if (!xdr_opaque(xdrsp
, (caddr_t
)&fhrp
->fhr_fh
, len
))
694 if (fhrp
->fhr_flag
& DP_KERB
)
695 auth
= RPCAUTH_KERB4
;
699 if (!xdr_long(xdrsp
, &len
))
701 return (xdr_long(xdrsp
, &auth
));
711 struct mountlist
*mlp
;
718 if (!xdr_bool(xdrsp
, &trueval
))
720 strp
= &mlp
->ml_host
[0];
721 if (!xdr_string(xdrsp
, &strp
, RPCMNT_NAMELEN
))
723 strp
= &mlp
->ml_dirp
[0];
724 if (!xdr_string(xdrsp
, &strp
, RPCMNT_PATHLEN
))
728 if (!xdr_bool(xdrsp
, &falseval
))
734 * Xdr conversion for export list
737 xdr_explist(xdrsp
, cp
)
741 struct exportlist
*ep
;
744 sigset_t sighup_mask
;
746 (void)sigemptyset(&sighup_mask
);
747 (void)sigaddset(&sighup_mask
, SIGHUP
);
748 (void)sigprocmask(SIG_BLOCK
, &sighup_mask
, NULL
);
752 if (put_exlist(ep
->ex_dirl
, xdrsp
, ep
->ex_defdir
, &putdef
))
754 if (ep
->ex_defdir
&& putdef
== 0 &&
755 put_exlist(ep
->ex_defdir
, xdrsp
, NULL
, &putdef
))
759 (void)sigprocmask(SIG_UNBLOCK
, &sighup_mask
, NULL
);
760 if (!xdr_bool(xdrsp
, &falseval
))
764 (void)sigprocmask(SIG_UNBLOCK
, &sighup_mask
, NULL
);
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
)
779 struct grouplist
*grp
;
787 if (put_exlist(dp
->dp_left
, xdrsp
, adp
, putdefp
))
789 if (!xdr_bool(xdrsp
, &trueval
))
792 if (!xdr_string(xdrsp
, &strp
, RPCMNT_PATHLEN
))
794 if (adp
&& !strcmp(dp
->dp_dirp
, adp
->dp_dirp
)) {
798 if ((dp
->dp_flag
& DP_DEFSET
) == 0 &&
799 (gotalldir
== 0 || (adp
->dp_flag
& DP_DEFSET
) == 0)) {
803 if (grp
->gr_type
== GT_HOST
) {
804 if (!xdr_bool(xdrsp
, &trueval
))
807 grp
->gr_ptr
.gt_addrinfo
->ai_canonname
;
808 if (!xdr_string(xdrsp
, &strp
,
811 } else if (grp
->gr_type
== GT_NET
) {
812 if (!xdr_bool(xdrsp
, &trueval
))
814 strp
= grp
->gr_ptr
.gt_net
.nt_name
;
815 if (!xdr_string(xdrsp
, &strp
,
820 if (gotalldir
&& hp
== NULL
) {
826 if (!xdr_bool(xdrsp
, &falseval
))
828 if (put_exlist(dp
->dp_right
, xdrsp
, adp
, putdefp
))
835 parse_host_netgroup(line
, lineno
, ep
, tgrp
, cp
, has_host
, grp
)
838 struct exportlist
*ep
;
839 struct grouplist
*tgrp
;
842 struct grouplist
**grp
;
844 const char *hst
, *usr
, *dom
;
848 syslog(LOG_ERR
, "\"%s\", line %ld: No current export",
849 line
, (unsigned long)lineno
);
853 netgrp
= getnetgrent(&hst
, &usr
, &dom
);
856 (*grp
)->gr_next
= get_grp();
857 *grp
= (*grp
)->gr_next
;
862 "\"%s\", line %ld: No host in netgroup %s",
863 line
, (unsigned long)lineno
, cp
);
866 if (get_host(line
, lineno
, hst
, *grp
))
868 } else if (get_host(line
, lineno
, cp
, *grp
))
871 } while (netgrp
&& getnetgrent(&hst
, &usr
, &dom
));
882 parse_directory(line
, lineno
, tgrp
, got_nondir
, cp
, ep
, fsp
)
885 struct grouplist
*tgrp
;
888 struct exportlist
**ep
;
891 if (!check_dirpath(line
, lineno
, cp
))
894 if (statvfs(cp
, fsp
) == -1) {
895 syslog(LOG_ERR
, "\"%s\", line %ld: statvfs for `%s' failed: %m",
896 line
, (unsigned long)lineno
, cp
);
902 "\"%s\", line %ld: Directories must precede files",
903 line
, (unsigned long)lineno
);
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]) {
910 "\"%s\", line %ld: filesystem ids disagree",
911 line
, (unsigned long)lineno
);
916 * See if this directory is already
919 *ep
= ex_search(&fsp
->f_fsidx
);
922 (*ep
)->ex_fs
= fsp
->f_fsidx
;
923 (*ep
)->ex_fsdir
= estrdup(fsp
->f_mntonname
);
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]);
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]);
941 * Get the export list
948 struct exportlist
*ep
, *ep2
;
949 struct grouplist
*grp
, *tgrp
;
950 struct exportlist
**epp
;
951 struct dirlist
*dirhead
;
952 struct statvfs fsb
, *fsp
;
955 char *cp
, *endcp
, *dirp
, savedc
;
956 int has_host
, exflags
, got_nondir
, dirplen
, num
, i
;
959 size_t lineno
= 0, len
;
963 * First, get rid of the old list
984 * And delete exports that are in the kernel for all local
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 &&
996 syslog(LOG_ERR
, "Can't delete exports for %s (%m)",
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
1012 (void)fprintf(stderr
, "Can't open %s: %s\n", exname
,
1017 while ((line
= fparseln(exp_file
, &len
, &lineno
, NULL
, 0)) != NULL
) {
1019 (void)fprintf(stderr
, "Got line %s\n", line
);
1021 nextfield(&cp
, &endcp
);
1023 goto nextline
; /* skip empty line */
1029 exflags
= MNT_EXPORTED
;
1035 opt_flags
|= OP_NORESMNT
| OP_NORESPORT
;
1036 exflags
|= MNT_EXNORESPORT
;
1040 * Create new exports list entry
1043 tgrp
= grp
= get_grp();
1045 if (len
> RPCMNT_NAMELEN
) {
1048 "\"%s\", line %ld: name `%s' is too long",
1049 line
, (unsigned long)lineno
, cp
);
1059 "\"%s\", line %ld: No current export list",
1060 line
, (unsigned long)lineno
);
1064 (void)fprintf(stderr
, "doing opt %s\n",
1067 if (do_opt(line
, lineno
, &cp
, &endcp
, ep
, grp
,
1068 &has_host
, &exflags
, &anon
))
1079 if (!parse_directory(line
, lineno
, tgrp
,
1080 got_nondir
, cp
, &ep
, &fsb
))
1083 * Add dirpath to export mount point.
1085 dirp
= add_expdir(&dirhead
, cp
, len
);
1098 if (!parse_host_netgroup(line
, lineno
, ep
,
1099 tgrp
, cp
, &has_host
, &grp
))
1109 nextfield(&cp
, &endcp
);
1112 if (check_options(line
, lineno
, dirhead
))
1116 grp
->gr_type
= GT_HOST
;
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
));
1123 ai
->ai_family
= AF_INET
; /* XXXX */
1124 ai
->ai_socktype
= SOCK_DGRAM
;
1125 /* setting the length to 0 will match anything */
1127 ai
->ai_flags
= AI_CANONNAME
;
1128 ai
->ai_canonname
= estrdup("Default");
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.
1139 "\"%s\", line %ld: Mixed exporting of networks and hosts is disallowed",
1140 line
, (unsigned long)lineno
);
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.
1150 if (do_nfssvc(line
, lineno
, ep
, grp
, exflags
, &anon
,
1151 dirp
, dirplen
, &fsb
))
1153 } while (grp
->gr_next
&& (grp
= grp
->gr_next
));
1156 * Success. Update the data structures.
1159 hang_dirp(dirhead
, tgrp
, ep
, opt_flags
);
1160 grp
->gr_next
= grphead
;
1163 hang_dirp(dirhead
, NULL
, ep
, opt_flags
);
1168 if ((ep
->ex_flag
& EX_LINKED
) == 0) {
1173 * Insert in the list in alphabetical order.
1175 while (ep2
&& strcmp(ep2
->ex_fsdir
, ep
->ex_fsdir
) < 0) {
1176 epp
= &ep2
->ex_next
;
1182 ep
->ex_flag
|= EX_LINKED
;
1186 free_exp_grp(ep
, grp
);
1194 (void)fclose(exp_file
);
1198 * Allocate an export list element
1200 static struct exportlist
*
1203 struct exportlist
*ep
;
1205 ep
= emalloc(sizeof(struct exportlist
));
1206 (void)memset(ep
, 0, sizeof(struct exportlist
));
1211 * Allocate a group list element
1213 static struct grouplist
*
1216 struct grouplist
*gp
;
1218 gp
= emalloc(sizeof(struct grouplist
));
1219 (void)memset(gp
, 0, sizeof(struct grouplist
));
1224 * Clean up upon an error in get_exportlist().
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)
1243 * Search the export list for a matching fs.
1245 static struct exportlist
*
1249 struct exportlist
*ep
;
1253 if (ep
->ex_fs
.__fsid_val
[0] == fsid
->__fsid_val
[0] &&
1254 ep
->ex_fs
.__fsid_val
[1] == fsid
->__fsid_val
[1])
1262 * Add a directory path to the list.
1265 add_expdir(dpp
, cp
, len
)
1266 struct dirlist
**dpp
;
1272 dp
= emalloc(sizeof(struct dirlist
) + len
);
1274 dp
->dp_right
= NULL
;
1276 dp
->dp_hosts
= NULL
;
1277 (void)strcpy(dp
->dp_dirp
, cp
);
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.
1287 hang_dirp(dp
, grp
, ep
, flags
)
1289 struct grouplist
*grp
;
1290 struct exportlist
*ep
;
1293 struct hostlist
*hp
;
1294 struct dirlist
*dp2
;
1296 if (flags
& OP_ALLDIRS
) {
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
;
1310 if (flags
& OP_KERB
)
1311 hp
->ht_flag
|= DP_KERB
;
1312 if (flags
& OP_NORESMNT
)
1313 hp
->ht_flag
|= DP_NORESMNT
;
1315 hp
->ht_next
= ep
->ex_defdir
->dp_hosts
;
1316 ep
->ex_defdir
->dp_hosts
= hp
;
1322 * Loop through the directories adding them to the tree.
1326 add_dlist(&ep
->ex_dirl
, dp
, grp
, flags
);
1333 * Traverse the binary tree either updating a node that is already there
1334 * for the new directory or adding the new node.
1337 add_dlist(dpp
, newdp
, grp
, flags
)
1338 struct dirlist
**dpp
;
1339 struct dirlist
*newdp
;
1340 struct grouplist
*grp
;
1344 struct hostlist
*hp
;
1349 cmp
= strcmp(dp
->dp_dirp
, newdp
->dp_dirp
);
1351 add_dlist(&dp
->dp_left
, newdp
, grp
, flags
);
1353 } else if (cmp
< 0) {
1354 add_dlist(&dp
->dp_right
, newdp
, grp
, flags
);
1366 * Hang all of the host(s) off of the directory point.
1370 if (flags
& OP_KERB
)
1371 hp
->ht_flag
|= DP_KERB
;
1372 if (flags
& OP_NORESMNT
)
1373 hp
->ht_flag
|= DP_NORESMNT
;
1375 hp
->ht_next
= dp
->dp_hosts
;
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
)
1399 cmp
= strcmp(dp
->dp_dirp
, dirp
);
1401 return (dirp_search(dp
->dp_left
, dirp
));
1403 return (dirp_search(dp
->dp_right
, dirp
));
1411 * Some helper functions for netmasks. They all assume masks in network
1412 * order (big endian).
1415 bitcmp(void *dst
, void *src
, int bitlen
)
1418 u_int8_t
*p1
= dst
, *p2
= src
;
1420 int bytelen
, bitsleft
;
1422 bytelen
= bitlen
/ 8;
1423 bitsleft
= bitlen
% 8;
1426 printf("comparing:\n");
1427 for (i
= 0; i
< (bitsleft
? bytelen
+ 1 : bytelen
); i
++)
1428 printf("%02x", p1
[i
]);
1430 for (i
= 0; i
< (bitsleft
? bytelen
+ 1 : bytelen
); i
++)
1431 printf("%02x", p2
[i
]);
1435 for (i
= 0; i
< bytelen
; i
++) {
1442 for (i
= 0; i
< bitsleft
; i
++) {
1443 bitmask
= 1 << (7 - i
);
1444 if ((*p1
& bitmask
) != (*p2
& bitmask
))
1452 netpartcmp(struct sockaddr
*s1
, struct sockaddr
*s2
, int bitlen
)
1456 if (s1
->sa_family
!= s2
->sa_family
)
1459 switch (s1
->sa_family
) {
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)
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
)
1472 if (bitlen
> (int)sizeof(((struct sockaddr_in6
*)s1
)->sin6_addr
) * 8)
1479 return bitcmp(src
, dst
, bitlen
);
1483 allones(struct sockaddr_storage
*ssp
, int bitlen
)
1486 int bytelen
, bitsleft
, i
;
1489 switch (ssp
->ss_family
) {
1491 p
= (u_int8_t
*)&((struct sockaddr_in
*)ssp
)->sin_addr
;
1492 zerolen
= sizeof (((struct sockaddr_in
*)ssp
)->sin_addr
);
1495 p
= (u_int8_t
*)&((struct sockaddr_in6
*)ssp
)->sin6_addr
;
1496 zerolen
= sizeof (((struct sockaddr_in6
*)ssp
)->sin6_addr
);
1502 memset(p
, 0, zerolen
);
1504 bytelen
= bitlen
/ 8;
1505 bitsleft
= bitlen
% 8;
1507 if (bytelen
> zerolen
)
1510 for (i
= 0; i
< bytelen
; i
++)
1513 for (i
= 0; i
< bitsleft
; i
++)
1520 countones(struct sockaddr
*sa
)
1523 int i
, bits
= 0, bytelen
;
1526 switch (sa
->sa_family
) {
1528 mask
= (u_int8_t
*)&((struct sockaddr_in
*)sa
)->sin_addr
;
1532 mask
= (u_int8_t
*)&((struct sockaddr_in6
*)sa
)->sin6_addr
;
1541 for (i
= 0; i
< bytelen
; i
++, p
++) {
1543 for (bits
= 0; bits
< 8; bits
++) {
1544 if (!(*p
& (1 << (7 - bits
))))
1551 return (i
* 8 + bits
);
1555 sacmp(struct sockaddr
*sa1
, struct sockaddr
*sa2
)
1560 if (sa1
->sa_family
!= sa2
->sa_family
)
1563 switch (sa1
->sa_family
) {
1565 p1
= &((struct sockaddr_in
*)sa1
)->sin_addr
;
1566 p2
= &((struct sockaddr_in
*)sa2
)->sin_addr
;
1570 p1
= &((struct sockaddr_in6
*)sa1
)->sin6_addr
;
1571 p2
= &((struct sockaddr_in6
*)sa2
)->sin6_addr
;
1573 if (((struct sockaddr_in6
*)sa1
)->sin6_scope_id
!=
1574 ((struct sockaddr_in6
*)sa2
)->sin6_scope_id
)
1581 return memcmp(p1
, p2
, len
);
1585 * Scan for a host match in a directory tree.
1588 chk_host(dp
, saddr
, defsetp
, hostsetp
)
1590 struct sockaddr
*saddr
;
1594 struct hostlist
*hp
;
1595 struct grouplist
*grp
;
1596 struct addrinfo
*ai
;
1599 if (dp
->dp_flag
& DP_DEFSET
)
1600 *defsetp
= dp
->dp_flag
;
1604 switch (grp
->gr_type
) {
1606 ai
= grp
->gr_ptr
.gt_addrinfo
;
1607 for (; ai
; ai
= ai
->ai_next
) {
1608 if (!sacmp(ai
->ai_addr
, saddr
)) {
1610 (hp
->ht_flag
| DP_HOSTSET
);
1616 if (!netpartcmp(saddr
,
1618 &grp
->gr_ptr
.gt_net
.nt_net
,
1619 grp
->gr_ptr
.gt_net
.nt_len
)) {
1620 *hostsetp
= (hp
->ht_flag
| DP_HOSTSET
);
1632 * Scan tree for a host that matches the address.
1635 scan_tree(dp
, saddr
)
1637 struct sockaddr
*saddr
;
1639 int defset
, hostset
;
1642 if (scan_tree(dp
->dp_left
, saddr
))
1644 if (chk_host(dp
, saddr
, &defset
, &hostset
))
1646 if (scan_tree(dp
->dp_right
, saddr
))
1653 * Traverse the dirlist tree and free it up.
1661 free_dir(dp
->dp_left
);
1662 free_dir(dp
->dp_right
);
1663 free_host(dp
->dp_hosts
);
1669 * Parse the option string and update fields.
1670 * Option arguments may either be -<option>=<value> or
1674 do_opt(line
, lineno
, cpp
, endcpp
, ep
, grp
, has_hostp
, exflagsp
, cr
)
1677 char **cpp
, **endcpp
;
1678 struct exportlist
*ep
;
1679 struct grouplist
*grp
;
1684 char *cpoptarg
, *cpoptend
;
1685 char *cp
, *cpopt
, savedc
, savedc2
;
1686 char *endcp
= NULL
; /* XXX: GCC */
1687 int allflag
, usedarg
;
1694 while (cpopt
&& *cpopt
) {
1698 if ((cpoptend
= strchr(cpopt
, ',')) != NULL
) {
1700 if ((cpoptarg
= strchr(cpopt
, '=')) != NULL
)
1703 if ((cpoptarg
= strchr(cpopt
, '=')) != NULL
)
1707 nextfield(&cp
, &endcp
);
1709 if (endcp
> cp
&& *cp
!= '-') {
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"))) {
1723 parsecred(cpoptarg
, cr
);
1725 *exflagsp
|= MNT_EXPORTANON
;
1726 opt_flags
|= OP_MAPALL
;
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)) {
1736 "\"%s\", line %ld: Bad mask: %s",
1737 line
, (unsigned long)lineno
, cpoptarg
);
1741 opt_flags
|= OP_MASK
;
1742 } else if (cpoptarg
&& (!strcmp(cpopt
, "network") ||
1743 !strcmp(cpopt
, "n"))) {
1744 if (strchr(cpoptarg
, '/') != NULL
) {
1746 fprintf(stderr
, "setting OP_MASKLEN\n");
1747 opt_flags
|= OP_MASKLEN
;
1749 if (grp
->gr_type
!= GT_NULL
) {
1751 "\"%s\", line %ld: Network/host conflict",
1752 line
, (unsigned long)lineno
);
1754 } else if (get_net(cpoptarg
, &grp
->gr_ptr
.gt_net
, 0)) {
1756 "\"%s\", line %ld: Bad net: %s",
1757 line
, (unsigned long)lineno
, cpoptarg
);
1760 grp
->gr_type
= GT_NET
;
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
);
1782 "\"%s\", line %ld: Bad opt %s",
1783 line
, (unsigned long)lineno
, cpopt
);
1802 * Translate a character string to the corresponding list of network
1803 * addresses for a hostname.
1806 get_host(line
, lineno
, cp
, grp
)
1810 struct grouplist
*grp
;
1812 struct addrinfo
*ai
, hints
;
1814 char host
[NI_MAXHOST
];
1816 if (grp
->gr_type
!= GT_NULL
) {
1818 "\"%s\", line %ld: Bad netgroup type for ip host %s",
1819 line
, (unsigned long)lineno
, cp
);
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
);
1827 syslog(LOG_ERR
, "\"%s\", line %ld: can't get address info for "
1829 line
, (long)lineno
, cp
);
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
;
1842 ai
->ai_flags
&= ~AI_CANONNAME
;
1844 (void)fprintf(stderr
, "got host %s\n", ai
->ai_canonname
);
1851 * Free up an exports list component
1855 struct exportlist
*ep
;
1858 if (ep
->ex_defdir
) {
1859 free_host(ep
->ex_defdir
->dp_hosts
);
1860 free(ep
->ex_defdir
);
1864 if (ep
->ex_indexfile
)
1865 free(ep
->ex_indexfile
);
1866 free_dir(ep
->ex_dirl
);
1875 struct hostlist
*hp
;
1877 struct hostlist
*hp2
;
1886 static struct hostlist
*
1889 struct hostlist
*hp
;
1891 hp
= emalloc(sizeof(struct hostlist
));
1898 * Do the nfssvc syscall to push the export info into the kernel.
1901 do_nfssvc(line
, lineno
, ep
, grp
, exflags
, anoncrp
, dirp
, dirplen
, fsb
)
1904 struct exportlist
*ep
;
1905 struct grouplist
*grp
;
1907 struct uucred
*anoncrp
;
1910 struct statvfs
*fsb
;
1912 struct sockaddr
*addrp
;
1913 struct sockaddr_storage ss
;
1914 struct addrinfo
*ai
;
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
;
1928 ai
= NULL
; /* XXXGCC -Wuninitialized */
1929 addrlen
= 0; /* XXXGCC -Wuninitialized */
1933 struct mountd_exports_list mel
;
1935 switch (grp
->gr_type
) {
1937 if (addrp
!= NULL
&& addrp
->sa_family
== AF_INET6
&&
1940 export
.ex_addr
= addrp
;
1941 export
.ex_addrlen
= addrlen
;
1942 export
.ex_masklen
= 0;
1945 export
.ex_addr
= (struct sockaddr
*)
1946 &grp
->gr_ptr
.gt_net
.nt_net
;
1947 if (export
.ex_addr
->sa_family
== AF_INET6
&&
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) {
1956 "\"%s\", line %ld: Bad network flag",
1957 line
, (unsigned long)lineno
);
1960 export
.ex_mask
= (struct sockaddr
*)&ss
;
1961 export
.ex_masklen
= ss
.ss_len
;
1964 syslog(LOG_ERR
, "\"%s\", line %ld: Bad netgroup type",
1965 line
, (unsigned long)lineno
);
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) {
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
:
1995 addrp
= ai
->ai_addr
;
1996 addrlen
= ai
->ai_addrlen
;
2005 * Translate a net address.
2008 get_net(cp
, net
, maskflg
)
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
];
2022 (void)memset(&sin
, 0, sizeof(sin
));
2023 if ((opt_flags
& OP_MASKLEN
) && !maskflg
) {
2024 p
= strchr(cp
, '/');
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);
2050 fprintf(stderr
, "get_net: v4 addr %x\n",
2051 sin
.sin_addr
.s_addr
);
2052 sa
= (struct sockaddr
*)&sin
;
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)
2067 * Only allow /pref notation for v6 addresses.
2069 if (sa
->sa_family
== AF_INET6
&& (!(opt_flags
& OP_MASKLEN
) || maskflg
))
2072 ecode
= getnameinfo(sa
, sa
->sa_len
, netname
, sizeof netname
,
2073 NULL
, 0, ninumeric
);
2078 net
->nt_len
= countones(sa
);
2080 if (opt_flags
& OP_MASKLEN
) {
2082 preflen
= strtol(prefp
, NULL
, 10);
2083 if (preflen
== LONG_MIN
&& errno
== ERANGE
)
2085 net
->nt_len
= (int)preflen
;
2092 if (getnameinfo(sa
, sa
->sa_len
, netname
, sizeof netname
,
2093 NULL
, 0, ninumeric
) != 0)
2094 strlcpy(netname
, "?", sizeof(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
))
2106 else if (IN_CLASSB(sinp
->sin_addr
.s_addr
))
2108 else if (IN_CLASSC(sinp
->sin_addr
.s_addr
))
2110 else if (IN_CLASSD(sinp
->sin_addr
.s_addr
))
2113 net
->nt_len
= 32; /* XXX */
2127 * Parse out the next white space separated field
2130 nextfield(cp
, endcp
)
2137 while (*p
== ' ' || *p
== '\t')
2139 if (*p
== '\n' || *p
== '\0')
2143 while (*p
!= ' ' && *p
!= '\t' && *p
!= '\n' && *p
!= '\0')
2150 * Parse a description of a credential.
2153 parsecred(namelist
, cr
)
2163 gid_t usergroups
[NGROUPS
+ 1];
2166 * Set up the unprivileged user.
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
));
2177 pw
= getpwnam(username
);
2179 * Credentials specified as those of a user.
2181 if (names
== NULL
) {
2183 syslog(LOG_ERR
, "Unknown user: %s", username
);
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
];
2200 * Explicit credential specified as a colon separated list:
2204 cr
->cr_uid
= pw
->pw_uid
;
2205 else if (isdigit((unsigned char)*username
) || *username
== '-')
2206 cr
->cr_uid
= atoi(username
);
2208 syslog(LOG_ERR
, "Unknown user: %s", username
);
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
);
2217 if ((gr
= getgrnam(username
)) == NULL
) {
2218 syslog(LOG_ERR
, "Unknown group: %s", username
);
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
2235 struct mountlist
*mlp
, **mlpp
;
2236 char *host
, *dirp
, *cp
;
2240 if ((mlfile
= fopen(_PATH_RMOUNTLIST
, "r")) == NULL
) {
2241 syslog(LOG_ERR
, "Can't open %s: %m", _PATH_RMOUNTLIST
);
2245 while (fgets(str
, STRSIZ
, mlfile
) != NULL
) {
2247 host
= strsep(&cp
, " \t\n");
2248 dirp
= strsep(&cp
, " \t\n");
2249 if (host
== NULL
|| dirp
== NULL
)
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
;
2258 mlpp
= &mlp
->ml_next
;
2260 (void)fclose(mlfile
);
2264 del_mlist(hostp
, dirp
, saddr
)
2266 struct sockaddr
*saddr
;
2268 struct mountlist
*mlp
, **mlpp
;
2269 struct mountlist
*mlp2
;
2272 int fnd
= 0, ret
= 0;
2273 char host
[NI_MAXHOST
];
2275 switch (saddr
->sa_family
) {
2277 sport
= ntohs(((struct sockaddr_in6
*)saddr
)->sin6_port
);
2280 sport
= ntohs(((struct sockaddr_in
*)saddr
)->sin_port
);
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
));
2296 "Umount request for %s:%s from %s refused\n",
2297 mlp
->ml_host
, mlp
->ml_dirp
, host
);
2303 *mlpp
= mlp
= mlp
->ml_next
;
2307 mlpp
= &mlp
->ml_next
;
2312 if ((mlfile
= fopen(_PATH_RMOUNTLIST
, "w")) == NULL
) {
2313 syslog(LOG_ERR
, "Can't update %s: %m",
2319 (void)fprintf(mlfile
, "%s %s\n", mlp
->ml_host
,
2323 (void)fclose(mlfile
);
2329 add_mlist(hostp
, dirp
, flags
)
2333 struct mountlist
*mlp
, **mlpp
;
2339 if (!strcmp(mlp
->ml_host
, hostp
) && !strcmp(mlp
->ml_dirp
, dirp
))
2341 mlpp
= &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
;
2352 if ((mlfile
= fopen(_PATH_RMOUNTLIST
, "a")) == NULL
) {
2353 syslog(LOG_ERR
, "Can't update %s: %m", _PATH_RMOUNTLIST
);
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.
2369 (void)clnt_broadcast(RPCPROG_MNT
, RPCMNT_VER1
, RPCMNT_UMNTALL
,
2370 xdr_void
, NULL
, xdr_void
, NULL
, (resultproc_t
)umntall_each
);
2375 umntall_each(resultsp
, raddr
)
2377 struct sockaddr_in
*raddr
;
2383 * Free up a group list.
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
);
2402 SYSLOG(int pri
, const char *fmt
,...)
2409 vfprintf(stderr
, fmt
, ap
);
2411 vsyslog(pri
, fmt
, ap
);
2418 * Check options for consistency.
2421 check_options(line
, lineno
, dp
)
2429 "\"%s\", line %ld: missing directory list",
2430 line
, (unsigned long)lineno
);
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
)) {
2437 "\"%s\", line %ld: -mapall, -maproot and -kerb mutually exclusive",
2438 line
, (unsigned long)lineno
);
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
);
2446 if ((opt_flags
& OP_MASK
) && (opt_flags
& OP_MASKLEN
) != 0) {
2447 syslog(LOG_ERR
, "\"%s\", line %ld: /pref and -mask mutually"
2449 line
, (unsigned long)lineno
);
2452 if ((opt_flags
& OP_ALLDIRS
) && dp
->dp_left
) {
2454 "\"%s\", line %ld: -alldirs has multiple directories",
2455 line
, (unsigned long)lineno
);
2462 * Check an absolute directory path for any symbolic links. Return true
2463 * if no symbolic links are found.
2466 check_dirpath(line
, lineno
, dirp
)
2473 const char *file
= "";
2475 for (cp
= dirp
+ 1; *cp
; cp
++) {
2478 if (lstat(dirp
, &sb
) == -1)
2480 if (!S_ISDIR(sb
.st_mode
))
2487 if (lstat(dirp
, &sb
) == -1)
2490 if (!S_ISDIR(sb
.st_mode
) && !S_ISREG(sb
.st_mode
)) {
2491 file
= " file or a";
2499 "\"%s\", line %ld: lstat for `%s' failed: %m",
2500 line
, (unsigned long)lineno
, dirp
);
2507 "\"%s\", line %ld: `%s' is not a%s directory",
2508 line
, (unsigned long)lineno
, dirp
, file
);
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
;
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
;
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
;
2537 syslog(LOG_ERR
, "Unsupported address family %d", family
);
2540 if (bindresvport_sa(sock
, sa
) == -1)
2541 syslog(LOG_ERR
, "Cannot bind to reserved port %d (%m)", port
);
2548 syslog(LOG_ERR
, "kernel NFS support not present; exiting");