2a3e5eeb7c
- Remove all NFS related stuff from file system specific code. - Drop the vfs_checkexp hook and generalize it in the new nfs_check_export function, thus removing redundancy from all file systems. - Move all NFS export-related stuff from kern/vfs_subr.c to the new file sys/nfs/nfs_export.c. The former was becoming large and its code is always compiled, regardless of the build options. Using the latter, the code is only compiled in when NFSSERVER is enabled. While doing this, also make some functions in nfs_subs.c conditional to NFSSERVER. - Add a new command in nfssvc(2), called NFSSVC_SETEXPORTSLIST, that takes a path and a set of export entries. At the moment it can only clear the exports list or append entries, one by one, but it is done in a way that allows setting the whole set of entries atomically in the future (see the comment in mountd_set_exports_list or in doc/TODO). - Change mountd(8) to use the nfssvc(2) system call instead of mount(2) so that it becomes file system agnostic. In fact, all this whole thing was done to remove a 'XXX' block from this utility! - Change the mount*, newfs and fsck* userland utilities to not deal with NFS exports initialization; done internally by the kernel when initializing the NFS support for each file system. - Implement an interface for VFS (called VFS hooks) so that several kernel subsystems can run arbitrary code upon receipt of specific VFS events. At the moment, this only provides support for unmount and is used to destroy NFS exports lists from the file systems being unmounted, though it has room for extension. Thanks go to yamt@, chs@, thorpej@, wrstuden@ and others for their comments and advice in the development of this patch.
2691 lines
60 KiB
C
2691 lines
60 KiB
C
/* $NetBSD: mountd.c,v 1.98 2005/09/23 12:10:35 jmmv Exp $ */
|
|
|
|
/*
|
|
* Copyright (c) 1989, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to Berkeley by
|
|
* Herb Hasler and Rick Macklem at The University of Guelph.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
/*
|
|
* XXX The ISO support can't possibly work..
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#ifndef lint
|
|
__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\
|
|
The Regents of the University of California. All rights reserved.\n");
|
|
#endif /* not lint */
|
|
|
|
#ifndef lint
|
|
#if 0
|
|
static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95";
|
|
#else
|
|
__RCSID("$NetBSD: mountd.c,v 1.98 2005/09/23 12:10:35 jmmv Exp $");
|
|
#endif
|
|
#endif /* not lint */
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/file.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/mount.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/stat.h>
|
|
#include <syslog.h>
|
|
#include <sys/ucred.h>
|
|
|
|
#include <rpc/rpc.h>
|
|
#include <rpc/pmap_clnt.h>
|
|
#include <rpc/pmap_prot.h>
|
|
#include <rpcsvc/mount.h>
|
|
#ifdef ISO
|
|
#include <netiso/iso.h>
|
|
#endif
|
|
#include <nfs/rpcv2.h>
|
|
#include <nfs/nfsproto.h>
|
|
#include <nfs/nfs.h>
|
|
#include <nfs/nfsmount.h>
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#include <ctype.h>
|
|
#include <errno.h>
|
|
#include <grp.h>
|
|
#include <netdb.h>
|
|
#include <pwd.h>
|
|
#include <netgroup.h>
|
|
#include <signal.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <netgroup.h>
|
|
#include <err.h>
|
|
#include <util.h>
|
|
#include "pathnames.h"
|
|
#ifdef KERBEROS
|
|
#include <kerberosIV/krb.h>
|
|
#include "kuid.h"
|
|
#endif
|
|
|
|
#ifdef IPSEC
|
|
#include <netinet6/ipsec.h>
|
|
#ifndef IPSEC_POLICY_IPSEC /* no ipsec support on old ipsec */
|
|
#undef IPSEC
|
|
#endif
|
|
#include "ipsec.h"
|
|
#endif
|
|
|
|
#include <stdarg.h>
|
|
|
|
/*
|
|
* Structures for keeping the mount list and export list
|
|
*/
|
|
struct mountlist {
|
|
struct mountlist *ml_next;
|
|
char ml_host[RPCMNT_NAMELEN + 1];
|
|
char ml_dirp[RPCMNT_PATHLEN + 1];
|
|
int ml_flag;/* XXX more flags (same as dp_flag) */
|
|
};
|
|
|
|
struct dirlist {
|
|
struct dirlist *dp_left;
|
|
struct dirlist *dp_right;
|
|
int dp_flag;
|
|
struct hostlist *dp_hosts; /* List of hosts this dir exported to */
|
|
char dp_dirp[1]; /* Actually malloc'd to size of dir */
|
|
};
|
|
/* dp_flag bits */
|
|
#define DP_DEFSET 0x1
|
|
#define DP_HOSTSET 0x2
|
|
#define DP_KERB 0x4
|
|
#define DP_NORESMNT 0x8
|
|
|
|
struct exportlist {
|
|
struct exportlist *ex_next;
|
|
struct dirlist *ex_dirl;
|
|
struct dirlist *ex_defdir;
|
|
int ex_flag;
|
|
fsid_t ex_fs;
|
|
char *ex_fsdir;
|
|
char *ex_indexfile;
|
|
};
|
|
/* ex_flag bits */
|
|
#define EX_LINKED 0x1
|
|
|
|
struct netmsk {
|
|
struct sockaddr_storage nt_net;
|
|
int nt_len;
|
|
char *nt_name;
|
|
};
|
|
|
|
union grouptypes {
|
|
struct addrinfo *gt_addrinfo;
|
|
struct netmsk gt_net;
|
|
#ifdef ISO
|
|
struct sockaddr_iso *gt_isoaddr;
|
|
#endif
|
|
};
|
|
|
|
struct grouplist {
|
|
int gr_type;
|
|
union grouptypes gr_ptr;
|
|
struct grouplist *gr_next;
|
|
};
|
|
/* Group types */
|
|
#define GT_NULL 0x0
|
|
#define GT_HOST 0x1
|
|
#define GT_NET 0x2
|
|
#define GT_ISO 0x4
|
|
|
|
struct hostlist {
|
|
int ht_flag;/* Uses DP_xx bits */
|
|
struct grouplist *ht_grp;
|
|
struct hostlist *ht_next;
|
|
};
|
|
|
|
struct fhreturn {
|
|
int fhr_flag;
|
|
int fhr_vers;
|
|
nfsfh_t fhr_fh;
|
|
};
|
|
|
|
/* Global defs */
|
|
static char *add_expdir __P((struct dirlist **, char *, int));
|
|
static void add_dlist __P((struct dirlist **, struct dirlist *,
|
|
struct grouplist *, int));
|
|
static void add_mlist __P((char *, char *, int));
|
|
static int check_dirpath __P((const char *, size_t, char *));
|
|
static int check_options __P((const char *, size_t, struct dirlist *));
|
|
static int chk_host __P((struct dirlist *, struct sockaddr *, int *, int *));
|
|
static int del_mlist __P((char *, char *, struct sockaddr *));
|
|
static struct dirlist *dirp_search __P((struct dirlist *, char *));
|
|
static int do_nfssvc __P((const char *, size_t, struct exportlist *,
|
|
struct grouplist *, int, struct uucred *, char *, int, struct statvfs *));
|
|
static int do_opt __P((const char *, size_t, char **, char **,
|
|
struct exportlist *, struct grouplist *, int *, int *, struct uucred *));
|
|
static struct exportlist *ex_search __P((fsid_t *));
|
|
static int parse_directory __P((const char *, size_t, struct grouplist *,
|
|
int, char *, struct exportlist **, struct statvfs *));
|
|
static int parse_host_netgroup __P((const char *, size_t, struct exportlist *,
|
|
struct grouplist *, char *, int *, struct grouplist **));
|
|
static struct exportlist *get_exp __P((void));
|
|
static void free_dir __P((struct dirlist *));
|
|
static void free_exp __P((struct exportlist *));
|
|
static void free_grp __P((struct grouplist *));
|
|
static void free_host __P((struct hostlist *));
|
|
static void get_exportlist __P((int));
|
|
static int get_host __P((const char *, size_t, const char *,
|
|
struct grouplist *));
|
|
static struct hostlist *get_ht __P((void));
|
|
static void get_mountlist __P((void));
|
|
static int get_net __P((char *, struct netmsk *, int));
|
|
static void free_exp_grp __P((struct exportlist *, struct grouplist *));
|
|
static struct grouplist *get_grp __P((void));
|
|
static void hang_dirp __P((struct dirlist *, struct grouplist *,
|
|
struct exportlist *, int));
|
|
static void mntsrv __P((struct svc_req *, SVCXPRT *));
|
|
static void nextfield __P((char **, char **));
|
|
static void parsecred __P((char *, struct uucred *));
|
|
static int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
|
|
static int scan_tree __P((struct dirlist *, struct sockaddr *));
|
|
static void send_umntall __P((int));
|
|
static int umntall_each __P((caddr_t, struct sockaddr_in *));
|
|
static int xdr_dir __P((XDR *, char *));
|
|
static int xdr_explist __P((XDR *, caddr_t));
|
|
static int xdr_fhs __P((XDR *, caddr_t));
|
|
static int xdr_mlist __P((XDR *, caddr_t));
|
|
static void *emalloc __P((size_t));
|
|
static char *estrdup __P((const char *));
|
|
static int bitcmp __P((void *, void *, int));
|
|
static int netpartcmp __P((struct sockaddr *, struct sockaddr *, int));
|
|
static int sacmp __P((struct sockaddr *, struct sockaddr *));
|
|
static int allones __P((struct sockaddr_storage *, int));
|
|
static int countones __P((struct sockaddr *));
|
|
#ifdef ISO
|
|
static int get_isoaddr __P((const char *, size_t, char *, struct grouplist *));
|
|
#endif
|
|
static void bind_resv_port __P((int, sa_family_t, in_port_t));
|
|
static struct exportlist *exphead;
|
|
static struct mountlist *mlhead;
|
|
static struct grouplist *grphead;
|
|
static char *exname;
|
|
static struct uucred def_anon = {
|
|
1,
|
|
(uid_t) -2,
|
|
(gid_t) -2,
|
|
0,
|
|
{}
|
|
};
|
|
|
|
static int opt_flags;
|
|
static int have_v6 = 1;
|
|
static const int ninumeric = NI_NUMERICHOST;
|
|
|
|
/* Bits for above */
|
|
#define OP_MAPROOT 0x001
|
|
#define OP_MAPALL 0x002
|
|
#define OP_KERB 0x004
|
|
#define OP_MASK 0x008
|
|
#define OP_NET 0x010
|
|
#define OP_ISO 0x020
|
|
#define OP_ALLDIRS 0x040
|
|
#define OP_NORESPORT 0x080
|
|
#define OP_NORESMNT 0x100
|
|
#define OP_MASKLEN 0x200
|
|
|
|
static int debug = 0;
|
|
#if 0
|
|
static void SYSLOG __P((int, const char *,...));
|
|
#endif
|
|
int main __P((int, char *[]));
|
|
|
|
/*
|
|
* If this is non-zero, -noresvport and -noresvmnt are implied for
|
|
* each export.
|
|
*/
|
|
static int noprivports;
|
|
|
|
/*
|
|
* Mountd server for NFS mount protocol as described in:
|
|
* NFS: Network File System Protocol Specification, RFC1094, Appendix A
|
|
* The optional arguments are the exports file name
|
|
* default: _PATH_EXPORTS
|
|
* "-d" to enable debugging
|
|
* and "-n" to allow nonroot mount.
|
|
*/
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
char **argv;
|
|
{
|
|
SVCXPRT *udptransp, *tcptransp, *udp6transp, *tcp6transp;
|
|
struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
|
|
int udpsock, tcpsock, udp6sock, tcp6sock;
|
|
int xcreated = 0, s;
|
|
int c, one = 1;
|
|
int maxrec = RPC_MAXDATASIZE;
|
|
in_port_t forcedport = 0;
|
|
#ifdef IPSEC
|
|
char *policy = NULL;
|
|
#define ADDOPTS "P:"
|
|
#else
|
|
#define ADDOPTS
|
|
#endif
|
|
|
|
while ((c = getopt(argc, argv, "dNnrp:" ADDOPTS)) != -1)
|
|
switch (c) {
|
|
#ifdef IPSEC
|
|
case 'P':
|
|
if (ipsecsetup_test(policy = optarg))
|
|
errx(1, "Invalid ipsec policy `%s'", policy);
|
|
break;
|
|
#endif
|
|
case 'p':
|
|
/* A forced port "0" will dynamically allocate a port */
|
|
forcedport = atoi(optarg);
|
|
break;
|
|
case 'd':
|
|
debug = 1;
|
|
break;
|
|
case 'N':
|
|
noprivports = 1;
|
|
break;
|
|
/* Compatibility */
|
|
case 'n':
|
|
case 'r':
|
|
break;
|
|
default:
|
|
fprintf(stderr, "usage: %s [-dNn]"
|
|
#ifdef IPSEC
|
|
" [-P policy]"
|
|
#endif
|
|
" [-p port] [exportsfile]\n", getprogname());
|
|
exit(1);
|
|
};
|
|
argc -= optind;
|
|
argv += optind;
|
|
grphead = NULL;
|
|
exphead = NULL;
|
|
mlhead = NULL;
|
|
if (argc == 1)
|
|
exname = *argv;
|
|
else
|
|
exname = _PATH_EXPORTS;
|
|
openlog("mountd", LOG_PID | (debug ? LOG_PERROR : 0), LOG_DAEMON);
|
|
|
|
s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
|
if (s < 0)
|
|
have_v6 = 0;
|
|
else
|
|
close(s);
|
|
|
|
if (debug)
|
|
(void)fprintf(stderr, "Getting export list.\n");
|
|
get_exportlist(0);
|
|
if (debug)
|
|
(void)fprintf(stderr, "Getting mount list.\n");
|
|
get_mountlist();
|
|
if (debug)
|
|
(void)fprintf(stderr, "Here we go.\n");
|
|
if (debug == 0) {
|
|
daemon(0, 0);
|
|
(void)signal(SIGINT, SIG_IGN);
|
|
(void)signal(SIGQUIT, SIG_IGN);
|
|
}
|
|
(void)signal(SIGHUP, get_exportlist);
|
|
(void)signal(SIGTERM, send_umntall);
|
|
pidfile(NULL);
|
|
|
|
rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
|
|
rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
|
|
|
|
udpsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
tcpsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
udp6sock = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
|
tcp6sock = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
|
|
|
|
/*
|
|
* We're doing host-based access checks here, so don't allow
|
|
* v4-in-v6 to confuse things. The kernel will disable it
|
|
* by default on NFS sockets too.
|
|
*/
|
|
if (udp6sock != -1 && setsockopt(udp6sock, IPPROTO_IPV6,
|
|
IPV6_V6ONLY, &one, sizeof one) < 0){
|
|
syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
|
|
exit(1);
|
|
}
|
|
if (tcp6sock != -1 && setsockopt(tcp6sock, IPPROTO_IPV6,
|
|
IPV6_V6ONLY, &one, sizeof one) < 0){
|
|
syslog(LOG_ERR, "can't disable v4-in-v6 on UDP socket");
|
|
exit(1);
|
|
}
|
|
|
|
udpconf = getnetconfigent("udp");
|
|
tcpconf = getnetconfigent("tcp");
|
|
udp6conf = getnetconfigent("udp6");
|
|
tcp6conf = getnetconfigent("tcp6");
|
|
|
|
rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
|
|
|
|
if (udpsock != -1 && udpconf != NULL) {
|
|
bind_resv_port(udpsock, AF_INET, forcedport);
|
|
#ifdef IPSEC
|
|
if (policy)
|
|
ipsecsetup(AF_INET, udpsock, policy);
|
|
#endif
|
|
udptransp = svc_dg_create(udpsock, 0, 0);
|
|
if (udptransp != NULL) {
|
|
if (!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER1,
|
|
mntsrv, udpconf) ||
|
|
!svc_reg(udptransp, RPCPROG_MNT, RPCMNT_VER3,
|
|
mntsrv, udpconf))
|
|
syslog(LOG_WARNING, "can't register UDP service");
|
|
else
|
|
xcreated++;
|
|
} else
|
|
syslog(LOG_WARNING, "can't create UDP service");
|
|
|
|
}
|
|
|
|
if (tcpsock != -1 && tcpconf != NULL) {
|
|
bind_resv_port(tcpsock, AF_INET, forcedport);
|
|
#ifdef IPSEC
|
|
if (policy)
|
|
ipsecsetup(AF_INET, tcpsock, policy);
|
|
#endif
|
|
listen(tcpsock, SOMAXCONN);
|
|
tcptransp = svc_vc_create(tcpsock, RPC_MAXDATASIZE,
|
|
RPC_MAXDATASIZE);
|
|
if (tcptransp != NULL) {
|
|
if (!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER1,
|
|
mntsrv, tcpconf) ||
|
|
!svc_reg(tcptransp, RPCPROG_MNT, RPCMNT_VER3,
|
|
mntsrv, tcpconf))
|
|
syslog(LOG_WARNING, "can't register TCP service");
|
|
else
|
|
xcreated++;
|
|
} else
|
|
syslog(LOG_WARNING, "can't create TCP service");
|
|
|
|
}
|
|
|
|
if (udp6sock != -1 && udp6conf != NULL) {
|
|
bind_resv_port(udp6sock, AF_INET6, forcedport);
|
|
#ifdef IPSEC
|
|
if (policy)
|
|
ipsecsetup(AF_INET6, tcpsock, policy);
|
|
#endif
|
|
udp6transp = svc_dg_create(udp6sock, 0, 0);
|
|
if (udp6transp != NULL) {
|
|
if (!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER1,
|
|
mntsrv, udp6conf) ||
|
|
!svc_reg(udp6transp, RPCPROG_MNT, RPCMNT_VER3,
|
|
mntsrv, udp6conf))
|
|
syslog(LOG_WARNING, "can't register UDP6 service");
|
|
else
|
|
xcreated++;
|
|
} else
|
|
syslog(LOG_WARNING, "can't create UDP6 service");
|
|
|
|
}
|
|
|
|
if (tcp6sock != -1 && tcp6conf != NULL) {
|
|
bind_resv_port(tcp6sock, AF_INET6, forcedport);
|
|
#ifdef IPSEC
|
|
if (policy)
|
|
ipsecsetup(AF_INET6, tcpsock, policy);
|
|
#endif
|
|
listen(tcp6sock, SOMAXCONN);
|
|
tcp6transp = svc_vc_create(tcp6sock, RPC_MAXDATASIZE,
|
|
RPC_MAXDATASIZE);
|
|
if (tcp6transp != NULL) {
|
|
if (!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER1,
|
|
mntsrv, tcp6conf) ||
|
|
!svc_reg(tcp6transp, RPCPROG_MNT, RPCMNT_VER3,
|
|
mntsrv, tcp6conf))
|
|
syslog(LOG_WARNING, "can't register TCP6 service");
|
|
else
|
|
xcreated++;
|
|
} else
|
|
syslog(LOG_WARNING, "can't create TCP6 service");
|
|
|
|
}
|
|
|
|
if (xcreated == 0) {
|
|
syslog(LOG_ERR, "could not create any services");
|
|
exit(1);
|
|
}
|
|
|
|
#ifdef KERBEROS
|
|
kuidinit();
|
|
#endif
|
|
svc_run();
|
|
syslog(LOG_ERR, "Mountd died");
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* The mount rpc service
|
|
*/
|
|
void
|
|
mntsrv(rqstp, transp)
|
|
struct svc_req *rqstp;
|
|
SVCXPRT *transp;
|
|
{
|
|
struct exportlist *ep;
|
|
struct dirlist *dp;
|
|
struct fhreturn fhr;
|
|
struct stat stb;
|
|
struct statvfs fsb;
|
|
struct addrinfo *ai;
|
|
char host[NI_MAXHOST], numerichost[NI_MAXHOST];
|
|
int lookup_failed = 1;
|
|
struct sockaddr *saddr;
|
|
u_short sport;
|
|
char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
|
|
long bad = EACCES;
|
|
int defset, hostset, ret;
|
|
sigset_t sighup_mask;
|
|
struct sockaddr_in6 *sin6;
|
|
struct sockaddr_in *sin;
|
|
|
|
(void)sigemptyset(&sighup_mask);
|
|
(void)sigaddset(&sighup_mask, SIGHUP);
|
|
saddr = svc_getrpccaller(transp)->buf;
|
|
switch (saddr->sa_family) {
|
|
case AF_INET6:
|
|
sin6 = (struct sockaddr_in6 *)saddr;
|
|
sport = ntohs(sin6->sin6_port);
|
|
break;
|
|
case AF_INET:
|
|
sin = (struct sockaddr_in *)saddr;
|
|
sport = ntohs(sin->sin_port);
|
|
break;
|
|
default:
|
|
syslog(LOG_ERR, "request from unknown address family");
|
|
return;
|
|
}
|
|
lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
|
|
NULL, 0, 0);
|
|
if (getnameinfo(saddr, saddr->sa_len, numerichost,
|
|
sizeof numerichost, NULL, 0, ninumeric) != 0)
|
|
strlcpy(numerichost, "?", sizeof(numerichost));
|
|
ai = NULL;
|
|
#ifdef KERBEROS
|
|
kuidreset();
|
|
#endif
|
|
ret = 0;
|
|
switch (rqstp->rq_proc) {
|
|
case NULLPROC:
|
|
if (!svc_sendreply(transp, xdr_void, NULL))
|
|
syslog(LOG_ERR, "Can't send reply");
|
|
return;
|
|
case MOUNTPROC_MNT:
|
|
if (debug)
|
|
fprintf(stderr,
|
|
"got mount request from %s\n", numerichost);
|
|
if (!svc_getargs(transp, xdr_dir, rpcpath)) {
|
|
if (debug)
|
|
fprintf(stderr, "-> garbage args\n");
|
|
svcerr_decode(transp);
|
|
return;
|
|
}
|
|
if (debug)
|
|
fprintf(stderr,
|
|
"-> rpcpath: %s\n", rpcpath);
|
|
/*
|
|
* Get the real pathname and make sure it is a file or
|
|
* directory that exists.
|
|
*/
|
|
if (realpath(rpcpath, dirpath) == 0 ||
|
|
stat(dirpath, &stb) < 0 ||
|
|
(!S_ISDIR(stb.st_mode) && !S_ISREG(stb.st_mode)) ||
|
|
statvfs(dirpath, &fsb) < 0) {
|
|
(void)chdir("/"); /* Just in case realpath doesn't */
|
|
if (debug)
|
|
(void)fprintf(stderr, "-> stat failed on %s\n",
|
|
dirpath);
|
|
if (!svc_sendreply(transp, xdr_long, (caddr_t) &bad))
|
|
syslog(LOG_ERR, "Can't send reply");
|
|
return;
|
|
}
|
|
if (debug)
|
|
fprintf(stderr,
|
|
"-> dirpath: %s\n", dirpath);
|
|
/* Check in the exports list */
|
|
(void)sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
|
|
ep = ex_search(&fsb.f_fsidx);
|
|
hostset = defset = 0;
|
|
if (ep && (chk_host(ep->ex_defdir, saddr, &defset,
|
|
&hostset) || ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
|
|
chk_host(dp, saddr, &defset, &hostset)) ||
|
|
(defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
|
|
scan_tree(ep->ex_dirl, saddr) == 0))) {
|
|
if (sport >= IPPORT_RESERVED &&
|
|
!(hostset & DP_NORESMNT)) {
|
|
syslog(LOG_NOTICE,
|
|
"Refused mount RPC from host %s port %d",
|
|
numerichost, sport);
|
|
svcerr_weakauth(transp);
|
|
goto out;
|
|
}
|
|
if (hostset & DP_HOSTSET)
|
|
fhr.fhr_flag = hostset;
|
|
else
|
|
fhr.fhr_flag = defset;
|
|
fhr.fhr_vers = rqstp->rq_vers;
|
|
/* Get the file handle */
|
|
(void)memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
|
|
if (getfh(dirpath, (fhandle_t *) &fhr.fhr_fh) < 0) {
|
|
bad = errno;
|
|
syslog(LOG_ERR, "Can't get fh for %s", dirpath);
|
|
if (!svc_sendreply(transp, xdr_long,
|
|
(char *)&bad))
|
|
syslog(LOG_ERR, "Can't send reply");
|
|
goto out;
|
|
}
|
|
if (!svc_sendreply(transp, xdr_fhs, (char *) &fhr))
|
|
syslog(LOG_ERR, "Can't send reply");
|
|
if (!lookup_failed)
|
|
add_mlist(host, dirpath, hostset);
|
|
else
|
|
add_mlist(numerichost, dirpath, hostset);
|
|
if (debug)
|
|
(void)fprintf(stderr, "Mount successful.\n");
|
|
} else {
|
|
if (!svc_sendreply(transp, xdr_long, (caddr_t) &bad))
|
|
syslog(LOG_ERR, "Can't send reply");
|
|
}
|
|
out:
|
|
(void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
|
|
return;
|
|
case MOUNTPROC_DUMP:
|
|
if (!svc_sendreply(transp, xdr_mlist, NULL))
|
|
syslog(LOG_ERR, "Can't send reply");
|
|
return;
|
|
case MOUNTPROC_UMNT:
|
|
if (!svc_getargs(transp, xdr_dir, dirpath)) {
|
|
svcerr_decode(transp);
|
|
return;
|
|
}
|
|
if (!lookup_failed)
|
|
ret = del_mlist(host, dirpath, saddr);
|
|
ret |= del_mlist(numerichost, dirpath, saddr);
|
|
if (ret) {
|
|
svcerr_weakauth(transp);
|
|
return;
|
|
}
|
|
if (!svc_sendreply(transp, xdr_void, NULL))
|
|
syslog(LOG_ERR, "Can't send reply");
|
|
return;
|
|
case MOUNTPROC_UMNTALL:
|
|
if (!lookup_failed)
|
|
ret = del_mlist(host, NULL, saddr);
|
|
ret |= del_mlist(numerichost, NULL, saddr);
|
|
if (ret) {
|
|
svcerr_weakauth(transp);
|
|
return;
|
|
}
|
|
if (!svc_sendreply(transp, xdr_void, NULL))
|
|
syslog(LOG_ERR, "Can't send reply");
|
|
return;
|
|
case MOUNTPROC_EXPORT:
|
|
case MOUNTPROC_EXPORTALL:
|
|
if (!svc_sendreply(transp, xdr_explist, NULL))
|
|
syslog(LOG_ERR, "Can't send reply");
|
|
return;
|
|
|
|
#ifdef KERBEROS
|
|
case MOUNTPROC_KUIDMAP:
|
|
case MOUNTPROC_KUIDUMAP:
|
|
case MOUNTPROC_KUIDPURGE:
|
|
case MOUNTPROC_KUIDUPURGE:
|
|
kuidops(rqstp, transp);
|
|
return;
|
|
#endif
|
|
|
|
default:
|
|
svcerr_noproc(transp);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Xdr conversion for a dirpath string
|
|
*/
|
|
static int
|
|
xdr_dir(xdrsp, dirp)
|
|
XDR *xdrsp;
|
|
char *dirp;
|
|
{
|
|
|
|
return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
|
|
}
|
|
|
|
/*
|
|
* Xdr routine to generate file handle reply
|
|
*/
|
|
static int
|
|
xdr_fhs(xdrsp, cp)
|
|
XDR *xdrsp;
|
|
caddr_t cp;
|
|
{
|
|
struct fhreturn *fhrp = (struct fhreturn *) cp;
|
|
long ok = 0, len, auth;
|
|
|
|
if (!xdr_long(xdrsp, &ok))
|
|
return (0);
|
|
switch (fhrp->fhr_vers) {
|
|
case 1:
|
|
return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
|
|
case 3:
|
|
len = NFSX_V3FH;
|
|
if (!xdr_long(xdrsp, &len))
|
|
return (0);
|
|
if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
|
|
return (0);
|
|
if (fhrp->fhr_flag & DP_KERB)
|
|
auth = RPCAUTH_KERB4;
|
|
else
|
|
auth = RPCAUTH_UNIX;
|
|
len = 1;
|
|
if (!xdr_long(xdrsp, &len))
|
|
return (0);
|
|
return (xdr_long(xdrsp, &auth));
|
|
};
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
xdr_mlist(xdrsp, cp)
|
|
XDR *xdrsp;
|
|
caddr_t cp;
|
|
{
|
|
struct mountlist *mlp;
|
|
int true = 1;
|
|
int false = 0;
|
|
char *strp;
|
|
|
|
mlp = mlhead;
|
|
while (mlp) {
|
|
if (!xdr_bool(xdrsp, &true))
|
|
return (0);
|
|
strp = &mlp->ml_host[0];
|
|
if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
|
|
return (0);
|
|
strp = &mlp->ml_dirp[0];
|
|
if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
|
|
return (0);
|
|
mlp = mlp->ml_next;
|
|
}
|
|
if (!xdr_bool(xdrsp, &false))
|
|
return (0);
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Xdr conversion for export list
|
|
*/
|
|
int
|
|
xdr_explist(xdrsp, cp)
|
|
XDR *xdrsp;
|
|
caddr_t cp;
|
|
{
|
|
struct exportlist *ep;
|
|
int false = 0;
|
|
int putdef;
|
|
sigset_t sighup_mask;
|
|
|
|
(void)sigemptyset(&sighup_mask);
|
|
(void)sigaddset(&sighup_mask, SIGHUP);
|
|
(void)sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
|
|
ep = exphead;
|
|
while (ep) {
|
|
putdef = 0;
|
|
if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
|
|
goto errout;
|
|
if (ep->ex_defdir && putdef == 0 &&
|
|
put_exlist(ep->ex_defdir, xdrsp, NULL, &putdef))
|
|
goto errout;
|
|
ep = ep->ex_next;
|
|
}
|
|
(void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
|
|
if (!xdr_bool(xdrsp, &false))
|
|
return (0);
|
|
return (1);
|
|
errout:
|
|
(void)sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Called from xdr_explist() to traverse the tree and export the
|
|
* directory paths. Assumes SIGHUP has already been masked.
|
|
*/
|
|
int
|
|
put_exlist(dp, xdrsp, adp, putdefp)
|
|
struct dirlist *dp;
|
|
XDR *xdrsp;
|
|
struct dirlist *adp;
|
|
int *putdefp;
|
|
{
|
|
struct grouplist *grp;
|
|
struct hostlist *hp;
|
|
int true = 1;
|
|
int false = 0;
|
|
int gotalldir = 0;
|
|
char *strp;
|
|
|
|
if (dp) {
|
|
if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
|
|
return (1);
|
|
if (!xdr_bool(xdrsp, &true))
|
|
return (1);
|
|
strp = dp->dp_dirp;
|
|
if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
|
|
return (1);
|
|
if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
|
|
gotalldir = 1;
|
|
*putdefp = 1;
|
|
}
|
|
if ((dp->dp_flag & DP_DEFSET) == 0 &&
|
|
(gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
|
|
hp = dp->dp_hosts;
|
|
while (hp) {
|
|
grp = hp->ht_grp;
|
|
if (grp->gr_type == GT_HOST) {
|
|
if (!xdr_bool(xdrsp, &true))
|
|
return (1);
|
|
strp =
|
|
grp->gr_ptr.gt_addrinfo->ai_canonname;
|
|
if (!xdr_string(xdrsp, &strp,
|
|
RPCMNT_NAMELEN))
|
|
return (1);
|
|
} else if (grp->gr_type == GT_NET) {
|
|
if (!xdr_bool(xdrsp, &true))
|
|
return (1);
|
|
strp = grp->gr_ptr.gt_net.nt_name;
|
|
if (!xdr_string(xdrsp, &strp,
|
|
RPCMNT_NAMELEN))
|
|
return (1);
|
|
}
|
|
hp = hp->ht_next;
|
|
if (gotalldir && hp == NULL) {
|
|
hp = adp->dp_hosts;
|
|
gotalldir = 0;
|
|
}
|
|
}
|
|
}
|
|
if (!xdr_bool(xdrsp, &false))
|
|
return (1);
|
|
if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
parse_host_netgroup(line, lineno, ep, tgrp, cp, has_host, grp)
|
|
const char *line;
|
|
size_t lineno;
|
|
struct exportlist *ep;
|
|
struct grouplist *tgrp;
|
|
char *cp;
|
|
int *has_host;
|
|
struct grouplist **grp;
|
|
{
|
|
const char *hst, *usr, *dom;
|
|
int netgrp;
|
|
|
|
if (ep == NULL) {
|
|
syslog(LOG_ERR, "\"%s\", line %ld: No current export",
|
|
line, (unsigned long)lineno);
|
|
return 0;
|
|
}
|
|
setnetgrent(cp);
|
|
netgrp = getnetgrent(&hst, &usr, &dom);
|
|
do {
|
|
if (*has_host) {
|
|
(*grp)->gr_next = get_grp();
|
|
*grp = (*grp)->gr_next;
|
|
}
|
|
if (netgrp) {
|
|
if (hst == NULL) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: No host in netgroup %s",
|
|
line, (unsigned long)lineno, cp);
|
|
goto bad;
|
|
}
|
|
if (get_host(line, lineno, hst, *grp))
|
|
goto bad;
|
|
} else if (get_host(line, lineno, cp, *grp))
|
|
goto bad;
|
|
*has_host = TRUE;
|
|
} while (netgrp && getnetgrent(&hst, &usr, &dom));
|
|
|
|
endnetgrent();
|
|
return 1;
|
|
bad:
|
|
endnetgrent();
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int
|
|
parse_directory(line, lineno, tgrp, got_nondir, cp, ep, fsp)
|
|
const char *line;
|
|
size_t lineno;
|
|
struct grouplist *tgrp;
|
|
int got_nondir;
|
|
char *cp;
|
|
struct exportlist **ep;
|
|
struct statvfs *fsp;
|
|
{
|
|
if (!check_dirpath(line, lineno, cp))
|
|
return 0;
|
|
|
|
if (statvfs(cp, fsp) == -1) {
|
|
syslog(LOG_ERR, "\"%s\", line %ld: statvfs for `%s' failed: %m",
|
|
line, (unsigned long)lineno, cp);
|
|
return 0;
|
|
}
|
|
|
|
if (got_nondir) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: Directories must precede files",
|
|
line, (unsigned long)lineno);
|
|
return 0;
|
|
}
|
|
if (*ep) {
|
|
if ((*ep)->ex_fs.__fsid_val[0] != fsp->f_fsidx.__fsid_val[0] ||
|
|
(*ep)->ex_fs.__fsid_val[1] != fsp->f_fsidx.__fsid_val[1]) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: filesystem ids disagree",
|
|
line, (unsigned long)lineno);
|
|
return 0;
|
|
}
|
|
} else {
|
|
/*
|
|
* See if this directory is already
|
|
* in the list.
|
|
*/
|
|
*ep = ex_search(&fsp->f_fsidx);
|
|
if (*ep == NULL) {
|
|
*ep = get_exp();
|
|
(*ep)->ex_fs = fsp->f_fsidx;
|
|
(*ep)->ex_fsdir = estrdup(fsp->f_mntonname);
|
|
if (debug)
|
|
(void)fprintf(stderr,
|
|
"Making new ep fs=0x%x,0x%x\n",
|
|
fsp->f_fsidx.__fsid_val[0], fsp->f_fsidx.__fsid_val[1]);
|
|
} else {
|
|
if (debug)
|
|
(void)fprintf(stderr,
|
|
"Found ep fs=0x%x,0x%x\n",
|
|
fsp->f_fsidx.__fsid_val[0], fsp->f_fsidx.__fsid_val[1]);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the export list
|
|
*/
|
|
/* ARGSUSED */
|
|
void
|
|
get_exportlist(n)
|
|
int n;
|
|
{
|
|
struct exportlist *ep, *ep2;
|
|
struct grouplist *grp, *tgrp;
|
|
struct exportlist **epp;
|
|
struct dirlist *dirhead;
|
|
struct statvfs fsb, *fsp;
|
|
struct addrinfo *ai;
|
|
struct uucred anon;
|
|
char *cp, *endcp, *dirp, savedc;
|
|
int has_host, exflags, got_nondir, dirplen, num, i;
|
|
FILE *exp_file;
|
|
char *line;
|
|
size_t lineno = 0, len;
|
|
|
|
|
|
/*
|
|
* First, get rid of the old list
|
|
*/
|
|
ep = exphead;
|
|
while (ep) {
|
|
ep2 = ep;
|
|
ep = ep->ex_next;
|
|
free_exp(ep2);
|
|
}
|
|
exphead = NULL;
|
|
|
|
dirp = NULL;
|
|
dirplen = 0;
|
|
grp = grphead;
|
|
while (grp) {
|
|
tgrp = grp;
|
|
grp = grp->gr_next;
|
|
free_grp(tgrp);
|
|
}
|
|
grphead = NULL;
|
|
|
|
/*
|
|
* And delete exports that are in the kernel for all local
|
|
* file systems.
|
|
*/
|
|
num = getmntinfo(&fsp, MNT_NOWAIT);
|
|
for (i = 0; i < num; i++) {
|
|
struct mountd_exports_list mel;
|
|
|
|
/* Delete all entries from the export list. */
|
|
mel.mel_path = fsp->f_mntonname;
|
|
mel.mel_nexports = 0;
|
|
if (nfssvc(NFSSVC_SETEXPORTSLIST, &mel) == -1)
|
|
syslog(LOG_ERR, "Can't delete exports for %s",
|
|
fsp->f_mntonname);
|
|
|
|
fsp++;
|
|
}
|
|
|
|
/*
|
|
* Read in the exports file and build the list, calling
|
|
* mount() as we go along to push the export rules into the kernel.
|
|
*/
|
|
if ((exp_file = fopen(exname, "r")) == NULL) {
|
|
/*
|
|
* Don't exit here; we can still reload the config
|
|
* after a SIGHUP.
|
|
*/
|
|
if (debug)
|
|
(void)fprintf(stderr, "Can't open %s: %s\n", exname,
|
|
strerror(errno));
|
|
return;
|
|
}
|
|
dirhead = NULL;
|
|
while ((line = fparseln(exp_file, &len, &lineno, NULL, 0)) != NULL) {
|
|
if (debug)
|
|
(void)fprintf(stderr, "Got line %s\n", line);
|
|
cp = line;
|
|
nextfield(&cp, &endcp);
|
|
if (cp == endcp)
|
|
goto nextline; /* skip empty line */
|
|
/*
|
|
* Set defaults.
|
|
*/
|
|
has_host = FALSE;
|
|
anon = def_anon;
|
|
exflags = MNT_EXPORTED;
|
|
got_nondir = 0;
|
|
opt_flags = 0;
|
|
ep = NULL;
|
|
|
|
if (noprivports) {
|
|
opt_flags |= OP_NORESMNT | OP_NORESPORT;
|
|
exflags |= MNT_EXNORESPORT;
|
|
}
|
|
|
|
/*
|
|
* Create new exports list entry
|
|
*/
|
|
len = endcp - cp;
|
|
tgrp = grp = get_grp();
|
|
while (len > 0) {
|
|
if (len > RPCMNT_NAMELEN) {
|
|
*endcp = '\0';
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: name `%s' is too long",
|
|
line, (unsigned long)lineno, cp);
|
|
goto badline;
|
|
}
|
|
switch (*cp) {
|
|
case '-':
|
|
/*
|
|
* Option
|
|
*/
|
|
if (ep == NULL) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: No current export list",
|
|
line, (unsigned long)lineno);
|
|
goto badline;
|
|
}
|
|
if (debug)
|
|
(void)fprintf(stderr, "doing opt %s\n",
|
|
cp);
|
|
got_nondir = 1;
|
|
if (do_opt(line, lineno, &cp, &endcp, ep, grp,
|
|
&has_host, &exflags, &anon))
|
|
goto badline;
|
|
break;
|
|
|
|
case '/':
|
|
/*
|
|
* Directory
|
|
*/
|
|
savedc = *endcp;
|
|
*endcp = '\0';
|
|
|
|
if (!parse_directory(line, lineno, tgrp,
|
|
got_nondir, cp, &ep, &fsb))
|
|
goto badline;
|
|
/*
|
|
* Add dirpath to export mount point.
|
|
*/
|
|
dirp = add_expdir(&dirhead, cp, len);
|
|
dirplen = len;
|
|
|
|
*endcp = savedc;
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* Host or netgroup.
|
|
*/
|
|
savedc = *endcp;
|
|
*endcp = '\0';
|
|
|
|
if (!parse_host_netgroup(line, lineno, ep,
|
|
tgrp, cp, &has_host, &grp))
|
|
goto badline;
|
|
|
|
got_nondir = 1;
|
|
|
|
*endcp = savedc;
|
|
break;
|
|
}
|
|
|
|
cp = endcp;
|
|
nextfield(&cp, &endcp);
|
|
len = endcp - cp;
|
|
}
|
|
if (check_options(line, lineno, dirhead))
|
|
goto badline;
|
|
|
|
if (!has_host) {
|
|
grp->gr_type = GT_HOST;
|
|
if (debug)
|
|
(void)fprintf(stderr,
|
|
"Adding a default entry\n");
|
|
/* add a default group and make the grp list NULL */
|
|
ai = emalloc(sizeof(struct addrinfo));
|
|
ai->ai_flags = 0;
|
|
ai->ai_family = AF_INET; /* XXXX */
|
|
ai->ai_socktype = SOCK_DGRAM;
|
|
/* setting the length to 0 will match anything */
|
|
ai->ai_addrlen = 0;
|
|
ai->ai_flags = AI_CANONNAME;
|
|
ai->ai_canonname = estrdup("Default");
|
|
ai->ai_addr = NULL;
|
|
ai->ai_next = NULL;
|
|
grp->gr_ptr.gt_addrinfo = ai;
|
|
|
|
} else if ((opt_flags & OP_NET) && tgrp->gr_next) {
|
|
/*
|
|
* Don't allow a network export coincide with a list of
|
|
* host(s) on the same line.
|
|
*/
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: Mixed exporting of networks and hosts is disallowed",
|
|
line, (unsigned long)lineno);
|
|
goto badline;
|
|
}
|
|
/*
|
|
* Loop through hosts, pushing the exports into the kernel.
|
|
* After loop, tgrp points to the start of the list and
|
|
* grp points to the last entry in the list.
|
|
*/
|
|
grp = tgrp;
|
|
do {
|
|
if (do_nfssvc(line, lineno, ep, grp, exflags, &anon,
|
|
dirp, dirplen, &fsb))
|
|
goto badline;
|
|
} while (grp->gr_next && (grp = grp->gr_next));
|
|
|
|
/*
|
|
* Success. Update the data structures.
|
|
*/
|
|
if (has_host) {
|
|
hang_dirp(dirhead, tgrp, ep, opt_flags);
|
|
grp->gr_next = grphead;
|
|
grphead = tgrp;
|
|
} else {
|
|
hang_dirp(dirhead, NULL, ep, opt_flags);
|
|
free_grp(grp);
|
|
}
|
|
dirhead = NULL;
|
|
if ((ep->ex_flag & EX_LINKED) == 0) {
|
|
ep2 = exphead;
|
|
epp = &exphead;
|
|
|
|
/*
|
|
* Insert in the list in alphabetical order.
|
|
*/
|
|
while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
|
|
epp = &ep2->ex_next;
|
|
ep2 = ep2->ex_next;
|
|
}
|
|
if (ep2)
|
|
ep->ex_next = ep2;
|
|
*epp = ep;
|
|
ep->ex_flag |= EX_LINKED;
|
|
}
|
|
goto nextline;
|
|
badline:
|
|
free_exp_grp(ep, grp);
|
|
nextline:
|
|
if (dirhead) {
|
|
free_dir(dirhead);
|
|
dirhead = NULL;
|
|
}
|
|
free(line);
|
|
}
|
|
(void)fclose(exp_file);
|
|
}
|
|
|
|
/*
|
|
* Allocate an export list element
|
|
*/
|
|
static struct exportlist *
|
|
get_exp()
|
|
{
|
|
struct exportlist *ep;
|
|
|
|
ep = emalloc(sizeof(struct exportlist));
|
|
(void)memset(ep, 0, sizeof(struct exportlist));
|
|
return (ep);
|
|
}
|
|
|
|
/*
|
|
* Allocate a group list element
|
|
*/
|
|
static struct grouplist *
|
|
get_grp()
|
|
{
|
|
struct grouplist *gp;
|
|
|
|
gp = emalloc(sizeof(struct grouplist));
|
|
(void)memset(gp, 0, sizeof(struct grouplist));
|
|
return (gp);
|
|
}
|
|
|
|
/*
|
|
* Clean up upon an error in get_exportlist().
|
|
*/
|
|
static void
|
|
free_exp_grp(ep, grp)
|
|
struct exportlist *ep;
|
|
struct grouplist *grp;
|
|
{
|
|
struct grouplist *tgrp;
|
|
|
|
if (ep && (ep->ex_flag & EX_LINKED) == 0)
|
|
free_exp(ep);
|
|
while (grp) {
|
|
tgrp = grp;
|
|
grp = grp->gr_next;
|
|
free_grp(tgrp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Search the export list for a matching fs.
|
|
*/
|
|
static struct exportlist *
|
|
ex_search(fsid)
|
|
fsid_t *fsid;
|
|
{
|
|
struct exportlist *ep;
|
|
|
|
ep = exphead;
|
|
while (ep) {
|
|
if (ep->ex_fs.__fsid_val[0] == fsid->__fsid_val[0] &&
|
|
ep->ex_fs.__fsid_val[1] == fsid->__fsid_val[1])
|
|
return (ep);
|
|
ep = ep->ex_next;
|
|
}
|
|
return (ep);
|
|
}
|
|
|
|
/*
|
|
* Add a directory path to the list.
|
|
*/
|
|
static char *
|
|
add_expdir(dpp, cp, len)
|
|
struct dirlist **dpp;
|
|
char *cp;
|
|
int len;
|
|
{
|
|
struct dirlist *dp;
|
|
|
|
dp = emalloc(sizeof(struct dirlist) + len);
|
|
dp->dp_left = *dpp;
|
|
dp->dp_right = NULL;
|
|
dp->dp_flag = 0;
|
|
dp->dp_hosts = NULL;
|
|
(void)strcpy(dp->dp_dirp, cp);
|
|
*dpp = dp;
|
|
return (dp->dp_dirp);
|
|
}
|
|
|
|
/*
|
|
* Hang the dir list element off the dirpath binary tree as required
|
|
* and update the entry for host.
|
|
*/
|
|
void
|
|
hang_dirp(dp, grp, ep, flags)
|
|
struct dirlist *dp;
|
|
struct grouplist *grp;
|
|
struct exportlist *ep;
|
|
int flags;
|
|
{
|
|
struct hostlist *hp;
|
|
struct dirlist *dp2;
|
|
|
|
if (flags & OP_ALLDIRS) {
|
|
if (ep->ex_defdir)
|
|
free(dp);
|
|
else
|
|
ep->ex_defdir = dp;
|
|
if (grp == NULL) {
|
|
ep->ex_defdir->dp_flag |= DP_DEFSET;
|
|
if (flags & OP_KERB)
|
|
ep->ex_defdir->dp_flag |= DP_KERB;
|
|
if (flags & OP_NORESMNT)
|
|
ep->ex_defdir->dp_flag |= DP_NORESMNT;
|
|
} else
|
|
while (grp) {
|
|
hp = get_ht();
|
|
if (flags & OP_KERB)
|
|
hp->ht_flag |= DP_KERB;
|
|
if (flags & OP_NORESMNT)
|
|
hp->ht_flag |= DP_NORESMNT;
|
|
hp->ht_grp = grp;
|
|
hp->ht_next = ep->ex_defdir->dp_hosts;
|
|
ep->ex_defdir->dp_hosts = hp;
|
|
grp = grp->gr_next;
|
|
}
|
|
} else {
|
|
|
|
/*
|
|
* Loop through the directories adding them to the tree.
|
|
*/
|
|
while (dp) {
|
|
dp2 = dp->dp_left;
|
|
add_dlist(&ep->ex_dirl, dp, grp, flags);
|
|
dp = dp2;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Traverse the binary tree either updating a node that is already there
|
|
* for the new directory or adding the new node.
|
|
*/
|
|
static void
|
|
add_dlist(dpp, newdp, grp, flags)
|
|
struct dirlist **dpp;
|
|
struct dirlist *newdp;
|
|
struct grouplist *grp;
|
|
int flags;
|
|
{
|
|
struct dirlist *dp;
|
|
struct hostlist *hp;
|
|
int cmp;
|
|
|
|
dp = *dpp;
|
|
if (dp) {
|
|
cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
|
|
if (cmp > 0) {
|
|
add_dlist(&dp->dp_left, newdp, grp, flags);
|
|
return;
|
|
} else if (cmp < 0) {
|
|
add_dlist(&dp->dp_right, newdp, grp, flags);
|
|
return;
|
|
} else
|
|
free(newdp);
|
|
} else {
|
|
dp = newdp;
|
|
dp->dp_left = NULL;
|
|
*dpp = dp;
|
|
}
|
|
if (grp) {
|
|
|
|
/*
|
|
* Hang all of the host(s) off of the directory point.
|
|
*/
|
|
do {
|
|
hp = get_ht();
|
|
if (flags & OP_KERB)
|
|
hp->ht_flag |= DP_KERB;
|
|
if (flags & OP_NORESMNT)
|
|
hp->ht_flag |= DP_NORESMNT;
|
|
hp->ht_grp = grp;
|
|
hp->ht_next = dp->dp_hosts;
|
|
dp->dp_hosts = hp;
|
|
grp = grp->gr_next;
|
|
} while (grp);
|
|
} else {
|
|
dp->dp_flag |= DP_DEFSET;
|
|
if (flags & OP_KERB)
|
|
dp->dp_flag |= DP_KERB;
|
|
if (flags & OP_NORESMNT)
|
|
dp->dp_flag |= DP_NORESMNT;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Search for a dirpath on the export point.
|
|
*/
|
|
static struct dirlist *
|
|
dirp_search(dp, dirp)
|
|
struct dirlist *dp;
|
|
char *dirp;
|
|
{
|
|
int cmp;
|
|
|
|
if (dp) {
|
|
cmp = strcmp(dp->dp_dirp, dirp);
|
|
if (cmp > 0)
|
|
return (dirp_search(dp->dp_left, dirp));
|
|
else if (cmp < 0)
|
|
return (dirp_search(dp->dp_right, dirp));
|
|
else
|
|
return (dp);
|
|
}
|
|
return (dp);
|
|
}
|
|
|
|
/*
|
|
* Some helper functions for netmasks. They all assume masks in network
|
|
* order (big endian).
|
|
*/
|
|
static int
|
|
bitcmp(void *dst, void *src, int bitlen)
|
|
{
|
|
int i;
|
|
u_int8_t *p1 = dst, *p2 = src;
|
|
u_int8_t bitmask;
|
|
int bytelen, bitsleft;
|
|
|
|
bytelen = bitlen / 8;
|
|
bitsleft = bitlen % 8;
|
|
|
|
if (debug) {
|
|
printf("comparing:\n");
|
|
for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++)
|
|
printf("%02x", p1[i]);
|
|
printf("\n");
|
|
for (i = 0; i < (bitsleft ? bytelen + 1 : bytelen); i++)
|
|
printf("%02x", p2[i]);
|
|
printf("\n");
|
|
}
|
|
|
|
for (i = 0; i < bytelen; i++) {
|
|
if (*p1 != *p2)
|
|
return 1;
|
|
p1++;
|
|
p2++;
|
|
}
|
|
|
|
for (i = 0; i < bitsleft; i++) {
|
|
bitmask = 1 << (7 - i);
|
|
if ((*p1 & bitmask) != (*p2 & bitmask))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
netpartcmp(struct sockaddr *s1, struct sockaddr *s2, int bitlen)
|
|
{
|
|
void *src, *dst;
|
|
|
|
if (s1->sa_family != s2->sa_family)
|
|
return 1;
|
|
|
|
switch (s1->sa_family) {
|
|
case AF_INET:
|
|
src = &((struct sockaddr_in *)s1)->sin_addr;
|
|
dst = &((struct sockaddr_in *)s2)->sin_addr;
|
|
if (bitlen > sizeof(((struct sockaddr_in *)s1)->sin_addr) * 8)
|
|
return 1;
|
|
break;
|
|
case AF_INET6:
|
|
src = &((struct sockaddr_in6 *)s1)->sin6_addr;
|
|
dst = &((struct sockaddr_in6 *)s2)->sin6_addr;
|
|
if (((struct sockaddr_in6 *)s1)->sin6_scope_id !=
|
|
((struct sockaddr_in6 *)s2)->sin6_scope_id)
|
|
return 1;
|
|
if (bitlen > sizeof(((struct sockaddr_in6 *)s1)->sin6_addr) * 8)
|
|
return 1;
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
|
|
return bitcmp(src, dst, bitlen);
|
|
}
|
|
|
|
static int
|
|
allones(struct sockaddr_storage *ssp, int bitlen)
|
|
{
|
|
u_int8_t *p;
|
|
int bytelen, bitsleft, i;
|
|
int zerolen;
|
|
|
|
switch (ssp->ss_family) {
|
|
case AF_INET:
|
|
p = (u_int8_t *)&((struct sockaddr_in *)ssp)->sin_addr;
|
|
zerolen = sizeof (((struct sockaddr_in *)ssp)->sin_addr);
|
|
break;
|
|
case AF_INET6:
|
|
p = (u_int8_t *)&((struct sockaddr_in6 *)ssp)->sin6_addr;
|
|
zerolen = sizeof (((struct sockaddr_in6 *)ssp)->sin6_addr);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
|
|
memset(p, 0, zerolen);
|
|
|
|
bytelen = bitlen / 8;
|
|
bitsleft = bitlen % 8;
|
|
|
|
if (bytelen > zerolen)
|
|
return -1;
|
|
|
|
for (i = 0; i < bytelen; i++)
|
|
*p++ = 0xff;
|
|
|
|
for (i = 0; i < bitsleft; i++)
|
|
*p |= 1 << (7 - i);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
countones(struct sockaddr *sa)
|
|
{
|
|
void *mask;
|
|
int i, bits = 0, bytelen;
|
|
u_int8_t *p;
|
|
|
|
switch (sa->sa_family) {
|
|
case AF_INET:
|
|
mask = (u_int8_t *)&((struct sockaddr_in *)sa)->sin_addr;
|
|
bytelen = 4;
|
|
break;
|
|
case AF_INET6:
|
|
mask = (u_int8_t *)&((struct sockaddr_in6 *)sa)->sin6_addr;
|
|
bytelen = 16;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
p = mask;
|
|
|
|
for (i = 0; i < bytelen; i++, p++) {
|
|
if (*p != 0xff) {
|
|
for (bits = 0; bits < 8; bits++) {
|
|
if (!(*p & (1 << (7 - bits))))
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return (i * 8 + bits);
|
|
}
|
|
|
|
static int
|
|
sacmp(struct sockaddr *sa1, struct sockaddr *sa2)
|
|
{
|
|
void *p1, *p2;
|
|
int len;
|
|
|
|
if (sa1->sa_family != sa2->sa_family)
|
|
return 1;
|
|
|
|
switch (sa1->sa_family) {
|
|
case AF_INET:
|
|
p1 = &((struct sockaddr_in *)sa1)->sin_addr;
|
|
p2 = &((struct sockaddr_in *)sa2)->sin_addr;
|
|
len = 4;
|
|
break;
|
|
case AF_INET6:
|
|
p1 = &((struct sockaddr_in6 *)sa1)->sin6_addr;
|
|
p2 = &((struct sockaddr_in6 *)sa2)->sin6_addr;
|
|
len = 16;
|
|
if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
|
|
((struct sockaddr_in6 *)sa2)->sin6_scope_id)
|
|
return 1;
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
|
|
return memcmp(p1, p2, len);
|
|
}
|
|
|
|
/*
|
|
* Scan for a host match in a directory tree.
|
|
*/
|
|
static int
|
|
chk_host(dp, saddr, defsetp, hostsetp)
|
|
struct dirlist *dp;
|
|
struct sockaddr *saddr;
|
|
int *defsetp;
|
|
int *hostsetp;
|
|
{
|
|
struct hostlist *hp;
|
|
struct grouplist *grp;
|
|
struct addrinfo *ai;
|
|
|
|
if (dp) {
|
|
if (dp->dp_flag & DP_DEFSET)
|
|
*defsetp = dp->dp_flag;
|
|
hp = dp->dp_hosts;
|
|
while (hp) {
|
|
grp = hp->ht_grp;
|
|
switch (grp->gr_type) {
|
|
case GT_HOST:
|
|
ai = grp->gr_ptr.gt_addrinfo;
|
|
for (; ai; ai = ai->ai_next) {
|
|
if (!sacmp(ai->ai_addr, saddr)) {
|
|
*hostsetp =
|
|
(hp->ht_flag | DP_HOSTSET);
|
|
return (1);
|
|
}
|
|
}
|
|
break;
|
|
case GT_NET:
|
|
if (!netpartcmp(saddr,
|
|
(struct sockaddr *)
|
|
&grp->gr_ptr.gt_net.nt_net,
|
|
grp->gr_ptr.gt_net.nt_len)) {
|
|
*hostsetp = (hp->ht_flag | DP_HOSTSET);
|
|
return (1);
|
|
}
|
|
break;
|
|
};
|
|
hp = hp->ht_next;
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Scan tree for a host that matches the address.
|
|
*/
|
|
static int
|
|
scan_tree(dp, saddr)
|
|
struct dirlist *dp;
|
|
struct sockaddr *saddr;
|
|
{
|
|
int defset, hostset;
|
|
|
|
if (dp) {
|
|
if (scan_tree(dp->dp_left, saddr))
|
|
return (1);
|
|
if (chk_host(dp, saddr, &defset, &hostset))
|
|
return (1);
|
|
if (scan_tree(dp->dp_right, saddr))
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Traverse the dirlist tree and free it up.
|
|
*/
|
|
static void
|
|
free_dir(dp)
|
|
struct dirlist *dp;
|
|
{
|
|
|
|
if (dp) {
|
|
free_dir(dp->dp_left);
|
|
free_dir(dp->dp_right);
|
|
free_host(dp->dp_hosts);
|
|
free(dp);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse the option string and update fields.
|
|
* Option arguments may either be -<option>=<value> or
|
|
* -<option> <value>
|
|
*/
|
|
static int
|
|
do_opt(line, lineno, cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
|
|
const char *line;
|
|
size_t lineno;
|
|
char **cpp, **endcpp;
|
|
struct exportlist *ep;
|
|
struct grouplist *grp;
|
|
int *has_hostp;
|
|
int *exflagsp;
|
|
struct uucred *cr;
|
|
{
|
|
char *cpoptarg, *cpoptend;
|
|
char *cp, *endcp, *cpopt, savedc, savedc2;
|
|
int allflag, usedarg;
|
|
|
|
cpopt = *cpp;
|
|
cpopt++;
|
|
cp = *endcpp;
|
|
savedc = *cp;
|
|
*cp = '\0';
|
|
while (cpopt && *cpopt) {
|
|
allflag = 1;
|
|
usedarg = -2;
|
|
savedc2 = '\0';
|
|
if ((cpoptend = strchr(cpopt, ',')) != NULL) {
|
|
*cpoptend++ = '\0';
|
|
if ((cpoptarg = strchr(cpopt, '=')) != NULL)
|
|
*cpoptarg++ = '\0';
|
|
} else {
|
|
if ((cpoptarg = strchr(cpopt, '=')) != NULL)
|
|
*cpoptarg++ = '\0';
|
|
else {
|
|
*cp = savedc;
|
|
nextfield(&cp, &endcp);
|
|
**endcpp = '\0';
|
|
if (endcp > cp && *cp != '-') {
|
|
cpoptarg = cp;
|
|
savedc2 = *endcp;
|
|
*endcp = '\0';
|
|
usedarg = 0;
|
|
}
|
|
}
|
|
}
|
|
if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
|
|
*exflagsp |= MNT_EXRDONLY;
|
|
} else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
|
|
!(allflag = strcmp(cpopt, "mapall")) ||
|
|
!strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
|
|
usedarg++;
|
|
parsecred(cpoptarg, cr);
|
|
if (allflag == 0) {
|
|
*exflagsp |= MNT_EXPORTANON;
|
|
opt_flags |= OP_MAPALL;
|
|
} else
|
|
opt_flags |= OP_MAPROOT;
|
|
} else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
|
|
*exflagsp |= MNT_EXKERB;
|
|
opt_flags |= OP_KERB;
|
|
} else if (cpoptarg && (!strcmp(cpopt, "mask") ||
|
|
!strcmp(cpopt, "m"))) {
|
|
if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: Bad mask: %s",
|
|
line, (unsigned long)lineno, cpoptarg);
|
|
return (1);
|
|
}
|
|
usedarg++;
|
|
opt_flags |= OP_MASK;
|
|
} else if (cpoptarg && (!strcmp(cpopt, "network") ||
|
|
!strcmp(cpopt, "n"))) {
|
|
if (strchr(cpoptarg, '/') != NULL) {
|
|
if (debug)
|
|
fprintf(stderr, "setting OP_MASKLEN\n");
|
|
opt_flags |= OP_MASKLEN;
|
|
}
|
|
if (grp->gr_type != GT_NULL) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: Network/host conflict",
|
|
line, (unsigned long)lineno);
|
|
return (1);
|
|
} else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: Bad net: %s",
|
|
line, (unsigned long)lineno, cpoptarg);
|
|
return (1);
|
|
}
|
|
grp->gr_type = GT_NET;
|
|
*has_hostp = 1;
|
|
usedarg++;
|
|
opt_flags |= OP_NET;
|
|
} else if (!strcmp(cpopt, "alldirs")) {
|
|
opt_flags |= OP_ALLDIRS;
|
|
} else if (!strcmp(cpopt, "noresvmnt")) {
|
|
opt_flags |= OP_NORESMNT;
|
|
} else if (!strcmp(cpopt, "noresvport")) {
|
|
opt_flags |= OP_NORESPORT;
|
|
*exflagsp |= MNT_EXNORESPORT;
|
|
} else if (!strcmp(cpopt, "public")) {
|
|
*exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC);
|
|
opt_flags |= OP_NORESPORT;
|
|
} else if (!strcmp(cpopt, "webnfs")) {
|
|
*exflagsp |= (MNT_EXNORESPORT | MNT_EXPUBLIC |
|
|
MNT_EXRDONLY | MNT_EXPORTANON);
|
|
opt_flags |= (OP_MAPALL | OP_NORESPORT);
|
|
} else if (cpoptarg && !strcmp(cpopt, "index")) {
|
|
ep->ex_indexfile = strdup(cpoptarg);
|
|
#ifdef ISO
|
|
} else if (cpoptarg && !strcmp(cpopt, "iso")) {
|
|
if (get_isoaddr(line, lineno, cpoptarg, grp))
|
|
return (1);
|
|
*has_hostp = 1;
|
|
usedarg++;
|
|
opt_flags |= OP_ISO;
|
|
#endif /* ISO */
|
|
} else {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: Bad opt %s",
|
|
line, (unsigned long)lineno, cpopt);
|
|
return (1);
|
|
}
|
|
if (usedarg >= 0) {
|
|
*endcp = savedc2;
|
|
**endcpp = savedc;
|
|
if (usedarg > 0) {
|
|
*cpp = cp;
|
|
*endcpp = endcp;
|
|
}
|
|
return (0);
|
|
}
|
|
cpopt = cpoptend;
|
|
}
|
|
**endcpp = savedc;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Translate a character string to the corresponding list of network
|
|
* addresses for a hostname.
|
|
*/
|
|
static int
|
|
get_host(line, lineno, cp, grp)
|
|
const char *line;
|
|
size_t lineno;
|
|
const char *cp;
|
|
struct grouplist *grp;
|
|
{
|
|
struct addrinfo *ai, hints;
|
|
int ecode;
|
|
char host[NI_MAXHOST];
|
|
|
|
if (grp->gr_type != GT_NULL) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: Bad netgroup type for ip host %s",
|
|
line, (unsigned long)lineno, cp);
|
|
return (1);
|
|
}
|
|
memset(&hints, 0, sizeof hints);
|
|
hints.ai_flags = AI_CANONNAME;
|
|
hints.ai_protocol = IPPROTO_UDP;
|
|
ecode = getaddrinfo(cp, NULL, &hints, &ai);
|
|
if (ecode != 0) {
|
|
syslog(LOG_ERR, "\"%s\", line %ld: can't get address info for "
|
|
"host %s",
|
|
line, (long)lineno, cp);
|
|
return 1;
|
|
}
|
|
grp->gr_type = GT_HOST;
|
|
grp->gr_ptr.gt_addrinfo = ai;
|
|
while (ai != NULL) {
|
|
if (ai->ai_canonname == NULL) {
|
|
if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
|
|
sizeof host, NULL, 0, ninumeric) != 0)
|
|
strlcpy(host, "?", sizeof(host));
|
|
ai->ai_canonname = estrdup(host);
|
|
ai->ai_flags |= AI_CANONNAME;
|
|
} else
|
|
ai->ai_flags &= ~AI_CANONNAME;
|
|
if (debug)
|
|
(void)fprintf(stderr, "got host %s\n", ai->ai_canonname);
|
|
ai = ai->ai_next;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Free up an exports list component
|
|
*/
|
|
static void
|
|
free_exp(ep)
|
|
struct exportlist *ep;
|
|
{
|
|
|
|
if (ep->ex_defdir) {
|
|
free_host(ep->ex_defdir->dp_hosts);
|
|
free(ep->ex_defdir);
|
|
}
|
|
if (ep->ex_fsdir)
|
|
free(ep->ex_fsdir);
|
|
if (ep->ex_indexfile)
|
|
free(ep->ex_indexfile);
|
|
free_dir(ep->ex_dirl);
|
|
free(ep);
|
|
}
|
|
|
|
/*
|
|
* Free hosts.
|
|
*/
|
|
static void
|
|
free_host(hp)
|
|
struct hostlist *hp;
|
|
{
|
|
struct hostlist *hp2;
|
|
|
|
while (hp) {
|
|
hp2 = hp;
|
|
hp = hp->ht_next;
|
|
free(hp2);
|
|
}
|
|
}
|
|
|
|
static struct hostlist *
|
|
get_ht()
|
|
{
|
|
struct hostlist *hp;
|
|
|
|
hp = emalloc(sizeof(struct hostlist));
|
|
hp->ht_next = NULL;
|
|
hp->ht_flag = 0;
|
|
return (hp);
|
|
}
|
|
|
|
#ifdef ISO
|
|
/*
|
|
* Translate an iso address.
|
|
*/
|
|
static int
|
|
get_isoaddr(line, lineno, cp, grp)
|
|
const char *line;
|
|
size_t lineno;
|
|
char *cp;
|
|
struct grouplist *grp;
|
|
{
|
|
struct iso_addr *isop;
|
|
struct sockaddr_iso *isoaddr;
|
|
|
|
if (grp->gr_type != GT_NULL) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: Bad netgroup type for iso addr %s",
|
|
line, (unsigned long)lineno, cp);
|
|
return (1);
|
|
}
|
|
if ((isop = iso_addr(cp)) == NULL) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: Bad iso addr %s",
|
|
line, (unsigned long)lineno, cp);
|
|
return (1);
|
|
}
|
|
isoaddr = emalloc(sizeof(struct sockaddr_iso));
|
|
(void)memset(isoaddr, 0, sizeof(struct sockaddr_iso));
|
|
(void)memcpy(&isoaddr->siso_addr, isop, sizeof(struct iso_addr));
|
|
isoaddr->siso_len = sizeof(struct sockaddr_iso);
|
|
isoaddr->siso_family = AF_ISO;
|
|
grp->gr_type = GT_ISO;
|
|
grp->gr_ptr.gt_isoaddr = isoaddr;
|
|
return (0);
|
|
}
|
|
#endif /* ISO */
|
|
|
|
/*
|
|
* error checked malloc and strdup
|
|
*/
|
|
static void *
|
|
emalloc(n)
|
|
size_t n;
|
|
{
|
|
void *ptr = malloc(n);
|
|
|
|
if (ptr == NULL) {
|
|
syslog(LOG_ERR, "%m");
|
|
exit(2);
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
static char *
|
|
estrdup(s)
|
|
const char *s;
|
|
{
|
|
char *n = strdup(s);
|
|
|
|
if (n == NULL) {
|
|
syslog(LOG_ERR, "%m");
|
|
exit(2);
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/*
|
|
* Do the nfssvc syscall to push the export info into the kernel.
|
|
*/
|
|
static int
|
|
do_nfssvc(line, lineno, ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
|
|
const char *line;
|
|
size_t lineno;
|
|
struct exportlist *ep;
|
|
struct grouplist *grp;
|
|
int exflags;
|
|
struct uucred *anoncrp;
|
|
char *dirp;
|
|
int dirplen;
|
|
struct statvfs *fsb;
|
|
{
|
|
struct sockaddr *addrp;
|
|
struct sockaddr_storage ss;
|
|
struct addrinfo *ai;
|
|
int addrlen;
|
|
char *cp = NULL;
|
|
int done;
|
|
char savedc = '\0';
|
|
struct export_args export;
|
|
|
|
export.ex_flags = exflags;
|
|
export.ex_anon = *anoncrp;
|
|
export.ex_indexfile = ep->ex_indexfile;
|
|
if (grp->gr_type == GT_HOST) {
|
|
ai = grp->gr_ptr.gt_addrinfo;
|
|
addrp = ai->ai_addr;
|
|
addrlen = ai->ai_addrlen;
|
|
} else {
|
|
addrp = NULL;
|
|
ai = NULL; /* XXXGCC -Wuninitialized */
|
|
addrlen = 0; /* XXXGCC -Wuninitialized */
|
|
}
|
|
done = FALSE;
|
|
while (!done) {
|
|
switch (grp->gr_type) {
|
|
case GT_HOST:
|
|
if (addrp != NULL && addrp->sa_family == AF_INET6 &&
|
|
have_v6 == 0)
|
|
goto skip;
|
|
export.ex_addr = addrp;
|
|
export.ex_addrlen = addrlen;
|
|
export.ex_masklen = 0;
|
|
break;
|
|
case GT_NET:
|
|
export.ex_addr = (struct sockaddr *)
|
|
&grp->gr_ptr.gt_net.nt_net;
|
|
if (export.ex_addr->sa_family == AF_INET6 &&
|
|
have_v6 == 0)
|
|
goto skip;
|
|
export.ex_addrlen = export.ex_addr->sa_len;
|
|
memset(&ss, 0, sizeof ss);
|
|
ss.ss_family = export.ex_addr->sa_family;
|
|
ss.ss_len = export.ex_addr->sa_len;
|
|
if (allones(&ss, grp->gr_ptr.gt_net.nt_len) != 0) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: Bad network flag",
|
|
line, (unsigned long)lineno);
|
|
if (cp)
|
|
*cp = savedc;
|
|
return (1);
|
|
}
|
|
export.ex_mask = (struct sockaddr *)&ss;
|
|
export.ex_masklen = ss.ss_len;
|
|
break;
|
|
#ifdef ISO
|
|
case GT_ISO:
|
|
export.ex_addr =
|
|
(struct sockaddr *) grp->gr_ptr.gt_isoaddr;
|
|
export.ex_addrlen =
|
|
sizeof(struct sockaddr_iso);
|
|
export.ex_masklen = 0;
|
|
break;
|
|
#endif /* ISO */
|
|
default:
|
|
syslog(LOG_ERR, "\"%s\", line %ld: Bad netgroup type",
|
|
line, (unsigned long)lineno);
|
|
if (cp)
|
|
*cp = savedc;
|
|
return (1);
|
|
};
|
|
|
|
/*
|
|
* XXX:
|
|
* Maybe I should just use the fsb->f_mntonname path instead
|
|
* of looping back up the dirp to the mount point??
|
|
*/
|
|
for (;;) {
|
|
struct mountd_exports_list mel;
|
|
|
|
mel.mel_path = dirp;
|
|
mel.mel_nexports = 1;
|
|
mel.mel_exports = &export;
|
|
|
|
if (nfssvc(NFSSVC_SETEXPORTSLIST, &mel) != -1)
|
|
break;
|
|
|
|
if (cp)
|
|
*cp-- = savedc;
|
|
else
|
|
cp = dirp + dirplen - 1;
|
|
if (errno == EPERM) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: Can't change attributes for %s to %s: %m",
|
|
line, (unsigned long)lineno,
|
|
dirp, (grp->gr_type == GT_HOST) ?
|
|
grp->gr_ptr.gt_addrinfo->ai_canonname :
|
|
(grp->gr_type == GT_NET) ?
|
|
grp->gr_ptr.gt_net.nt_name :
|
|
"Unknown");
|
|
return (1);
|
|
}
|
|
if (opt_flags & OP_ALLDIRS) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: Could not remount %s: %m",
|
|
line, (unsigned long)lineno,
|
|
dirp);
|
|
return (1);
|
|
}
|
|
/* back up over the last component */
|
|
while (*cp == '/' && cp > dirp)
|
|
cp--;
|
|
while (*(cp - 1) != '/' && cp > dirp)
|
|
cp--;
|
|
if (cp == dirp) {
|
|
if (debug)
|
|
(void)fprintf(stderr, "mnt unsucc\n");
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: Can't export %s: %m",
|
|
line, (unsigned long)lineno, dirp);
|
|
return (1);
|
|
}
|
|
savedc = *cp;
|
|
*cp = '\0';
|
|
}
|
|
skip:
|
|
if (addrp) {
|
|
ai = ai->ai_next;
|
|
if (ai == NULL)
|
|
done = TRUE;
|
|
else {
|
|
addrp = ai->ai_addr;
|
|
addrlen = ai->ai_addrlen;
|
|
}
|
|
} else
|
|
done = TRUE;
|
|
}
|
|
if (cp)
|
|
*cp = savedc;
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Translate a net address.
|
|
*/
|
|
static int
|
|
get_net(cp, net, maskflg)
|
|
char *cp;
|
|
struct netmsk *net;
|
|
int maskflg;
|
|
{
|
|
struct netent *np;
|
|
char *name, *p, *prefp;
|
|
struct sockaddr_in sin, *sinp;
|
|
struct sockaddr *sa;
|
|
struct addrinfo hints, *ai = NULL;
|
|
char netname[NI_MAXHOST];
|
|
long preflen;
|
|
int ecode;
|
|
|
|
(void)memset(&sin, 0, sizeof(sin));
|
|
if ((opt_flags & OP_MASKLEN) && !maskflg) {
|
|
p = strchr(cp, '/');
|
|
*p = '\0';
|
|
prefp = p + 1;
|
|
} else {
|
|
p = NULL; /* XXXGCC -Wuninitialized */
|
|
prefp = NULL; /* XXXGCC -Wuninitialized */
|
|
}
|
|
|
|
if ((np = getnetbyname(cp)) != NULL) {
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_len = sizeof sin;
|
|
sin.sin_addr = inet_makeaddr(np->n_net, 0);
|
|
sa = (struct sockaddr *)&sin;
|
|
} else if (isdigit((unsigned char)*cp)) {
|
|
memset(&hints, 0, sizeof hints);
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
if (getaddrinfo(cp, NULL, &hints, &ai) != 0) {
|
|
/*
|
|
* If getaddrinfo() failed, try the inet4 network
|
|
* notation with less than 3 dots.
|
|
*/
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_len = sizeof sin;
|
|
sin.sin_addr = inet_makeaddr(inet_network(cp),0);
|
|
if (debug)
|
|
fprintf(stderr, "get_net: v4 addr %x\n",
|
|
sin.sin_addr.s_addr);
|
|
sa = (struct sockaddr *)&sin;
|
|
} else
|
|
sa = ai->ai_addr;
|
|
} else if (isxdigit((unsigned char)*cp) || *cp == ':') {
|
|
memset(&hints, 0, sizeof hints);
|
|
hints.ai_family = AF_UNSPEC;
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
|
|
sa = ai->ai_addr;
|
|
else
|
|
goto fail;
|
|
} else
|
|
goto fail;
|
|
|
|
/*
|
|
* Only allow /pref notation for v6 addresses.
|
|
*/
|
|
if (sa->sa_family == AF_INET6 && (!(opt_flags & OP_MASKLEN) || maskflg))
|
|
return 1;
|
|
|
|
ecode = getnameinfo(sa, sa->sa_len, netname, sizeof netname,
|
|
NULL, 0, ninumeric);
|
|
if (ecode != 0)
|
|
goto fail;
|
|
|
|
if (maskflg)
|
|
net->nt_len = countones(sa);
|
|
else {
|
|
if (opt_flags & OP_MASKLEN) {
|
|
errno = 0;
|
|
preflen = strtol(prefp, NULL, 10);
|
|
if (preflen == LONG_MIN && errno == ERANGE)
|
|
goto fail;
|
|
net->nt_len = (int)preflen;
|
|
*p = '/';
|
|
}
|
|
|
|
if (np)
|
|
name = np->n_name;
|
|
else {
|
|
if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
|
|
NULL, 0, ninumeric) != 0)
|
|
strlcpy(netname, "?", sizeof(netname));
|
|
name = netname;
|
|
}
|
|
net->nt_name = estrdup(name);
|
|
memcpy(&net->nt_net, sa, sa->sa_len);
|
|
}
|
|
|
|
if (!maskflg && sa->sa_family == AF_INET &&
|
|
!(opt_flags & (OP_MASK|OP_MASKLEN))) {
|
|
sinp = (struct sockaddr_in *)sa;
|
|
if (IN_CLASSA(sinp->sin_addr.s_addr))
|
|
net->nt_len = 8;
|
|
else if (IN_CLASSB(sinp->sin_addr.s_addr))
|
|
net->nt_len = 16;
|
|
else if (IN_CLASSC(sinp->sin_addr.s_addr))
|
|
net->nt_len = 24;
|
|
else if (IN_CLASSD(sinp->sin_addr.s_addr))
|
|
net->nt_len = 28;
|
|
else
|
|
net->nt_len = 32; /* XXX */
|
|
}
|
|
|
|
if (ai)
|
|
freeaddrinfo(ai);
|
|
return 0;
|
|
|
|
fail:
|
|
if (ai)
|
|
freeaddrinfo(ai);
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* Parse out the next white space separated field
|
|
*/
|
|
static void
|
|
nextfield(cp, endcp)
|
|
char **cp;
|
|
char **endcp;
|
|
{
|
|
char *p;
|
|
|
|
p = *cp;
|
|
while (*p == ' ' || *p == '\t')
|
|
p++;
|
|
if (*p == '\n' || *p == '\0')
|
|
*cp = *endcp = p;
|
|
else {
|
|
*cp = p++;
|
|
while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
|
|
p++;
|
|
*endcp = p;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parse a description of a credential.
|
|
*/
|
|
static void
|
|
parsecred(namelist, cr)
|
|
char *namelist;
|
|
struct uucred *cr;
|
|
{
|
|
char *name;
|
|
int cnt;
|
|
char *names;
|
|
struct passwd *pw;
|
|
struct group *gr;
|
|
int ngroups, groups[NGROUPS + 1];
|
|
|
|
/*
|
|
* Set up the unprivileged user.
|
|
*/
|
|
*cr = def_anon;
|
|
/*
|
|
* Get the user's password table entry.
|
|
*/
|
|
names = strsep(&namelist, " \t\n");
|
|
name = strsep(&names, ":");
|
|
if (isdigit((unsigned char)*name) || *name == '-')
|
|
pw = getpwuid(atoi(name));
|
|
else
|
|
pw = getpwnam(name);
|
|
/*
|
|
* Credentials specified as those of a user.
|
|
*/
|
|
if (names == NULL) {
|
|
if (pw == NULL) {
|
|
syslog(LOG_ERR, "Unknown user: %s", name);
|
|
return;
|
|
}
|
|
cr->cr_uid = pw->pw_uid;
|
|
ngroups = NGROUPS + 1;
|
|
if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
|
|
syslog(LOG_ERR, "Too many groups");
|
|
/*
|
|
* Convert from int's to gid_t's and compress out duplicate
|
|
*/
|
|
cr->cr_ngroups = ngroups - 1;
|
|
cr->cr_gid = groups[0];
|
|
for (cnt = 1; cnt < ngroups; cnt++)
|
|
cr->cr_groups[cnt - 1] = groups[cnt];
|
|
return;
|
|
}
|
|
/*
|
|
* Explicit credential specified as a colon separated list:
|
|
* uid:gid:gid:...
|
|
*/
|
|
if (pw != NULL)
|
|
cr->cr_uid = pw->pw_uid;
|
|
else if (isdigit((unsigned char)*name) || *name == '-')
|
|
cr->cr_uid = atoi(name);
|
|
else {
|
|
syslog(LOG_ERR, "Unknown user: %s", name);
|
|
return;
|
|
}
|
|
cr->cr_ngroups = 0;
|
|
while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
|
|
name = strsep(&names, ":");
|
|
if (isdigit((unsigned char)*name) || *name == '-') {
|
|
cr->cr_groups[cr->cr_ngroups++] = atoi(name);
|
|
} else {
|
|
if ((gr = getgrnam(name)) == NULL) {
|
|
syslog(LOG_ERR, "Unknown group: %s", name);
|
|
continue;
|
|
}
|
|
cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
|
|
}
|
|
}
|
|
if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
|
|
syslog(LOG_ERR, "Too many groups");
|
|
}
|
|
|
|
#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
|
|
/*
|
|
* Routines that maintain the remote mounttab
|
|
*/
|
|
static void
|
|
get_mountlist()
|
|
{
|
|
struct mountlist *mlp, **mlpp;
|
|
char *host, *dirp, *cp;
|
|
char str[STRSIZ];
|
|
FILE *mlfile;
|
|
|
|
if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
|
|
syslog(LOG_ERR, "Can't open %s: %m", _PATH_RMOUNTLIST);
|
|
return;
|
|
}
|
|
mlpp = &mlhead;
|
|
while (fgets(str, STRSIZ, mlfile) != NULL) {
|
|
cp = str;
|
|
host = strsep(&cp, " \t\n");
|
|
dirp = strsep(&cp, " \t\n");
|
|
if (host == NULL || dirp == NULL)
|
|
continue;
|
|
mlp = emalloc(sizeof(*mlp));
|
|
(void)strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
|
|
mlp->ml_host[RPCMNT_NAMELEN] = '\0';
|
|
(void)strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
|
|
mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
|
|
mlp->ml_next = NULL;
|
|
*mlpp = mlp;
|
|
mlpp = &mlp->ml_next;
|
|
}
|
|
(void)fclose(mlfile);
|
|
}
|
|
|
|
static int
|
|
del_mlist(hostp, dirp, saddr)
|
|
char *hostp, *dirp;
|
|
struct sockaddr *saddr;
|
|
{
|
|
struct mountlist *mlp, **mlpp;
|
|
struct mountlist *mlp2;
|
|
u_short sport;
|
|
FILE *mlfile;
|
|
int fnd = 0, ret = 0;
|
|
char host[NI_MAXHOST];
|
|
|
|
switch (saddr->sa_family) {
|
|
case AF_INET6:
|
|
sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
|
|
break;
|
|
case AF_INET:
|
|
sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
|
|
break;
|
|
default:
|
|
return -1;
|
|
}
|
|
mlpp = &mlhead;
|
|
mlp = mlhead;
|
|
while (mlp) {
|
|
if (!strcmp(mlp->ml_host, hostp) &&
|
|
(!dirp || !strcmp(mlp->ml_dirp, dirp))) {
|
|
if (!(mlp->ml_flag & DP_NORESMNT) &&
|
|
sport >= IPPORT_RESERVED) {
|
|
if (getnameinfo(saddr, saddr->sa_len, host,
|
|
sizeof host, NULL, 0, ninumeric) != 0)
|
|
strlcpy(host, "?", sizeof(host));
|
|
syslog(LOG_NOTICE,
|
|
"Umount request for %s:%s from %s refused\n",
|
|
mlp->ml_host, mlp->ml_dirp, host);
|
|
ret = -1;
|
|
goto cont;
|
|
}
|
|
fnd = 1;
|
|
mlp2 = mlp;
|
|
*mlpp = mlp = mlp->ml_next;
|
|
free(mlp2);
|
|
} else {
|
|
cont:
|
|
mlpp = &mlp->ml_next;
|
|
mlp = mlp->ml_next;
|
|
}
|
|
}
|
|
if (fnd) {
|
|
if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
|
|
syslog(LOG_ERR, "Can't update %s: %m",
|
|
_PATH_RMOUNTLIST);
|
|
return ret;
|
|
}
|
|
mlp = mlhead;
|
|
while (mlp) {
|
|
(void)fprintf(mlfile, "%s %s\n", mlp->ml_host,
|
|
mlp->ml_dirp);
|
|
mlp = mlp->ml_next;
|
|
}
|
|
(void)fclose(mlfile);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static void
|
|
add_mlist(hostp, dirp, flags)
|
|
char *hostp, *dirp;
|
|
int flags;
|
|
{
|
|
struct mountlist *mlp, **mlpp;
|
|
FILE *mlfile;
|
|
|
|
mlpp = &mlhead;
|
|
mlp = mlhead;
|
|
while (mlp) {
|
|
if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
|
|
return;
|
|
mlpp = &mlp->ml_next;
|
|
mlp = mlp->ml_next;
|
|
}
|
|
mlp = emalloc(sizeof(*mlp));
|
|
strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
|
|
mlp->ml_host[RPCMNT_NAMELEN] = '\0';
|
|
strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
|
|
mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
|
|
mlp->ml_flag = flags;
|
|
mlp->ml_next = NULL;
|
|
*mlpp = mlp;
|
|
if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
|
|
syslog(LOG_ERR, "Can't update %s: %m", _PATH_RMOUNTLIST);
|
|
return;
|
|
}
|
|
(void)fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
|
|
(void)fclose(mlfile);
|
|
}
|
|
|
|
/*
|
|
* This function is called via. SIGTERM when the system is going down.
|
|
* It sends a broadcast RPCMNT_UMNTALL.
|
|
*/
|
|
/* ARGSUSED */
|
|
static void
|
|
send_umntall(n)
|
|
int n;
|
|
{
|
|
(void)clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
|
|
xdr_void, NULL, xdr_void, NULL, (resultproc_t)umntall_each);
|
|
exit(0);
|
|
}
|
|
|
|
static int
|
|
umntall_each(resultsp, raddr)
|
|
caddr_t resultsp;
|
|
struct sockaddr_in *raddr;
|
|
{
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Free up a group list.
|
|
*/
|
|
static void
|
|
free_grp(grp)
|
|
struct grouplist *grp;
|
|
{
|
|
|
|
if (grp->gr_type == GT_HOST) {
|
|
if (grp->gr_ptr.gt_addrinfo != NULL)
|
|
freeaddrinfo(grp->gr_ptr.gt_addrinfo);
|
|
} else if (grp->gr_type == GT_NET) {
|
|
if (grp->gr_ptr.gt_net.nt_name)
|
|
free(grp->gr_ptr.gt_net.nt_name);
|
|
}
|
|
#ifdef ISO
|
|
else if (grp->gr_type == GT_ISO)
|
|
free(grp->gr_ptr.gt_isoaddr);
|
|
#endif
|
|
free(grp);
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
SYSLOG(int pri, const char *fmt,...)
|
|
{
|
|
va_list ap;
|
|
|
|
va_start(ap, fmt);
|
|
|
|
if (debug)
|
|
vfprintf(stderr, fmt, ap);
|
|
else
|
|
vsyslog(pri, fmt, ap);
|
|
|
|
va_end(ap);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* Check options for consistency.
|
|
*/
|
|
static int
|
|
check_options(line, lineno, dp)
|
|
const char *line;
|
|
size_t lineno;
|
|
struct dirlist *dp;
|
|
{
|
|
|
|
if (dp == NULL) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: missing directory list",
|
|
line, (unsigned long)lineno);
|
|
return (1);
|
|
}
|
|
if ((opt_flags & (OP_MAPROOT|OP_MAPALL)) == (OP_MAPROOT|OP_MAPALL) ||
|
|
(opt_flags & (OP_MAPROOT|OP_KERB)) == (OP_MAPROOT|OP_KERB) ||
|
|
(opt_flags & (OP_MAPALL|OP_KERB)) == (OP_MAPALL|OP_KERB)) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: -mapall, -maproot and -kerb mutually exclusive",
|
|
line, (unsigned long)lineno);
|
|
return (1);
|
|
}
|
|
if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
|
|
syslog(LOG_ERR, "\"%s\", line %ld: -mask requires -net",
|
|
line, (unsigned long)lineno);
|
|
return (1);
|
|
}
|
|
if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN) != 0) {
|
|
syslog(LOG_ERR, "\"%s\", line %ld: /pref and -mask mutually"
|
|
" exclusive",
|
|
line, (unsigned long)lineno);
|
|
return (1);
|
|
}
|
|
if ((opt_flags & (OP_NET|OP_ISO)) == (OP_NET|OP_ISO)) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: -net and -iso mutually exclusive",
|
|
line, (unsigned long)lineno);
|
|
return (1);
|
|
}
|
|
if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: -alldir has multiple directories",
|
|
line, (unsigned long)lineno);
|
|
return (1);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Check an absolute directory path for any symbolic links. Return true
|
|
* if no symbolic links are found.
|
|
*/
|
|
static int
|
|
check_dirpath(line, lineno, dirp)
|
|
const char *line;
|
|
size_t lineno;
|
|
char *dirp;
|
|
{
|
|
char *cp;
|
|
struct stat sb;
|
|
char *file = "";
|
|
|
|
for (cp = dirp + 1; *cp; cp++) {
|
|
if (*cp == '/') {
|
|
*cp = '\0';
|
|
if (lstat(dirp, &sb) == -1)
|
|
goto bad;
|
|
if (!S_ISDIR(sb.st_mode))
|
|
goto bad1;
|
|
*cp = '/';
|
|
}
|
|
}
|
|
|
|
cp = NULL;
|
|
if (lstat(dirp, &sb) == -1)
|
|
goto bad;
|
|
|
|
if (!S_ISDIR(sb.st_mode) && !S_ISREG(sb.st_mode)) {
|
|
file = " file or a";
|
|
goto bad1;
|
|
}
|
|
|
|
return 1;
|
|
|
|
bad:
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: lstat for `%s' failed: %m",
|
|
line, (unsigned long)lineno, dirp);
|
|
if (cp)
|
|
*cp = '/';
|
|
return 0;
|
|
|
|
bad1:
|
|
syslog(LOG_ERR,
|
|
"\"%s\", line %ld: `%s' is not a%s directory",
|
|
line, (unsigned long)lineno, dirp, file);
|
|
if (cp)
|
|
*cp = '/';
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
bind_resv_port(int sock, sa_family_t family, in_port_t port)
|
|
{
|
|
struct sockaddr *sa;
|
|
struct sockaddr_in sasin;
|
|
struct sockaddr_in6 sasin6;
|
|
|
|
switch (family) {
|
|
case AF_INET:
|
|
(void)memset(&sasin, 0, sizeof(sasin));
|
|
sasin.sin_len = sizeof(sasin);
|
|
sasin.sin_family = family;
|
|
sasin.sin_port = htons(port);
|
|
sa = (struct sockaddr *)(void *)&sasin;
|
|
break;
|
|
case AF_INET6:
|
|
(void)memset(&sasin6, 0, sizeof(sasin6));
|
|
sasin6.sin6_len = sizeof(sasin6);
|
|
sasin6.sin6_family = family;
|
|
sasin6.sin6_port = htons(port);
|
|
sa = (struct sockaddr *)(void *)&sasin6;
|
|
break;
|
|
default:
|
|
syslog(LOG_ERR, "Unsupported address family %d", family);
|
|
return;
|
|
}
|
|
if (bindresvport_sa(sock, sa) == -1)
|
|
syslog(LOG_ERR, "Cannot bind to reserved port %d (%m)", port);
|
|
}
|