RPCSEC_GSS: Share all credential caches on a per-transport basis
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 28 Aug 2013 19:26:25 +0000 (15:26 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Mon, 2 Sep 2013 16:48:40 +0000 (12:48 -0400)
Ensure that all struct rpc_clnt for any given socket/rdma channel
share the same RPCSEC_GSS/krb5,krb5i,krb5p caches.

Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
net/sunrpc/auth_gss/auth_gss.c

index 5ec15bb..dc4b449 100644 (file)
@@ -51,6 +51,7 @@
 #include <linux/sunrpc/rpc_pipe_fs.h>
 #include <linux/sunrpc/gss_api.h>
 #include <asm/uaccess.h>
+#include <linux/hashtable.h>
 
 #include "../netns.h"
 
@@ -71,6 +72,9 @@ static unsigned int gss_expired_cred_retry_delay = GSS_RETRY_EXPIRED;
  * using integrity (two 4-byte integers): */
 #define GSS_VERF_SLACK         100
 
+static DEFINE_HASHTABLE(gss_auth_hash_table, 16);
+static DEFINE_SPINLOCK(gss_auth_hash_lock);
+
 struct gss_pipe {
        struct rpc_pipe_dir_object pdo;
        struct rpc_pipe *pipe;
@@ -81,6 +85,7 @@ struct gss_pipe {
 
 struct gss_auth {
        struct kref kref;
+       struct hlist_node hash;
        struct rpc_auth rpc_auth;
        struct gss_api_mech *mech;
        enum rpc_gss_svc service;
@@ -940,8 +945,8 @@ static void gss_pipe_free(struct gss_pipe *p)
  * NOTE: we have the opportunity to use different
  * parameters based on the input flavor (which must be a pseudoflavor)
  */
-static struct rpc_auth *
-gss_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
+static struct gss_auth *
+gss_create_new(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
 {
        rpc_authflavor_t flavor = args->pseudoflavor;
        struct gss_auth *gss_auth;
@@ -955,6 +960,7 @@ gss_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
                return ERR_PTR(err);
        if (!(gss_auth = kmalloc(sizeof(*gss_auth), GFP_KERNEL)))
                goto out_dec;
+       INIT_HLIST_NODE(&gss_auth->hash);
        gss_auth->target_name = NULL;
        if (args->target_name) {
                gss_auth->target_name = kstrdup(args->target_name, GFP_KERNEL);
@@ -1004,7 +1010,7 @@ gss_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
        }
        gss_auth->gss_pipe[0] = gss_pipe;
 
-       return auth;
+       return gss_auth;
 err_destroy_pipe_1:
        gss_pipe_free(gss_auth->gss_pipe[1]);
 err_destroy_credcache:
@@ -1051,6 +1057,12 @@ gss_destroy(struct rpc_auth *auth)
        dprintk("RPC:       destroying GSS authenticator %p flavor %d\n",
                        auth, auth->au_flavor);
 
+       if (hash_hashed(&gss_auth->hash)) {
+               spin_lock(&gss_auth_hash_lock);
+               hash_del(&gss_auth->hash);
+               spin_unlock(&gss_auth_hash_lock);
+       }
+
        gss_pipe_free(gss_auth->gss_pipe[0]);
        gss_auth->gss_pipe[0] = NULL;
        gss_pipe_free(gss_auth->gss_pipe[1]);
@@ -1060,6 +1072,80 @@ gss_destroy(struct rpc_auth *auth)
        kref_put(&gss_auth->kref, gss_free_callback);
 }
 
+static struct gss_auth *
+gss_auth_find_or_add_hashed(struct rpc_auth_create_args *args,
+               struct rpc_clnt *clnt,
+               struct gss_auth *new)
+{
+       struct gss_auth *gss_auth;
+       unsigned long hashval = (unsigned long)clnt;
+
+       spin_lock(&gss_auth_hash_lock);
+       hash_for_each_possible(gss_auth_hash_table,
+                       gss_auth,
+                       hash,
+                       hashval) {
+               if (gss_auth->rpc_auth.au_flavor != args->pseudoflavor)
+                       continue;
+               if (gss_auth->target_name != args->target_name) {
+                       if (gss_auth->target_name == NULL)
+                               continue;
+                       if (args->target_name == NULL)
+                               continue;
+                       if (strcmp(gss_auth->target_name, args->target_name))
+                               continue;
+               }
+               if (!atomic_inc_not_zero(&gss_auth->rpc_auth.au_count))
+                       continue;
+               goto out;
+       }
+       if (new)
+               hash_add(gss_auth_hash_table, &new->hash, hashval);
+       gss_auth = new;
+out:
+       spin_unlock(&gss_auth_hash_lock);
+       return gss_auth;
+}
+
+static struct gss_auth *
+gss_create_hashed(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
+{
+       struct gss_auth *gss_auth;
+       struct gss_auth *new;
+
+       gss_auth = gss_auth_find_or_add_hashed(args, clnt, NULL);
+       if (gss_auth != NULL)
+               goto out;
+       new = gss_create_new(args, clnt);
+       if (IS_ERR(new))
+               return new;
+       gss_auth = gss_auth_find_or_add_hashed(args, clnt, new);
+       if (gss_auth != new)
+               gss_destroy(&new->rpc_auth);
+out:
+       return gss_auth;
+}
+
+static struct rpc_auth *
+gss_create(struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
+{
+       struct gss_auth *gss_auth;
+       struct rpc_xprt *xprt = rcu_access_pointer(clnt->cl_xprt);
+
+       while (clnt != clnt->cl_parent) {
+               struct rpc_clnt *parent = clnt->cl_parent;
+               /* Find the original parent for this transport */
+               if (rcu_access_pointer(parent->cl_xprt) != xprt)
+                       break;
+               clnt = parent;
+       }
+
+       gss_auth = gss_create_hashed(args, clnt);
+       if (IS_ERR(gss_auth))
+               return ERR_CAST(gss_auth);
+       return &gss_auth->rpc_auth;
+}
+
 /*
  * gss_destroying_context will cause the RPCSEC_GSS to send a NULL RPC call
  * to the server with the GSS control procedure field set to