RPC: Remove dependency of RPCSEC_GSS upcalls on the credential cache Ensure that credentials that are referenced by an RPC task, but that have been booted out of the credcache may still be refreshed. Signed-off-by: Trond Myklebust --- include/linux/sunrpc/auth_gss.h | 2 + net/sunrpc/auth_gss/auth_gss.c | 62 +++++++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 20 deletions(-) Index: linux-2.6.11/include/linux/sunrpc/auth_gss.h =================================================================== --- linux-2.6.11.orig/include/linux/sunrpc/auth_gss.h +++ linux-2.6.11/include/linux/sunrpc/auth_gss.h @@ -78,10 +78,12 @@ struct gss_cl_ctx { char gc_principal[0]; }; +struct gss_upcall_msg; struct gss_cred { struct rpc_cred gc_base; enum rpc_gss_svc gc_service; struct gss_cl_ctx *gc_ctx; + struct gss_upcall_msg *gc_upcall; }; #define gc_uid gc_base.cr_uid Index: linux-2.6.11/net/sunrpc/auth_gss/auth_gss.c =================================================================== --- linux-2.6.11.orig/net/sunrpc/auth_gss/auth_gss.c +++ linux-2.6.11/net/sunrpc/auth_gss/auth_gss.c @@ -291,12 +291,13 @@ err: struct gss_upcall_msg { + atomic_t count; + uid_t uid; struct rpc_pipe_msg msg; struct list_head list; struct gss_auth *auth; struct rpc_wait_queue waitq; - uid_t uid; - atomic_t count; + struct gss_cl_ctx *ctx; }; static void @@ -305,6 +306,8 @@ gss_release_msg(struct gss_upcall_msg *g if (!atomic_dec_and_test(&gss_msg->count)) return; BUG_ON(!list_empty(&gss_msg->list)); + if (gss_msg->ctx != NULL) + gss_put_ctx(gss_msg->ctx); kfree(gss_msg); } @@ -346,11 +349,29 @@ gss_unhash_msg(struct gss_upcall_msg *gs spin_unlock(&gss_auth->lock); } +static void +gss_upcall_callback(struct rpc_task *task) +{ + struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred, + struct gss_cred, gc_base); + struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall; + + BUG_ON(gss_msg == NULL); + if (gss_msg->ctx) + gss_cred_set_ctx(task->tk_msg.rpc_cred, gss_get_ctx(gss_msg->ctx)); + else + task->tk_status = gss_msg->msg.errno; + gss_cred->gc_upcall = NULL; + gss_release_msg(gss_msg); +} + static int gss_upcall(struct rpc_clnt *clnt, struct rpc_task *task, struct rpc_cred *cred) { struct gss_auth *gss_auth = container_of(clnt->cl_auth, struct gss_auth, rpc_auth); + struct gss_cred *gss_cred = container_of(cred, + struct gss_cred, gc_base); struct gss_upcall_msg *gss_msg, *gss_new = NULL; struct rpc_pipe_msg *msg; struct dentry *dentry = gss_auth->dentry; @@ -389,7 +410,9 @@ retry: if (!gss_cred_is_uptodate_ctx(cred)) { /* No, so do upcall and sleep */ task->tk_timeout = 0; - rpc_sleep_on(&gss_msg->waitq, task, NULL, NULL); + /* gss_upcall_callback will release the reference to gss_msg */ + gss_cred->gc_upcall = gss_msg; + rpc_sleep_on(&gss_msg->waitq, task, gss_upcall_callback, NULL); spin_unlock(&gss_auth->lock); res = rpc_queue_upcall(dentry->d_inode, msg); if (res) @@ -398,23 +421,20 @@ retry: /* Yes, so cancel upcall */ __gss_unhash_msg(gss_msg); spin_unlock(&gss_auth->lock); + gss_release_msg(gss_msg); } - gss_release_msg(gss_msg); dprintk("RPC: %4u gss_upcall for uid %u result %d\n", task->tk_pid, uid, res); return res; out_sleep: task->tk_timeout = 0; - rpc_sleep_on(&gss_msg->waitq, task, NULL, NULL); + /* gss_upcall_callback will release the reference to gss_msg */ + gss_cred->gc_upcall = gss_msg; + rpc_sleep_on(&gss_msg->waitq, task, gss_upcall_callback, NULL); spin_unlock(&gss_auth->lock); dprintk("RPC: %4u gss_upcall sleeping\n", task->tk_pid); if (gss_new) kfree(gss_new); - /* Note: we drop the reference here: we are automatically removed - * from the queue when we're woken up, and we should in any case - * have no further responsabilities w.r.t. the upcall. - */ - gss_release_msg(gss_msg); return 0; } @@ -448,7 +468,6 @@ gss_pipe_downcall(struct file *filp, con void *buf; struct rpc_clnt *clnt; struct gss_auth *gss_auth; - struct auth_cred acred = { 0 }; struct rpc_cred *cred; struct gss_upcall_msg *gss_msg; struct gss_cl_ctx *ctx; @@ -474,11 +493,6 @@ gss_pipe_downcall(struct file *filp, con err = PTR_ERR(p); goto err; } - acred.uid = uid; - err = -ENOENT; - cred = rpcauth_lookup_credcache(clnt->cl_auth, &acred, 0); - if (!cred) - goto err; err = -ENOMEM; snprintf(principal, sizeof(principal), "%u@%s", uid, clnt->cl_server); @@ -492,17 +506,25 @@ gss_pipe_downcall(struct file *filp, con err = PTR_ERR(p); if (err != -EACCES) goto err_put_ctx; - } else - gss_cred_set_ctx(cred, gss_get_ctx(ctx)); + } spin_lock(&gss_auth->lock); - gss_msg = __gss_find_upcall(gss_auth, acred.uid); + gss_msg = __gss_find_upcall(gss_auth, uid); if (gss_msg) { + if (err == 0 && gss_msg->ctx == NULL) + gss_msg->ctx = gss_get_ctx(ctx); gss_msg->msg.errno = err; __gss_unhash_msg(gss_msg); spin_unlock(&gss_auth->lock); gss_release_msg(gss_msg); - } else + } else { + struct auth_cred acred = { .uid = uid }; spin_unlock(&gss_auth->lock); + err = -ENOENT; + cred = rpcauth_lookup_credcache(clnt->cl_auth, &acred, 0); + if (!cred) + goto err_put_ctx; + gss_cred_set_ctx(cred, gss_get_ctx(ctx)); + } gss_put_ctx(ctx); kfree(buf); dprintk("RPC: gss_pipe_downcall returning length %Zu\n", mlen);