aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTrond Myklebust <Trond.Myklebust@netapp.com>2006-02-01 12:18:36 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2006-02-01 12:52:23 -0500
commit8a3177604b729ec3b80e43790ee978863ac7551b (patch)
tree6eeafbdae55e2b0821e524f2c16bca52fa150cdc
parentaaaa99423b4b1f9cfd33ea5643d9274c25f62491 (diff)
SUNRPC: Fix a lock recursion in the auth_gss downcall
When we look up a new cred in the auth_gss downcall so that we can stuff the credcache, we do not want that lookup to queue up an upcall in order to initialise it. To do an upcall here not only redundant, but since we are already holding the inode->i_mutex, it will trigger a lock recursion. This patch allows rpcauth cache searches to indicate that they can cope with uninitialised credentials. Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r--include/linux/sunrpc/auth.h5
-rw-r--r--net/sunrpc/auth.c17
-rw-r--r--net/sunrpc/auth_gss/auth_gss.c25
-rw-r--r--net/sunrpc/auth_unix.c6
4 files changed, 37 insertions, 16 deletions
diff --git a/include/linux/sunrpc/auth.h b/include/linux/sunrpc/auth.h
index b68c11a2d6dd..bfc5fb279539 100644
--- a/include/linux/sunrpc/auth.h
+++ b/include/linux/sunrpc/auth.h
@@ -50,6 +50,7 @@ struct rpc_cred {
50}; 50};
51#define RPCAUTH_CRED_LOCKED 0x0001 51#define RPCAUTH_CRED_LOCKED 0x0001
52#define RPCAUTH_CRED_UPTODATE 0x0002 52#define RPCAUTH_CRED_UPTODATE 0x0002
53#define RPCAUTH_CRED_NEW 0x0004
53 54
54#define RPCAUTH_CRED_MAGIC 0x0f4aa4f0 55#define RPCAUTH_CRED_MAGIC 0x0f4aa4f0
55 56
@@ -87,6 +88,10 @@ struct rpc_auth {
87 * uid/gid, fs[ug]id, gids) 88 * uid/gid, fs[ug]id, gids)
88 */ 89 */
89 90
91/* Flags for rpcauth_lookupcred() */
92#define RPCAUTH_LOOKUP_NEW 0x01 /* Accept an uninitialised cred */
93#define RPCAUTH_LOOKUP_ROOTCREDS 0x02 /* This really ought to go! */
94
90/* 95/*
91 * Client authentication ops 96 * Client authentication ops
92 */ 97 */
diff --git a/net/sunrpc/auth.c b/net/sunrpc/auth.c
index 9ac1b8c26c01..1ca89c36da7a 100644
--- a/net/sunrpc/auth.c
+++ b/net/sunrpc/auth.c
@@ -184,7 +184,7 @@ rpcauth_gc_credcache(struct rpc_auth *auth, struct hlist_head *free)
184 */ 184 */
185struct rpc_cred * 185struct rpc_cred *
186rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, 186rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
187 int taskflags) 187 int flags)
188{ 188{
189 struct rpc_cred_cache *cache = auth->au_credcache; 189 struct rpc_cred_cache *cache = auth->au_credcache;
190 HLIST_HEAD(free); 190 HLIST_HEAD(free);
@@ -193,7 +193,7 @@ rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
193 *cred = NULL; 193 *cred = NULL;
194 int nr = 0; 194 int nr = 0;
195 195
196 if (!(taskflags & RPC_TASK_ROOTCREDS)) 196 if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS))
197 nr = acred->uid & RPC_CREDCACHE_MASK; 197 nr = acred->uid & RPC_CREDCACHE_MASK;
198retry: 198retry:
199 spin_lock(&rpc_credcache_lock); 199 spin_lock(&rpc_credcache_lock);
@@ -202,7 +202,7 @@ retry:
202 hlist_for_each_safe(pos, next, &cache->hashtable[nr]) { 202 hlist_for_each_safe(pos, next, &cache->hashtable[nr]) {
203 struct rpc_cred *entry; 203 struct rpc_cred *entry;
204 entry = hlist_entry(pos, struct rpc_cred, cr_hash); 204 entry = hlist_entry(pos, struct rpc_cred, cr_hash);
205 if (entry->cr_ops->crmatch(acred, entry, taskflags)) { 205 if (entry->cr_ops->crmatch(acred, entry, flags)) {
206 hlist_del(&entry->cr_hash); 206 hlist_del(&entry->cr_hash);
207 cred = entry; 207 cred = entry;
208 break; 208 break;
@@ -224,7 +224,7 @@ retry:
224 rpcauth_destroy_credlist(&free); 224 rpcauth_destroy_credlist(&free);
225 225
226 if (!cred) { 226 if (!cred) {
227 new = auth->au_ops->crcreate(auth, acred, taskflags); 227 new = auth->au_ops->crcreate(auth, acred, flags);
228 if (!IS_ERR(new)) { 228 if (!IS_ERR(new)) {
229#ifdef RPC_DEBUG 229#ifdef RPC_DEBUG
230 new->cr_magic = RPCAUTH_CRED_MAGIC; 230 new->cr_magic = RPCAUTH_CRED_MAGIC;
@@ -238,7 +238,7 @@ retry:
238} 238}
239 239
240struct rpc_cred * 240struct rpc_cred *
241rpcauth_lookupcred(struct rpc_auth *auth, int taskflags) 241rpcauth_lookupcred(struct rpc_auth *auth, int flags)
242{ 242{
243 struct auth_cred acred = { 243 struct auth_cred acred = {
244 .uid = current->fsuid, 244 .uid = current->fsuid,
@@ -250,7 +250,7 @@ rpcauth_lookupcred(struct rpc_auth *auth, int taskflags)
250 dprintk("RPC: looking up %s cred\n", 250 dprintk("RPC: looking up %s cred\n",
251 auth->au_ops->au_name); 251 auth->au_ops->au_name);
252 get_group_info(acred.group_info); 252 get_group_info(acred.group_info);
253 ret = auth->au_ops->lookup_cred(auth, &acred, taskflags); 253 ret = auth->au_ops->lookup_cred(auth, &acred, flags);
254 put_group_info(acred.group_info); 254 put_group_info(acred.group_info);
255 return ret; 255 return ret;
256} 256}
@@ -265,11 +265,14 @@ rpcauth_bindcred(struct rpc_task *task)
265 .group_info = current->group_info, 265 .group_info = current->group_info,
266 }; 266 };
267 struct rpc_cred *ret; 267 struct rpc_cred *ret;
268 int flags = 0;
268 269
269 dprintk("RPC: %4d looking up %s cred\n", 270 dprintk("RPC: %4d looking up %s cred\n",
270 task->tk_pid, task->tk_auth->au_ops->au_name); 271 task->tk_pid, task->tk_auth->au_ops->au_name);
271 get_group_info(acred.group_info); 272 get_group_info(acred.group_info);
272 ret = auth->au_ops->lookup_cred(auth, &acred, task->tk_flags); 273 if (task->tk_flags & RPC_TASK_ROOTCREDS)
274 flags |= RPCAUTH_LOOKUP_ROOTCREDS;
275 ret = auth->au_ops->lookup_cred(auth, &acred, flags);
273 if (!IS_ERR(ret)) 276 if (!IS_ERR(ret))
274 task->tk_msg.rpc_cred = ret; 277 task->tk_msg.rpc_cred = ret;
275 else 278 else
diff --git a/net/sunrpc/auth_gss/auth_gss.c b/net/sunrpc/auth_gss/auth_gss.c
index 8d782282ec19..03affcbf6292 100644
--- a/net/sunrpc/auth_gss/auth_gss.c
+++ b/net/sunrpc/auth_gss/auth_gss.c
@@ -158,6 +158,7 @@ gss_cred_set_ctx(struct rpc_cred *cred, struct gss_cl_ctx *ctx)
158 old = gss_cred->gc_ctx; 158 old = gss_cred->gc_ctx;
159 gss_cred->gc_ctx = ctx; 159 gss_cred->gc_ctx = ctx;
160 cred->cr_flags |= RPCAUTH_CRED_UPTODATE; 160 cred->cr_flags |= RPCAUTH_CRED_UPTODATE;
161 cred->cr_flags &= ~RPCAUTH_CRED_NEW;
161 write_unlock(&gss_ctx_lock); 162 write_unlock(&gss_ctx_lock);
162 if (old) 163 if (old)
163 gss_put_ctx(old); 164 gss_put_ctx(old);
@@ -580,7 +581,7 @@ gss_pipe_downcall(struct file *filp, const char __user *src, size_t mlen)
580 } else { 581 } else {
581 struct auth_cred acred = { .uid = uid }; 582 struct auth_cred acred = { .uid = uid };
582 spin_unlock(&gss_auth->lock); 583 spin_unlock(&gss_auth->lock);
583 cred = rpcauth_lookup_credcache(clnt->cl_auth, &acred, 0); 584 cred = rpcauth_lookup_credcache(clnt->cl_auth, &acred, RPCAUTH_LOOKUP_NEW);
584 if (IS_ERR(cred)) { 585 if (IS_ERR(cred)) {
585 err = PTR_ERR(cred); 586 err = PTR_ERR(cred);
586 goto err_put_ctx; 587 goto err_put_ctx;
@@ -758,13 +759,13 @@ gss_destroy_cred(struct rpc_cred *rc)
758 * Lookup RPCSEC_GSS cred for the current process 759 * Lookup RPCSEC_GSS cred for the current process
759 */ 760 */
760static struct rpc_cred * 761static struct rpc_cred *
761gss_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int taskflags) 762gss_lookup_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
762{ 763{
763 return rpcauth_lookup_credcache(auth, acred, taskflags); 764 return rpcauth_lookup_credcache(auth, acred, flags);
764} 765}
765 766
766static struct rpc_cred * 767static struct rpc_cred *
767gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int taskflags) 768gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
768{ 769{
769 struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth); 770 struct gss_auth *gss_auth = container_of(auth, struct gss_auth, rpc_auth);
770 struct gss_cred *cred = NULL; 771 struct gss_cred *cred = NULL;
@@ -785,13 +786,17 @@ gss_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int taskflags)
785 */ 786 */
786 cred->gc_flags = 0; 787 cred->gc_flags = 0;
787 cred->gc_base.cr_ops = &gss_credops; 788 cred->gc_base.cr_ops = &gss_credops;
789 cred->gc_base.cr_flags = RPCAUTH_CRED_NEW;
788 cred->gc_service = gss_auth->service; 790 cred->gc_service = gss_auth->service;
791 /* Is the caller prepared to initialise the credential? */
792 if (flags & RPCAUTH_LOOKUP_NEW)
793 goto out;
789 do { 794 do {
790 err = gss_create_upcall(gss_auth, cred); 795 err = gss_create_upcall(gss_auth, cred);
791 } while (err == -EAGAIN); 796 } while (err == -EAGAIN);
792 if (err < 0) 797 if (err < 0)
793 goto out_err; 798 goto out_err;
794 799out:
795 return &cred->gc_base; 800 return &cred->gc_base;
796 801
797out_err: 802out_err:
@@ -801,13 +806,21 @@ out_err:
801} 806}
802 807
803static int 808static int
804gss_match(struct auth_cred *acred, struct rpc_cred *rc, int taskflags) 809gss_match(struct auth_cred *acred, struct rpc_cred *rc, int flags)
805{ 810{
806 struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base); 811 struct gss_cred *gss_cred = container_of(rc, struct gss_cred, gc_base);
807 812
813 /*
814 * If the searchflags have set RPCAUTH_LOOKUP_NEW, then
815 * we don't really care if the credential has expired or not,
816 * since the caller should be prepared to reinitialise it.
817 */
818 if ((flags & RPCAUTH_LOOKUP_NEW) && (rc->cr_flags & RPCAUTH_CRED_NEW))
819 goto out;
808 /* Don't match with creds that have expired. */ 820 /* Don't match with creds that have expired. */
809 if (gss_cred->gc_ctx && time_after(jiffies, gss_cred->gc_ctx->gc_expiry)) 821 if (gss_cred->gc_ctx && time_after(jiffies, gss_cred->gc_ctx->gc_expiry))
810 return 0; 822 return 0;
823out:
811 return (rc->cr_uid == acred->uid); 824 return (rc->cr_uid == acred->uid);
812} 825}
813 826
diff --git a/net/sunrpc/auth_unix.c b/net/sunrpc/auth_unix.c
index 1b3ed4fd1987..df14b6bfbf10 100644
--- a/net/sunrpc/auth_unix.c
+++ b/net/sunrpc/auth_unix.c
@@ -75,7 +75,7 @@ unx_create_cred(struct rpc_auth *auth, struct auth_cred *acred, int flags)
75 75
76 atomic_set(&cred->uc_count, 1); 76 atomic_set(&cred->uc_count, 1);
77 cred->uc_flags = RPCAUTH_CRED_UPTODATE; 77 cred->uc_flags = RPCAUTH_CRED_UPTODATE;
78 if (flags & RPC_TASK_ROOTCREDS) { 78 if (flags & RPCAUTH_LOOKUP_ROOTCREDS) {
79 cred->uc_uid = 0; 79 cred->uc_uid = 0;
80 cred->uc_gid = 0; 80 cred->uc_gid = 0;
81 cred->uc_gids[0] = NOGROUP; 81 cred->uc_gids[0] = NOGROUP;
@@ -108,12 +108,12 @@ unx_destroy_cred(struct rpc_cred *cred)
108 * request root creds (e.g. for NFS swapping). 108 * request root creds (e.g. for NFS swapping).
109 */ 109 */
110static int 110static int
111unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int taskflags) 111unx_match(struct auth_cred *acred, struct rpc_cred *rcred, int flags)
112{ 112{
113 struct unx_cred *cred = (struct unx_cred *) rcred; 113 struct unx_cred *cred = (struct unx_cred *) rcred;
114 int i; 114 int i;
115 115
116 if (!(taskflags & RPC_TASK_ROOTCREDS)) { 116 if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS)) {
117 int groups; 117 int groups;
118 118
119 if (cred->uc_uid != acred->uid 119 if (cred->uc_uid != acred->uid