SUNRPC: add side channel to use non-generic cred for rpc call.
authorNeilBrown <neilb@suse.com>
Mon, 3 Dec 2018 00:30:30 +0000 (11:30 +1100)
committerAnna Schumaker <Anna.Schumaker@Netapp.com>
Wed, 19 Dec 2018 18:52:45 +0000 (13:52 -0500)
The credential passed in rpc_message.rpc_cred is always a
generic credential except in one instance.
When gss_destroying_context() calls rpc_call_null(), it passes
a specific credential that it needs to destroy.
In this case the RPC acts *on* the credential rather than
being authorized by it.

This special case deserves explicit support and providing that will
mean that rpc_message.rpc_cred is *always* generic, allowing
some optimizations.

So add "tk_op_cred" to rpc_task and "rpc_op_cred" to the setup data.
Use this to pass the cred down from rpc_call_null(), and have
rpcauth_bindcred() notice it and bind it in place.

Credit to kernel test robot <fengguang.wu@intel.com> for finding
a bug in earlier version of this patch.

Signed-off-by: NeilBrown <neilb@suse.com>
Signed-off-by: Anna Schumaker <Anna.Schumaker@Netapp.com>
include/linux/sunrpc/sched.h
net/sunrpc/auth.c
net/sunrpc/clnt.c
net/sunrpc/sched.c

index bd722eb..4e2b893 100644 (file)
@@ -71,6 +71,7 @@ struct rpc_task {
 
        struct rpc_clnt *       tk_client;      /* RPC client */
        struct rpc_xprt *       tk_xprt;        /* Transport */
+       struct rpc_cred *       tk_op_cred;     /* cred being operated on */
 
        struct rpc_rqst *       tk_rqstp;       /* RPC request */
 
@@ -105,6 +106,7 @@ struct rpc_task_setup {
        struct rpc_task *task;
        struct rpc_clnt *rpc_client;
        struct rpc_xprt *rpc_xprt;
+       struct rpc_cred *rpc_op_cred;   /* credential being operated on */
        const struct rpc_message *rpc_message;
        const struct rpc_call_ops *callback_ops;
        void *callback_data;
index dcfcc59..27d9057 100644 (file)
@@ -751,7 +751,11 @@ rpcauth_bindcred(struct rpc_task *task, struct rpc_cred *cred, int flags)
 
        if (flags & RPC_TASK_ASYNC)
                lookupflags |= RPCAUTH_LOOKUP_NEW;
-       if (cred != NULL && cred != &machine_cred)
+       if (task->tk_op_cred)
+               /* Task must use exactly this rpc_cred */
+               new = task->tk_op_cred->cr_ops->crbind(task, task->tk_op_cred,
+                                                      lookupflags);
+       else if (cred != NULL && cred != &machine_cred)
                new = cred->cr_ops->crbind(task, cred, lookupflags);
        else if (cred == &machine_cred)
                new = rpcauth_bind_machine_cred(task, lookupflags);
index 26bea23..4cb697c 100644 (file)
@@ -2534,12 +2534,12 @@ struct rpc_task *rpc_call_null_helper(struct rpc_clnt *clnt,
 {
        struct rpc_message msg = {
                .rpc_proc = &rpcproc_null,
-               .rpc_cred = cred,
        };
        struct rpc_task_setup task_setup_data = {
                .rpc_client = clnt,
                .rpc_xprt = xprt,
                .rpc_message = &msg,
+               .rpc_op_cred = cred,
                .callback_ops = (ops != NULL) ? ops : &rpc_default_ops,
                .callback_data = data,
                .flags = flags,
index 57ca5be..c9f6503 100644 (file)
@@ -997,6 +997,8 @@ static void rpc_init_task(struct rpc_task *task, const struct rpc_task_setup *ta
 
        task->tk_xprt = xprt_get(task_setup_data->rpc_xprt);
 
+       task->tk_op_cred = get_rpccred(task_setup_data->rpc_op_cred);
+
        if (task->tk_ops->rpc_call_prepare != NULL)
                task->tk_action = rpc_prepare_task;
 
@@ -1054,6 +1056,7 @@ static void rpc_free_task(struct rpc_task *task)
 {
        unsigned short tk_flags = task->tk_flags;
 
+       put_rpccred(task->tk_op_cred);
        rpc_release_calldata(task->tk_ops, task->tk_calldata);
 
        if (tk_flags & RPC_TASK_DYNAMIC) {