]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/blame - net/sunrpc/auth.c
SUNRPC: Clean up the AUTH cache code
[mirror_ubuntu-jammy-kernel.git] / net / sunrpc / auth.c
CommitLineData
1da177e4
LT
1/*
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>
5b825c3a 11#include <linux/cred.h>
1da177e4
LT
12#include <linux/module.h>
13#include <linux/slab.h>
14#include <linux/errno.h>
25337fdc 15#include <linux/hash.h>
1da177e4 16#include <linux/sunrpc/clnt.h>
6a1a1e34 17#include <linux/sunrpc/gss_api.h>
1da177e4
LT
18#include <linux/spinlock.h>
19
f895b252 20#if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
1da177e4
LT
21# define RPCDBG_FACILITY RPCDBG_AUTH
22#endif
23
241269bd
TM
24#define RPC_CREDCACHE_DEFAULT_HASHBITS (4)
25struct rpc_cred_cache {
26 struct hlist_head *hashtable;
27 unsigned int hashbits;
28 spinlock_t lock;
29};
30
31static unsigned int auth_hashbits = RPC_CREDCACHE_DEFAULT_HASHBITS;
32
4e4c3bef
TM
33static const struct rpc_authops __rcu *auth_flavors[RPC_AUTH_MAXFLAVOR] = {
34 [RPC_AUTH_NULL] = (const struct rpc_authops __force __rcu *)&authnull_ops,
35 [RPC_AUTH_UNIX] = (const struct rpc_authops __force __rcu *)&authunix_ops,
1da177e4
LT
36 NULL, /* others can be loadable modules */
37};
38
e092bdcd 39static LIST_HEAD(cred_unused);
f5c2187c 40static unsigned long number_cred_unused;
e092bdcd 41
db5fe265 42#define MAX_HASHTABLE_BITS (14)
8e4e15d4 43static int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp)
241269bd
TM
44{
45 unsigned long num;
46 unsigned int nbits;
47 int ret;
48
49 if (!val)
50 goto out_inval;
00cfaa94 51 ret = kstrtoul(val, 0, &num);
1a54c0cf 52 if (ret)
241269bd 53 goto out_inval;
34ae685c 54 nbits = fls(num - 1);
241269bd
TM
55 if (nbits > MAX_HASHTABLE_BITS || nbits < 2)
56 goto out_inval;
57 *(unsigned int *)kp->arg = nbits;
58 return 0;
59out_inval:
60 return -EINVAL;
61}
62
8e4e15d4 63static int param_get_hashtbl_sz(char *buffer, const struct kernel_param *kp)
241269bd
TM
64{
65 unsigned int nbits;
66
67 nbits = *(unsigned int *)kp->arg;
68 return sprintf(buffer, "%u", 1U << nbits);
69}
70
71#define param_check_hashtbl_sz(name, p) __param_check(name, p, unsigned int);
72
9c27847d 73static const struct kernel_param_ops param_ops_hashtbl_sz = {
8e4e15d4
SR
74 .set = param_set_hashtbl_sz,
75 .get = param_get_hashtbl_sz,
76};
77
241269bd
TM
78module_param_named(auth_hashtable_size, auth_hashbits, hashtbl_sz, 0644);
79MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size");
80
bae6746f
TM
81static unsigned long auth_max_cred_cachesize = ULONG_MAX;
82module_param(auth_max_cred_cachesize, ulong, 0644);
83MODULE_PARM_DESC(auth_max_cred_cachesize, "RPC credential maximum total cache size");
84
1da177e4
LT
85static u32
86pseudoflavor_to_flavor(u32 flavor) {
1c74a244 87 if (flavor > RPC_AUTH_MAXFLAVOR)
1da177e4
LT
88 return RPC_AUTH_GSS;
89 return flavor;
90}
91
92int
f1c0a861 93rpcauth_register(const struct rpc_authops *ops)
1da177e4 94{
4e4c3bef 95 const struct rpc_authops *old;
1da177e4
LT
96 rpc_authflavor_t flavor;
97
98 if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
99 return -EINVAL;
4e4c3bef
TM
100 old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], NULL, ops);
101 if (old == NULL || old == ops)
102 return 0;
103 return -EPERM;
1da177e4 104}
e8914c65 105EXPORT_SYMBOL_GPL(rpcauth_register);
1da177e4
LT
106
107int
f1c0a861 108rpcauth_unregister(const struct rpc_authops *ops)
1da177e4 109{
4e4c3bef 110 const struct rpc_authops *old;
1da177e4
LT
111 rpc_authflavor_t flavor;
112
113 if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
114 return -EINVAL;
4e4c3bef
TM
115
116 old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], ops, NULL);
117 if (old == ops || old == NULL)
118 return 0;
119 return -EPERM;
1da177e4 120}
e8914c65 121EXPORT_SYMBOL_GPL(rpcauth_unregister);
1da177e4 122
4e4c3bef
TM
123static const struct rpc_authops *
124rpcauth_get_authops(rpc_authflavor_t flavor)
125{
126 const struct rpc_authops *ops;
127
128 if (flavor >= RPC_AUTH_MAXFLAVOR)
129 return NULL;
130
131 rcu_read_lock();
132 ops = rcu_dereference(auth_flavors[flavor]);
133 if (ops == NULL) {
134 rcu_read_unlock();
135 request_module("rpc-auth-%u", flavor);
136 rcu_read_lock();
137 ops = rcu_dereference(auth_flavors[flavor]);
138 if (ops == NULL)
139 goto out;
140 }
141 if (!try_module_get(ops->owner))
142 ops = NULL;
143out:
144 rcu_read_unlock();
145 return ops;
146}
147
148static void
149rpcauth_put_authops(const struct rpc_authops *ops)
150{
151 module_put(ops->owner);
152}
153
9568c5e9
CL
154/**
155 * rpcauth_get_pseudoflavor - check if security flavor is supported
156 * @flavor: a security flavor
157 * @info: a GSS mech OID, quality of protection, and service value
158 *
159 * Verifies that an appropriate kernel module is available or already loaded.
160 * Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is
161 * not supported locally.
162 */
163rpc_authflavor_t
164rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
165{
4e4c3bef 166 const struct rpc_authops *ops = rpcauth_get_authops(flavor);
9568c5e9
CL
167 rpc_authflavor_t pseudoflavor;
168
4e4c3bef 169 if (!ops)
9568c5e9 170 return RPC_AUTH_MAXFLAVOR;
9568c5e9
CL
171 pseudoflavor = flavor;
172 if (ops->info2flavor != NULL)
173 pseudoflavor = ops->info2flavor(info);
174
4e4c3bef 175 rpcauth_put_authops(ops);
9568c5e9
CL
176 return pseudoflavor;
177}
178EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);
179
a77c806f
CL
180/**
181 * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor
182 * @pseudoflavor: GSS pseudoflavor to match
183 * @info: rpcsec_gss_info structure to fill in
184 *
185 * Returns zero and fills in "info" if pseudoflavor matches a
186 * supported mechanism.
187 */
188int
189rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
190{
191 rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor);
192 const struct rpc_authops *ops;
193 int result;
194
4e4c3bef 195 ops = rpcauth_get_authops(flavor);
a77c806f 196 if (ops == NULL)
a77c806f 197 return -ENOENT;
a77c806f
CL
198
199 result = -ENOENT;
200 if (ops->flavor2info != NULL)
201 result = ops->flavor2info(pseudoflavor, info);
202
4e4c3bef 203 rpcauth_put_authops(ops);
a77c806f
CL
204 return result;
205}
206EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);
207
6a1a1e34
CL
208/**
209 * rpcauth_list_flavors - discover registered flavors and pseudoflavors
210 * @array: array to fill in
211 * @size: size of "array"
212 *
213 * Returns the number of array items filled in, or a negative errno.
214 *
215 * The returned array is not sorted by any policy. Callers should not
216 * rely on the order of the items in the returned array.
217 */
218int
219rpcauth_list_flavors(rpc_authflavor_t *array, int size)
220{
4e4c3bef
TM
221 const struct rpc_authops *ops;
222 rpc_authflavor_t flavor, pseudos[4];
223 int i, len, result = 0;
6a1a1e34 224
4e4c3bef 225 rcu_read_lock();
6a1a1e34 226 for (flavor = 0; flavor < RPC_AUTH_MAXFLAVOR; flavor++) {
4e4c3bef 227 ops = rcu_dereference(auth_flavors[flavor]);
6a1a1e34
CL
228 if (result >= size) {
229 result = -ENOMEM;
230 break;
231 }
232
233 if (ops == NULL)
234 continue;
235 if (ops->list_pseudoflavors == NULL) {
236 array[result++] = ops->au_flavor;
237 continue;
238 }
239 len = ops->list_pseudoflavors(pseudos, ARRAY_SIZE(pseudos));
240 if (len < 0) {
241 result = len;
242 break;
243 }
244 for (i = 0; i < len; i++) {
245 if (result >= size) {
246 result = -ENOMEM;
247 break;
248 }
249 array[result++] = pseudos[i];
250 }
251 }
4e4c3bef 252 rcu_read_unlock();
6a1a1e34
CL
253
254 dprintk("RPC: %s returns %d\n", __func__, result);
255 return result;
256}
257EXPORT_SYMBOL_GPL(rpcauth_list_flavors);
258
1da177e4 259struct rpc_auth *
82b98ca5 260rpcauth_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
1da177e4 261{
4e4c3bef 262 struct rpc_auth *auth = ERR_PTR(-EINVAL);
f1c0a861 263 const struct rpc_authops *ops;
4e4c3bef 264 u32 flavor = pseudoflavor_to_flavor(args->pseudoflavor);
1da177e4 265
4e4c3bef
TM
266 ops = rpcauth_get_authops(flavor);
267 if (ops == NULL)
f344f6df
OK
268 goto out;
269
c2190661 270 auth = ops->create(args, clnt);
4e4c3bef
TM
271
272 rpcauth_put_authops(ops);
6a19275a
BF
273 if (IS_ERR(auth))
274 return auth;
1da177e4 275 if (clnt->cl_auth)
de7a8ce3 276 rpcauth_release(clnt->cl_auth);
1da177e4 277 clnt->cl_auth = auth;
f344f6df
OK
278
279out:
1da177e4
LT
280 return auth;
281}
e8914c65 282EXPORT_SYMBOL_GPL(rpcauth_create);
1da177e4
LT
283
284void
de7a8ce3 285rpcauth_release(struct rpc_auth *auth)
1da177e4
LT
286{
287 if (!atomic_dec_and_test(&auth->au_count))
288 return;
289 auth->au_ops->destroy(auth);
290}
291
292static DEFINE_SPINLOCK(rpc_credcache_lock);
293
95cd6232
TM
294/*
295 * On success, the caller is responsible for freeing the reference
296 * held by the hashtable
297 */
298static bool
31be5bf1
TM
299rpcauth_unhash_cred_locked(struct rpc_cred *cred)
300{
95cd6232
TM
301 if (!test_and_clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
302 return false;
31be5bf1 303 hlist_del_rcu(&cred->cr_hash);
95cd6232 304 return true;
31be5bf1
TM
305}
306
95cd6232 307static bool
9499b434
TM
308rpcauth_unhash_cred(struct rpc_cred *cred)
309{
310 spinlock_t *cache_lock;
95cd6232 311 bool ret;
9499b434 312
95cd6232
TM
313 if (!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
314 return false;
9499b434
TM
315 cache_lock = &cred->cr_auth->au_credcache->lock;
316 spin_lock(cache_lock);
95cd6232 317 ret = rpcauth_unhash_cred_locked(cred);
9499b434 318 spin_unlock(cache_lock);
f0380f3d 319 return ret;
9499b434
TM
320}
321
1da177e4
LT
322/*
323 * Initialize RPC credential cache
324 */
325int
f5c2187c 326rpcauth_init_credcache(struct rpc_auth *auth)
1da177e4
LT
327{
328 struct rpc_cred_cache *new;
988664a0 329 unsigned int hashsize;
1da177e4 330
8b3a7005 331 new = kmalloc(sizeof(*new), GFP_KERNEL);
1da177e4 332 if (!new)
241269bd
TM
333 goto out_nocache;
334 new->hashbits = auth_hashbits;
988664a0 335 hashsize = 1U << new->hashbits;
241269bd
TM
336 new->hashtable = kcalloc(hashsize, sizeof(new->hashtable[0]), GFP_KERNEL);
337 if (!new->hashtable)
338 goto out_nohashtbl;
9499b434 339 spin_lock_init(&new->lock);
1da177e4
LT
340 auth->au_credcache = new;
341 return 0;
241269bd
TM
342out_nohashtbl:
343 kfree(new);
344out_nocache:
345 return -ENOMEM;
1da177e4 346}
e8914c65 347EXPORT_SYMBOL_GPL(rpcauth_init_credcache);
1da177e4 348
4de6caa2
AA
349/*
350 * Setup a credential key lifetime timeout notification
351 */
352int
353rpcauth_key_timeout_notify(struct rpc_auth *auth, struct rpc_cred *cred)
354{
355 if (!cred->cr_auth->au_ops->key_timeout)
356 return 0;
357 return cred->cr_auth->au_ops->key_timeout(auth, cred);
358}
359EXPORT_SYMBOL_GPL(rpcauth_key_timeout_notify);
360
361bool
ce52914e 362rpcauth_cred_key_to_expire(struct rpc_auth *auth, struct rpc_cred *cred)
4de6caa2 363{
ce52914e
SM
364 if (auth->au_flags & RPCAUTH_AUTH_NO_CRKEY_TIMEOUT)
365 return false;
4de6caa2
AA
366 if (!cred->cr_ops->crkey_to_expire)
367 return false;
368 return cred->cr_ops->crkey_to_expire(cred);
369}
370EXPORT_SYMBOL_GPL(rpcauth_cred_key_to_expire);
371
a0337d1d
JL
372char *
373rpcauth_stringify_acceptor(struct rpc_cred *cred)
374{
375 if (!cred->cr_ops->crstringify_acceptor)
376 return NULL;
377 return cred->cr_ops->crstringify_acceptor(cred);
378}
379EXPORT_SYMBOL_GPL(rpcauth_stringify_acceptor);
380
1da177e4
LT
381/*
382 * Destroy a list of credentials
383 */
384static inline
e092bdcd 385void rpcauth_destroy_credlist(struct list_head *head)
1da177e4
LT
386{
387 struct rpc_cred *cred;
388
e092bdcd
TM
389 while (!list_empty(head)) {
390 cred = list_entry(head->next, struct rpc_cred, cr_lru);
391 list_del_init(&cred->cr_lru);
1da177e4
LT
392 put_rpccred(cred);
393 }
394}
395
95cd6232
TM
396static void
397rpcauth_lru_add_locked(struct rpc_cred *cred)
398{
399 if (!list_empty(&cred->cr_lru))
400 return;
401 number_cred_unused++;
402 list_add_tail(&cred->cr_lru, &cred_unused);
403}
404
405static void
406rpcauth_lru_add(struct rpc_cred *cred)
407{
408 if (!list_empty(&cred->cr_lru))
409 return;
410 spin_lock(&rpc_credcache_lock);
411 rpcauth_lru_add_locked(cred);
412 spin_unlock(&rpc_credcache_lock);
413}
414
415static void
416rpcauth_lru_remove_locked(struct rpc_cred *cred)
417{
418 if (list_empty(&cred->cr_lru))
419 return;
420 number_cred_unused--;
421 list_del_init(&cred->cr_lru);
422}
423
424static void
425rpcauth_lru_remove(struct rpc_cred *cred)
426{
427 if (list_empty(&cred->cr_lru))
428 return;
429 spin_lock(&rpc_credcache_lock);
430 rpcauth_lru_remove_locked(cred);
431 spin_unlock(&rpc_credcache_lock);
432}
433
1da177e4
LT
434/*
435 * Clear the RPC credential cache, and delete those credentials
436 * that are not referenced.
437 */
438void
3ab9bb72 439rpcauth_clear_credcache(struct rpc_cred_cache *cache)
1da177e4 440{
e092bdcd
TM
441 LIST_HEAD(free);
442 struct hlist_head *head;
1da177e4 443 struct rpc_cred *cred;
988664a0 444 unsigned int hashsize = 1U << cache->hashbits;
1da177e4
LT
445 int i;
446
447 spin_lock(&rpc_credcache_lock);
9499b434 448 spin_lock(&cache->lock);
988664a0 449 for (i = 0; i < hashsize; i++) {
e092bdcd
TM
450 head = &cache->hashtable[i];
451 while (!hlist_empty(head)) {
452 cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
31be5bf1 453 rpcauth_unhash_cred_locked(cred);
95cd6232
TM
454 /* Note: We now hold a reference to cred */
455 rpcauth_lru_remove_locked(cred);
456 list_add_tail(&cred->cr_lru, &free);
1da177e4
LT
457 }
458 }
9499b434 459 spin_unlock(&cache->lock);
1da177e4
LT
460 spin_unlock(&rpc_credcache_lock);
461 rpcauth_destroy_credlist(&free);
462}
463
3ab9bb72
TM
464/*
465 * Destroy the RPC credential cache
466 */
467void
468rpcauth_destroy_credcache(struct rpc_auth *auth)
469{
470 struct rpc_cred_cache *cache = auth->au_credcache;
471
472 if (cache) {
473 auth->au_credcache = NULL;
474 rpcauth_clear_credcache(cache);
241269bd 475 kfree(cache->hashtable);
3ab9bb72
TM
476 kfree(cache);
477 }
478}
e8914c65 479EXPORT_SYMBOL_GPL(rpcauth_destroy_credcache);
3ab9bb72 480
d2b83141
TM
481
482#define RPC_AUTH_EXPIRY_MORATORIUM (60 * HZ)
483
e092bdcd
TM
484/*
485 * Remove stale credentials. Avoid sleeping inside the loop.
486 */
70534a73 487static long
f5c2187c 488rpcauth_prune_expired(struct list_head *free, int nr_to_scan)
1da177e4 489{
eac0d18d 490 struct rpc_cred *cred, *next;
d2b83141 491 unsigned long expired = jiffies - RPC_AUTH_EXPIRY_MORATORIUM;
70534a73 492 long freed = 0;
e092bdcd 493
eac0d18d
TM
494 list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) {
495
20673406
TM
496 if (nr_to_scan-- == 0)
497 break;
95cd6232
TM
498 if (atomic_read(&cred->cr_count) > 1) {
499 rpcauth_lru_remove_locked(cred);
500 continue;
501 }
93a05e65
TM
502 /*
503 * Enforce a 60 second garbage collection moratorium
504 * Note that the cred_unused list must be time-ordered.
505 */
95cd6232
TM
506 if (!time_in_range(cred->cr_expire, expired, jiffies))
507 continue;
508 if (!rpcauth_unhash_cred(cred))
e092bdcd 509 continue;
eac0d18d 510
95cd6232
TM
511 rpcauth_lru_remove_locked(cred);
512 freed++;
513 list_add_tail(&cred->cr_lru, free);
1da177e4 514 }
95cd6232 515 return freed ? freed : SHRINK_STOP;
1da177e4
LT
516}
517
bae6746f
TM
518static unsigned long
519rpcauth_cache_do_shrink(int nr_to_scan)
520{
521 LIST_HEAD(free);
522 unsigned long freed;
523
524 spin_lock(&rpc_credcache_lock);
525 freed = rpcauth_prune_expired(&free, nr_to_scan);
526 spin_unlock(&rpc_credcache_lock);
527 rpcauth_destroy_credlist(&free);
528
529 return freed;
530}
531
1da177e4 532/*
f5c2187c 533 * Run memory cache shrinker.
1da177e4 534 */
70534a73
DC
535static unsigned long
536rpcauth_cache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
537
1da177e4 538{
70534a73
DC
539 if ((sc->gfp_mask & GFP_KERNEL) != GFP_KERNEL)
540 return SHRINK_STOP;
f5c2187c 541
70534a73 542 /* nothing left, don't come back */
f5c2187c 543 if (list_empty(&cred_unused))
70534a73
DC
544 return SHRINK_STOP;
545
bae6746f 546 return rpcauth_cache_do_shrink(sc->nr_to_scan);
70534a73
DC
547}
548
549static unsigned long
550rpcauth_cache_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
551
552{
4c3ffd05 553 return number_cred_unused * sysctl_vfs_cache_pressure / 100;
1da177e4
LT
554}
555
bae6746f
TM
556static void
557rpcauth_cache_enforce_limit(void)
558{
559 unsigned long diff;
560 unsigned int nr_to_scan;
561
562 if (number_cred_unused <= auth_max_cred_cachesize)
563 return;
564 diff = number_cred_unused - auth_max_cred_cachesize;
565 nr_to_scan = 100;
566 if (diff < nr_to_scan)
567 nr_to_scan = diff;
568 rpcauth_cache_do_shrink(nr_to_scan);
569}
570
1da177e4
LT
571/*
572 * Look up a process' credentials in the authentication cache
573 */
574struct rpc_cred *
575rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
3c6e0bc8 576 int flags, gfp_t gfp)
1da177e4 577{
e092bdcd 578 LIST_HEAD(free);
1da177e4 579 struct rpc_cred_cache *cache = auth->au_credcache;
31be5bf1
TM
580 struct rpc_cred *cred = NULL,
581 *entry, *new;
25337fdc
TM
582 unsigned int nr;
583
66cbd4ba 584 nr = auth->au_ops->hash_cred(acred, cache->hashbits);
1da177e4 585
31be5bf1 586 rcu_read_lock();
b67bfe0d 587 hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) {
e092bdcd
TM
588 if (!entry->cr_ops->crmatch(acred, entry, flags))
589 continue;
bd956080
N
590 if (flags & RPCAUTH_LOOKUP_RCU) {
591 if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) &&
592 !test_bit(RPCAUTH_CRED_NEW, &entry->cr_flags))
593 cred = entry;
594 break;
595 }
9499b434 596 spin_lock(&cache->lock);
31be5bf1 597 if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) == 0) {
9499b434 598 spin_unlock(&cache->lock);
31be5bf1
TM
599 continue;
600 }
e092bdcd 601 cred = get_rpccred(entry);
9499b434 602 spin_unlock(&cache->lock);
e092bdcd 603 break;
1da177e4 604 }
31be5bf1
TM
605 rcu_read_unlock();
606
9499b434 607 if (cred != NULL)
31be5bf1 608 goto found;
1da177e4 609
bd956080
N
610 if (flags & RPCAUTH_LOOKUP_RCU)
611 return ERR_PTR(-ECHILD);
612
3c6e0bc8 613 new = auth->au_ops->crcreate(auth, acred, flags, gfp);
31be5bf1
TM
614 if (IS_ERR(new)) {
615 cred = new;
616 goto out;
617 }
1da177e4 618
9499b434 619 spin_lock(&cache->lock);
b67bfe0d 620 hlist_for_each_entry(entry, &cache->hashtable[nr], cr_hash) {
31be5bf1
TM
621 if (!entry->cr_ops->crmatch(acred, entry, flags))
622 continue;
623 cred = get_rpccred(entry);
624 break;
625 }
626 if (cred == NULL) {
5fe4755e 627 cred = new;
31be5bf1 628 set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags);
95cd6232 629 atomic_inc(&cred->cr_count);
31be5bf1
TM
630 hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]);
631 } else
632 list_add_tail(&new->cr_lru, &free);
9499b434 633 spin_unlock(&cache->lock);
bae6746f 634 rpcauth_cache_enforce_limit();
31be5bf1 635found:
f64f9e71
JP
636 if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) &&
637 cred->cr_ops->cr_init != NULL &&
638 !(flags & RPCAUTH_LOOKUP_NEW)) {
fba3bad4
TM
639 int res = cred->cr_ops->cr_init(auth, cred);
640 if (res < 0) {
641 put_rpccred(cred);
642 cred = ERR_PTR(res);
643 }
1da177e4 644 }
31be5bf1
TM
645 rpcauth_destroy_credlist(&free);
646out:
647 return cred;
1da177e4 648}
e8914c65 649EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache);
1da177e4
LT
650
651struct rpc_cred *
8a317760 652rpcauth_lookupcred(struct rpc_auth *auth, int flags)
1da177e4 653{
86a264ab 654 struct auth_cred acred;
1da177e4 655 struct rpc_cred *ret;
86a264ab 656 const struct cred *cred = current_cred();
1da177e4 657
46121cf7 658 dprintk("RPC: looking up %s cred\n",
1da177e4 659 auth->au_ops->au_name);
86a264ab
DH
660
661 memset(&acred, 0, sizeof(acred));
662 acred.uid = cred->fsuid;
663 acred.gid = cred->fsgid;
122a8cda 664 acred.group_info = cred->group_info;
8a317760 665 ret = auth->au_ops->lookup_cred(auth, &acred, flags);
1da177e4
LT
666 return ret;
667}
66b06860 668EXPORT_SYMBOL_GPL(rpcauth_lookupcred);
1da177e4 669
5fe4755e
TM
670void
671rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred,
672 struct rpc_auth *auth, const struct rpc_credops *ops)
673{
674 INIT_HLIST_NODE(&cred->cr_hash);
e092bdcd 675 INIT_LIST_HEAD(&cred->cr_lru);
5fe4755e
TM
676 atomic_set(&cred->cr_count, 1);
677 cred->cr_auth = auth;
678 cred->cr_ops = ops;
679 cred->cr_expire = jiffies;
5fe4755e
TM
680 cred->cr_uid = acred->uid;
681}
e8914c65 682EXPORT_SYMBOL_GPL(rpcauth_init_cred);
5fe4755e 683
8572b8e2 684struct rpc_cred *
5d351754 685rpcauth_generic_bind_cred(struct rpc_task *task, struct rpc_cred *cred, int lookupflags)
4ccda2cd 686{
4ccda2cd
TM
687 dprintk("RPC: %5u holding %s cred %p\n", task->tk_pid,
688 cred->cr_auth->au_ops->au_name, cred);
8572b8e2 689 return get_rpccred(cred);
4ccda2cd 690}
5c691044 691EXPORT_SYMBOL_GPL(rpcauth_generic_bind_cred);
4ccda2cd 692
8572b8e2 693static struct rpc_cred *
5d351754 694rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags)
1da177e4 695{
1be27f36 696 struct rpc_auth *auth = task->tk_client->cl_auth;
1da177e4 697 struct auth_cred acred = {
bf37f794
EB
698 .uid = GLOBAL_ROOT_UID,
699 .gid = GLOBAL_ROOT_GID,
1da177e4 700 };
1da177e4 701
46121cf7 702 dprintk("RPC: %5u looking up %s cred\n",
1be27f36 703 task->tk_pid, task->tk_client->cl_auth->au_ops->au_name);
8572b8e2 704 return auth->au_ops->lookup_cred(auth, &acred, lookupflags);
af093835
TM
705}
706
8572b8e2 707static struct rpc_cred *
5d351754 708rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags)
af093835
TM
709{
710 struct rpc_auth *auth = task->tk_client->cl_auth;
af093835
TM
711
712 dprintk("RPC: %5u looking up %s cred\n",
713 task->tk_pid, auth->au_ops->au_name);
8572b8e2 714 return rpcauth_lookupcred(auth, lookupflags);
1da177e4
LT
715}
716
a17c2153 717static int
4ccda2cd 718rpcauth_bindcred(struct rpc_task *task, struct rpc_cred *cred, int flags)
1da177e4 719{
a17c2153 720 struct rpc_rqst *req = task->tk_rqstp;
8572b8e2 721 struct rpc_cred *new;
5d351754
TM
722 int lookupflags = 0;
723
724 if (flags & RPC_TASK_ASYNC)
725 lookupflags |= RPCAUTH_LOOKUP_NEW;
4ccda2cd 726 if (cred != NULL)
8572b8e2 727 new = cred->cr_ops->crbind(task, cred, lookupflags);
4ccda2cd 728 else if (flags & RPC_TASK_ROOTCREDS)
8572b8e2 729 new = rpcauth_bind_root_cred(task, lookupflags);
4ccda2cd 730 else
8572b8e2
TM
731 new = rpcauth_bind_new_cred(task, lookupflags);
732 if (IS_ERR(new))
733 return PTR_ERR(new);
9a8f6b5e 734 put_rpccred(req->rq_cred);
a17c2153 735 req->rq_cred = new;
8572b8e2 736 return 0;
1da177e4
LT
737}
738
739void
740put_rpccred(struct rpc_cred *cred)
741{
9a8f6b5e
TM
742 if (cred == NULL)
743 return;
95cd6232
TM
744 rcu_read_lock();
745 if (atomic_dec_and_test(&cred->cr_count))
746 goto destroy;
747 if (atomic_read(&cred->cr_count) != 1 ||
748 !test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
749 goto out;
750 if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) {
751 cred->cr_expire = jiffies;
752 rpcauth_lru_add(cred);
753 /* Race breaker */
754 if (unlikely(!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags)))
755 rpcauth_lru_remove(cred);
756 } else if (rpcauth_unhash_cred(cred)) {
757 rpcauth_lru_remove(cred);
f0380f3d 758 if (atomic_dec_and_test(&cred->cr_count))
95cd6232 759 goto destroy;
e092bdcd 760 }
95cd6232
TM
761out:
762 rcu_read_unlock();
f0380f3d 763 return;
95cd6232
TM
764destroy:
765 rcu_read_unlock();
766 cred->cr_ops->crdestroy(cred);
1da177e4 767}
e8914c65 768EXPORT_SYMBOL_GPL(put_rpccred);
1da177e4 769
d8ed029d
AD
770__be32 *
771rpcauth_marshcred(struct rpc_task *task, __be32 *p)
1da177e4 772{
a17c2153 773 struct rpc_cred *cred = task->tk_rqstp->rq_cred;
1da177e4 774
46121cf7 775 dprintk("RPC: %5u marshaling %s cred %p\n",
1be27f36 776 task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
0bbacc40 777
1da177e4
LT
778 return cred->cr_ops->crmarshal(task, p);
779}
780
d8ed029d
AD
781__be32 *
782rpcauth_checkverf(struct rpc_task *task, __be32 *p)
1da177e4 783{
a17c2153 784 struct rpc_cred *cred = task->tk_rqstp->rq_cred;
1da177e4 785
46121cf7 786 dprintk("RPC: %5u validating %s cred %p\n",
1be27f36 787 task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
0bbacc40 788
1da177e4
LT
789 return cred->cr_ops->crvalidate(task, p);
790}
791
9f06c719
CL
792static void rpcauth_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp,
793 __be32 *data, void *obj)
794{
795 struct xdr_stream xdr;
796
797 xdr_init_encode(&xdr, &rqstp->rq_snd_buf, data);
798 encode(rqstp, &xdr, obj);
799}
800
1da177e4 801int
9f06c719 802rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp,
d8ed029d 803 __be32 *data, void *obj)
1da177e4 804{
a17c2153 805 struct rpc_cred *cred = task->tk_rqstp->rq_cred;
1da177e4 806
46121cf7 807 dprintk("RPC: %5u using %s cred %p to wrap rpc data\n",
1da177e4
LT
808 task->tk_pid, cred->cr_ops->cr_name, cred);
809 if (cred->cr_ops->crwrap_req)
810 return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
811 /* By default, we encode the arguments normally. */
9f06c719
CL
812 rpcauth_wrap_req_encode(encode, rqstp, data, obj);
813 return 0;
1da177e4
LT
814}
815
bf269551
CL
816static int
817rpcauth_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp,
818 __be32 *data, void *obj)
819{
820 struct xdr_stream xdr;
821
822 xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, data);
823 return decode(rqstp, &xdr, obj);
824}
825
1da177e4 826int
bf269551 827rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp,
d8ed029d 828 __be32 *data, void *obj)
1da177e4 829{
a17c2153 830 struct rpc_cred *cred = task->tk_rqstp->rq_cred;
1da177e4 831
46121cf7 832 dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n",
1da177e4
LT
833 task->tk_pid, cred->cr_ops->cr_name, cred);
834 if (cred->cr_ops->crunwrap_resp)
835 return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
836 data, obj);
837 /* By default, we decode the arguments normally. */
bf269551 838 return rpcauth_unwrap_req_decode(decode, rqstp, data, obj);
1da177e4
LT
839}
840
3021a5bb
TM
841bool
842rpcauth_xmit_need_reencode(struct rpc_task *task)
843{
844 struct rpc_cred *cred = task->tk_rqstp->rq_cred;
845
846 if (!cred || !cred->cr_ops->crneed_reencode)
847 return false;
848 return cred->cr_ops->crneed_reencode(task);
849}
850
1da177e4
LT
851int
852rpcauth_refreshcred(struct rpc_task *task)
853{
9a84d380 854 struct rpc_cred *cred;
1da177e4
LT
855 int err;
856
a17c2153
TM
857 cred = task->tk_rqstp->rq_cred;
858 if (cred == NULL) {
859 err = rpcauth_bindcred(task, task->tk_msg.rpc_cred, task->tk_flags);
860 if (err < 0)
861 goto out;
862 cred = task->tk_rqstp->rq_cred;
f81c6224 863 }
46121cf7 864 dprintk("RPC: %5u refreshing %s cred %p\n",
1be27f36 865 task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
0bbacc40 866
1da177e4 867 err = cred->cr_ops->crrefresh(task);
a17c2153 868out:
1da177e4
LT
869 if (err < 0)
870 task->tk_status = err;
871 return err;
872}
873
874void
875rpcauth_invalcred(struct rpc_task *task)
876{
a17c2153 877 struct rpc_cred *cred = task->tk_rqstp->rq_cred;
fc432dd9 878
46121cf7 879 dprintk("RPC: %5u invalidating %s cred %p\n",
1be27f36 880 task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
fc432dd9
TM
881 if (cred)
882 clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
1da177e4
LT
883}
884
885int
886rpcauth_uptodatecred(struct rpc_task *task)
887{
a17c2153 888 struct rpc_cred *cred = task->tk_rqstp->rq_cred;
fc432dd9
TM
889
890 return cred == NULL ||
891 test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0;
1da177e4 892}
f5c2187c 893
8e1f936b 894static struct shrinker rpc_cred_shrinker = {
70534a73
DC
895 .count_objects = rpcauth_cache_shrink_count,
896 .scan_objects = rpcauth_cache_shrink_scan,
8e1f936b
RR
897 .seeks = DEFAULT_SEEKS,
898};
f5c2187c 899
5d8d9a4d 900int __init rpcauth_init_module(void)
f5c2187c 901{
5d8d9a4d
TM
902 int err;
903
904 err = rpc_init_authunix();
905 if (err < 0)
906 goto out1;
907 err = rpc_init_generic_auth();
908 if (err < 0)
909 goto out2;
2864486b
KM
910 err = register_shrinker(&rpc_cred_shrinker);
911 if (err < 0)
912 goto out3;
5d8d9a4d 913 return 0;
2864486b
KM
914out3:
915 rpc_destroy_generic_auth();
5d8d9a4d
TM
916out2:
917 rpc_destroy_authunix();
918out1:
919 return err;
f5c2187c
TM
920}
921
c135e84a 922void rpcauth_remove_module(void)
f5c2187c 923{
5d8d9a4d
TM
924 rpc_destroy_authunix();
925 rpc_destroy_generic_auth();
8e1f936b 926 unregister_shrinker(&rpc_cred_shrinker);
f5c2187c 927}