Author: jamie
Date: Mon Sep 28 18:54:26 2009
New Revision: 197583
URL: http://svn.freebsd.org/changeset/base/197583

Log:
  Back out r197581, which replaced this file witk sys/kern/vfs_export.c.
  
  Who knew that "svn export" was an actual command, or that I would have
  vfs_export.c stuck in my mind deep enough to type "export" instead of
  "commit"?
  
  Pointy Hat to:  jamie

Modified:
  head/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c

Modified: head/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c
==============================================================================
--- head/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c    Mon Sep 28 18:32:28 2009        
(r197582)
+++ head/sys/rpc/rpcsec_gss/svc_rpcsec_gss.c    Mon Sep 28 18:54:26 2009        
(r197583)
@@ -1,11 +1,6 @@
 /*-
- * 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.
+ * Copyright (c) 2008 Doug Rabson
+ * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -15,14 +10,11 @@
  * 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 REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR 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)
@@ -30,463 +22,1465 @@
  * 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.
- *
- *     @(#)vfs_subr.c  8.31 (Berkeley) 5/26/95
+ */
+/*
+  svc_rpcsec_gss.c
+  
+  Copyright (c) 2000 The Regents of the University of Michigan.
+  All rights reserved.
+
+  Copyright (c) 2000 Dug Song <dugs...@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 $
  */
 
 #include <sys/cdefs.h>
 __FBSDID("$FreeBSD$");
 
 #include <sys/param.h>
-#include <sys/dirent.h>
-#include <sys/domain.h>
+#include <sys/systm.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/rwlock.h>
-#include <sys/refcount.h>
-#include <sys/socket.h>
-#include <sys/systm.h>
-#include <sys/vnode.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,
+};
 
-#include <net/radix.h>
+struct sx svc_rpc_gss_lock;
 
-static MALLOC_DEFINE(M_NETADDR, "export_host", "Export host address 
structure");
+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);
 
-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_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);
 
-/*
- * 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];
+enum svc_rpc_gss_client_state {
+       CLIENT_NEW,                             /* still authenticating */
+       CLIENT_ESTABLISHED,                     /* context established */
+       CLIENT_STALE                            /* garbage to collect */
 };
 
-/*
- * Network export information
- */
-struct netexport {
-       struct  netcred ne_defexported;               /* Default export */
-       struct  radix_node_head *ne_rtable[AF_MAX+1]; /* Individual exports */
+#define SVC_RPC_GSS_SEQWINDOW  128
+
+struct svc_rpc_gss_clientid {
+       unsigned long           ci_hostid;
+       uint32_t                ci_boottime;
+       uint32_t                ci_id;
 };
 
+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 */
+};
+TAILQ_HEAD(svc_rpc_gss_client_list, svc_rpc_gss_client);
+
 /*
- * 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.
+ * 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).
  */
-static int
-vfs_hang_addrlist(struct mount *mp, struct netexport *nep,
-    struct export_args *argp)
+struct svc_rpc_gss_cookedcred {
+       struct svc_rpc_gss_client *cc_client;
+       rpc_gss_service_t       cc_service;
+       uint32_t                cc_seq;
+};
+
+#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)
 {
-       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);
+       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);
 
-#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);
+       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;
+               }
        }
-#endif
+       sx_xunlock(&svc_rpc_gss_lock);
+}
 
-       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;
+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);
        }
-       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;
+       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);
        }
-       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_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;
                }
        }
-       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;
+       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/n...@domain" where node and domain are optional.
+        */
+       namelen = strlen(name);
+       if (node) {
+               namelen += strlen(node) + 1;
        }
-       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);
+       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);
 }
 
-/* Helper for vfs_free_addrlist. */
-/* ARGSUSED */
-static int
-vfs_free_netcred(struct radix_node *rn, void *w)
+bool_t
+rpc_gss_getcred(struct svc_req *req, rpc_gss_rawcred_t **rcred,
+    rpc_gss_ucred_t **ucred, void **cookie)
 {
-       struct radix_node_head *rnh = (struct radix_node_head *) w;
-       struct ucred *cred;
+       struct svc_rpc_gss_cookedcred *cc;
+       struct svc_rpc_gss_client *client;
+
+       if (req->rq_cred.oa_flavor != RPCSEC_GSS)
+               return (FALSE);
 
-       (*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);
+       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);
 }
 
 /*
- * Free the net address hash lists that are hanging off the mount points.
+ * This simpler interface is used by svc_getcred to copy the cred data
+ * into a kernel cred structure.
  */
-static void
-vfs_free_addrlist(struct netexport *nep)
+static int
+rpc_gss_svc_getcred(struct svc_req *req, struct ucred **crp, int *flavorp)
 {
-       int i;
-       struct radix_node_head *rnh;
-       struct ucred *cred;
+       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);
+       }
 
-       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);
+       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);
 }
 
-/*
- * 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
- */
 int
-vfs_export(struct mount *mp, struct export_args *argp)
+rpc_gss_svc_max_data_length(struct svc_req *req, int max_tp_unit_len)
 {
-       struct netexport *nep;
-       int error;
+       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;
 
-       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);
+       default:
+               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);
+       }
+}
+
+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);
+
+       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;
                }
-               if ((error = vfs_hang_addrlist(mp, nep, argp)))
-                       goto out;
-               MNT_ILOCK(mp);
-               mp->mnt_flag |= MNT_EXPORTED;
-               MNT_IUNLOCK(mp);
        }
+       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);
 
-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);
+        * 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));
 }
 
 /*
- * Set the publicly exported filesystem (WebNFS). Currently, only
- * one public filesystem is possible in the spec (RFC 2054 and 2055)
+ * Drop a reference to a client and free it if that was the last reference.
  */
-int
-vfs_setpublicfs(struct mount *mp, struct netexport *nep,
-    struct export_args *argp)
+static void
+svc_rpc_gss_release_client(struct svc_rpc_gss_client *client)
 {
-       int error;
-       struct vnode *rvp;
-       char *cp;
-
-       /*
-        * 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.
-        */
-       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 (!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.
+ */
+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)
+{
+       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);
+               }
+       }
+}
+
+#ifdef DEBUG
+/*
+ * OID<->string routines.  These are uuuuugly.
+ */
+static OM_uint32
+gss_oid_to_str(OM_uint32 *minor_status, gss_OID oid, gss_buffer_t oid_str)
+{
+       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;
+               }
+               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;
+               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;
                        }
                }
-               return (0);
+               strcat(bp, "}");
+               oid_str->length = strlen(bp)+1;
+               oid_str->value = (void *) bp;
+               *minor_status = 0;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to