From owner-svn-src-all@FreeBSD.ORG Mon Sep 28 18:07:16 2009 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 641FF1065692; Mon, 28 Sep 2009 18:07:16 +0000 (UTC) (envelope-from jamie@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 523C68FC14; Mon, 28 Sep 2009 18:07:16 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id n8SI7Gsx061840; Mon, 28 Sep 2009 18:07:16 GMT (envelope-from jamie@svn.freebsd.org) Received: (from jamie@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id n8SI7GiO061837; Mon, 28 Sep 2009 18:07:16 GMT (envelope-from jamie@svn.freebsd.org) Message-Id: <200909281807.n8SI7GiO061837@svn.freebsd.org> From: Jamie Gritton Date: Mon, 28 Sep 2009 18:07:16 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r197581 - in head/sys: kern rpc/rpcsec_gss X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 28 Sep 2009 18:07:16 -0000 Author: jamie Date: Mon Sep 28 18:07:16 2009 New Revision: 197581 URL: http://svn.freebsd.org/changeset/base/197581 Log: Set the prison in NFS anon and GSS SVC creds. Reviewed by: marcel MFC after: 3 days Modified: head/sys/kern/vfs_export.c head/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c Modified: head/sys/kern/vfs_export.c ============================================================================== --- head/sys/kern/vfs_export.c Mon Sep 28 17:10:27 2009 (r197580) +++ head/sys/kern/vfs_export.c Mon Sep 28 18:07:16 2009 (r197581) @@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$"); #include #include #include +#include #include #include #include @@ -122,6 +123,8 @@ vfs_hang_addrlist(struct mount *mp, stru 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)); @@ -206,6 +209,8 @@ vfs_hang_addrlist(struct mount *mp, stru 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)); Modified: head/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c ============================================================================== --- head/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c Mon Sep 28 17:10:27 2009 (r197580) +++ head/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c Mon Sep 28 18:07:16 2009 (r197581) @@ -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 . - 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 __FBSDID("$FreeBSD$"); #include -#include +#include +#include #include #include -#include #include #include #include +#include #include -#include -#include -#include - -#include -#include - -#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 +#include +#include +#include +#include -enum svc_rpc_gss_client_state { - CLIENT_NEW, /* still authenticating */ - CLIENT_ESTABLISHED, /* context established */ - CLIENT_STALE /* garbage to collect */ -}; +#include -#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); - - 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); - } - - 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: + 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; + + /* + * 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); + } + + 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); + 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); } -static struct svc_rpc_gss_client * -svc_rpc_gss_create_client(void) +/* Helper for vfs_free_addrlist. */ +/* ARGSUSED */ +static int +vfs_free_netcred(struct radix_node *rn, void *w) { - 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); + struct radix_node_head *rnh = (struct radix_node_head *) w; + struct ucred *cred; - /* - * 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)); + (*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); } /* - * Drop a reference to a client and free it if that was the last reference. + * Free the net address hash lists that are hanging off the mount points. */ static void -svc_rpc_gss_release_client(struct svc_rpc_gss_client *client) +vfs_free_addrlist(struct netexport *nep) { + 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); - if (!refcount_release(&client->cl_refs)) - return; - svc_rpc_gss_destroy_client(client); } /* - * Remove a client from our global lists and free it if we can. + * 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 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) +int +vfs_export(struct mount *mp, struct export_args *argp) { - struct svc_rpc_gss_client *client; - struct svc_rpc_gss_client *nclient; - time_t now = time_uptime; + struct netexport *nep; + int error; - 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); + 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; + } + if (mp->mnt_flag & MNT_EXPUBLIC) { + vfs_setpublicfs(NULL, NULL, NULL); + MNT_ILOCK(mp); + mp->mnt_flag &= ~MNT_EXPUBLIC; + MNT_IUNLOCK(mp); + } + 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 (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); } + if ((error = vfs_hang_addrlist(mp, nep, argp))) + goto out; + MNT_ILOCK(mp); + mp->mnt_flag |= MNT_EXPORTED; + MNT_IUNLOCK(mp); } + +out: + lockmgr(&mp->mnt_explock, LK_RELEASE, NULL); + /* + * 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. + */ + vfs_deleteopt(mp->mnt_optnew, "export"); + vfs_deleteopt(mp->mnt_opt, "export"); + return (error); } -#ifdef DEBUG /* - * OID<->string routines. These are uuuuugly. + * Set the publicly exported filesystem (WebNFS). Currently, only + * one public filesystem is possible in the spec (RFC 2054 and 2055) */ -static OM_uint32 -gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str) +int +vfs_setpublicfs(struct mount *mp, struct netexport *nep, + 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; ilength; i++) { - if ( (size_t) (numshift+7) < (sizeof(unsigned long)*8)) { - number = (number << 7) | (cp[i] & 0x7f); - numshift += 7; - } - else { - *minor_status = 0; - return(GSS_S_FAILURE); - } - if ((cp[i] & 0x80) == 0) { - sprintf(numstr, "%ld ", number); - string_length += strlen(numstr); - number = 0; - numshift = 0; - } - } - /* - * 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; *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***