diff options
author | jamie <jamie@FreeBSD.org> | 2009-09-28 18:07:16 +0000 |
---|---|---|
committer | jamie <jamie@FreeBSD.org> | 2009-09-28 18:07:16 +0000 |
commit | 59de460f513833504eb8d74225b0a94e18a340a7 (patch) | |
tree | 3ea189121257cbe17440ad86fe7c29355181ba84 /sys/rpc | |
parent | 102b592620af78c55bb2c13684860b1f2e7e07e2 (diff) | |
download | FreeBSD-src-59de460f513833504eb8d74225b0a94e18a340a7.zip FreeBSD-src-59de460f513833504eb8d74225b0a94e18a340a7.tar.gz |
Set the prison in NFS anon and GSS SVC creds.
Reviewed by: marcel
MFC after: 3 days
Diffstat (limited to 'sys/rpc')
-rw-r--r-- | sys/rpc/rpcsec_gss/svc_rpcsec_gss.c | 1732 |
1 files changed, 369 insertions, 1363 deletions
diff --git a/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c b/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c index 2e7f3c8..4185211 100644 --- a/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c +++ b/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c @@ -1,6 +1,11 @@ /*- - * Copyright (c) 2008 Doug Rabson - * All rights reserved. + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -10,11 +15,14 @@ * 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. + * 4. 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 AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * 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 AUTHOR OR CONTRIBUTORS BE LIABLE + * 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) @@ -22,1465 +30,463 @@ * 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. - */ -/* - svc_rpcsec_gss.c - - Copyright (c) 2000 The Regents of the University of Michigan. - All rights reserved. - - Copyright (c) 2000 Dug Song <dugsong@UMICH.EDU>. - All rights reserved, all wrongs reversed. - - 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 ``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. - - $Id: svc_auth_gss.c,v 1.27 2002/01/15 15:43:00 andros Exp $ + * + * @(#)vfs_subr.c 8.31 (Berkeley) 5/26/95 */ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); #include <sys/param.h> -#include <sys/systm.h> +#include <sys/dirent.h> +#include <sys/domain.h> #include <sys/jail.h> #include <sys/kernel.h> -#include <sys/kobj.h> #include <sys/lock.h> #include <sys/malloc.h> #include <sys/mbuf.h> +#include <sys/mount.h> #include <sys/mutex.h> -#include <sys/proc.h> -#include <sys/sx.h> -#include <sys/ucred.h> - -#include <rpc/rpc.h> -#include <rpc/rpcsec_gss.h> - -#include "rpcsec_gss_int.h" - -static bool_t svc_rpc_gss_wrap(SVCAUTH *, struct mbuf **); -static bool_t svc_rpc_gss_unwrap(SVCAUTH *, struct mbuf **); -static void svc_rpc_gss_release(SVCAUTH *); -static enum auth_stat svc_rpc_gss(struct svc_req *, struct rpc_msg *); -static int rpc_gss_svc_getcred(struct svc_req *, struct ucred **, int *); - -static struct svc_auth_ops svc_auth_gss_ops = { - svc_rpc_gss_wrap, - svc_rpc_gss_unwrap, - svc_rpc_gss_release, -}; - -struct sx svc_rpc_gss_lock; - -struct svc_rpc_gss_callback { - SLIST_ENTRY(svc_rpc_gss_callback) cb_link; - rpc_gss_callback_t cb_callback; -}; -static SLIST_HEAD(svc_rpc_gss_callback_list, svc_rpc_gss_callback) - svc_rpc_gss_callbacks = SLIST_HEAD_INITIALIZER(&svc_rpc_gss_callbacks); - -struct svc_rpc_gss_svc_name { - SLIST_ENTRY(svc_rpc_gss_svc_name) sn_link; - char *sn_principal; - gss_OID sn_mech; - u_int sn_req_time; - gss_cred_id_t sn_cred; - u_int sn_program; - u_int sn_version; -}; -static SLIST_HEAD(svc_rpc_gss_svc_name_list, svc_rpc_gss_svc_name) - svc_rpc_gss_svc_names = SLIST_HEAD_INITIALIZER(&svc_rpc_gss_svc_names); +#include <sys/rwlock.h> +#include <sys/refcount.h> +#include <sys/socket.h> +#include <sys/systm.h> +#include <sys/vnode.h> -enum svc_rpc_gss_client_state { - CLIENT_NEW, /* still authenticating */ - CLIENT_ESTABLISHED, /* context established */ - CLIENT_STALE /* garbage to collect */ -}; +#include <net/radix.h> -#define SVC_RPC_GSS_SEQWINDOW 128 +static MALLOC_DEFINE(M_NETADDR, "export_host", "Export host address structure"); -struct svc_rpc_gss_clientid { - unsigned long ci_hostid; - uint32_t ci_boottime; - uint32_t ci_id; -}; +static void vfs_free_addrlist(struct netexport *nep); +static int vfs_free_netcred(struct radix_node *rn, void *w); +static int vfs_hang_addrlist(struct mount *mp, struct netexport *nep, + struct export_args *argp); +static struct netcred *vfs_export_lookup(struct mount *, struct sockaddr *); -struct svc_rpc_gss_client { - TAILQ_ENTRY(svc_rpc_gss_client) cl_link; - TAILQ_ENTRY(svc_rpc_gss_client) cl_alllink; - volatile u_int cl_refs; - struct sx cl_lock; - struct svc_rpc_gss_clientid cl_id; - time_t cl_expiration; /* when to gc */ - enum svc_rpc_gss_client_state cl_state; /* client state */ - bool_t cl_locked; /* fixed service+qop */ - gss_ctx_id_t cl_ctx; /* context id */ - gss_cred_id_t cl_creds; /* delegated creds */ - gss_name_t cl_cname; /* client name */ - struct svc_rpc_gss_svc_name *cl_sname; /* server name used */ - rpc_gss_rawcred_t cl_rawcred; /* raw credentials */ - rpc_gss_ucred_t cl_ucred; /* unix-style credentials */ - struct ucred *cl_cred; /* kernel-style credentials */ - int cl_rpcflavor; /* RPC pseudo sec flavor */ - bool_t cl_done_callback; /* TRUE after call */ - void *cl_cookie; /* user cookie from callback */ - gid_t cl_gid_storage[NGROUPS]; - gss_OID cl_mech; /* mechanism */ - gss_qop_t cl_qop; /* quality of protection */ - uint32_t cl_seqlast; /* sequence window origin */ - uint32_t cl_seqmask[SVC_RPC_GSS_SEQWINDOW/32]; /* bitmask of seqnums */ +/* + * Network address lookup element + */ +struct netcred { + struct radix_node netc_rnodes[2]; + int netc_exflags; + struct ucred *netc_anon; + int netc_numsecflavors; + int netc_secflavors[MAXSECFLAVORS]; }; -TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client); /* - * This structure holds enough information to unwrap arguments or wrap - * results for a given request. We use the rq_clntcred area for this - * (which is a per-request buffer). + * Network export information */ -struct svc_rpc_gss_cookedcred { - struct svc_rpc_gss_client *cc_client; - rpc_gss_service_t cc_service; - uint32_t cc_seq; +struct netexport { + struct netcred ne_defexported; /* Default export */ + struct radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */ }; -#define CLIENT_HASH_SIZE 256 -#define CLIENT_MAX 128 -struct svc_rpc_gss_client_list svc_rpc_gss_client_hash[CLIENT_HASH_SIZE]; -struct svc_rpc_gss_client_list svc_rpc_gss_clients; -static size_t svc_rpc_gss_client_count; -static uint32_t svc_rpc_gss_next_clientid = 1; - -static void -svc_rpc_gss_init(void *arg) -{ - int i; - - for (i = 0; i < CLIENT_HASH_SIZE; i++) - TAILQ_INIT(&svc_rpc_gss_client_hash[i]); - TAILQ_INIT(&svc_rpc_gss_clients); - svc_auth_reg(RPCSEC_GSS, svc_rpc_gss, rpc_gss_svc_getcred); - sx_init(&svc_rpc_gss_lock, "gsslock"); -} -SYSINIT(svc_rpc_gss_init, SI_SUB_KMEM, SI_ORDER_ANY, svc_rpc_gss_init, NULL); - -bool_t -rpc_gss_set_callback(rpc_gss_callback_t *cb) -{ - struct svc_rpc_gss_callback *scb; - - scb = mem_alloc(sizeof(struct svc_rpc_gss_callback)); - if (!scb) { - _rpc_gss_set_error(RPC_GSS_ER_SYSTEMERROR, ENOMEM); - return (FALSE); - } - scb->cb_callback = *cb; - sx_xlock(&svc_rpc_gss_lock); - SLIST_INSERT_HEAD(&svc_rpc_gss_callbacks, scb, cb_link); - sx_xunlock(&svc_rpc_gss_lock); - - return (TRUE); -} - -void -rpc_gss_clear_callback(rpc_gss_callback_t *cb) -{ - struct svc_rpc_gss_callback *scb; - - sx_xlock(&svc_rpc_gss_lock); - SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { - if (scb->cb_callback.program == cb->program - && scb->cb_callback.version == cb->version - && scb->cb_callback.callback == cb->callback) { - SLIST_REMOVE(&svc_rpc_gss_callbacks, scb, - svc_rpc_gss_callback, cb_link); - sx_xunlock(&svc_rpc_gss_lock); - mem_free(scb, sizeof(*scb)); - return; - } - } - sx_xunlock(&svc_rpc_gss_lock); -} - -static bool_t -rpc_gss_acquire_svc_cred(struct svc_rpc_gss_svc_name *sname) -{ - OM_uint32 maj_stat, min_stat; - gss_buffer_desc namebuf; - gss_name_t name; - gss_OID_set_desc oid_set; - - oid_set.count = 1; - oid_set.elements = sname->sn_mech; - - namebuf.value = (void *) sname->sn_principal; - namebuf.length = strlen(sname->sn_principal); - - maj_stat = gss_import_name(&min_stat, &namebuf, - GSS_C_NT_HOSTBASED_SERVICE, &name); - if (maj_stat != GSS_S_COMPLETE) - return (FALSE); - - if (sname->sn_cred != GSS_C_NO_CREDENTIAL) - gss_release_cred(&min_stat, &sname->sn_cred); - - maj_stat = gss_acquire_cred(&min_stat, name, - sname->sn_req_time, &oid_set, GSS_C_ACCEPT, &sname->sn_cred, - NULL, NULL); - if (maj_stat != GSS_S_COMPLETE) { - gss_release_name(&min_stat, &name); - return (FALSE); - } - gss_release_name(&min_stat, &name); - - return (TRUE); -} - -bool_t -rpc_gss_set_svc_name(const char *principal, const char *mechanism, - u_int req_time, u_int program, u_int version) -{ - struct svc_rpc_gss_svc_name *sname; - gss_OID mech_oid; - - if (!rpc_gss_mech_to_oid(mechanism, &mech_oid)) - return (FALSE); - - sname = mem_alloc(sizeof(*sname)); - if (!sname) - return (FALSE); - sname->sn_principal = strdup(principal, M_RPC); - sname->sn_mech = mech_oid; - sname->sn_req_time = req_time; - sname->sn_cred = GSS_C_NO_CREDENTIAL; - sname->sn_program = program; - sname->sn_version = version; - - if (!rpc_gss_acquire_svc_cred(sname)) { - free(sname->sn_principal, M_RPC); - mem_free(sname, sizeof(*sname)); - return (FALSE); - } - - sx_xlock(&svc_rpc_gss_lock); - SLIST_INSERT_HEAD(&svc_rpc_gss_svc_names, sname, sn_link); - sx_xunlock(&svc_rpc_gss_lock); - - return (TRUE); -} - -void -rpc_gss_clear_svc_name(u_int program, u_int version) -{ - OM_uint32 min_stat; - struct svc_rpc_gss_svc_name *sname; - - sx_xlock(&svc_rpc_gss_lock); - SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { - if (sname->sn_program == program - && sname->sn_version == version) { - SLIST_REMOVE(&svc_rpc_gss_svc_names, sname, - svc_rpc_gss_svc_name, sn_link); - sx_xunlock(&svc_rpc_gss_lock); - gss_release_cred(&min_stat, &sname->sn_cred); - free(sname->sn_principal, M_RPC); - mem_free(sname, sizeof(*sname)); - return; - } - } - sx_xunlock(&svc_rpc_gss_lock); -} - -bool_t -rpc_gss_get_principal_name(rpc_gss_principal_t *principal, - const char *mech, const char *name, const char *node, const char *domain) -{ - OM_uint32 maj_stat, min_stat; - gss_OID mech_oid; - size_t namelen; - gss_buffer_desc buf; - gss_name_t gss_name, gss_mech_name; - rpc_gss_principal_t result; - - if (!rpc_gss_mech_to_oid(mech, &mech_oid)) - return (FALSE); - - /* - * Construct a gss_buffer containing the full name formatted - * as "name/node@domain" where node and domain are optional. - */ - namelen = strlen(name); - if (node) { - namelen += strlen(node) + 1; - } - if (domain) { - namelen += strlen(domain) + 1; - } - - buf.value = mem_alloc(namelen); - buf.length = namelen; - strcpy((char *) buf.value, name); - if (node) { - strcat((char *) buf.value, "/"); - strcat((char *) buf.value, node); - } - if (domain) { - strcat((char *) buf.value, "@"); - strcat((char *) buf.value, domain); - } - - /* - * Convert that to a gss_name_t and then convert that to a - * mechanism name in the selected mechanism. - */ - maj_stat = gss_import_name(&min_stat, &buf, - GSS_C_NT_USER_NAME, &gss_name); - mem_free(buf.value, buf.length); - if (maj_stat != GSS_S_COMPLETE) { - rpc_gss_log_status("gss_import_name", mech_oid, maj_stat, min_stat); - return (FALSE); - } - maj_stat = gss_canonicalize_name(&min_stat, gss_name, mech_oid, - &gss_mech_name); - if (maj_stat != GSS_S_COMPLETE) { - rpc_gss_log_status("gss_canonicalize_name", mech_oid, maj_stat, - min_stat); - gss_release_name(&min_stat, &gss_name); - return (FALSE); - } - gss_release_name(&min_stat, &gss_name); - - /* - * Export the mechanism name and use that to construct the - * rpc_gss_principal_t result. - */ - maj_stat = gss_export_name(&min_stat, gss_mech_name, &buf); - if (maj_stat != GSS_S_COMPLETE) { - rpc_gss_log_status("gss_export_name", mech_oid, maj_stat, min_stat); - gss_release_name(&min_stat, &gss_mech_name); - return (FALSE); - } - gss_release_name(&min_stat, &gss_mech_name); - - result = mem_alloc(sizeof(int) + buf.length); - if (!result) { - gss_release_buffer(&min_stat, &buf); - return (FALSE); - } - result->len = buf.length; - memcpy(result->name, buf.value, buf.length); - gss_release_buffer(&min_stat, &buf); - - *principal = result; - return (TRUE); -} - -bool_t -rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred, - rpc_gss_ucred_t **ucred, void **cookie) -{ - struct svc_rpc_gss_cookedcred *cc; - struct svc_rpc_gss_client *client; - - if (req->rq_cred.oa_flavor != RPCSEC_GSS) - return (FALSE); - - cc = req->rq_clntcred; - client = cc->cc_client; - if (rcred) - *rcred = &client->cl_rawcred; - if (ucred) - *ucred = &client->cl_ucred; - if (cookie) - *cookie = client->cl_cookie; - return (TRUE); -} - /* - * This simpler interface is used by svc_getcred to copy the cred data - * into a kernel cred structure. + * Build hash lists of net addresses and hang them off the mount point. + * Called by vfs_export() to set up the lists of export addresses. */ static int -rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp) +vfs_hang_addrlist(struct mount *mp, struct netexport *nep, + struct export_args *argp) { - struct ucred *cr; - struct svc_rpc_gss_cookedcred *cc; - struct svc_rpc_gss_client *client; - rpc_gss_ucred_t *uc; - - if (req->rq_cred.oa_flavor != RPCSEC_GSS) - return (FALSE); + register struct netcred *np; + register struct radix_node_head *rnh; + register int i; + struct radix_node *rn; + struct sockaddr *saddr, *smask = 0; + struct domain *dom; + int error; - cc = req->rq_clntcred; - client = cc->cc_client; - - if (flavorp) - *flavorp = client->cl_rpcflavor; - - if (client->cl_cred) { - *crp = crhold(client->cl_cred); - return (TRUE); + /* + * XXX: This routine converts from a `struct xucred' + * (argp->ex_anon) to a `struct ucred' (np->netc_anon). This + * operation is questionable; for example, what should be done + * with fields like cr_uidinfo and cr_prison? Currently, this + * routine does not touch them (leaves them as NULL). + */ + if (argp->ex_anon.cr_version != XUCRED_VERSION) { + vfs_mount_error(mp, "ex_anon.cr_version: %d != %d", + argp->ex_anon.cr_version, XUCRED_VERSION); + return (EINVAL); } - uc = &client->cl_ucred; - cr = client->cl_cred = crget(); - cr->cr_uid = cr->cr_ruid = cr->cr_svuid = uc->uid; - cr->cr_rgid = cr->cr_svgid = uc->gid; - crsetgroups(cr, uc->gidlen, uc->gidlist); - *crp = crhold(cr); - - return (TRUE); -} - -int -rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len) -{ - struct svc_rpc_gss_cookedcred *cc = req->rq_clntcred; - struct svc_rpc_gss_client *client = cc->cc_client; - int want_conf; - OM_uint32 max; - OM_uint32 maj_stat, min_stat; - int result; - - switch (client->cl_rawcred.service) { - case rpc_gss_svc_none: - return (max_tp_unit_len); - break; - - case rpc_gss_svc_default: - case rpc_gss_svc_integrity: - want_conf = FALSE; - break; - - case rpc_gss_svc_privacy: - want_conf = TRUE; - break; - - default: + if (argp->ex_addrlen == 0) { + if (mp->mnt_flag & MNT_DEFEXPORTED) { + vfs_mount_error(mp, + "MNT_DEFEXPORTED already set for mount %p", mp); + return (EPERM); + } + np = &nep->ne_defexported; + np->netc_exflags = argp->ex_flags; + np->netc_anon = crget(); + np->netc_anon->cr_uid = argp->ex_anon.cr_uid; + crsetgroups(np->netc_anon, argp->ex_anon.cr_ngroups, + argp->ex_anon.cr_groups); + np->netc_anon->cr_prison = &prison0; + prison_hold(np->netc_anon->cr_prison); + np->netc_numsecflavors = argp->ex_numsecflavors; + bcopy(argp->ex_secflavors, np->netc_secflavors, + sizeof(np->netc_secflavors)); + MNT_ILOCK(mp); + mp->mnt_flag |= MNT_DEFEXPORTED; + MNT_IUNLOCK(mp); return (0); } - maj_stat = gss_wrap_size_limit(&min_stat, client->cl_ctx, want_conf, - client->cl_qop, max_tp_unit_len, &max); - - if (maj_stat == GSS_S_COMPLETE) { - result = (int) max; - if (result < 0) - result = 0; - return (result); - } else { - rpc_gss_log_status("gss_wrap_size_limit", client->cl_mech, - maj_stat, min_stat); - return (0); +#if MSIZE <= 256 + if (argp->ex_addrlen > MLEN) { + vfs_mount_error(mp, "ex_addrlen %d is greater than %d", + argp->ex_addrlen, MLEN); + return (EINVAL); } -} - -static struct svc_rpc_gss_client * -svc_rpc_gss_find_client(struct svc_rpc_gss_clientid *id) -{ - struct svc_rpc_gss_client *client; - struct svc_rpc_gss_client_list *list; - unsigned long hostid; - - rpc_gss_log_debug("in svc_rpc_gss_find_client(%d)", id->ci_id); - - getcredhostid(curthread->td_ucred, &hostid); - if (id->ci_hostid != hostid || id->ci_boottime != boottime.tv_sec) - return (NULL); +#endif - list = &svc_rpc_gss_client_hash[id->ci_id % CLIENT_HASH_SIZE]; - sx_xlock(&svc_rpc_gss_lock); - TAILQ_FOREACH(client, list, cl_link) { - if (client->cl_id.ci_id == id->ci_id) { - /* - * Move this client to the front of the LRU - * list. - */ - TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); - TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, - cl_alllink); - refcount_acquire(&client->cl_refs); - break; + i = sizeof(struct netcred) + argp->ex_addrlen + argp->ex_masklen; + np = (struct netcred *) malloc(i, M_NETADDR, M_WAITOK | M_ZERO); + saddr = (struct sockaddr *) (np + 1); + if ((error = copyin(argp->ex_addr, saddr, argp->ex_addrlen))) + goto out; + if (saddr->sa_family == AF_UNSPEC || saddr->sa_family > AF_MAX) { + error = EINVAL; + vfs_mount_error(mp, "Invalid saddr->sa_family: %d"); + goto out; + } + if (saddr->sa_len > argp->ex_addrlen) + saddr->sa_len = argp->ex_addrlen; + if (argp->ex_masklen) { + smask = (struct sockaddr *)((caddr_t)saddr + argp->ex_addrlen); + error = copyin(argp->ex_mask, smask, argp->ex_masklen); + if (error) + goto out; + if (smask->sa_len > argp->ex_masklen) + smask->sa_len = argp->ex_masklen; + } + i = saddr->sa_family; + if ((rnh = nep->ne_rtable[i]) == NULL) { + /* + * Seems silly to initialize every AF when most are not used, + * do so on demand here + */ + for (dom = domains; dom; dom = dom->dom_next) { + KASSERT(((i == AF_INET) || (i == AF_INET6)), + ("unexpected protocol in vfs_hang_addrlist")); + if (dom->dom_family == i && dom->dom_rtattach) { + /* + * XXX MRT + * The INET and INET6 domains know the + * offset already. We don't need to send it + * So we just use it as a flag to say that + * we are or are not setting up a real routing + * table. Only IP and IPV6 need have this + * be 0 so all other protocols can stay the + * same (ABI compatible). + */ + dom->dom_rtattach( + (void **) &nep->ne_rtable[i], 0); + break; + } + } + if ((rnh = nep->ne_rtable[i]) == NULL) { + error = ENOBUFS; + vfs_mount_error(mp, "%s %s %d", + "Unable to initialize radix node head ", + "for address family", i); + goto out; } } - sx_xunlock(&svc_rpc_gss_lock); - - return (client); -} - -static struct svc_rpc_gss_client * -svc_rpc_gss_create_client(void) -{ - struct svc_rpc_gss_client *client; - struct svc_rpc_gss_client_list *list; - unsigned long hostid; - - rpc_gss_log_debug("in svc_rpc_gss_create_client()"); - - client = mem_alloc(sizeof(struct svc_rpc_gss_client)); - memset(client, 0, sizeof(struct svc_rpc_gss_client)); - refcount_init(&client->cl_refs, 1); - sx_init(&client->cl_lock, "GSS-client"); - getcredhostid(curthread->td_ucred, &hostid); - client->cl_id.ci_hostid = hostid; - client->cl_id.ci_boottime = boottime.tv_sec; - client->cl_id.ci_id = svc_rpc_gss_next_clientid++; - list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE]; - sx_xlock(&svc_rpc_gss_lock); - TAILQ_INSERT_HEAD(list, client, cl_link); - TAILQ_INSERT_HEAD(&svc_rpc_gss_clients, client, cl_alllink); - svc_rpc_gss_client_count++; - sx_xunlock(&svc_rpc_gss_lock); - - /* - * Start the client off with a short expiration time. We will - * try to get a saner value from the client creds later. - */ - client->cl_state = CLIENT_NEW; - client->cl_locked = FALSE; - client->cl_expiration = time_uptime + 5*60; - - return (client); -} - -static void -svc_rpc_gss_destroy_client(struct svc_rpc_gss_client *client) -{ - OM_uint32 min_stat; - - rpc_gss_log_debug("in svc_rpc_gss_destroy_client()"); - - if (client->cl_ctx) - gss_delete_sec_context(&min_stat, - &client->cl_ctx, GSS_C_NO_BUFFER); - - if (client->cl_cname) - gss_release_name(&min_stat, &client->cl_cname); - - if (client->cl_rawcred.client_principal) - mem_free(client->cl_rawcred.client_principal, - sizeof(*client->cl_rawcred.client_principal) - + client->cl_rawcred.client_principal->len); - - if (client->cl_cred) - crfree(client->cl_cred); - - sx_destroy(&client->cl_lock); - mem_free(client, sizeof(*client)); + RADIX_NODE_HEAD_LOCK(rnh); + rn = (*rnh->rnh_addaddr)(saddr, smask, rnh, np->netc_rnodes); + RADIX_NODE_HEAD_UNLOCK(rnh); + if (rn == NULL || np != (struct netcred *)rn) { /* already exists */ + error = EPERM; + vfs_mount_error(mp, "Invalid radix node head, rn: %p %p", + rn, np); + goto out; + } + np->netc_exflags = argp->ex_flags; + np->netc_anon = crget(); + np->netc_anon->cr_uid = argp->ex_anon.cr_uid; + crsetgroups(np->netc_anon, argp->ex_anon.cr_ngroups, + np->netc_anon->cr_groups); + np->netc_anon->cr_prison = &prison0; + prison_hold(np->netc_anon->cr_prison); + np->netc_numsecflavors = argp->ex_numsecflavors; + bcopy(argp->ex_secflavors, np->netc_secflavors, + sizeof(np->netc_secflavors)); + return (0); +out: + free(np, M_NETADDR); + return (error); } -/* - * Drop a reference to a client and free it if that was the last reference. - */ -static void -svc_rpc_gss_release_client(struct svc_rpc_gss_client *client) +/* Helper for vfs_free_addrlist. */ +/* ARGSUSED */ +static int +vfs_free_netcred(struct radix_node *rn, void *w) { - - if (!refcount_release(&client->cl_refs)) - return; - svc_rpc_gss_destroy_client(client); + struct radix_node_head *rnh = (struct radix_node_head *) w; + struct ucred *cred; + + (*rnh->rnh_deladdr) (rn->rn_key, rn->rn_mask, rnh); + cred = ((struct netcred *)rn)->netc_anon; + if (cred != NULL) + crfree(cred); + free(rn, M_NETADDR); + return (0); } /* - * Remove a client from our global lists and free it if we can. + * Free the net address hash lists that are hanging off the mount points. */ static void -svc_rpc_gss_forget_client(struct svc_rpc_gss_client *client) -{ - struct svc_rpc_gss_client_list *list; - - list = &svc_rpc_gss_client_hash[client->cl_id.ci_id % CLIENT_HASH_SIZE]; - sx_xlock(&svc_rpc_gss_lock); - TAILQ_REMOVE(list, client, cl_link); - TAILQ_REMOVE(&svc_rpc_gss_clients, client, cl_alllink); - svc_rpc_gss_client_count--; - sx_xunlock(&svc_rpc_gss_lock); - svc_rpc_gss_release_client(client); -} - -static void -svc_rpc_gss_timeout_clients(void) +vfs_free_addrlist(struct netexport *nep) { - struct svc_rpc_gss_client *client; - struct svc_rpc_gss_client *nclient; - time_t now = time_uptime; - - rpc_gss_log_debug("in svc_rpc_gss_timeout_clients()"); - - /* - * First enforce the max client limit. We keep - * svc_rpc_gss_clients in LRU order. - */ - while (svc_rpc_gss_client_count > CLIENT_MAX) - svc_rpc_gss_forget_client(TAILQ_LAST(&svc_rpc_gss_clients, - svc_rpc_gss_client_list)); - TAILQ_FOREACH_SAFE(client, &svc_rpc_gss_clients, cl_alllink, nclient) { - if (client->cl_state == CLIENT_STALE - || now > client->cl_expiration) { - rpc_gss_log_debug("expiring client %p", client); - svc_rpc_gss_forget_client(client); + int i; + struct radix_node_head *rnh; + struct ucred *cred; + + for (i = 0; i <= AF_MAX; i++) { + if ((rnh = nep->ne_rtable[i])) { + RADIX_NODE_HEAD_LOCK(rnh); + (*rnh->rnh_walktree) (rnh, vfs_free_netcred, rnh); + RADIX_NODE_HEAD_UNLOCK(rnh); + RADIX_NODE_HEAD_DESTROY(rnh); + free(rnh, M_RTABLE); + nep->ne_rtable[i] = NULL; /* not SMP safe XXX */ } } + cred = nep->ne_defexported.netc_anon; + if (cred != NULL) + crfree(cred); + } -#ifdef DEBUG /* - * OID<->string routines. These are uuuuugly. + * High level function to manipulate export options on a mount point + * and the passed in netexport. + * Struct export_args *argp is the variable used to twiddle options, + * the structure is described in sys/mount.h */ -static OM_uint32 -gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str) +int +vfs_export(struct mount *mp, struct export_args *argp) { - char numstr[128]; - unsigned long number; - int numshift; - size_t string_length; - size_t i; - unsigned char *cp; - char *bp; - - /* Decoded according to krb5/gssapi_krb5.c */ - - /* First determine the size of the string */ - string_length = 0; - number = 0; - numshift = 0; - cp = (unsigned char *) oid->elements; - number = (unsigned long) cp[0]; - sprintf(numstr, "%ld ", number/40); - string_length += strlen(numstr); - sprintf(numstr, "%ld ", number%40); - string_length += strlen(numstr); - for (i=1; i<oid->length; i++) { - if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { - number = (number << 7) | (cp[i] & 0x7f); - numshift += 7; + struct netexport *nep; + int error; + + if (argp->ex_numsecflavors < 0 + || argp->ex_numsecflavors >= MAXSECFLAVORS) + return (EINVAL); + + error = 0; + lockmgr(&mp->mnt_explock, LK_EXCLUSIVE, NULL); + nep = mp->mnt_export; + if (argp->ex_flags & MNT_DELEXPORT) { + if (nep == NULL) { + error = ENOENT; + goto out; } - else { - *minor_status = 0; - return(GSS_S_FAILURE); + if (mp->mnt_flag & MNT_EXPUBLIC) { + vfs_setpublicfs(NULL, NULL, NULL); + MNT_ILOCK(mp); + mp->mnt_flag &= ~MNT_EXPUBLIC; + MNT_IUNLOCK(mp); } - if ((cp[i] & 0x80) == 0) { - sprintf(numstr, "%ld ", number); - string_length += strlen(numstr); - number = 0; - numshift = 0; + vfs_free_addrlist(nep); + mp->mnt_export = NULL; + free(nep, M_MOUNT); + nep = NULL; + MNT_ILOCK(mp); + mp->mnt_flag &= ~(MNT_EXPORTED | MNT_DEFEXPORTED); + MNT_IUNLOCK(mp); + } + if (argp->ex_flags & MNT_EXPORTED) { + if (nep == NULL) { + nep = malloc(sizeof(struct netexport), M_MOUNT, M_WAITOK | M_ZERO); + mp->mnt_export = nep; } - } - /* - * If we get here, we've calculated the length of "n n n ... n ". Add 4 - * here for "{ " and "}\0". - */ - string_length += 4; - if ((bp = (char *) mem_alloc(string_length))) { - strcpy(bp, "{ "); - number = (unsigned long) cp[0]; - sprintf(numstr, "%ld ", number/40); - strcat(bp, numstr); - sprintf(numstr, "%ld ", number%40); - strcat(bp, numstr); - number = 0; - cp = (unsigned char *) oid->elements; - for (i=1; i<oid->length; i++) { - number = (number << 7) | (cp[i] & 0x7f); - if ((cp[i] & 0x80) == 0) { - sprintf(numstr, "%ld ", number); - strcat(bp, numstr); - number = 0; - } + if (argp->ex_flags & MNT_EXPUBLIC) { + if ((error = vfs_setpublicfs(mp, nep, argp)) != 0) + goto out; + MNT_ILOCK(mp); + mp->mnt_flag |= MNT_EXPUBLIC; + MNT_IUNLOCK(mp); } - strcat(bp, "}"); - oid_str->length = strlen(bp)+1; - oid_str->value = (void *) bp; - *minor_status = 0; - return(GSS_S_COMPLETE); + if ((error = vfs_hang_addrlist(mp, nep, argp))) + goto out; + MNT_ILOCK(mp); + mp->mnt_flag |= MNT_EXPORTED; + MNT_IUNLOCK(mp); } - *minor_status = 0; - return(GSS_S_FAILURE); -} -#endif - -static void -svc_rpc_gss_build_ucred(struct svc_rpc_gss_client *client, - const gss_name_t name) -{ - OM_uint32 maj_stat, min_stat; - rpc_gss_ucred_t *uc = &client->cl_ucred; - int numgroups; - - uc->uid = 65534; - uc->gid = 65534; - uc->gidlist = client->cl_gid_storage; - - numgroups = NGROUPS; - maj_stat = gss_pname_to_unix_cred(&min_stat, name, client->cl_mech, - &uc->uid, &uc->gid, &numgroups, &uc->gidlist[0]); - if (GSS_ERROR(maj_stat)) - uc->gidlen = 0; - else - uc->gidlen = numgroups; -} - -static void -svc_rpc_gss_set_flavor(struct svc_rpc_gss_client *client) -{ - static gss_OID_desc krb5_mech_oid = - {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; +out: + lockmgr(&mp->mnt_explock, LK_RELEASE, NULL); /* - * Attempt to translate mech type and service into a - * 'pseudo flavor'. Hardwire in krb5 support for now. + * Once we have executed the vfs_export() command, we do + * not want to keep the "export" option around in the + * options list, since that will cause subsequent MNT_UPDATE + * calls to fail. The export information is saved in + * mp->mnt_export, so we can safely delete the "export" mount option + * here. */ - if (kgss_oid_equal(client->cl_mech, &krb5_mech_oid)) { - switch (client->cl_rawcred.service) { - case rpc_gss_svc_default: - case rpc_gss_svc_none: - client->cl_rpcflavor = RPCSEC_GSS_KRB5; - break; - case rpc_gss_svc_integrity: - client->cl_rpcflavor = RPCSEC_GSS_KRB5I; - break; - case rpc_gss_svc_privacy: - client->cl_rpcflavor = RPCSEC_GSS_KRB5P; - break; - } - } else { - client->cl_rpcflavor = RPCSEC_GSS; - } + vfs_deleteopt(mp->mnt_optnew, "export"); + vfs_deleteopt(mp->mnt_opt, "export"); + return (error); } -static bool_t -svc_rpc_gss_accept_sec_context(struct svc_rpc_gss_client *client, - struct svc_req *rqst, - struct rpc_gss_init_res *gr, - struct rpc_gss_cred *gc) +/* + * Set the publicly exported filesystem (WebNFS). Currently, only + * one public filesystem is possible in the spec (RFC 2054 and 2055) + */ +int +vfs_setpublicfs(struct mount *mp, struct netexport *nep, + struct export_args *argp) { - gss_buffer_desc recv_tok; - gss_OID mech; - OM_uint32 maj_stat = 0, min_stat = 0, ret_flags; - OM_uint32 cred_lifetime; - struct svc_rpc_gss_svc_name *sname; - - rpc_gss_log_debug("in svc_rpc_gss_accept_context()"); - - /* Deserialize arguments. */ - memset(&recv_tok, 0, sizeof(recv_tok)); - - if (!svc_getargs(rqst, - (xdrproc_t) xdr_gss_buffer_desc, - (caddr_t) &recv_tok)) { - client->cl_state = CLIENT_STALE; - return (FALSE); - } + int error; + struct vnode *rvp; + char *cp; /* - * First time round, try all the server names we have until - * one matches. Afterwards, stick with that one. + * mp == NULL -> invalidate the current info, the FS is + * no longer exported. May be called from either vfs_export + * or unmount, so check if it hasn't already been done. */ - sx_xlock(&svc_rpc_gss_lock); - if (!client->cl_sname) { - SLIST_FOREACH(sname, &svc_rpc_gss_svc_names, sn_link) { - if (sname->sn_program == rqst->rq_prog - && sname->sn_version == rqst->rq_vers) { - retry: - gr->gr_major = gss_accept_sec_context( - &gr->gr_minor, - &client->cl_ctx, - sname->sn_cred, - &recv_tok, - GSS_C_NO_CHANNEL_BINDINGS, - &client->cl_cname, - &mech, - &gr->gr_token, - &ret_flags, - &cred_lifetime, - &client->cl_creds); - if (gr->gr_major == - GSS_S_CREDENTIALS_EXPIRED) { - /* - * Either our creds really did - * expire or gssd was - * restarted. - */ - if (rpc_gss_acquire_svc_cred(sname)) - goto retry; - } - client->cl_sname = sname; - break; + if (mp == NULL) { + if (nfs_pub.np_valid) { + nfs_pub.np_valid = 0; + if (nfs_pub.np_index != NULL) { + free(nfs_pub.np_index, M_TEMP); + nfs_pub.np_index = NULL; } } - if (!sname) { - xdr_free((xdrproc_t) xdr_gss_buffer_desc, - (char *) &recv_tok); - sx_xunlock(&svc_rpc_gss_lock); - return (FALSE); - } - } else { - gr->gr_major = gss_accept_sec_context( - &gr->gr_minor, - &client->cl_ctx, - client->cl_sname->sn_cred, - &recv_tok, - GSS_C_NO_CHANNEL_BINDINGS, - &client->cl_cname, - &mech, - &gr->gr_token, - &ret_flags, - &cred_lifetime, - NULL); + return (0); } - sx_xunlock(&svc_rpc_gss_lock); - - xdr_free((xdrproc_t) xdr_gss_buffer_desc, (char *) &recv_tok); /* - * If we get an error from gss_accept_sec_context, send the - * reply anyway so that the client gets a chance to see what - * is wrong. + * Only one allowed at a time. */ - if (gr->gr_major != GSS_S_COMPLETE && - gr->gr_major != GSS_S_CONTINUE_NEEDED) { - rpc_gss_log_status("accept_sec_context", client->cl_mech, - gr->gr_major, gr->gr_minor); - client->cl_state = CLIENT_STALE; - return (TRUE); - } - - gr->gr_handle.value = &client->cl_id; - gr->gr_handle.length = sizeof(client->cl_id); - gr->gr_win = SVC_RPC_GSS_SEQWINDOW; - - /* Save client info. */ - client->cl_mech = mech; - client->cl_qop = GSS_C_QOP_DEFAULT; - client->cl_done_callback = FALSE; - - if (gr->gr_major == GSS_S_COMPLETE) { - gss_buffer_desc export_name; - - /* - * Change client expiration time to be near when the - * client creds expire (or 24 hours if we can't figure - * that out). - */ - if (cred_lifetime == GSS_C_INDEFINITE) - cred_lifetime = time_uptime + 24*60*60; - - client->cl_expiration = time_uptime + cred_lifetime; - - /* - * Fill in cred details in the rawcred structure. - */ - client->cl_rawcred.version = RPCSEC_GSS_VERSION; - rpc_gss_oid_to_mech(mech, &client->cl_rawcred.mechanism); - maj_stat = gss_export_name(&min_stat, client->cl_cname, - &export_name); - if (maj_stat != GSS_S_COMPLETE) { - rpc_gss_log_status("gss_export_name", client->cl_mech, - maj_stat, min_stat); - return (FALSE); - } - client->cl_rawcred.client_principal = - mem_alloc(sizeof(*client->cl_rawcred.client_principal) - + export_name.length); - client->cl_rawcred.client_principal->len = export_name.length; - memcpy(client->cl_rawcred.client_principal->name, - export_name.value, export_name.length); - gss_release_buffer(&min_stat, &export_name); - client->cl_rawcred.svc_principal = - client->cl_sname->sn_principal; - client->cl_rawcred.service = gc->gc_svc; - - /* - * Use gss_pname_to_uid to map to unix creds. For - * kerberos5, this uses krb5_aname_to_localname. - */ - svc_rpc_gss_build_ucred(client, client->cl_cname); - svc_rpc_gss_set_flavor(client); - gss_release_name(&min_stat, &client->cl_cname); - -#ifdef DEBUG - { - gss_buffer_desc mechname; - - gss_oid_to_str(&min_stat, mech, &mechname); - - rpc_gss_log_debug("accepted context for %s with " - "<mech %.*s, qop %d, svc %d>", - client->cl_rawcred.client_principal->name, - mechname.length, (char *)mechname.value, - client->cl_qop, client->rawcred.service); - - gss_release_buffer(&min_stat, &mechname); - } -#endif /* DEBUG */ - } - return (TRUE); -} - -static bool_t -svc_rpc_gss_validate(struct svc_rpc_gss_client *client, struct rpc_msg *msg, - gss_qop_t *qop) -{ - struct opaque_auth *oa; - gss_buffer_desc rpcbuf, checksum; - OM_uint32 maj_stat, min_stat; - gss_qop_t qop_state; - int32_t rpchdr[128 / sizeof(int32_t)]; - int32_t *buf; - - rpc_gss_log_debug("in svc_rpc_gss_validate()"); - - memset(rpchdr, 0, sizeof(rpchdr)); - - /* Reconstruct RPC header for signing (from xdr_callmsg). */ - buf = rpchdr; - IXDR_PUT_LONG(buf, msg->rm_xid); - IXDR_PUT_ENUM(buf, msg->rm_direction); - IXDR_PUT_LONG(buf, msg->rm_call.cb_rpcvers); - IXDR_PUT_LONG(buf, msg->rm_call.cb_prog); - IXDR_PUT_LONG(buf, msg->rm_call.cb_vers); - IXDR_PUT_LONG(buf, msg->rm_call.cb_proc); - oa = &msg->rm_call.cb_cred; - IXDR_PUT_ENUM(buf, oa->oa_flavor); - IXDR_PUT_LONG(buf, oa->oa_length); - if (oa->oa_length) { - memcpy((caddr_t)buf, oa->oa_base, oa->oa_length); - buf += RNDUP(oa->oa_length) / sizeof(int32_t); - } - rpcbuf.value = rpchdr; - rpcbuf.length = (u_char *)buf - (u_char *)rpchdr; - - checksum.value = msg->rm_call.cb_verf.oa_base; - checksum.length = msg->rm_call.cb_verf.oa_length; - - maj_stat = gss_verify_mic(&min_stat, client->cl_ctx, &rpcbuf, &checksum, - &qop_state); - - if (maj_stat != GSS_S_COMPLETE) { - rpc_gss_log_status("gss_verify_mic", client->cl_mech, - maj_stat, min_stat); - client->cl_state = CLIENT_STALE; - return (FALSE); - } - - *qop = qop_state; - return (TRUE); -} - -static bool_t -svc_rpc_gss_nextverf(struct svc_rpc_gss_client *client, - struct svc_req *rqst, u_int seq) -{ - gss_buffer_desc signbuf; - gss_buffer_desc mic; - OM_uint32 maj_stat, min_stat; - uint32_t nseq; - - rpc_gss_log_debug("in svc_rpc_gss_nextverf()"); - - nseq = htonl(seq); - signbuf.value = &nseq; - signbuf.length = sizeof(nseq); + if (nfs_pub.np_valid != 0 && mp != nfs_pub.np_mount) + return (EBUSY); - maj_stat = gss_get_mic(&min_stat, client->cl_ctx, client->cl_qop, - &signbuf, &mic); - - if (maj_stat != GSS_S_COMPLETE) { - rpc_gss_log_status("gss_get_mic", client->cl_mech, maj_stat, min_stat); - client->cl_state = CLIENT_STALE; - return (FALSE); - } - - KASSERT(mic.length <= MAX_AUTH_BYTES, - ("MIC too large for RPCSEC_GSS")); + /* + * Get real filehandle for root of exported FS. + */ + bzero(&nfs_pub.np_handle, sizeof(nfs_pub.np_handle)); + nfs_pub.np_handle.fh_fsid = mp->mnt_stat.f_fsid; - rqst->rq_verf.oa_flavor = RPCSEC_GSS; - rqst->rq_verf.oa_length = mic.length; - bcopy(mic.value, rqst->rq_verf.oa_base, mic.length); + if ((error = VFS_ROOT(mp, LK_EXCLUSIVE, &rvp))) + return (error); - gss_release_buffer(&min_stat, &mic); - - return (TRUE); -} + if ((error = VOP_VPTOFH(rvp, &nfs_pub.np_handle.fh_fid))) + return (error); -static bool_t -svc_rpc_gss_callback(struct svc_rpc_gss_client *client, struct svc_req *rqst) -{ - struct svc_rpc_gss_callback *scb; - rpc_gss_lock_t lock; - void *cookie; - bool_t cb_res; - bool_t result; + vput(rvp); /* - * See if we have a callback for this guy. + * If an indexfile was specified, pull it in. */ - result = TRUE; - SLIST_FOREACH(scb, &svc_rpc_gss_callbacks, cb_link) { - if (scb->cb_callback.program == rqst->rq_prog - && scb->cb_callback.version == rqst->rq_vers) { + if (argp->ex_indexfile != NULL) { + if (nfs_pub.np_index != NULL) + nfs_pub.np_index = malloc(MAXNAMLEN + 1, M_TEMP, + M_WAITOK); + error = copyinstr(argp->ex_indexfile, nfs_pub.np_index, + MAXNAMLEN, (size_t *)0); + if (!error) { /* - * This one matches. Call the callback and see - * if it wants to veto or something. + * Check for illegal filenames. */ - lock.locked = FALSE; - lock.raw_cred = &client->cl_rawcred; - cb_res = scb->cb_callback.callback(rqst, - client->cl_creds, - client->cl_ctx, - &lock, - &cookie); - - if (!cb_res) { - client->cl_state = CLIENT_STALE; - result = FALSE; - break; + for (cp = nfs_pub.np_index; *cp; cp++) { + if (*cp == '/') { + error = EINVAL; + break; + } } - - /* - * The callback accepted the connection - it - * is responsible for freeing client->cl_creds - * now. - */ - client->cl_creds = GSS_C_NO_CREDENTIAL; - client->cl_locked = lock.locked; - client->cl_cookie = cookie; - return (TRUE); } - } - - /* - * Either no callback exists for this program/version or one - * of the callbacks rejected the connection. We just need to - * clean up the delegated client creds, if any. - */ - if (client->cl_creds) { - OM_uint32 min_ver; - gss_release_cred(&min_ver, &client->cl_creds); - } - return (result); -} - -static bool_t -svc_rpc_gss_check_replay(struct svc_rpc_gss_client *client, uint32_t seq) -{ - u_int32_t offset; - int word, bit; - bool_t result; - - sx_xlock(&client->cl_lock); - if (seq <= client->cl_seqlast) { - /* - * The request sequence number is less than - * the largest we have seen so far. If it is - * outside the window or if we have seen a - * request with this sequence before, silently - * discard it. - */ - offset = client->cl_seqlast - seq; - if (offset >= SVC_RPC_GSS_SEQWINDOW) { - result = FALSE; - goto out; - } - word = offset / 32; - bit = offset % 32; - if (client->cl_seqmask[word] & (1 << bit)) { - result = FALSE; - goto out; + if (error) { + free(nfs_pub.np_index, M_TEMP); + nfs_pub.np_index = NULL; + return (error); } } - result = TRUE; -out: - sx_xunlock(&client->cl_lock); - return (result); + nfs_pub.np_mount = mp; + nfs_pub.np_valid = 1; + return (0); } -static void -svc_rpc_gss_update_seq(struct svc_rpc_gss_client *client, uint32_t seq) -{ - int offset, i, word, bit; - uint32_t carry, newcarry; - - sx_xlock(&client->cl_lock); - if (seq > client->cl_seqlast) { - /* - * This request has a sequence number greater - * than any we have seen so far. Advance the - * seq window and set bit zero of the window - * (which corresponds to the new sequence - * number) - */ - offset = seq - client->cl_seqlast; - while (offset > 32) { - for (i = (SVC_RPC_GSS_SEQWINDOW / 32) - 1; - i > 0; i--) { - client->cl_seqmask[i] = client->cl_seqmask[i-1]; - } - client->cl_seqmask[0] = 0; - offset -= 32; - } - carry = 0; - for (i = 0; i < SVC_RPC_GSS_SEQWINDOW / 32; i++) { - newcarry = client->cl_seqmask[i] >> (32 - offset); - client->cl_seqmask[i] = - (client->cl_seqmask[i] << offset) | carry; - carry = newcarry; - } - client->cl_seqmask[0] |= 1; - client->cl_seqlast = seq; - } else { - offset = client->cl_seqlast - seq; - word = offset / 32; - bit = offset % 32; - client->cl_seqmask[word] |= (1 << bit); - } - sx_xunlock(&client->cl_lock); -} - -enum auth_stat -svc_rpc_gss(struct svc_req *rqst, struct rpc_msg *msg) - +/* + * Used by the filesystems to determine if a given network address + * (passed in 'nam') is present in their exports list, returns a pointer + * to struct netcred so that the filesystem can examine it for + * access rights (read/write/etc). + */ +static struct netcred * +vfs_export_lookup(struct mount *mp, struct sockaddr *nam) { - OM_uint32 min_stat; - XDR xdrs; - struct svc_rpc_gss_cookedcred *cc; - struct svc_rpc_gss_client *client; - struct rpc_gss_cred gc; - struct rpc_gss_init_res gr; - gss_qop_t qop; - int call_stat; - enum auth_stat result; - - rpc_gss_log_debug("in svc_rpc_gss()"); - - /* Garbage collect old clients. */ - svc_rpc_gss_timeout_clients(); - - /* Initialize reply. */ - rqst->rq_verf = _null_auth; - - /* Deserialize client credentials. */ - if (rqst->rq_cred.oa_length <= 0) - return (AUTH_BADCRED); - - memset(&gc, 0, sizeof(gc)); - - xdrmem_create(&xdrs, rqst->rq_cred.oa_base, - rqst->rq_cred.oa_length, XDR_DECODE); - - if (!xdr_rpc_gss_cred(&xdrs, &gc)) { - XDR_DESTROY(&xdrs); - return (AUTH_BADCRED); - } - XDR_DESTROY(&xdrs); - - client = NULL; - - /* Check version. */ - if (gc.gc_version != RPCSEC_GSS_VERSION) { - result = AUTH_BADCRED; - goto out; - } - - /* Check the proc and find the client (or create it) */ - if (gc.gc_proc == RPCSEC_GSS_INIT) { - if (gc.gc_handle.length != 0) { - result = AUTH_BADCRED; - goto out; - } - client = svc_rpc_gss_create_client(); - refcount_acquire(&client->cl_refs); - } else { - struct svc_rpc_gss_clientid *p; - if (gc.gc_handle.length != sizeof(*p)) { - result = AUTH_BADCRED; - goto out; - } - p = gc.gc_handle.value; - client = svc_rpc_gss_find_client(p); - if (!client) { - /* - * Can't find the client - we may have - * destroyed it - tell the other side to - * re-authenticate. - */ - result = RPCSEC_GSS_CREDPROBLEM; - goto out; - } - } - cc = rqst->rq_clntcred; - cc->cc_client = client; - cc->cc_service = gc.gc_svc; - cc->cc_seq = gc.gc_seq; - - /* - * The service and sequence number must be ignored for - * RPCSEC_GSS_INIT and RPCSEC_GSS_CONTINUE_INIT. - */ - if (gc.gc_proc != RPCSEC_GSS_INIT - && gc.gc_proc != RPCSEC_GSS_CONTINUE_INIT) { - /* - * Check for sequence number overflow. - */ - if (gc.gc_seq >= MAXSEQ) { - result = RPCSEC_GSS_CTXPROBLEM; - goto out; - } + struct netexport *nep; + register struct netcred *np; + register struct radix_node_head *rnh; + struct sockaddr *saddr; + nep = mp->mnt_export; + if (nep == NULL) + return (NULL); + np = NULL; + if (mp->mnt_flag & MNT_EXPORTED) { /* - * Check for valid service. + * Lookup in the export list first. */ - if (gc.gc_svc != rpc_gss_svc_none && - gc.gc_svc != rpc_gss_svc_integrity && - gc.gc_svc != rpc_gss_svc_privacy) { - result = AUTH_BADCRED; - goto out; - } - } - - /* Handle RPCSEC_GSS control procedure. */ - switch (gc.gc_proc) { - - case RPCSEC_GSS_INIT: - case RPCSEC_GSS_CONTINUE_INIT: - if (rqst->rq_proc != NULLPROC) { - result = AUTH_REJECTEDCRED; - break; - } - - memset(&gr, 0, sizeof(gr)); - if (!svc_rpc_gss_accept_sec_context(client, rqst, &gr, &gc)) { - result = AUTH_REJECTEDCRED; - break; - } - - if (gr.gr_major == GSS_S_COMPLETE) { - /* - * We borrow the space for the call verf to - * pack our reply verf. - */ - rqst->rq_verf = msg->rm_call.cb_verf; - if (!svc_rpc_gss_nextverf(client, rqst, gr.gr_win)) { - result = AUTH_REJECTEDCRED; - break; + if (nam != NULL) { + saddr = nam; + rnh = nep->ne_rtable[saddr->sa_family]; + if (rnh != NULL) { + RADIX_NODE_HEAD_RLOCK(rnh); + np = (struct netcred *) + (*rnh->rnh_matchaddr)(saddr, rnh); + RADIX_NODE_HEAD_RUNLOCK(rnh); + if (np && np->netc_rnodes->rn_flags & RNF_ROOT) + np = NULL; } - } else { - rqst->rq_verf = _null_auth; - } - - call_stat = svc_sendreply(rqst, - (xdrproc_t) xdr_rpc_gss_init_res, - (caddr_t) &gr); - - gss_release_buffer(&min_stat, &gr.gr_token); - - if (!call_stat) { - result = AUTH_FAILED; - break; } - - if (gr.gr_major == GSS_S_COMPLETE) - client->cl_state = CLIENT_ESTABLISHED; - - result = RPCSEC_GSS_NODISPATCH; - break; - - case RPCSEC_GSS_DATA: - case RPCSEC_GSS_DESTROY: - if (!svc_rpc_gss_check_replay(client, gc.gc_seq)) { - result = RPCSEC_GSS_NODISPATCH; - break; - } - - if (!svc_rpc_gss_validate(client, msg, &qop)) { - result = RPCSEC_GSS_CREDPROBLEM; - break; - } - - /* - * We borrow the space for the call verf to pack our - * reply verf. - */ - rqst->rq_verf = msg->rm_call.cb_verf; - if (!svc_rpc_gss_nextverf(client, rqst, gc.gc_seq)) { - result = RPCSEC_GSS_CTXPROBLEM; - break; - } - - svc_rpc_gss_update_seq(client, gc.gc_seq); - /* - * Change the SVCAUTH ops on the request to point at - * our own code so that we can unwrap the arguments - * and wrap the result. The caller will re-set this on - * every request to point to a set of null wrap/unwrap - * methods. Acquire an extra reference to the client - * which will be released by svc_rpc_gss_release() - * after the request has finished processing. + * If no address match, use the default if it exists. */ - refcount_acquire(&client->cl_refs); - rqst->rq_auth.svc_ah_ops = &svc_auth_gss_ops; - rqst->rq_auth.svc_ah_private = cc; - - if (gc.gc_proc == RPCSEC_GSS_DATA) { - /* - * We might be ready to do a callback to the server to - * see if it wants to accept/reject the connection. - */ - sx_xlock(&client->cl_lock); - if (!client->cl_done_callback) { - client->cl_done_callback = TRUE; - client->cl_qop = qop; - client->cl_rawcred.qop = _rpc_gss_num_to_qop( - client->cl_rawcred.mechanism, qop); - if (!svc_rpc_gss_callback(client, rqst)) { - result = AUTH_REJECTEDCRED; - sx_xunlock(&client->cl_lock); - break; - } - } - sx_xunlock(&client->cl_lock); - - /* - * If the server has locked this client to a - * particular service+qop pair, enforce that - * restriction now. - */ - if (client->cl_locked) { - if (client->cl_rawcred.service != gc.gc_svc) { - result = AUTH_FAILED; - break; - } else if (client->cl_qop != qop) { - result = AUTH_BADVERF; - break; - } - } - - /* - * If the qop changed, look up the new qop - * name for rawcred. - */ - if (client->cl_qop != qop) { - client->cl_qop = qop; - client->cl_rawcred.qop = _rpc_gss_num_to_qop( - client->cl_rawcred.mechanism, qop); - } - - /* - * Make sure we use the right service value - * for unwrap/wrap. - */ - if (client->cl_rawcred.service != gc.gc_svc) { - client->cl_rawcred.service = gc.gc_svc; - svc_rpc_gss_set_flavor(client); - } - - result = AUTH_OK; - } else { - if (rqst->rq_proc != NULLPROC) { - result = AUTH_REJECTEDCRED; - break; - } - - call_stat = svc_sendreply(rqst, - (xdrproc_t) xdr_void, (caddr_t) NULL); - - if (!call_stat) { - result = AUTH_FAILED; - break; - } - - svc_rpc_gss_forget_client(client); - - result = RPCSEC_GSS_NODISPATCH; - break; - } - break; - - default: - result = AUTH_BADCRED; - break; + if (np == NULL && mp->mnt_flag & MNT_DEFEXPORTED) + np = &nep->ne_defexported; } -out: - if (client) - svc_rpc_gss_release_client(client); - - xdr_free((xdrproc_t) xdr_rpc_gss_cred, (char *) &gc); - return (result); + return (np); } -static bool_t -svc_rpc_gss_wrap(SVCAUTH *auth, struct mbuf **mp) -{ - struct svc_rpc_gss_cookedcred *cc; - struct svc_rpc_gss_client *client; - - rpc_gss_log_debug("in svc_rpc_gss_wrap()"); - - cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; - client = cc->cc_client; - if (client->cl_state != CLIENT_ESTABLISHED - || cc->cc_service == rpc_gss_svc_none || *mp == NULL) { - return (TRUE); - } - - return (xdr_rpc_gss_wrap_data(mp, - client->cl_ctx, client->cl_qop, - cc->cc_service, cc->cc_seq)); -} +/* + * XXX: This comment comes from the deprecated ufs_check_export() + * XXX: and may not entirely apply, but lacking something better: + * This is the generic part of fhtovp called after the underlying + * filesystem has validated the file handle. + * + * Verify that a host should have access to a filesystem. + */ -static bool_t -svc_rpc_gss_unwrap(SVCAUTH *auth, struct mbuf **mp) +int +vfs_stdcheckexp(struct mount *mp, struct sockaddr *nam, int *extflagsp, + struct ucred **credanonp, int *numsecflavors, int **secflavors) { - struct svc_rpc_gss_cookedcred *cc; - struct svc_rpc_gss_client *client; - - rpc_gss_log_debug("in svc_rpc_gss_unwrap()"); - - cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; - client = cc->cc_client; - if (client->cl_state != CLIENT_ESTABLISHED - || cc->cc_service == rpc_gss_svc_none) { - return (TRUE); - } - - return (xdr_rpc_gss_unwrap_data(mp, - client->cl_ctx, client->cl_qop, - cc->cc_service, cc->cc_seq)); + struct netcred *np; + + lockmgr(&mp->mnt_explock, LK_SHARED, NULL); + np = vfs_export_lookup(mp, nam); + if (np == NULL) { + lockmgr(&mp->mnt_explock, LK_RELEASE, NULL); + *credanonp = NULL; + return (EACCES); + } + *extflagsp = np->netc_exflags; + if ((*credanonp = np->netc_anon) != NULL) + crhold(*credanonp); + if (numsecflavors) + *numsecflavors = np->netc_numsecflavors; + if (secflavors) + *secflavors = np->netc_secflavors; + lockmgr(&mp->mnt_explock, LK_RELEASE, NULL); + return (0); } -static void -svc_rpc_gss_release(SVCAUTH *auth) -{ - struct svc_rpc_gss_cookedcred *cc; - struct svc_rpc_gss_client *client; - - rpc_gss_log_debug("in svc_rpc_gss_release()"); - - cc = (struct svc_rpc_gss_cookedcred *) auth->svc_ah_private; - client = cc->cc_client; - svc_rpc_gss_release_client(client); -} |