blob: f6b6c81cbc3e86a592391acd297dcb23da4942ff [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * linux/net/sunrpc/auth.c
3 *
4 * Generic RPC client authentication API.
5 *
6 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
7 */
8
9#include <linux/types.h>
10#include <linux/sched.h>
11#include <linux/module.h>
12#include <linux/slab.h>
13#include <linux/errno.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include <linux/sunrpc/clnt.h>
15#include <linux/spinlock.h>
16
17#ifdef RPC_DEBUG
18# define RPCDBG_FACILITY RPCDBG_AUTH
19#endif
20
21static struct rpc_authops * auth_flavors[RPC_AUTH_MAXFLAVOR] = {
22 &authnull_ops, /* AUTH_NULL */
23 &authunix_ops, /* AUTH_UNIX */
24 NULL, /* others can be loadable modules */
25};
26
27static u32
28pseudoflavor_to_flavor(u32 flavor) {
29 if (flavor >= RPC_AUTH_MAXFLAVOR)
30 return RPC_AUTH_GSS;
31 return flavor;
32}
33
34int
35rpcauth_register(struct rpc_authops *ops)
36{
37 rpc_authflavor_t flavor;
38
39 if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
40 return -EINVAL;
41 if (auth_flavors[flavor] != NULL)
42 return -EPERM; /* what else? */
43 auth_flavors[flavor] = ops;
44 return 0;
45}
46
47int
48rpcauth_unregister(struct rpc_authops *ops)
49{
50 rpc_authflavor_t flavor;
51
52 if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
53 return -EINVAL;
54 if (auth_flavors[flavor] != ops)
55 return -EPERM; /* what else? */
56 auth_flavors[flavor] = NULL;
57 return 0;
58}
59
60struct rpc_auth *
61rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
62{
63 struct rpc_auth *auth;
64 struct rpc_authops *ops;
65 u32 flavor = pseudoflavor_to_flavor(pseudoflavor);
66
Olaf Kirchf344f6d2006-03-20 13:44:08 -050067 auth = ERR_PTR(-EINVAL);
68 if (flavor >= RPC_AUTH_MAXFLAVOR)
69 goto out;
70
71 /* FIXME - auth_flavors[] really needs an rw lock,
72 * and module refcounting. */
73#ifdef CONFIG_KMOD
74 if ((ops = auth_flavors[flavor]) == NULL)
75 request_module("rpc-auth-%u", flavor);
76#endif
77 if ((ops = auth_flavors[flavor]) == NULL)
78 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -070079 auth = ops->create(clnt, pseudoflavor);
J. Bruce Fields6a192752005-06-22 17:16:23 +000080 if (IS_ERR(auth))
81 return auth;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082 if (clnt->cl_auth)
83 rpcauth_destroy(clnt->cl_auth);
84 clnt->cl_auth = auth;
Olaf Kirchf344f6d2006-03-20 13:44:08 -050085
86out:
Linus Torvalds1da177e2005-04-16 15:20:36 -070087 return auth;
88}
89
90void
91rpcauth_destroy(struct rpc_auth *auth)
92{
93 if (!atomic_dec_and_test(&auth->au_count))
94 return;
95 auth->au_ops->destroy(auth);
96}
97
98static DEFINE_SPINLOCK(rpc_credcache_lock);
99
100/*
101 * Initialize RPC credential cache
102 */
103int
104rpcauth_init_credcache(struct rpc_auth *auth, unsigned long expire)
105{
106 struct rpc_cred_cache *new;
107 int i;
108
Kris Katterjohn8b3a7002006-01-11 15:56:43 -0800109 new = kmalloc(sizeof(*new), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 if (!new)
111 return -ENOMEM;
112 for (i = 0; i < RPC_CREDCACHE_NR; i++)
113 INIT_HLIST_HEAD(&new->hashtable[i]);
114 new->expire = expire;
115 new->nextgc = jiffies + (expire >> 1);
116 auth->au_credcache = new;
117 return 0;
118}
119
120/*
121 * Destroy a list of credentials
122 */
123static inline
124void rpcauth_destroy_credlist(struct hlist_head *head)
125{
126 struct rpc_cred *cred;
127
128 while (!hlist_empty(head)) {
129 cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
130 hlist_del_init(&cred->cr_hash);
131 put_rpccred(cred);
132 }
133}
134
135/*
136 * Clear the RPC credential cache, and delete those credentials
137 * that are not referenced.
138 */
139void
Trond Myklebust3ab9bb72007-06-09 15:41:42 -0400140rpcauth_clear_credcache(struct rpc_cred_cache *cache)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700141{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 HLIST_HEAD(free);
143 struct hlist_node *pos, *next;
144 struct rpc_cred *cred;
145 int i;
146
147 spin_lock(&rpc_credcache_lock);
148 for (i = 0; i < RPC_CREDCACHE_NR; i++) {
149 hlist_for_each_safe(pos, next, &cache->hashtable[i]) {
150 cred = hlist_entry(pos, struct rpc_cred, cr_hash);
151 __hlist_del(&cred->cr_hash);
152 hlist_add_head(&cred->cr_hash, &free);
153 }
154 }
155 spin_unlock(&rpc_credcache_lock);
156 rpcauth_destroy_credlist(&free);
157}
158
Trond Myklebust3ab9bb72007-06-09 15:41:42 -0400159/*
160 * Destroy the RPC credential cache
161 */
162void
163rpcauth_destroy_credcache(struct rpc_auth *auth)
164{
165 struct rpc_cred_cache *cache = auth->au_credcache;
166
167 if (cache) {
168 auth->au_credcache = NULL;
169 rpcauth_clear_credcache(cache);
170 kfree(cache);
171 }
172}
173
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174static void
175rpcauth_prune_expired(struct rpc_auth *auth, struct rpc_cred *cred, struct hlist_head *free)
176{
177 if (atomic_read(&cred->cr_count) != 1)
178 return;
179 if (time_after(jiffies, cred->cr_expire + auth->au_credcache->expire))
180 cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
181 if (!(cred->cr_flags & RPCAUTH_CRED_UPTODATE)) {
182 __hlist_del(&cred->cr_hash);
183 hlist_add_head(&cred->cr_hash, free);
184 }
185}
186
187/*
188 * Remove stale credentials. Avoid sleeping inside the loop.
189 */
190static void
191rpcauth_gc_credcache(struct rpc_auth *auth, struct hlist_head *free)
192{
193 struct rpc_cred_cache *cache = auth->au_credcache;
194 struct hlist_node *pos, *next;
195 struct rpc_cred *cred;
196 int i;
197
Chuck Lever46121cf2007-01-31 12:14:08 -0500198 dprintk("RPC: gc'ing RPC credentials for auth %p\n", auth);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 for (i = 0; i < RPC_CREDCACHE_NR; i++) {
200 hlist_for_each_safe(pos, next, &cache->hashtable[i]) {
201 cred = hlist_entry(pos, struct rpc_cred, cr_hash);
202 rpcauth_prune_expired(auth, cred, free);
203 }
204 }
205 cache->nextgc = jiffies + cache->expire;
206}
207
208/*
209 * Look up a process' credentials in the authentication cache
210 */
211struct rpc_cred *
212rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
Trond Myklebust8a317762006-02-01 12:18:36 -0500213 int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700214{
215 struct rpc_cred_cache *cache = auth->au_credcache;
216 HLIST_HEAD(free);
217 struct hlist_node *pos, *next;
218 struct rpc_cred *new = NULL,
219 *cred = NULL;
220 int nr = 0;
221
Trond Myklebust8a317762006-02-01 12:18:36 -0500222 if (!(flags & RPCAUTH_LOOKUP_ROOTCREDS))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 nr = acred->uid & RPC_CREDCACHE_MASK;
224retry:
225 spin_lock(&rpc_credcache_lock);
226 if (time_before(cache->nextgc, jiffies))
227 rpcauth_gc_credcache(auth, &free);
228 hlist_for_each_safe(pos, next, &cache->hashtable[nr]) {
229 struct rpc_cred *entry;
YOSHIFUJI Hideakicca51722007-02-09 15:38:13 -0800230 entry = hlist_entry(pos, struct rpc_cred, cr_hash);
Trond Myklebust8a317762006-02-01 12:18:36 -0500231 if (entry->cr_ops->crmatch(acred, entry, flags)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 hlist_del(&entry->cr_hash);
233 cred = entry;
234 break;
235 }
236 rpcauth_prune_expired(auth, entry, &free);
237 }
238 if (new) {
239 if (cred)
240 hlist_add_head(&new->cr_hash, &free);
241 else
242 cred = new;
243 }
244 if (cred) {
245 hlist_add_head(&cred->cr_hash, &cache->hashtable[nr]);
246 get_rpccred(cred);
247 }
248 spin_unlock(&rpc_credcache_lock);
249
250 rpcauth_destroy_credlist(&free);
251
252 if (!cred) {
Trond Myklebust8a317762006-02-01 12:18:36 -0500253 new = auth->au_ops->crcreate(auth, acred, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 if (!IS_ERR(new)) {
255#ifdef RPC_DEBUG
256 new->cr_magic = RPCAUTH_CRED_MAGIC;
257#endif
258 goto retry;
259 } else
260 cred = new;
Trond Myklebustfba3bad2006-02-01 12:19:27 -0500261 } else if ((cred->cr_flags & RPCAUTH_CRED_NEW)
262 && cred->cr_ops->cr_init != NULL
263 && !(flags & RPCAUTH_LOOKUP_NEW)) {
264 int res = cred->cr_ops->cr_init(auth, cred);
265 if (res < 0) {
266 put_rpccred(cred);
267 cred = ERR_PTR(res);
268 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700269 }
270
271 return (struct rpc_cred *) cred;
272}
273
274struct rpc_cred *
Trond Myklebust8a317762006-02-01 12:18:36 -0500275rpcauth_lookupcred(struct rpc_auth *auth, int flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276{
277 struct auth_cred acred = {
278 .uid = current->fsuid,
279 .gid = current->fsgid,
280 .group_info = current->group_info,
281 };
282 struct rpc_cred *ret;
283
Chuck Lever46121cf2007-01-31 12:14:08 -0500284 dprintk("RPC: looking up %s cred\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700285 auth->au_ops->au_name);
286 get_group_info(acred.group_info);
Trond Myklebust8a317762006-02-01 12:18:36 -0500287 ret = auth->au_ops->lookup_cred(auth, &acred, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288 put_group_info(acred.group_info);
289 return ret;
290}
291
292struct rpc_cred *
293rpcauth_bindcred(struct rpc_task *task)
294{
295 struct rpc_auth *auth = task->tk_auth;
296 struct auth_cred acred = {
297 .uid = current->fsuid,
298 .gid = current->fsgid,
299 .group_info = current->group_info,
300 };
301 struct rpc_cred *ret;
Trond Myklebust8a317762006-02-01 12:18:36 -0500302 int flags = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
Chuck Lever46121cf2007-01-31 12:14:08 -0500304 dprintk("RPC: %5u looking up %s cred\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305 task->tk_pid, task->tk_auth->au_ops->au_name);
306 get_group_info(acred.group_info);
Trond Myklebust8a317762006-02-01 12:18:36 -0500307 if (task->tk_flags & RPC_TASK_ROOTCREDS)
308 flags |= RPCAUTH_LOOKUP_ROOTCREDS;
309 ret = auth->au_ops->lookup_cred(auth, &acred, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310 if (!IS_ERR(ret))
311 task->tk_msg.rpc_cred = ret;
312 else
313 task->tk_status = PTR_ERR(ret);
314 put_group_info(acred.group_info);
315 return ret;
316}
317
318void
319rpcauth_holdcred(struct rpc_task *task)
320{
Chuck Lever46121cf2007-01-31 12:14:08 -0500321 dprintk("RPC: %5u holding %s cred %p\n",
322 task->tk_pid, task->tk_auth->au_ops->au_name,
323 task->tk_msg.rpc_cred);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324 if (task->tk_msg.rpc_cred)
325 get_rpccred(task->tk_msg.rpc_cred);
326}
327
328void
329put_rpccred(struct rpc_cred *cred)
330{
331 cred->cr_expire = jiffies;
332 if (!atomic_dec_and_test(&cred->cr_count))
333 return;
334 cred->cr_ops->crdestroy(cred);
335}
336
337void
338rpcauth_unbindcred(struct rpc_task *task)
339{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 struct rpc_cred *cred = task->tk_msg.rpc_cred;
341
Chuck Lever46121cf2007-01-31 12:14:08 -0500342 dprintk("RPC: %5u releasing %s cred %p\n",
Chuck Lever0bbacc42005-11-01 16:53:32 -0500343 task->tk_pid, task->tk_auth->au_ops->au_name, cred);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344
345 put_rpccred(cred);
346 task->tk_msg.rpc_cred = NULL;
347}
348
Alexey Dobriyand8ed0292006-09-26 22:29:38 -0700349__be32 *
350rpcauth_marshcred(struct rpc_task *task, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352 struct rpc_cred *cred = task->tk_msg.rpc_cred;
353
Chuck Lever46121cf2007-01-31 12:14:08 -0500354 dprintk("RPC: %5u marshaling %s cred %p\n",
Chuck Lever0bbacc42005-11-01 16:53:32 -0500355 task->tk_pid, task->tk_auth->au_ops->au_name, cred);
356
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357 return cred->cr_ops->crmarshal(task, p);
358}
359
Alexey Dobriyand8ed0292006-09-26 22:29:38 -0700360__be32 *
361rpcauth_checkverf(struct rpc_task *task, __be32 *p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 struct rpc_cred *cred = task->tk_msg.rpc_cred;
364
Chuck Lever46121cf2007-01-31 12:14:08 -0500365 dprintk("RPC: %5u validating %s cred %p\n",
Chuck Lever0bbacc42005-11-01 16:53:32 -0500366 task->tk_pid, task->tk_auth->au_ops->au_name, cred);
367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 return cred->cr_ops->crvalidate(task, p);
369}
370
371int
372rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,
Alexey Dobriyand8ed0292006-09-26 22:29:38 -0700373 __be32 *data, void *obj)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374{
375 struct rpc_cred *cred = task->tk_msg.rpc_cred;
376
Chuck Lever46121cf2007-01-31 12:14:08 -0500377 dprintk("RPC: %5u using %s cred %p to wrap rpc data\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 task->tk_pid, cred->cr_ops->cr_name, cred);
379 if (cred->cr_ops->crwrap_req)
380 return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
381 /* By default, we encode the arguments normally. */
382 return encode(rqstp, data, obj);
383}
384
385int
386rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
Alexey Dobriyand8ed0292006-09-26 22:29:38 -0700387 __be32 *data, void *obj)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388{
389 struct rpc_cred *cred = task->tk_msg.rpc_cred;
390
Chuck Lever46121cf2007-01-31 12:14:08 -0500391 dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 task->tk_pid, cred->cr_ops->cr_name, cred);
393 if (cred->cr_ops->crunwrap_resp)
394 return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
395 data, obj);
396 /* By default, we decode the arguments normally. */
397 return decode(rqstp, data, obj);
398}
399
400int
401rpcauth_refreshcred(struct rpc_task *task)
402{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700403 struct rpc_cred *cred = task->tk_msg.rpc_cred;
404 int err;
405
Chuck Lever46121cf2007-01-31 12:14:08 -0500406 dprintk("RPC: %5u refreshing %s cred %p\n",
Chuck Lever0bbacc42005-11-01 16:53:32 -0500407 task->tk_pid, task->tk_auth->au_ops->au_name, cred);
408
Linus Torvalds1da177e2005-04-16 15:20:36 -0700409 err = cred->cr_ops->crrefresh(task);
410 if (err < 0)
411 task->tk_status = err;
412 return err;
413}
414
415void
416rpcauth_invalcred(struct rpc_task *task)
417{
Chuck Lever46121cf2007-01-31 12:14:08 -0500418 dprintk("RPC: %5u invalidating %s cred %p\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700419 task->tk_pid, task->tk_auth->au_ops->au_name, task->tk_msg.rpc_cred);
420 spin_lock(&rpc_credcache_lock);
421 if (task->tk_msg.rpc_cred)
422 task->tk_msg.rpc_cred->cr_flags &= ~RPCAUTH_CRED_UPTODATE;
423 spin_unlock(&rpc_credcache_lock);
424}
425
426int
427rpcauth_uptodatecred(struct rpc_task *task)
428{
429 return !(task->tk_msg.rpc_cred) ||
430 (task->tk_msg.rpc_cred->cr_flags & RPCAUTH_CRED_UPTODATE);
431}