|
@@ -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
|