SUNRPC: Ensure RPCSEC_GSS destroys the security context when freeing a cred
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Tue, 26 Jun 2007 21:04:57 +0000 (17:04 -0400)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Wed, 11 Jul 2007 03:40:37 +0000 (23:40 -0400)
Do so by set the gc_proc field to RPC_GSS_PROC_DESTROY, and then sending a
NULL RPC call.

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

index 982aba697e4dfb1667b4dda272409a09159c81b0..17d460f85f0efdf73370bfa42fa397ca76efd40c 100644 (file)
@@ -57,6 +57,7 @@
 static const struct rpc_authops authgss_ops;
 
 static const struct rpc_credops gss_credops;
+static const struct rpc_credops gss_nullops;
 
 #ifdef RPC_DEBUG
 # define RPCDBG_FACILITY       RPCDBG_AUTH
@@ -695,7 +696,39 @@ gss_destroy(struct rpc_auth *auth)
        kref_put(&gss_auth->kref, gss_free_callback);
 }
 
-/* gss_destroy_cred (and gss_destroy_ctx) are used to clean up after failure
+/*
+ * 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
+ * RPC_GSS_PROC_DESTROY. This should normally cause the server to release
+ * all RPCSEC_GSS state associated with that context.
+ */
+static int
+gss_destroying_context(struct rpc_cred *cred)
+{
+       struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
+       struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
+       struct rpc_task *task;
+
+       if (gss_cred->gc_ctx == NULL ||
+                       gss_cred->gc_ctx->gc_proc == RPC_GSS_PROC_DESTROY)
+               return 0;
+
+       gss_cred->gc_ctx->gc_proc = RPC_GSS_PROC_DESTROY;
+       cred->cr_ops = &gss_nullops;
+
+       /* Take a reference to ensure the cred will be destroyed either
+        * by the RPC call or by the put_rpccred() below */
+       get_rpccred(cred);
+
+       task = rpc_call_null(gss_auth->client, cred, RPC_TASK_ASYNC);
+       if (!IS_ERR(task))
+               rpc_put_task(task);
+
+       put_rpccred(cred);
+       return 1;
+}
+
+/* gss_destroy_cred (and gss_free_ctx) are used to clean up after failure
  * to create a new cred or context, so they check that things have been
  * allocated before freeing them. */
 static void
@@ -744,6 +777,8 @@ gss_destroy_cred(struct rpc_cred *cred)
        struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
        struct gss_cl_ctx *ctx = gss_cred->gc_ctx;
 
+       if (gss_destroying_context(cred))
+               return;
        rcu_assign_pointer(gss_cred->gc_ctx, NULL);
        call_rcu(&cred->cr_rcu, gss_free_cred_callback);
        if (ctx)
@@ -892,6 +927,13 @@ gss_refresh(struct rpc_task *task)
        return 0;
 }
 
+/* Dummy refresh routine: used only when destroying the context */
+static int
+gss_refresh_null(struct rpc_task *task)
+{
+       return -EACCES;
+}
+
 static __be32 *
 gss_validate(struct rpc_task *task, __be32 *p)
 {
@@ -921,8 +963,11 @@ gss_validate(struct rpc_task *task, __be32 *p)
        maj_stat = gss_verify_mic(ctx->gc_gss_ctx, &verf_buf, &mic);
        if (maj_stat == GSS_S_CONTEXT_EXPIRED)
                clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
-       if (maj_stat)
+       if (maj_stat) {
+               dprintk("RPC: %5u gss_validate: gss_verify_mic returned"
+                               "error 0x%08x\n", task->tk_pid, maj_stat);
                goto out_bad;
+       }
        /* We leave it to unwrap to calculate au_rslack. For now we just
         * calculate the length of the verifier: */
        cred->cr_auth->au_verfsize = XDR_QUADLEN(len) + 2;
@@ -1260,6 +1305,17 @@ static const struct rpc_credops gss_credops = {
        .crunwrap_resp  = gss_unwrap_resp,
 };
 
+static const struct rpc_credops gss_nullops = {
+       .cr_name        = "AUTH_GSS",
+       .crdestroy      = gss_destroy_cred,
+       .crmatch        = gss_match,
+       .crmarshal      = gss_marshal,
+       .crrefresh      = gss_refresh_null,
+       .crvalidate     = gss_validate,
+       .crwrap_req     = gss_wrap_req,
+       .crunwrap_resp  = gss_unwrap_resp,
+};
+
 static struct rpc_pipe_ops gss_upcall_ops = {
        .upcall         = gss_pipe_upcall,
        .downcall       = gss_pipe_downcall,