xref: /openbmc/linux/net/sunrpc/auth.c (revision 2ac3ddc7)
1457c8996SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds  * linux/net/sunrpc/auth.c
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Generic RPC client authentication API.
61da177e4SLinus Torvalds  *
71da177e4SLinus Torvalds  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
81da177e4SLinus Torvalds  */
91da177e4SLinus Torvalds 
101da177e4SLinus Torvalds #include <linux/types.h>
111da177e4SLinus Torvalds #include <linux/sched.h>
125b825c3aSIngo Molnar #include <linux/cred.h>
131da177e4SLinus Torvalds #include <linux/module.h>
141da177e4SLinus Torvalds #include <linux/slab.h>
151da177e4SLinus Torvalds #include <linux/errno.h>
1625337fdcSTrond Myklebust #include <linux/hash.h>
171da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
186a1a1e34SChuck Lever #include <linux/sunrpc/gss_api.h>
191da177e4SLinus Torvalds #include <linux/spinlock.h>
201da177e4SLinus Torvalds 
21a0584ee9SChuck Lever #include <trace/events/sunrpc.h>
22a0584ee9SChuck Lever 
23241269bdSTrond Myklebust #define RPC_CREDCACHE_DEFAULT_HASHBITS	(4)
24241269bdSTrond Myklebust struct rpc_cred_cache {
25241269bdSTrond Myklebust 	struct hlist_head	*hashtable;
26241269bdSTrond Myklebust 	unsigned int		hashbits;
27241269bdSTrond Myklebust 	spinlock_t		lock;
28241269bdSTrond Myklebust };
29241269bdSTrond Myklebust 
30241269bdSTrond Myklebust static unsigned int auth_hashbits = RPC_CREDCACHE_DEFAULT_HASHBITS;
31241269bdSTrond Myklebust 
324e4c3befSTrond Myklebust static const struct rpc_authops __rcu *auth_flavors[RPC_AUTH_MAXFLAVOR] = {
334e4c3befSTrond Myklebust 	[RPC_AUTH_NULL] = (const struct rpc_authops __force __rcu *)&authnull_ops,
344e4c3befSTrond Myklebust 	[RPC_AUTH_UNIX] = (const struct rpc_authops __force __rcu *)&authunix_ops,
351da177e4SLinus Torvalds 	NULL,			/* others can be loadable modules */
361da177e4SLinus Torvalds };
371da177e4SLinus Torvalds 
38e092bdcdSTrond Myklebust static LIST_HEAD(cred_unused);
39f5c2187cSTrond Myklebust static unsigned long number_cred_unused;
40e092bdcdSTrond Myklebust 
41a52458b4SNeilBrown static struct cred machine_cred = {
42a52458b4SNeilBrown 	.usage = ATOMIC_INIT(1),
43e7f45099SSantosh kumar pradhan #ifdef CONFIG_DEBUG_CREDENTIALS
44e7f45099SSantosh kumar pradhan 	.magic = CRED_MAGIC,
45e7f45099SSantosh kumar pradhan #endif
465e16923bSNeilBrown };
475e16923bSNeilBrown 
485e16923bSNeilBrown /*
495e16923bSNeilBrown  * Return the machine_cred pointer to be used whenever
505e16923bSNeilBrown  * the a generic machine credential is needed.
515e16923bSNeilBrown  */
52a52458b4SNeilBrown const struct cred *rpc_machine_cred(void)
535e16923bSNeilBrown {
545e16923bSNeilBrown 	return &machine_cred;
555e16923bSNeilBrown }
565e16923bSNeilBrown EXPORT_SYMBOL_GPL(rpc_machine_cred);
575e16923bSNeilBrown 
58db5fe265SMiquel van Smoorenburg #define MAX_HASHTABLE_BITS (14)
598e4e15d4SStephen Rothwell static int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp)
60241269bdSTrond Myklebust {
61241269bdSTrond Myklebust 	unsigned long num;
62241269bdSTrond Myklebust 	unsigned int nbits;
63241269bdSTrond Myklebust 	int ret;
64241269bdSTrond Myklebust 
65241269bdSTrond Myklebust 	if (!val)
66241269bdSTrond Myklebust 		goto out_inval;
6700cfaa94SDaniel Walter 	ret = kstrtoul(val, 0, &num);
681a54c0cfSDan Carpenter 	if (ret)
69241269bdSTrond Myklebust 		goto out_inval;
7034ae685cSFrank Sorenson 	nbits = fls(num - 1);
71241269bdSTrond Myklebust 	if (nbits > MAX_HASHTABLE_BITS || nbits < 2)
72241269bdSTrond Myklebust 		goto out_inval;
73241269bdSTrond Myklebust 	*(unsigned int *)kp->arg = nbits;
74241269bdSTrond Myklebust 	return 0;
75241269bdSTrond Myklebust out_inval:
76241269bdSTrond Myklebust 	return -EINVAL;
77241269bdSTrond Myklebust }
78241269bdSTrond Myklebust 
798e4e15d4SStephen Rothwell static int param_get_hashtbl_sz(char *buffer, const struct kernel_param *kp)
80241269bdSTrond Myklebust {
81241269bdSTrond Myklebust 	unsigned int nbits;
82241269bdSTrond Myklebust 
83241269bdSTrond Myklebust 	nbits = *(unsigned int *)kp->arg;
842ac3ddc7SXiongfeng Wang 	return sprintf(buffer, "%u\n", 1U << nbits);
85241269bdSTrond Myklebust }
86241269bdSTrond Myklebust 
87241269bdSTrond Myklebust #define param_check_hashtbl_sz(name, p) __param_check(name, p, unsigned int);
88241269bdSTrond Myklebust 
899c27847dSLuis R. Rodriguez static const struct kernel_param_ops param_ops_hashtbl_sz = {
908e4e15d4SStephen Rothwell 	.set = param_set_hashtbl_sz,
918e4e15d4SStephen Rothwell 	.get = param_get_hashtbl_sz,
928e4e15d4SStephen Rothwell };
938e4e15d4SStephen Rothwell 
94241269bdSTrond Myklebust module_param_named(auth_hashtable_size, auth_hashbits, hashtbl_sz, 0644);
95241269bdSTrond Myklebust MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size");
96241269bdSTrond Myklebust 
97bae6746fSTrond Myklebust static unsigned long auth_max_cred_cachesize = ULONG_MAX;
98bae6746fSTrond Myklebust module_param(auth_max_cred_cachesize, ulong, 0644);
99bae6746fSTrond Myklebust MODULE_PARM_DESC(auth_max_cred_cachesize, "RPC credential maximum total cache size");
100bae6746fSTrond Myklebust 
1011da177e4SLinus Torvalds static u32
1021da177e4SLinus Torvalds pseudoflavor_to_flavor(u32 flavor) {
1031c74a244SChuck Lever 	if (flavor > RPC_AUTH_MAXFLAVOR)
1041da177e4SLinus Torvalds 		return RPC_AUTH_GSS;
1051da177e4SLinus Torvalds 	return flavor;
1061da177e4SLinus Torvalds }
1071da177e4SLinus Torvalds 
1081da177e4SLinus Torvalds int
109f1c0a861STrond Myklebust rpcauth_register(const struct rpc_authops *ops)
1101da177e4SLinus Torvalds {
1114e4c3befSTrond Myklebust 	const struct rpc_authops *old;
1121da177e4SLinus Torvalds 	rpc_authflavor_t flavor;
1131da177e4SLinus Torvalds 
1141da177e4SLinus Torvalds 	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
1151da177e4SLinus Torvalds 		return -EINVAL;
1164e4c3befSTrond Myklebust 	old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], NULL, ops);
1174e4c3befSTrond Myklebust 	if (old == NULL || old == ops)
1184e4c3befSTrond Myklebust 		return 0;
1194e4c3befSTrond Myklebust 	return -EPERM;
1201da177e4SLinus Torvalds }
121e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_register);
1221da177e4SLinus Torvalds 
1231da177e4SLinus Torvalds int
124f1c0a861STrond Myklebust rpcauth_unregister(const struct rpc_authops *ops)
1251da177e4SLinus Torvalds {
1264e4c3befSTrond Myklebust 	const struct rpc_authops *old;
1271da177e4SLinus Torvalds 	rpc_authflavor_t flavor;
1281da177e4SLinus Torvalds 
1291da177e4SLinus Torvalds 	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
1301da177e4SLinus Torvalds 		return -EINVAL;
1314e4c3befSTrond Myklebust 
1324e4c3befSTrond Myklebust 	old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], ops, NULL);
1334e4c3befSTrond Myklebust 	if (old == ops || old == NULL)
1344e4c3befSTrond Myklebust 		return 0;
1354e4c3befSTrond Myklebust 	return -EPERM;
1361da177e4SLinus Torvalds }
137e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_unregister);
1381da177e4SLinus Torvalds 
1394e4c3befSTrond Myklebust static const struct rpc_authops *
1404e4c3befSTrond Myklebust rpcauth_get_authops(rpc_authflavor_t flavor)
1414e4c3befSTrond Myklebust {
1424e4c3befSTrond Myklebust 	const struct rpc_authops *ops;
1434e4c3befSTrond Myklebust 
1444e4c3befSTrond Myklebust 	if (flavor >= RPC_AUTH_MAXFLAVOR)
1454e4c3befSTrond Myklebust 		return NULL;
1464e4c3befSTrond Myklebust 
1474e4c3befSTrond Myklebust 	rcu_read_lock();
1484e4c3befSTrond Myklebust 	ops = rcu_dereference(auth_flavors[flavor]);
1494e4c3befSTrond Myklebust 	if (ops == NULL) {
1504e4c3befSTrond Myklebust 		rcu_read_unlock();
1514e4c3befSTrond Myklebust 		request_module("rpc-auth-%u", flavor);
1524e4c3befSTrond Myklebust 		rcu_read_lock();
1534e4c3befSTrond Myklebust 		ops = rcu_dereference(auth_flavors[flavor]);
1544e4c3befSTrond Myklebust 		if (ops == NULL)
1554e4c3befSTrond Myklebust 			goto out;
1564e4c3befSTrond Myklebust 	}
1574e4c3befSTrond Myklebust 	if (!try_module_get(ops->owner))
1584e4c3befSTrond Myklebust 		ops = NULL;
1594e4c3befSTrond Myklebust out:
1604e4c3befSTrond Myklebust 	rcu_read_unlock();
1614e4c3befSTrond Myklebust 	return ops;
1624e4c3befSTrond Myklebust }
1634e4c3befSTrond Myklebust 
1644e4c3befSTrond Myklebust static void
1654e4c3befSTrond Myklebust rpcauth_put_authops(const struct rpc_authops *ops)
1664e4c3befSTrond Myklebust {
1674e4c3befSTrond Myklebust 	module_put(ops->owner);
1684e4c3befSTrond Myklebust }
1694e4c3befSTrond Myklebust 
1706a1a1e34SChuck Lever /**
1719568c5e9SChuck Lever  * rpcauth_get_pseudoflavor - check if security flavor is supported
1729568c5e9SChuck Lever  * @flavor: a security flavor
1739568c5e9SChuck Lever  * @info: a GSS mech OID, quality of protection, and service value
1749568c5e9SChuck Lever  *
1759568c5e9SChuck Lever  * Verifies that an appropriate kernel module is available or already loaded.
1769568c5e9SChuck Lever  * Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is
1779568c5e9SChuck Lever  * not supported locally.
1789568c5e9SChuck Lever  */
1799568c5e9SChuck Lever rpc_authflavor_t
1809568c5e9SChuck Lever rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
1819568c5e9SChuck Lever {
1824e4c3befSTrond Myklebust 	const struct rpc_authops *ops = rpcauth_get_authops(flavor);
1839568c5e9SChuck Lever 	rpc_authflavor_t pseudoflavor;
1849568c5e9SChuck Lever 
1854e4c3befSTrond Myklebust 	if (!ops)
1869568c5e9SChuck Lever 		return RPC_AUTH_MAXFLAVOR;
1879568c5e9SChuck Lever 	pseudoflavor = flavor;
1889568c5e9SChuck Lever 	if (ops->info2flavor != NULL)
1899568c5e9SChuck Lever 		pseudoflavor = ops->info2flavor(info);
1909568c5e9SChuck Lever 
1914e4c3befSTrond Myklebust 	rpcauth_put_authops(ops);
1929568c5e9SChuck Lever 	return pseudoflavor;
1939568c5e9SChuck Lever }
1949568c5e9SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);
1959568c5e9SChuck Lever 
1969568c5e9SChuck Lever /**
197a77c806fSChuck Lever  * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor
198a77c806fSChuck Lever  * @pseudoflavor: GSS pseudoflavor to match
199a77c806fSChuck Lever  * @info: rpcsec_gss_info structure to fill in
200a77c806fSChuck Lever  *
201a77c806fSChuck Lever  * Returns zero and fills in "info" if pseudoflavor matches a
202a77c806fSChuck Lever  * supported mechanism.
203a77c806fSChuck Lever  */
204a77c806fSChuck Lever int
205a77c806fSChuck Lever rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
206a77c806fSChuck Lever {
207a77c806fSChuck Lever 	rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor);
208a77c806fSChuck Lever 	const struct rpc_authops *ops;
209a77c806fSChuck Lever 	int result;
210a77c806fSChuck Lever 
2114e4c3befSTrond Myklebust 	ops = rpcauth_get_authops(flavor);
212a77c806fSChuck Lever 	if (ops == NULL)
213a77c806fSChuck Lever 		return -ENOENT;
214a77c806fSChuck Lever 
215a77c806fSChuck Lever 	result = -ENOENT;
216a77c806fSChuck Lever 	if (ops->flavor2info != NULL)
217a77c806fSChuck Lever 		result = ops->flavor2info(pseudoflavor, info);
218a77c806fSChuck Lever 
2194e4c3befSTrond Myklebust 	rpcauth_put_authops(ops);
220a77c806fSChuck Lever 	return result;
221a77c806fSChuck Lever }
222a77c806fSChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);
223a77c806fSChuck Lever 
2241da177e4SLinus Torvalds struct rpc_auth *
22582b98ca5SSargun Dhillon rpcauth_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
2261da177e4SLinus Torvalds {
2274e4c3befSTrond Myklebust 	struct rpc_auth	*auth = ERR_PTR(-EINVAL);
228f1c0a861STrond Myklebust 	const struct rpc_authops *ops;
229c2190661STrond Myklebust 	u32 flavor = pseudoflavor_to_flavor(args->pseudoflavor);
2301da177e4SLinus Torvalds 
2314e4c3befSTrond Myklebust 	ops = rpcauth_get_authops(flavor);
2324e4c3befSTrond Myklebust 	if (ops == NULL)
233f344f6dfSOlaf Kirch 		goto out;
234f344f6dfSOlaf Kirch 
235c2190661STrond Myklebust 	auth = ops->create(args, clnt);
2364e4c3befSTrond Myklebust 
2374e4c3befSTrond Myklebust 	rpcauth_put_authops(ops);
2386a19275aSJ. Bruce Fields 	if (IS_ERR(auth))
2396a19275aSJ. Bruce Fields 		return auth;
2401da177e4SLinus Torvalds 	if (clnt->cl_auth)
241de7a8ce3STrond Myklebust 		rpcauth_release(clnt->cl_auth);
2421da177e4SLinus Torvalds 	clnt->cl_auth = auth;
243f344f6dfSOlaf Kirch 
244f344f6dfSOlaf Kirch out:
2451da177e4SLinus Torvalds 	return auth;
2461da177e4SLinus Torvalds }
247e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_create);
2481da177e4SLinus Torvalds 
2491da177e4SLinus Torvalds void
250de7a8ce3STrond Myklebust rpcauth_release(struct rpc_auth *auth)
2511da177e4SLinus Torvalds {
252331bc71cSTrond Myklebust 	if (!refcount_dec_and_test(&auth->au_count))
2531da177e4SLinus Torvalds 		return;
2541da177e4SLinus Torvalds 	auth->au_ops->destroy(auth);
2551da177e4SLinus Torvalds }
2561da177e4SLinus Torvalds 
2571da177e4SLinus Torvalds static DEFINE_SPINLOCK(rpc_credcache_lock);
2581da177e4SLinus Torvalds 
25995cd6232STrond Myklebust /*
26095cd6232STrond Myklebust  * On success, the caller is responsible for freeing the reference
26195cd6232STrond Myklebust  * held by the hashtable
26295cd6232STrond Myklebust  */
26395cd6232STrond Myklebust static bool
26431be5bf1STrond Myklebust rpcauth_unhash_cred_locked(struct rpc_cred *cred)
26531be5bf1STrond Myklebust {
26695cd6232STrond Myklebust 	if (!test_and_clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
26795cd6232STrond Myklebust 		return false;
26831be5bf1STrond Myklebust 	hlist_del_rcu(&cred->cr_hash);
26995cd6232STrond Myklebust 	return true;
27031be5bf1STrond Myklebust }
27131be5bf1STrond Myklebust 
27295cd6232STrond Myklebust static bool
2739499b434STrond Myklebust rpcauth_unhash_cred(struct rpc_cred *cred)
2749499b434STrond Myklebust {
2759499b434STrond Myklebust 	spinlock_t *cache_lock;
27695cd6232STrond Myklebust 	bool ret;
2779499b434STrond Myklebust 
27895cd6232STrond Myklebust 	if (!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
27995cd6232STrond Myklebust 		return false;
2809499b434STrond Myklebust 	cache_lock = &cred->cr_auth->au_credcache->lock;
2819499b434STrond Myklebust 	spin_lock(cache_lock);
28295cd6232STrond Myklebust 	ret = rpcauth_unhash_cred_locked(cred);
2839499b434STrond Myklebust 	spin_unlock(cache_lock);
284f0380f3dSTrond Myklebust 	return ret;
2859499b434STrond Myklebust }
2869499b434STrond Myklebust 
2871da177e4SLinus Torvalds /*
2881da177e4SLinus Torvalds  * Initialize RPC credential cache
2891da177e4SLinus Torvalds  */
2901da177e4SLinus Torvalds int
291f5c2187cSTrond Myklebust rpcauth_init_credcache(struct rpc_auth *auth)
2921da177e4SLinus Torvalds {
2931da177e4SLinus Torvalds 	struct rpc_cred_cache *new;
294988664a0STrond Myklebust 	unsigned int hashsize;
2951da177e4SLinus Torvalds 
2968b3a7005SKris Katterjohn 	new = kmalloc(sizeof(*new), GFP_KERNEL);
2971da177e4SLinus Torvalds 	if (!new)
298241269bdSTrond Myklebust 		goto out_nocache;
299241269bdSTrond Myklebust 	new->hashbits = auth_hashbits;
300988664a0STrond Myklebust 	hashsize = 1U << new->hashbits;
301241269bdSTrond Myklebust 	new->hashtable = kcalloc(hashsize, sizeof(new->hashtable[0]), GFP_KERNEL);
302241269bdSTrond Myklebust 	if (!new->hashtable)
303241269bdSTrond Myklebust 		goto out_nohashtbl;
3049499b434STrond Myklebust 	spin_lock_init(&new->lock);
3051da177e4SLinus Torvalds 	auth->au_credcache = new;
3061da177e4SLinus Torvalds 	return 0;
307241269bdSTrond Myklebust out_nohashtbl:
308241269bdSTrond Myklebust 	kfree(new);
309241269bdSTrond Myklebust out_nocache:
310241269bdSTrond Myklebust 	return -ENOMEM;
3111da177e4SLinus Torvalds }
312e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_credcache);
3131da177e4SLinus Torvalds 
314a0337d1dSJeff Layton char *
315a0337d1dSJeff Layton rpcauth_stringify_acceptor(struct rpc_cred *cred)
316a0337d1dSJeff Layton {
317a0337d1dSJeff Layton 	if (!cred->cr_ops->crstringify_acceptor)
318a0337d1dSJeff Layton 		return NULL;
319a0337d1dSJeff Layton 	return cred->cr_ops->crstringify_acceptor(cred);
320a0337d1dSJeff Layton }
321a0337d1dSJeff Layton EXPORT_SYMBOL_GPL(rpcauth_stringify_acceptor);
322a0337d1dSJeff Layton 
3234de6caa2SAndy Adamson /*
3241da177e4SLinus Torvalds  * Destroy a list of credentials
3251da177e4SLinus Torvalds  */
3261da177e4SLinus Torvalds static inline
327e092bdcdSTrond Myklebust void rpcauth_destroy_credlist(struct list_head *head)
3281da177e4SLinus Torvalds {
3291da177e4SLinus Torvalds 	struct rpc_cred *cred;
3301da177e4SLinus Torvalds 
331e092bdcdSTrond Myklebust 	while (!list_empty(head)) {
332e092bdcdSTrond Myklebust 		cred = list_entry(head->next, struct rpc_cred, cr_lru);
333e092bdcdSTrond Myklebust 		list_del_init(&cred->cr_lru);
3341da177e4SLinus Torvalds 		put_rpccred(cred);
3351da177e4SLinus Torvalds 	}
3361da177e4SLinus Torvalds }
3371da177e4SLinus Torvalds 
33895cd6232STrond Myklebust static void
33995cd6232STrond Myklebust rpcauth_lru_add_locked(struct rpc_cred *cred)
34095cd6232STrond Myklebust {
34195cd6232STrond Myklebust 	if (!list_empty(&cred->cr_lru))
34295cd6232STrond Myklebust 		return;
34395cd6232STrond Myklebust 	number_cred_unused++;
34495cd6232STrond Myklebust 	list_add_tail(&cred->cr_lru, &cred_unused);
34595cd6232STrond Myklebust }
34695cd6232STrond Myklebust 
34795cd6232STrond Myklebust static void
34895cd6232STrond Myklebust rpcauth_lru_add(struct rpc_cred *cred)
34995cd6232STrond Myklebust {
35095cd6232STrond Myklebust 	if (!list_empty(&cred->cr_lru))
35195cd6232STrond Myklebust 		return;
35295cd6232STrond Myklebust 	spin_lock(&rpc_credcache_lock);
35395cd6232STrond Myklebust 	rpcauth_lru_add_locked(cred);
35495cd6232STrond Myklebust 	spin_unlock(&rpc_credcache_lock);
35595cd6232STrond Myklebust }
35695cd6232STrond Myklebust 
35795cd6232STrond Myklebust static void
35895cd6232STrond Myklebust rpcauth_lru_remove_locked(struct rpc_cred *cred)
35995cd6232STrond Myklebust {
36095cd6232STrond Myklebust 	if (list_empty(&cred->cr_lru))
36195cd6232STrond Myklebust 		return;
36295cd6232STrond Myklebust 	number_cred_unused--;
36395cd6232STrond Myklebust 	list_del_init(&cred->cr_lru);
36495cd6232STrond Myklebust }
36595cd6232STrond Myklebust 
36695cd6232STrond Myklebust static void
36795cd6232STrond Myklebust rpcauth_lru_remove(struct rpc_cred *cred)
36895cd6232STrond Myklebust {
36995cd6232STrond Myklebust 	if (list_empty(&cred->cr_lru))
37095cd6232STrond Myklebust 		return;
37195cd6232STrond Myklebust 	spin_lock(&rpc_credcache_lock);
37295cd6232STrond Myklebust 	rpcauth_lru_remove_locked(cred);
37395cd6232STrond Myklebust 	spin_unlock(&rpc_credcache_lock);
37495cd6232STrond Myklebust }
37595cd6232STrond Myklebust 
3761da177e4SLinus Torvalds /*
3771da177e4SLinus Torvalds  * Clear the RPC credential cache, and delete those credentials
3781da177e4SLinus Torvalds  * that are not referenced.
3791da177e4SLinus Torvalds  */
3801da177e4SLinus Torvalds void
3813ab9bb72STrond Myklebust rpcauth_clear_credcache(struct rpc_cred_cache *cache)
3821da177e4SLinus Torvalds {
383e092bdcdSTrond Myklebust 	LIST_HEAD(free);
384e092bdcdSTrond Myklebust 	struct hlist_head *head;
3851da177e4SLinus Torvalds 	struct rpc_cred	*cred;
386988664a0STrond Myklebust 	unsigned int hashsize = 1U << cache->hashbits;
3871da177e4SLinus Torvalds 	int		i;
3881da177e4SLinus Torvalds 
3891da177e4SLinus Torvalds 	spin_lock(&rpc_credcache_lock);
3909499b434STrond Myklebust 	spin_lock(&cache->lock);
391988664a0STrond Myklebust 	for (i = 0; i < hashsize; i++) {
392e092bdcdSTrond Myklebust 		head = &cache->hashtable[i];
393e092bdcdSTrond Myklebust 		while (!hlist_empty(head)) {
394e092bdcdSTrond Myklebust 			cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
39531be5bf1STrond Myklebust 			rpcauth_unhash_cred_locked(cred);
39695cd6232STrond Myklebust 			/* Note: We now hold a reference to cred */
39795cd6232STrond Myklebust 			rpcauth_lru_remove_locked(cred);
39895cd6232STrond Myklebust 			list_add_tail(&cred->cr_lru, &free);
3991da177e4SLinus Torvalds 		}
4001da177e4SLinus Torvalds 	}
4019499b434STrond Myklebust 	spin_unlock(&cache->lock);
4021da177e4SLinus Torvalds 	spin_unlock(&rpc_credcache_lock);
4031da177e4SLinus Torvalds 	rpcauth_destroy_credlist(&free);
4041da177e4SLinus Torvalds }
4051da177e4SLinus Torvalds 
4063ab9bb72STrond Myklebust /*
4073ab9bb72STrond Myklebust  * Destroy the RPC credential cache
4083ab9bb72STrond Myklebust  */
4093ab9bb72STrond Myklebust void
4103ab9bb72STrond Myklebust rpcauth_destroy_credcache(struct rpc_auth *auth)
4113ab9bb72STrond Myklebust {
4123ab9bb72STrond Myklebust 	struct rpc_cred_cache *cache = auth->au_credcache;
4133ab9bb72STrond Myklebust 
4143ab9bb72STrond Myklebust 	if (cache) {
4153ab9bb72STrond Myklebust 		auth->au_credcache = NULL;
4163ab9bb72STrond Myklebust 		rpcauth_clear_credcache(cache);
417241269bdSTrond Myklebust 		kfree(cache->hashtable);
4183ab9bb72STrond Myklebust 		kfree(cache);
4193ab9bb72STrond Myklebust 	}
4203ab9bb72STrond Myklebust }
421e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_destroy_credcache);
4223ab9bb72STrond Myklebust 
423d2b83141STrond Myklebust 
424d2b83141STrond Myklebust #define RPC_AUTH_EXPIRY_MORATORIUM (60 * HZ)
425d2b83141STrond Myklebust 
4261da177e4SLinus Torvalds /*
4271da177e4SLinus Torvalds  * Remove stale credentials. Avoid sleeping inside the loop.
4281da177e4SLinus Torvalds  */
42970534a73SDave Chinner static long
430f5c2187cSTrond Myklebust rpcauth_prune_expired(struct list_head *free, int nr_to_scan)
4311da177e4SLinus Torvalds {
432eac0d18dSTrond Myklebust 	struct rpc_cred *cred, *next;
433d2b83141STrond Myklebust 	unsigned long expired = jiffies - RPC_AUTH_EXPIRY_MORATORIUM;
43470534a73SDave Chinner 	long freed = 0;
4351da177e4SLinus Torvalds 
436eac0d18dSTrond Myklebust 	list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) {
437eac0d18dSTrond Myklebust 
43820673406STrond Myklebust 		if (nr_to_scan-- == 0)
43920673406STrond Myklebust 			break;
44079b18181STrond Myklebust 		if (refcount_read(&cred->cr_count) > 1) {
44195cd6232STrond Myklebust 			rpcauth_lru_remove_locked(cred);
44295cd6232STrond Myklebust 			continue;
44395cd6232STrond Myklebust 		}
44493a05e65STrond Myklebust 		/*
44593a05e65STrond Myklebust 		 * Enforce a 60 second garbage collection moratorium
44693a05e65STrond Myklebust 		 * Note that the cred_unused list must be time-ordered.
44793a05e65STrond Myklebust 		 */
44895cd6232STrond Myklebust 		if (!time_in_range(cred->cr_expire, expired, jiffies))
44995cd6232STrond Myklebust 			continue;
45095cd6232STrond Myklebust 		if (!rpcauth_unhash_cred(cred))
451e092bdcdSTrond Myklebust 			continue;
452eac0d18dSTrond Myklebust 
45395cd6232STrond Myklebust 		rpcauth_lru_remove_locked(cred);
45495cd6232STrond Myklebust 		freed++;
455e092bdcdSTrond Myklebust 		list_add_tail(&cred->cr_lru, free);
4561da177e4SLinus Torvalds 	}
45795cd6232STrond Myklebust 	return freed ? freed : SHRINK_STOP;
4581da177e4SLinus Torvalds }
459e092bdcdSTrond Myklebust 
460bae6746fSTrond Myklebust static unsigned long
461bae6746fSTrond Myklebust rpcauth_cache_do_shrink(int nr_to_scan)
462bae6746fSTrond Myklebust {
463bae6746fSTrond Myklebust 	LIST_HEAD(free);
464bae6746fSTrond Myklebust 	unsigned long freed;
465bae6746fSTrond Myklebust 
466bae6746fSTrond Myklebust 	spin_lock(&rpc_credcache_lock);
467bae6746fSTrond Myklebust 	freed = rpcauth_prune_expired(&free, nr_to_scan);
468bae6746fSTrond Myklebust 	spin_unlock(&rpc_credcache_lock);
469bae6746fSTrond Myklebust 	rpcauth_destroy_credlist(&free);
470bae6746fSTrond Myklebust 
471bae6746fSTrond Myklebust 	return freed;
472bae6746fSTrond Myklebust }
473bae6746fSTrond Myklebust 
474e092bdcdSTrond Myklebust /*
475f5c2187cSTrond Myklebust  * Run memory cache shrinker.
476e092bdcdSTrond Myklebust  */
47770534a73SDave Chinner static unsigned long
47870534a73SDave Chinner rpcauth_cache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
47970534a73SDave Chinner 
480e092bdcdSTrond Myklebust {
48170534a73SDave Chinner 	if ((sc->gfp_mask & GFP_KERNEL) != GFP_KERNEL)
48270534a73SDave Chinner 		return SHRINK_STOP;
48370534a73SDave Chinner 
48470534a73SDave Chinner 	/* nothing left, don't come back */
485f5c2187cSTrond Myklebust 	if (list_empty(&cred_unused))
48670534a73SDave Chinner 		return SHRINK_STOP;
48770534a73SDave Chinner 
488bae6746fSTrond Myklebust 	return rpcauth_cache_do_shrink(sc->nr_to_scan);
48970534a73SDave Chinner }
49070534a73SDave Chinner 
49170534a73SDave Chinner static unsigned long
49270534a73SDave Chinner rpcauth_cache_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
49370534a73SDave Chinner 
49470534a73SDave Chinner {
4954c3ffd05SNeilBrown 	return number_cred_unused * sysctl_vfs_cache_pressure / 100;
4961da177e4SLinus Torvalds }
4971da177e4SLinus Torvalds 
498bae6746fSTrond Myklebust static void
499bae6746fSTrond Myklebust rpcauth_cache_enforce_limit(void)
500bae6746fSTrond Myklebust {
501bae6746fSTrond Myklebust 	unsigned long diff;
502bae6746fSTrond Myklebust 	unsigned int nr_to_scan;
503bae6746fSTrond Myklebust 
504bae6746fSTrond Myklebust 	if (number_cred_unused <= auth_max_cred_cachesize)
505bae6746fSTrond Myklebust 		return;
506bae6746fSTrond Myklebust 	diff = number_cred_unused - auth_max_cred_cachesize;
507bae6746fSTrond Myklebust 	nr_to_scan = 100;
508bae6746fSTrond Myklebust 	if (diff < nr_to_scan)
509bae6746fSTrond Myklebust 		nr_to_scan = diff;
510bae6746fSTrond Myklebust 	rpcauth_cache_do_shrink(nr_to_scan);
511bae6746fSTrond Myklebust }
512bae6746fSTrond Myklebust 
5131da177e4SLinus Torvalds /*
5141da177e4SLinus Torvalds  * Look up a process' credentials in the authentication cache
5151da177e4SLinus Torvalds  */
5161da177e4SLinus Torvalds struct rpc_cred *
5171da177e4SLinus Torvalds rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
5183c6e0bc8SJeff Layton 		int flags, gfp_t gfp)
5191da177e4SLinus Torvalds {
520e092bdcdSTrond Myklebust 	LIST_HEAD(free);
5211da177e4SLinus Torvalds 	struct rpc_cred_cache *cache = auth->au_credcache;
52231be5bf1STrond Myklebust 	struct rpc_cred	*cred = NULL,
52331be5bf1STrond Myklebust 			*entry, *new;
52425337fdcSTrond Myklebust 	unsigned int nr;
52525337fdcSTrond Myklebust 
52666cbd4baSFrank Sorenson 	nr = auth->au_ops->hash_cred(acred, cache->hashbits);
5271da177e4SLinus Torvalds 
52831be5bf1STrond Myklebust 	rcu_read_lock();
529b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) {
53031be5bf1STrond Myklebust 		if (!entry->cr_ops->crmatch(acred, entry, flags))
53131be5bf1STrond Myklebust 			continue;
53231be5bf1STrond Myklebust 		cred = get_rpccred(entry);
53307d02a67STrond Myklebust 		if (cred)
53431be5bf1STrond Myklebust 			break;
53531be5bf1STrond Myklebust 	}
53631be5bf1STrond Myklebust 	rcu_read_unlock();
53731be5bf1STrond Myklebust 
5389499b434STrond Myklebust 	if (cred != NULL)
53931be5bf1STrond Myklebust 		goto found;
54031be5bf1STrond Myklebust 
5413c6e0bc8SJeff Layton 	new = auth->au_ops->crcreate(auth, acred, flags, gfp);
54231be5bf1STrond Myklebust 	if (IS_ERR(new)) {
54331be5bf1STrond Myklebust 		cred = new;
54431be5bf1STrond Myklebust 		goto out;
54531be5bf1STrond Myklebust 	}
54631be5bf1STrond Myklebust 
5479499b434STrond Myklebust 	spin_lock(&cache->lock);
548b67bfe0dSSasha Levin 	hlist_for_each_entry(entry, &cache->hashtable[nr], cr_hash) {
549e092bdcdSTrond Myklebust 		if (!entry->cr_ops->crmatch(acred, entry, flags))
550e092bdcdSTrond Myklebust 			continue;
551e092bdcdSTrond Myklebust 		cred = get_rpccred(entry);
55207d02a67STrond Myklebust 		if (cred)
5531da177e4SLinus Torvalds 			break;
5541da177e4SLinus Torvalds 	}
55531be5bf1STrond Myklebust 	if (cred == NULL) {
55631be5bf1STrond Myklebust 		cred = new;
55731be5bf1STrond Myklebust 		set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags);
55879b18181STrond Myklebust 		refcount_inc(&cred->cr_count);
55931be5bf1STrond Myklebust 		hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]);
56031be5bf1STrond Myklebust 	} else
561e092bdcdSTrond Myklebust 		list_add_tail(&new->cr_lru, &free);
5629499b434STrond Myklebust 	spin_unlock(&cache->lock);
563bae6746fSTrond Myklebust 	rpcauth_cache_enforce_limit();
56431be5bf1STrond Myklebust found:
565f64f9e71SJoe Perches 	if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) &&
566f64f9e71SJoe Perches 	    cred->cr_ops->cr_init != NULL &&
567f64f9e71SJoe Perches 	    !(flags & RPCAUTH_LOOKUP_NEW)) {
568fba3bad4STrond Myklebust 		int res = cred->cr_ops->cr_init(auth, cred);
569fba3bad4STrond Myklebust 		if (res < 0) {
570fba3bad4STrond Myklebust 			put_rpccred(cred);
571fba3bad4STrond Myklebust 			cred = ERR_PTR(res);
572fba3bad4STrond Myklebust 		}
5731da177e4SLinus Torvalds 	}
57431be5bf1STrond Myklebust 	rpcauth_destroy_credlist(&free);
57531be5bf1STrond Myklebust out:
57631be5bf1STrond Myklebust 	return cred;
5771da177e4SLinus Torvalds }
578e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache);
5791da177e4SLinus Torvalds 
5801da177e4SLinus Torvalds struct rpc_cred *
5818a317760STrond Myklebust rpcauth_lookupcred(struct rpc_auth *auth, int flags)
5821da177e4SLinus Torvalds {
58386a264abSDavid Howells 	struct auth_cred acred;
5841da177e4SLinus Torvalds 	struct rpc_cred *ret;
58586a264abSDavid Howells 	const struct cred *cred = current_cred();
5861da177e4SLinus Torvalds 
58786a264abSDavid Howells 	memset(&acred, 0, sizeof(acred));
58897f68c6bSNeilBrown 	acred.cred = cred;
5898a317760STrond Myklebust 	ret = auth->au_ops->lookup_cred(auth, &acred, flags);
5901da177e4SLinus Torvalds 	return ret;
5911da177e4SLinus Torvalds }
59266b06860SAndy Adamson EXPORT_SYMBOL_GPL(rpcauth_lookupcred);
5931da177e4SLinus Torvalds 
5945fe4755eSTrond Myklebust void
5955fe4755eSTrond Myklebust rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred,
5965fe4755eSTrond Myklebust 		  struct rpc_auth *auth, const struct rpc_credops *ops)
5975fe4755eSTrond Myklebust {
5985fe4755eSTrond Myklebust 	INIT_HLIST_NODE(&cred->cr_hash);
599e092bdcdSTrond Myklebust 	INIT_LIST_HEAD(&cred->cr_lru);
60079b18181STrond Myklebust 	refcount_set(&cred->cr_count, 1);
6015fe4755eSTrond Myklebust 	cred->cr_auth = auth;
6022edd8d74SNeilBrown 	cred->cr_flags = 0;
6035fe4755eSTrond Myklebust 	cred->cr_ops = ops;
6045fe4755eSTrond Myklebust 	cred->cr_expire = jiffies;
60597f68c6bSNeilBrown 	cred->cr_cred = get_cred(acred->cred);
6065fe4755eSTrond Myklebust }
607e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_cred);
6085fe4755eSTrond Myklebust 
6098572b8e2STrond Myklebust static struct rpc_cred *
6105d351754STrond Myklebust rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags)
6111da177e4SLinus Torvalds {
6121be27f36STrond Myklebust 	struct rpc_auth *auth = task->tk_client->cl_auth;
6131da177e4SLinus Torvalds 	struct auth_cred acred = {
61497f68c6bSNeilBrown 		.cred = get_task_cred(&init_task),
6151da177e4SLinus Torvalds 	};
61697f68c6bSNeilBrown 	struct rpc_cred *ret;
6171da177e4SLinus Torvalds 
61897f68c6bSNeilBrown 	ret = auth->au_ops->lookup_cred(auth, &acred, lookupflags);
61997f68c6bSNeilBrown 	put_cred(acred.cred);
62097f68c6bSNeilBrown 	return ret;
621af093835STrond Myklebust }
622af093835STrond Myklebust 
6238572b8e2STrond Myklebust static struct rpc_cred *
6245e16923bSNeilBrown rpcauth_bind_machine_cred(struct rpc_task *task, int lookupflags)
6255e16923bSNeilBrown {
6265e16923bSNeilBrown 	struct rpc_auth *auth = task->tk_client->cl_auth;
6275e16923bSNeilBrown 	struct auth_cred acred = {
6285e16923bSNeilBrown 		.principal = task->tk_client->cl_principal,
6295e16923bSNeilBrown 		.cred = init_task.cred,
6305e16923bSNeilBrown 	};
6315e16923bSNeilBrown 
6325e16923bSNeilBrown 	if (!acred.principal)
6335e16923bSNeilBrown 		return NULL;
6345e16923bSNeilBrown 	return auth->au_ops->lookup_cred(auth, &acred, lookupflags);
6355e16923bSNeilBrown }
6365e16923bSNeilBrown 
6375e16923bSNeilBrown static struct rpc_cred *
6385d351754STrond Myklebust rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags)
639af093835STrond Myklebust {
640af093835STrond Myklebust 	struct rpc_auth *auth = task->tk_client->cl_auth;
641af093835STrond Myklebust 
6428572b8e2STrond Myklebust 	return rpcauth_lookupcred(auth, lookupflags);
6431da177e4SLinus Torvalds }
6441da177e4SLinus Torvalds 
645a17c2153STrond Myklebust static int
646a52458b4SNeilBrown rpcauth_bindcred(struct rpc_task *task, const struct cred *cred, int flags)
6471da177e4SLinus Torvalds {
648a17c2153STrond Myklebust 	struct rpc_rqst *req = task->tk_rqstp;
6495e16923bSNeilBrown 	struct rpc_cred *new = NULL;
6505d351754STrond Myklebust 	int lookupflags = 0;
651a52458b4SNeilBrown 	struct rpc_auth *auth = task->tk_client->cl_auth;
652a52458b4SNeilBrown 	struct auth_cred acred = {
653a52458b4SNeilBrown 		.cred = cred,
654a52458b4SNeilBrown 	};
6555d351754STrond Myklebust 
6565d351754STrond Myklebust 	if (flags & RPC_TASK_ASYNC)
6575d351754STrond Myklebust 		lookupflags |= RPCAUTH_LOOKUP_NEW;
6581de7eea9SNeilBrown 	if (task->tk_op_cred)
6591de7eea9SNeilBrown 		/* Task must use exactly this rpc_cred */
660d6efccd9SNeilBrown 		new = get_rpccred(task->tk_op_cred);
6611de7eea9SNeilBrown 	else if (cred != NULL && cred != &machine_cred)
662a52458b4SNeilBrown 		new = auth->au_ops->lookup_cred(auth, &acred, lookupflags);
6635e16923bSNeilBrown 	else if (cred == &machine_cred)
6645e16923bSNeilBrown 		new = rpcauth_bind_machine_cred(task, lookupflags);
6655e16923bSNeilBrown 
6665e16923bSNeilBrown 	/* If machine cred couldn't be bound, try a root cred */
6675e16923bSNeilBrown 	if (new)
6685e16923bSNeilBrown 		;
6695e16923bSNeilBrown 	else if (cred == &machine_cred || (flags & RPC_TASK_ROOTCREDS))
6708572b8e2STrond Myklebust 		new = rpcauth_bind_root_cred(task, lookupflags);
671a68a72e1SNeilBrown 	else if (flags & RPC_TASK_NULLCREDS)
672a68a72e1SNeilBrown 		new = authnull_ops.lookup_cred(NULL, NULL, 0);
6734ccda2cdSTrond Myklebust 	else
6748572b8e2STrond Myklebust 		new = rpcauth_bind_new_cred(task, lookupflags);
6758572b8e2STrond Myklebust 	if (IS_ERR(new))
6768572b8e2STrond Myklebust 		return PTR_ERR(new);
677a17c2153STrond Myklebust 	put_rpccred(req->rq_cred);
678a17c2153STrond Myklebust 	req->rq_cred = new;
6798572b8e2STrond Myklebust 	return 0;
6801da177e4SLinus Torvalds }
6811da177e4SLinus Torvalds 
6821da177e4SLinus Torvalds void
6831da177e4SLinus Torvalds put_rpccred(struct rpc_cred *cred)
6841da177e4SLinus Torvalds {
6859a8f6b5eSTrond Myklebust 	if (cred == NULL)
6869a8f6b5eSTrond Myklebust 		return;
68795cd6232STrond Myklebust 	rcu_read_lock();
68879b18181STrond Myklebust 	if (refcount_dec_and_test(&cred->cr_count))
68995cd6232STrond Myklebust 		goto destroy;
69079b18181STrond Myklebust 	if (refcount_read(&cred->cr_count) != 1 ||
69195cd6232STrond Myklebust 	    !test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
69295cd6232STrond Myklebust 		goto out;
693f0380f3dSTrond Myklebust 	if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) {
694e092bdcdSTrond Myklebust 		cred->cr_expire = jiffies;
69595cd6232STrond Myklebust 		rpcauth_lru_add(cred);
69695cd6232STrond Myklebust 		/* Race breaker */
69795cd6232STrond Myklebust 		if (unlikely(!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags)))
69895cd6232STrond Myklebust 			rpcauth_lru_remove(cred);
69995cd6232STrond Myklebust 	} else if (rpcauth_unhash_cred(cred)) {
70095cd6232STrond Myklebust 		rpcauth_lru_remove(cred);
70179b18181STrond Myklebust 		if (refcount_dec_and_test(&cred->cr_count))
70295cd6232STrond Myklebust 			goto destroy;
703f0380f3dSTrond Myklebust 	}
70495cd6232STrond Myklebust out:
70595cd6232STrond Myklebust 	rcu_read_unlock();
706f0380f3dSTrond Myklebust 	return;
70795cd6232STrond Myklebust destroy:
70895cd6232STrond Myklebust 	rcu_read_unlock();
70995cd6232STrond Myklebust 	cred->cr_ops->crdestroy(cred);
7101da177e4SLinus Torvalds }
711e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(put_rpccred);
7121da177e4SLinus Torvalds 
713e8680a24SChuck Lever /**
714e8680a24SChuck Lever  * rpcauth_marshcred - Append RPC credential to end of @xdr
715e8680a24SChuck Lever  * @task: controlling RPC task
716e8680a24SChuck Lever  * @xdr: xdr_stream containing initial portion of RPC Call header
717e8680a24SChuck Lever  *
718e8680a24SChuck Lever  * On success, an appropriate verifier is added to @xdr, @xdr is
719e8680a24SChuck Lever  * updated to point past the verifier, and zero is returned.
720e8680a24SChuck Lever  * Otherwise, @xdr is in an undefined state and a negative errno
721e8680a24SChuck Lever  * is returned.
722e8680a24SChuck Lever  */
723e8680a24SChuck Lever int rpcauth_marshcred(struct rpc_task *task, struct xdr_stream *xdr)
7241da177e4SLinus Torvalds {
725e8680a24SChuck Lever 	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
7261da177e4SLinus Torvalds 
727e8680a24SChuck Lever 	return ops->crmarshal(task, xdr);
7281da177e4SLinus Torvalds }
7291da177e4SLinus Torvalds 
730e8680a24SChuck Lever /**
731e8680a24SChuck Lever  * rpcauth_wrap_req_encode - XDR encode the RPC procedure
732e8680a24SChuck Lever  * @task: controlling RPC task
733e8680a24SChuck Lever  * @xdr: stream where on-the-wire bytes are to be marshalled
734e8680a24SChuck Lever  *
735e8680a24SChuck Lever  * On success, @xdr contains the encoded and wrapped message.
736e8680a24SChuck Lever  * Otherwise, @xdr is in an undefined state.
737e8680a24SChuck Lever  */
738e8680a24SChuck Lever int rpcauth_wrap_req_encode(struct rpc_task *task, struct xdr_stream *xdr)
7399f06c719SChuck Lever {
740e8680a24SChuck Lever 	kxdreproc_t encode = task->tk_msg.rpc_proc->p_encode;
7419f06c719SChuck Lever 
742e8680a24SChuck Lever 	encode(task->tk_rqstp, xdr, task->tk_msg.rpc_argp);
7439f06c719SChuck Lever 	return 0;
7441da177e4SLinus Torvalds }
745e8680a24SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_wrap_req_encode);
746e8680a24SChuck Lever 
747e8680a24SChuck Lever /**
748e8680a24SChuck Lever  * rpcauth_wrap_req - XDR encode and wrap the RPC procedure
749e8680a24SChuck Lever  * @task: controlling RPC task
750e8680a24SChuck Lever  * @xdr: stream where on-the-wire bytes are to be marshalled
751e8680a24SChuck Lever  *
752e8680a24SChuck Lever  * On success, @xdr contains the encoded and wrapped message,
753e8680a24SChuck Lever  * and zero is returned. Otherwise, @xdr is in an undefined
754e8680a24SChuck Lever  * state and a negative errno is returned.
755e8680a24SChuck Lever  */
756e8680a24SChuck Lever int rpcauth_wrap_req(struct rpc_task *task, struct xdr_stream *xdr)
757e8680a24SChuck Lever {
758e8680a24SChuck Lever 	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
759e8680a24SChuck Lever 
760e8680a24SChuck Lever 	return ops->crwrap_req(task, xdr);
761e8680a24SChuck Lever }
7621da177e4SLinus Torvalds 
763a0584ee9SChuck Lever /**
764a0584ee9SChuck Lever  * rpcauth_checkverf - Validate verifier in RPC Reply header
765a0584ee9SChuck Lever  * @task: controlling RPC task
766a0584ee9SChuck Lever  * @xdr: xdr_stream containing RPC Reply header
767a0584ee9SChuck Lever  *
768a0584ee9SChuck Lever  * On success, @xdr is updated to point past the verifier and
769a0584ee9SChuck Lever  * zero is returned. Otherwise, @xdr is in an undefined state
770a0584ee9SChuck Lever  * and a negative errno is returned.
771a0584ee9SChuck Lever  */
772a0584ee9SChuck Lever int
773a0584ee9SChuck Lever rpcauth_checkverf(struct rpc_task *task, struct xdr_stream *xdr)
774bf269551SChuck Lever {
775a0584ee9SChuck Lever 	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
776bf269551SChuck Lever 
777a0584ee9SChuck Lever 	return ops->crvalidate(task, xdr);
778bf269551SChuck Lever }
779bf269551SChuck Lever 
780a0584ee9SChuck Lever /**
781a0584ee9SChuck Lever  * rpcauth_unwrap_resp_decode - Invoke XDR decode function
782a0584ee9SChuck Lever  * @task: controlling RPC task
783a0584ee9SChuck Lever  * @xdr: stream where the Reply message resides
784a0584ee9SChuck Lever  *
785a0584ee9SChuck Lever  * Returns zero on success; otherwise a negative errno is returned.
786a0584ee9SChuck Lever  */
7871da177e4SLinus Torvalds int
788a0584ee9SChuck Lever rpcauth_unwrap_resp_decode(struct rpc_task *task, struct xdr_stream *xdr)
7891da177e4SLinus Torvalds {
790a0584ee9SChuck Lever 	kxdrdproc_t decode = task->tk_msg.rpc_proc->p_decode;
7911da177e4SLinus Torvalds 
792a0584ee9SChuck Lever 	return decode(task->tk_rqstp, xdr, task->tk_msg.rpc_resp);
793a0584ee9SChuck Lever }
794a0584ee9SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_unwrap_resp_decode);
795a0584ee9SChuck Lever 
796a0584ee9SChuck Lever /**
797a0584ee9SChuck Lever  * rpcauth_unwrap_resp - Invoke unwrap and decode function for the cred
798a0584ee9SChuck Lever  * @task: controlling RPC task
799a0584ee9SChuck Lever  * @xdr: stream where the Reply message resides
800a0584ee9SChuck Lever  *
801a0584ee9SChuck Lever  * Returns zero on success; otherwise a negative errno is returned.
802a0584ee9SChuck Lever  */
803a0584ee9SChuck Lever int
804a0584ee9SChuck Lever rpcauth_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr)
805a0584ee9SChuck Lever {
806a0584ee9SChuck Lever 	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
807a0584ee9SChuck Lever 
808a0584ee9SChuck Lever 	return ops->crunwrap_resp(task, xdr);
8091da177e4SLinus Torvalds }
8101da177e4SLinus Torvalds 
8113021a5bbSTrond Myklebust bool
8123021a5bbSTrond Myklebust rpcauth_xmit_need_reencode(struct rpc_task *task)
8133021a5bbSTrond Myklebust {
8143021a5bbSTrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
8153021a5bbSTrond Myklebust 
8163021a5bbSTrond Myklebust 	if (!cred || !cred->cr_ops->crneed_reencode)
8173021a5bbSTrond Myklebust 		return false;
8183021a5bbSTrond Myklebust 	return cred->cr_ops->crneed_reencode(task);
8193021a5bbSTrond Myklebust }
8203021a5bbSTrond Myklebust 
8211da177e4SLinus Torvalds int
8221da177e4SLinus Torvalds rpcauth_refreshcred(struct rpc_task *task)
8231da177e4SLinus Torvalds {
8249a84d380STrond Myklebust 	struct rpc_cred	*cred;
8251da177e4SLinus Torvalds 	int err;
8261da177e4SLinus Torvalds 
827a17c2153STrond Myklebust 	cred = task->tk_rqstp->rq_cred;
828a17c2153STrond Myklebust 	if (cred == NULL) {
829a17c2153STrond Myklebust 		err = rpcauth_bindcred(task, task->tk_msg.rpc_cred, task->tk_flags);
830a17c2153STrond Myklebust 		if (err < 0)
831a17c2153STrond Myklebust 			goto out;
832a17c2153STrond Myklebust 		cred = task->tk_rqstp->rq_cred;
833f81c6224SJoe Perches 	}
8340bbacc40SChuck Lever 
8351da177e4SLinus Torvalds 	err = cred->cr_ops->crrefresh(task);
836a17c2153STrond Myklebust out:
8371da177e4SLinus Torvalds 	if (err < 0)
8381da177e4SLinus Torvalds 		task->tk_status = err;
8391da177e4SLinus Torvalds 	return err;
8401da177e4SLinus Torvalds }
8411da177e4SLinus Torvalds 
8421da177e4SLinus Torvalds void
8431da177e4SLinus Torvalds rpcauth_invalcred(struct rpc_task *task)
8441da177e4SLinus Torvalds {
845a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
846fc432dd9STrond Myklebust 
847fc432dd9STrond Myklebust 	if (cred)
848fc432dd9STrond Myklebust 		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
8491da177e4SLinus Torvalds }
8501da177e4SLinus Torvalds 
8511da177e4SLinus Torvalds int
8521da177e4SLinus Torvalds rpcauth_uptodatecred(struct rpc_task *task)
8531da177e4SLinus Torvalds {
854a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
855fc432dd9STrond Myklebust 
856fc432dd9STrond Myklebust 	return cred == NULL ||
857fc432dd9STrond Myklebust 		test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0;
8581da177e4SLinus Torvalds }
859f5c2187cSTrond Myklebust 
8608e1f936bSRusty Russell static struct shrinker rpc_cred_shrinker = {
86170534a73SDave Chinner 	.count_objects = rpcauth_cache_shrink_count,
86270534a73SDave Chinner 	.scan_objects = rpcauth_cache_shrink_scan,
8638e1f936bSRusty Russell 	.seeks = DEFAULT_SEEKS,
8648e1f936bSRusty Russell };
865f5c2187cSTrond Myklebust 
8665d8d9a4dSTrond Myklebust int __init rpcauth_init_module(void)
867f5c2187cSTrond Myklebust {
8685d8d9a4dSTrond Myklebust 	int err;
8695d8d9a4dSTrond Myklebust 
8705d8d9a4dSTrond Myklebust 	err = rpc_init_authunix();
8715d8d9a4dSTrond Myklebust 	if (err < 0)
8725d8d9a4dSTrond Myklebust 		goto out1;
8732864486bSKinglong Mee 	err = register_shrinker(&rpc_cred_shrinker);
8742864486bSKinglong Mee 	if (err < 0)
87589a4f758SNeilBrown 		goto out2;
8765d8d9a4dSTrond Myklebust 	return 0;
8775d8d9a4dSTrond Myklebust out2:
8785d8d9a4dSTrond Myklebust 	rpc_destroy_authunix();
8795d8d9a4dSTrond Myklebust out1:
8805d8d9a4dSTrond Myklebust 	return err;
881f5c2187cSTrond Myklebust }
882f5c2187cSTrond Myklebust 
883c135e84aSStephen Rothwell void rpcauth_remove_module(void)
884f5c2187cSTrond Myklebust {
8855d8d9a4dSTrond Myklebust 	rpc_destroy_authunix();
8868e1f936bSRusty Russell 	unregister_shrinker(&rpc_cred_shrinker);
887f5c2187cSTrond Myklebust }
888