xref: /linux/net/sunrpc/auth.c (revision e8680a24a269bd6dcb533f4e4a5faba9ae58925c)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  * linux/net/sunrpc/auth.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  * Generic RPC client authentication API.
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
71da177e4SLinus Torvalds  */
81da177e4SLinus Torvalds 
91da177e4SLinus Torvalds #include <linux/types.h>
101da177e4SLinus Torvalds #include <linux/sched.h>
115b825c3aSIngo Molnar #include <linux/cred.h>
121da177e4SLinus Torvalds #include <linux/module.h>
131da177e4SLinus Torvalds #include <linux/slab.h>
141da177e4SLinus Torvalds #include <linux/errno.h>
1525337fdcSTrond Myklebust #include <linux/hash.h>
161da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h>
176a1a1e34SChuck Lever #include <linux/sunrpc/gss_api.h>
181da177e4SLinus Torvalds #include <linux/spinlock.h>
191da177e4SLinus Torvalds 
20241269bdSTrond Myklebust #define RPC_CREDCACHE_DEFAULT_HASHBITS	(4)
21241269bdSTrond Myklebust struct rpc_cred_cache {
22241269bdSTrond Myklebust 	struct hlist_head	*hashtable;
23241269bdSTrond Myklebust 	unsigned int		hashbits;
24241269bdSTrond Myklebust 	spinlock_t		lock;
25241269bdSTrond Myklebust };
26241269bdSTrond Myklebust 
27241269bdSTrond Myklebust static unsigned int auth_hashbits = RPC_CREDCACHE_DEFAULT_HASHBITS;
28241269bdSTrond Myklebust 
294e4c3befSTrond Myklebust static const struct rpc_authops __rcu *auth_flavors[RPC_AUTH_MAXFLAVOR] = {
304e4c3befSTrond Myklebust 	[RPC_AUTH_NULL] = (const struct rpc_authops __force __rcu *)&authnull_ops,
314e4c3befSTrond Myklebust 	[RPC_AUTH_UNIX] = (const struct rpc_authops __force __rcu *)&authunix_ops,
321da177e4SLinus Torvalds 	NULL,			/* others can be loadable modules */
331da177e4SLinus Torvalds };
341da177e4SLinus Torvalds 
35e092bdcdSTrond Myklebust static LIST_HEAD(cred_unused);
36f5c2187cSTrond Myklebust static unsigned long number_cred_unused;
37e092bdcdSTrond Myklebust 
38a52458b4SNeilBrown static struct cred machine_cred = {
39a52458b4SNeilBrown 	.usage = ATOMIC_INIT(1),
40e7f45099SSantosh kumar pradhan #ifdef CONFIG_DEBUG_CREDENTIALS
41e7f45099SSantosh kumar pradhan 	.magic = CRED_MAGIC,
42e7f45099SSantosh kumar pradhan #endif
435e16923bSNeilBrown };
445e16923bSNeilBrown 
455e16923bSNeilBrown /*
465e16923bSNeilBrown  * Return the machine_cred pointer to be used whenever
475e16923bSNeilBrown  * the a generic machine credential is needed.
485e16923bSNeilBrown  */
49a52458b4SNeilBrown const struct cred *rpc_machine_cred(void)
505e16923bSNeilBrown {
515e16923bSNeilBrown 	return &machine_cred;
525e16923bSNeilBrown }
535e16923bSNeilBrown EXPORT_SYMBOL_GPL(rpc_machine_cred);
545e16923bSNeilBrown 
55db5fe265SMiquel van Smoorenburg #define MAX_HASHTABLE_BITS (14)
568e4e15d4SStephen Rothwell static int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp)
57241269bdSTrond Myklebust {
58241269bdSTrond Myklebust 	unsigned long num;
59241269bdSTrond Myklebust 	unsigned int nbits;
60241269bdSTrond Myklebust 	int ret;
61241269bdSTrond Myklebust 
62241269bdSTrond Myklebust 	if (!val)
63241269bdSTrond Myklebust 		goto out_inval;
6400cfaa94SDaniel Walter 	ret = kstrtoul(val, 0, &num);
651a54c0cfSDan Carpenter 	if (ret)
66241269bdSTrond Myklebust 		goto out_inval;
6734ae685cSFrank Sorenson 	nbits = fls(num - 1);
68241269bdSTrond Myklebust 	if (nbits > MAX_HASHTABLE_BITS || nbits < 2)
69241269bdSTrond Myklebust 		goto out_inval;
70241269bdSTrond Myklebust 	*(unsigned int *)kp->arg = nbits;
71241269bdSTrond Myklebust 	return 0;
72241269bdSTrond Myklebust out_inval:
73241269bdSTrond Myklebust 	return -EINVAL;
74241269bdSTrond Myklebust }
75241269bdSTrond Myklebust 
768e4e15d4SStephen Rothwell static int param_get_hashtbl_sz(char *buffer, const struct kernel_param *kp)
77241269bdSTrond Myklebust {
78241269bdSTrond Myklebust 	unsigned int nbits;
79241269bdSTrond Myklebust 
80241269bdSTrond Myklebust 	nbits = *(unsigned int *)kp->arg;
81241269bdSTrond Myklebust 	return sprintf(buffer, "%u", 1U << nbits);
82241269bdSTrond Myklebust }
83241269bdSTrond Myklebust 
84241269bdSTrond Myklebust #define param_check_hashtbl_sz(name, p) __param_check(name, p, unsigned int);
85241269bdSTrond Myklebust 
869c27847dSLuis R. Rodriguez static const struct kernel_param_ops param_ops_hashtbl_sz = {
878e4e15d4SStephen Rothwell 	.set = param_set_hashtbl_sz,
888e4e15d4SStephen Rothwell 	.get = param_get_hashtbl_sz,
898e4e15d4SStephen Rothwell };
908e4e15d4SStephen Rothwell 
91241269bdSTrond Myklebust module_param_named(auth_hashtable_size, auth_hashbits, hashtbl_sz, 0644);
92241269bdSTrond Myklebust MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size");
93241269bdSTrond Myklebust 
94bae6746fSTrond Myklebust static unsigned long auth_max_cred_cachesize = ULONG_MAX;
95bae6746fSTrond Myklebust module_param(auth_max_cred_cachesize, ulong, 0644);
96bae6746fSTrond Myklebust MODULE_PARM_DESC(auth_max_cred_cachesize, "RPC credential maximum total cache size");
97bae6746fSTrond Myklebust 
981da177e4SLinus Torvalds static u32
991da177e4SLinus Torvalds pseudoflavor_to_flavor(u32 flavor) {
1001c74a244SChuck Lever 	if (flavor > RPC_AUTH_MAXFLAVOR)
1011da177e4SLinus Torvalds 		return RPC_AUTH_GSS;
1021da177e4SLinus Torvalds 	return flavor;
1031da177e4SLinus Torvalds }
1041da177e4SLinus Torvalds 
1051da177e4SLinus Torvalds int
106f1c0a861STrond Myklebust rpcauth_register(const struct rpc_authops *ops)
1071da177e4SLinus Torvalds {
1084e4c3befSTrond Myklebust 	const struct rpc_authops *old;
1091da177e4SLinus Torvalds 	rpc_authflavor_t flavor;
1101da177e4SLinus Torvalds 
1111da177e4SLinus Torvalds 	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
1121da177e4SLinus Torvalds 		return -EINVAL;
1134e4c3befSTrond Myklebust 	old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], NULL, ops);
1144e4c3befSTrond Myklebust 	if (old == NULL || old == ops)
1154e4c3befSTrond Myklebust 		return 0;
1164e4c3befSTrond Myklebust 	return -EPERM;
1171da177e4SLinus Torvalds }
118e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_register);
1191da177e4SLinus Torvalds 
1201da177e4SLinus Torvalds int
121f1c0a861STrond Myklebust rpcauth_unregister(const struct rpc_authops *ops)
1221da177e4SLinus Torvalds {
1234e4c3befSTrond Myklebust 	const struct rpc_authops *old;
1241da177e4SLinus Torvalds 	rpc_authflavor_t flavor;
1251da177e4SLinus Torvalds 
1261da177e4SLinus Torvalds 	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
1271da177e4SLinus Torvalds 		return -EINVAL;
1284e4c3befSTrond Myklebust 
1294e4c3befSTrond Myklebust 	old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], ops, NULL);
1304e4c3befSTrond Myklebust 	if (old == ops || old == NULL)
1314e4c3befSTrond Myklebust 		return 0;
1324e4c3befSTrond Myklebust 	return -EPERM;
1331da177e4SLinus Torvalds }
134e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_unregister);
1351da177e4SLinus Torvalds 
1364e4c3befSTrond Myklebust static const struct rpc_authops *
1374e4c3befSTrond Myklebust rpcauth_get_authops(rpc_authflavor_t flavor)
1384e4c3befSTrond Myklebust {
1394e4c3befSTrond Myklebust 	const struct rpc_authops *ops;
1404e4c3befSTrond Myklebust 
1414e4c3befSTrond Myklebust 	if (flavor >= RPC_AUTH_MAXFLAVOR)
1424e4c3befSTrond Myklebust 		return NULL;
1434e4c3befSTrond Myklebust 
1444e4c3befSTrond Myklebust 	rcu_read_lock();
1454e4c3befSTrond Myklebust 	ops = rcu_dereference(auth_flavors[flavor]);
1464e4c3befSTrond Myklebust 	if (ops == NULL) {
1474e4c3befSTrond Myklebust 		rcu_read_unlock();
1484e4c3befSTrond Myklebust 		request_module("rpc-auth-%u", flavor);
1494e4c3befSTrond Myklebust 		rcu_read_lock();
1504e4c3befSTrond Myklebust 		ops = rcu_dereference(auth_flavors[flavor]);
1514e4c3befSTrond Myklebust 		if (ops == NULL)
1524e4c3befSTrond Myklebust 			goto out;
1534e4c3befSTrond Myklebust 	}
1544e4c3befSTrond Myklebust 	if (!try_module_get(ops->owner))
1554e4c3befSTrond Myklebust 		ops = NULL;
1564e4c3befSTrond Myklebust out:
1574e4c3befSTrond Myklebust 	rcu_read_unlock();
1584e4c3befSTrond Myklebust 	return ops;
1594e4c3befSTrond Myklebust }
1604e4c3befSTrond Myklebust 
1614e4c3befSTrond Myklebust static void
1624e4c3befSTrond Myklebust rpcauth_put_authops(const struct rpc_authops *ops)
1634e4c3befSTrond Myklebust {
1644e4c3befSTrond Myklebust 	module_put(ops->owner);
1654e4c3befSTrond Myklebust }
1664e4c3befSTrond Myklebust 
1676a1a1e34SChuck Lever /**
1689568c5e9SChuck Lever  * rpcauth_get_pseudoflavor - check if security flavor is supported
1699568c5e9SChuck Lever  * @flavor: a security flavor
1709568c5e9SChuck Lever  * @info: a GSS mech OID, quality of protection, and service value
1719568c5e9SChuck Lever  *
1729568c5e9SChuck Lever  * Verifies that an appropriate kernel module is available or already loaded.
1739568c5e9SChuck Lever  * Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is
1749568c5e9SChuck Lever  * not supported locally.
1759568c5e9SChuck Lever  */
1769568c5e9SChuck Lever rpc_authflavor_t
1779568c5e9SChuck Lever rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
1789568c5e9SChuck Lever {
1794e4c3befSTrond Myklebust 	const struct rpc_authops *ops = rpcauth_get_authops(flavor);
1809568c5e9SChuck Lever 	rpc_authflavor_t pseudoflavor;
1819568c5e9SChuck Lever 
1824e4c3befSTrond Myklebust 	if (!ops)
1839568c5e9SChuck Lever 		return RPC_AUTH_MAXFLAVOR;
1849568c5e9SChuck Lever 	pseudoflavor = flavor;
1859568c5e9SChuck Lever 	if (ops->info2flavor != NULL)
1869568c5e9SChuck Lever 		pseudoflavor = ops->info2flavor(info);
1879568c5e9SChuck Lever 
1884e4c3befSTrond Myklebust 	rpcauth_put_authops(ops);
1899568c5e9SChuck Lever 	return pseudoflavor;
1909568c5e9SChuck Lever }
1919568c5e9SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);
1929568c5e9SChuck Lever 
1939568c5e9SChuck Lever /**
194a77c806fSChuck Lever  * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor
195a77c806fSChuck Lever  * @pseudoflavor: GSS pseudoflavor to match
196a77c806fSChuck Lever  * @info: rpcsec_gss_info structure to fill in
197a77c806fSChuck Lever  *
198a77c806fSChuck Lever  * Returns zero and fills in "info" if pseudoflavor matches a
199a77c806fSChuck Lever  * supported mechanism.
200a77c806fSChuck Lever  */
201a77c806fSChuck Lever int
202a77c806fSChuck Lever rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
203a77c806fSChuck Lever {
204a77c806fSChuck Lever 	rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor);
205a77c806fSChuck Lever 	const struct rpc_authops *ops;
206a77c806fSChuck Lever 	int result;
207a77c806fSChuck Lever 
2084e4c3befSTrond Myklebust 	ops = rpcauth_get_authops(flavor);
209a77c806fSChuck Lever 	if (ops == NULL)
210a77c806fSChuck Lever 		return -ENOENT;
211a77c806fSChuck Lever 
212a77c806fSChuck Lever 	result = -ENOENT;
213a77c806fSChuck Lever 	if (ops->flavor2info != NULL)
214a77c806fSChuck Lever 		result = ops->flavor2info(pseudoflavor, info);
215a77c806fSChuck Lever 
2164e4c3befSTrond Myklebust 	rpcauth_put_authops(ops);
217a77c806fSChuck Lever 	return result;
218a77c806fSChuck Lever }
219a77c806fSChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);
220a77c806fSChuck Lever 
221a77c806fSChuck Lever /**
2226a1a1e34SChuck Lever  * rpcauth_list_flavors - discover registered flavors and pseudoflavors
2236a1a1e34SChuck Lever  * @array: array to fill in
2246a1a1e34SChuck Lever  * @size: size of "array"
2256a1a1e34SChuck Lever  *
2266a1a1e34SChuck Lever  * Returns the number of array items filled in, or a negative errno.
2276a1a1e34SChuck Lever  *
2286a1a1e34SChuck Lever  * The returned array is not sorted by any policy.  Callers should not
2296a1a1e34SChuck Lever  * rely on the order of the items in the returned array.
2306a1a1e34SChuck Lever  */
2316a1a1e34SChuck Lever int
2326a1a1e34SChuck Lever rpcauth_list_flavors(rpc_authflavor_t *array, int size)
2336a1a1e34SChuck Lever {
2344e4c3befSTrond Myklebust 	const struct rpc_authops *ops;
2354e4c3befSTrond Myklebust 	rpc_authflavor_t flavor, pseudos[4];
2364e4c3befSTrond Myklebust 	int i, len, result = 0;
2376a1a1e34SChuck Lever 
2384e4c3befSTrond Myklebust 	rcu_read_lock();
2396a1a1e34SChuck Lever 	for (flavor = 0; flavor < RPC_AUTH_MAXFLAVOR; flavor++) {
2404e4c3befSTrond Myklebust 		ops = rcu_dereference(auth_flavors[flavor]);
2416a1a1e34SChuck Lever 		if (result >= size) {
2426a1a1e34SChuck Lever 			result = -ENOMEM;
2436a1a1e34SChuck Lever 			break;
2446a1a1e34SChuck Lever 		}
2456a1a1e34SChuck Lever 
2466a1a1e34SChuck Lever 		if (ops == NULL)
2476a1a1e34SChuck Lever 			continue;
2486a1a1e34SChuck Lever 		if (ops->list_pseudoflavors == NULL) {
2496a1a1e34SChuck Lever 			array[result++] = ops->au_flavor;
2506a1a1e34SChuck Lever 			continue;
2516a1a1e34SChuck Lever 		}
2526a1a1e34SChuck Lever 		len = ops->list_pseudoflavors(pseudos, ARRAY_SIZE(pseudos));
2536a1a1e34SChuck Lever 		if (len < 0) {
2546a1a1e34SChuck Lever 			result = len;
2556a1a1e34SChuck Lever 			break;
2566a1a1e34SChuck Lever 		}
2576a1a1e34SChuck Lever 		for (i = 0; i < len; i++) {
2586a1a1e34SChuck Lever 			if (result >= size) {
2596a1a1e34SChuck Lever 				result = -ENOMEM;
2606a1a1e34SChuck Lever 				break;
2616a1a1e34SChuck Lever 			}
2626a1a1e34SChuck Lever 			array[result++] = pseudos[i];
2636a1a1e34SChuck Lever 		}
2646a1a1e34SChuck Lever 	}
2654e4c3befSTrond Myklebust 	rcu_read_unlock();
2666a1a1e34SChuck Lever 	return result;
2676a1a1e34SChuck Lever }
2686a1a1e34SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_list_flavors);
2696a1a1e34SChuck Lever 
2701da177e4SLinus Torvalds struct rpc_auth *
27182b98ca5SSargun Dhillon rpcauth_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
2721da177e4SLinus Torvalds {
2734e4c3befSTrond Myklebust 	struct rpc_auth	*auth = ERR_PTR(-EINVAL);
274f1c0a861STrond Myklebust 	const struct rpc_authops *ops;
275c2190661STrond Myklebust 	u32 flavor = pseudoflavor_to_flavor(args->pseudoflavor);
2761da177e4SLinus Torvalds 
2774e4c3befSTrond Myklebust 	ops = rpcauth_get_authops(flavor);
2784e4c3befSTrond Myklebust 	if (ops == NULL)
279f344f6dfSOlaf Kirch 		goto out;
280f344f6dfSOlaf Kirch 
281c2190661STrond Myklebust 	auth = ops->create(args, clnt);
2824e4c3befSTrond Myklebust 
2834e4c3befSTrond Myklebust 	rpcauth_put_authops(ops);
2846a19275aSJ. Bruce Fields 	if (IS_ERR(auth))
2856a19275aSJ. Bruce Fields 		return auth;
2861da177e4SLinus Torvalds 	if (clnt->cl_auth)
287de7a8ce3STrond Myklebust 		rpcauth_release(clnt->cl_auth);
2881da177e4SLinus Torvalds 	clnt->cl_auth = auth;
289f344f6dfSOlaf Kirch 
290f344f6dfSOlaf Kirch out:
2911da177e4SLinus Torvalds 	return auth;
2921da177e4SLinus Torvalds }
293e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_create);
2941da177e4SLinus Torvalds 
2951da177e4SLinus Torvalds void
296de7a8ce3STrond Myklebust rpcauth_release(struct rpc_auth *auth)
2971da177e4SLinus Torvalds {
298331bc71cSTrond Myklebust 	if (!refcount_dec_and_test(&auth->au_count))
2991da177e4SLinus Torvalds 		return;
3001da177e4SLinus Torvalds 	auth->au_ops->destroy(auth);
3011da177e4SLinus Torvalds }
3021da177e4SLinus Torvalds 
3031da177e4SLinus Torvalds static DEFINE_SPINLOCK(rpc_credcache_lock);
3041da177e4SLinus Torvalds 
30595cd6232STrond Myklebust /*
30695cd6232STrond Myklebust  * On success, the caller is responsible for freeing the reference
30795cd6232STrond Myklebust  * held by the hashtable
30895cd6232STrond Myklebust  */
30995cd6232STrond Myklebust static bool
31031be5bf1STrond Myklebust rpcauth_unhash_cred_locked(struct rpc_cred *cred)
31131be5bf1STrond Myklebust {
31295cd6232STrond Myklebust 	if (!test_and_clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
31395cd6232STrond Myklebust 		return false;
31431be5bf1STrond Myklebust 	hlist_del_rcu(&cred->cr_hash);
31595cd6232STrond Myklebust 	return true;
31631be5bf1STrond Myklebust }
31731be5bf1STrond Myklebust 
31895cd6232STrond Myklebust static bool
3199499b434STrond Myklebust rpcauth_unhash_cred(struct rpc_cred *cred)
3209499b434STrond Myklebust {
3219499b434STrond Myklebust 	spinlock_t *cache_lock;
32295cd6232STrond Myklebust 	bool ret;
3239499b434STrond Myklebust 
32495cd6232STrond Myklebust 	if (!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
32595cd6232STrond Myklebust 		return false;
3269499b434STrond Myklebust 	cache_lock = &cred->cr_auth->au_credcache->lock;
3279499b434STrond Myklebust 	spin_lock(cache_lock);
32895cd6232STrond Myklebust 	ret = rpcauth_unhash_cred_locked(cred);
3299499b434STrond Myklebust 	spin_unlock(cache_lock);
330f0380f3dSTrond Myklebust 	return ret;
3319499b434STrond Myklebust }
3329499b434STrond Myklebust 
3331da177e4SLinus Torvalds /*
3341da177e4SLinus Torvalds  * Initialize RPC credential cache
3351da177e4SLinus Torvalds  */
3361da177e4SLinus Torvalds int
337f5c2187cSTrond Myklebust rpcauth_init_credcache(struct rpc_auth *auth)
3381da177e4SLinus Torvalds {
3391da177e4SLinus Torvalds 	struct rpc_cred_cache *new;
340988664a0STrond Myklebust 	unsigned int hashsize;
3411da177e4SLinus Torvalds 
3428b3a7005SKris Katterjohn 	new = kmalloc(sizeof(*new), GFP_KERNEL);
3431da177e4SLinus Torvalds 	if (!new)
344241269bdSTrond Myklebust 		goto out_nocache;
345241269bdSTrond Myklebust 	new->hashbits = auth_hashbits;
346988664a0STrond Myklebust 	hashsize = 1U << new->hashbits;
347241269bdSTrond Myklebust 	new->hashtable = kcalloc(hashsize, sizeof(new->hashtable[0]), GFP_KERNEL);
348241269bdSTrond Myklebust 	if (!new->hashtable)
349241269bdSTrond Myklebust 		goto out_nohashtbl;
3509499b434STrond Myklebust 	spin_lock_init(&new->lock);
3511da177e4SLinus Torvalds 	auth->au_credcache = new;
3521da177e4SLinus Torvalds 	return 0;
353241269bdSTrond Myklebust out_nohashtbl:
354241269bdSTrond Myklebust 	kfree(new);
355241269bdSTrond Myklebust out_nocache:
356241269bdSTrond Myklebust 	return -ENOMEM;
3571da177e4SLinus Torvalds }
358e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_credcache);
3591da177e4SLinus Torvalds 
360a0337d1dSJeff Layton char *
361a0337d1dSJeff Layton rpcauth_stringify_acceptor(struct rpc_cred *cred)
362a0337d1dSJeff Layton {
363a0337d1dSJeff Layton 	if (!cred->cr_ops->crstringify_acceptor)
364a0337d1dSJeff Layton 		return NULL;
365a0337d1dSJeff Layton 	return cred->cr_ops->crstringify_acceptor(cred);
366a0337d1dSJeff Layton }
367a0337d1dSJeff Layton EXPORT_SYMBOL_GPL(rpcauth_stringify_acceptor);
368a0337d1dSJeff Layton 
3694de6caa2SAndy Adamson /*
3701da177e4SLinus Torvalds  * Destroy a list of credentials
3711da177e4SLinus Torvalds  */
3721da177e4SLinus Torvalds static inline
373e092bdcdSTrond Myklebust void rpcauth_destroy_credlist(struct list_head *head)
3741da177e4SLinus Torvalds {
3751da177e4SLinus Torvalds 	struct rpc_cred *cred;
3761da177e4SLinus Torvalds 
377e092bdcdSTrond Myklebust 	while (!list_empty(head)) {
378e092bdcdSTrond Myklebust 		cred = list_entry(head->next, struct rpc_cred, cr_lru);
379e092bdcdSTrond Myklebust 		list_del_init(&cred->cr_lru);
3801da177e4SLinus Torvalds 		put_rpccred(cred);
3811da177e4SLinus Torvalds 	}
3821da177e4SLinus Torvalds }
3831da177e4SLinus Torvalds 
38495cd6232STrond Myklebust static void
38595cd6232STrond Myklebust rpcauth_lru_add_locked(struct rpc_cred *cred)
38695cd6232STrond Myklebust {
38795cd6232STrond Myklebust 	if (!list_empty(&cred->cr_lru))
38895cd6232STrond Myklebust 		return;
38995cd6232STrond Myklebust 	number_cred_unused++;
39095cd6232STrond Myklebust 	list_add_tail(&cred->cr_lru, &cred_unused);
39195cd6232STrond Myklebust }
39295cd6232STrond Myklebust 
39395cd6232STrond Myklebust static void
39495cd6232STrond Myklebust rpcauth_lru_add(struct rpc_cred *cred)
39595cd6232STrond Myklebust {
39695cd6232STrond Myklebust 	if (!list_empty(&cred->cr_lru))
39795cd6232STrond Myklebust 		return;
39895cd6232STrond Myklebust 	spin_lock(&rpc_credcache_lock);
39995cd6232STrond Myklebust 	rpcauth_lru_add_locked(cred);
40095cd6232STrond Myklebust 	spin_unlock(&rpc_credcache_lock);
40195cd6232STrond Myklebust }
40295cd6232STrond Myklebust 
40395cd6232STrond Myklebust static void
40495cd6232STrond Myklebust rpcauth_lru_remove_locked(struct rpc_cred *cred)
40595cd6232STrond Myklebust {
40695cd6232STrond Myklebust 	if (list_empty(&cred->cr_lru))
40795cd6232STrond Myklebust 		return;
40895cd6232STrond Myklebust 	number_cred_unused--;
40995cd6232STrond Myklebust 	list_del_init(&cred->cr_lru);
41095cd6232STrond Myklebust }
41195cd6232STrond Myklebust 
41295cd6232STrond Myklebust static void
41395cd6232STrond Myklebust rpcauth_lru_remove(struct rpc_cred *cred)
41495cd6232STrond Myklebust {
41595cd6232STrond Myklebust 	if (list_empty(&cred->cr_lru))
41695cd6232STrond Myklebust 		return;
41795cd6232STrond Myklebust 	spin_lock(&rpc_credcache_lock);
41895cd6232STrond Myklebust 	rpcauth_lru_remove_locked(cred);
41995cd6232STrond Myklebust 	spin_unlock(&rpc_credcache_lock);
42095cd6232STrond Myklebust }
42195cd6232STrond Myklebust 
4221da177e4SLinus Torvalds /*
4231da177e4SLinus Torvalds  * Clear the RPC credential cache, and delete those credentials
4241da177e4SLinus Torvalds  * that are not referenced.
4251da177e4SLinus Torvalds  */
4261da177e4SLinus Torvalds void
4273ab9bb72STrond Myklebust rpcauth_clear_credcache(struct rpc_cred_cache *cache)
4281da177e4SLinus Torvalds {
429e092bdcdSTrond Myklebust 	LIST_HEAD(free);
430e092bdcdSTrond Myklebust 	struct hlist_head *head;
4311da177e4SLinus Torvalds 	struct rpc_cred	*cred;
432988664a0STrond Myklebust 	unsigned int hashsize = 1U << cache->hashbits;
4331da177e4SLinus Torvalds 	int		i;
4341da177e4SLinus Torvalds 
4351da177e4SLinus Torvalds 	spin_lock(&rpc_credcache_lock);
4369499b434STrond Myklebust 	spin_lock(&cache->lock);
437988664a0STrond Myklebust 	for (i = 0; i < hashsize; i++) {
438e092bdcdSTrond Myklebust 		head = &cache->hashtable[i];
439e092bdcdSTrond Myklebust 		while (!hlist_empty(head)) {
440e092bdcdSTrond Myklebust 			cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
44131be5bf1STrond Myklebust 			rpcauth_unhash_cred_locked(cred);
44295cd6232STrond Myklebust 			/* Note: We now hold a reference to cred */
44395cd6232STrond Myklebust 			rpcauth_lru_remove_locked(cred);
44495cd6232STrond Myklebust 			list_add_tail(&cred->cr_lru, &free);
4451da177e4SLinus Torvalds 		}
4461da177e4SLinus Torvalds 	}
4479499b434STrond Myklebust 	spin_unlock(&cache->lock);
4481da177e4SLinus Torvalds 	spin_unlock(&rpc_credcache_lock);
4491da177e4SLinus Torvalds 	rpcauth_destroy_credlist(&free);
4501da177e4SLinus Torvalds }
4511da177e4SLinus Torvalds 
4523ab9bb72STrond Myklebust /*
4533ab9bb72STrond Myklebust  * Destroy the RPC credential cache
4543ab9bb72STrond Myklebust  */
4553ab9bb72STrond Myklebust void
4563ab9bb72STrond Myklebust rpcauth_destroy_credcache(struct rpc_auth *auth)
4573ab9bb72STrond Myklebust {
4583ab9bb72STrond Myklebust 	struct rpc_cred_cache *cache = auth->au_credcache;
4593ab9bb72STrond Myklebust 
4603ab9bb72STrond Myklebust 	if (cache) {
4613ab9bb72STrond Myklebust 		auth->au_credcache = NULL;
4623ab9bb72STrond Myklebust 		rpcauth_clear_credcache(cache);
463241269bdSTrond Myklebust 		kfree(cache->hashtable);
4643ab9bb72STrond Myklebust 		kfree(cache);
4653ab9bb72STrond Myklebust 	}
4663ab9bb72STrond Myklebust }
467e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_destroy_credcache);
4683ab9bb72STrond Myklebust 
469d2b83141STrond Myklebust 
470d2b83141STrond Myklebust #define RPC_AUTH_EXPIRY_MORATORIUM (60 * HZ)
471d2b83141STrond Myklebust 
4721da177e4SLinus Torvalds /*
4731da177e4SLinus Torvalds  * Remove stale credentials. Avoid sleeping inside the loop.
4741da177e4SLinus Torvalds  */
47570534a73SDave Chinner static long
476f5c2187cSTrond Myklebust rpcauth_prune_expired(struct list_head *free, int nr_to_scan)
4771da177e4SLinus Torvalds {
478eac0d18dSTrond Myklebust 	struct rpc_cred *cred, *next;
479d2b83141STrond Myklebust 	unsigned long expired = jiffies - RPC_AUTH_EXPIRY_MORATORIUM;
48070534a73SDave Chinner 	long freed = 0;
4811da177e4SLinus Torvalds 
482eac0d18dSTrond Myklebust 	list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) {
483eac0d18dSTrond Myklebust 
48420673406STrond Myklebust 		if (nr_to_scan-- == 0)
48520673406STrond Myklebust 			break;
48679b18181STrond Myklebust 		if (refcount_read(&cred->cr_count) > 1) {
48795cd6232STrond Myklebust 			rpcauth_lru_remove_locked(cred);
48895cd6232STrond Myklebust 			continue;
48995cd6232STrond Myklebust 		}
49093a05e65STrond Myklebust 		/*
49193a05e65STrond Myklebust 		 * Enforce a 60 second garbage collection moratorium
49293a05e65STrond Myklebust 		 * Note that the cred_unused list must be time-ordered.
49393a05e65STrond Myklebust 		 */
49495cd6232STrond Myklebust 		if (!time_in_range(cred->cr_expire, expired, jiffies))
49595cd6232STrond Myklebust 			continue;
49695cd6232STrond Myklebust 		if (!rpcauth_unhash_cred(cred))
497e092bdcdSTrond Myklebust 			continue;
498eac0d18dSTrond Myklebust 
49995cd6232STrond Myklebust 		rpcauth_lru_remove_locked(cred);
50095cd6232STrond Myklebust 		freed++;
501e092bdcdSTrond Myklebust 		list_add_tail(&cred->cr_lru, free);
5021da177e4SLinus Torvalds 	}
50395cd6232STrond Myklebust 	return freed ? freed : SHRINK_STOP;
5041da177e4SLinus Torvalds }
505e092bdcdSTrond Myklebust 
506bae6746fSTrond Myklebust static unsigned long
507bae6746fSTrond Myklebust rpcauth_cache_do_shrink(int nr_to_scan)
508bae6746fSTrond Myklebust {
509bae6746fSTrond Myklebust 	LIST_HEAD(free);
510bae6746fSTrond Myklebust 	unsigned long freed;
511bae6746fSTrond Myklebust 
512bae6746fSTrond Myklebust 	spin_lock(&rpc_credcache_lock);
513bae6746fSTrond Myklebust 	freed = rpcauth_prune_expired(&free, nr_to_scan);
514bae6746fSTrond Myklebust 	spin_unlock(&rpc_credcache_lock);
515bae6746fSTrond Myklebust 	rpcauth_destroy_credlist(&free);
516bae6746fSTrond Myklebust 
517bae6746fSTrond Myklebust 	return freed;
518bae6746fSTrond Myklebust }
519bae6746fSTrond Myklebust 
520e092bdcdSTrond Myklebust /*
521f5c2187cSTrond Myklebust  * Run memory cache shrinker.
522e092bdcdSTrond Myklebust  */
52370534a73SDave Chinner static unsigned long
52470534a73SDave Chinner rpcauth_cache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
52570534a73SDave Chinner 
526e092bdcdSTrond Myklebust {
52770534a73SDave Chinner 	if ((sc->gfp_mask & GFP_KERNEL) != GFP_KERNEL)
52870534a73SDave Chinner 		return SHRINK_STOP;
52970534a73SDave Chinner 
53070534a73SDave Chinner 	/* nothing left, don't come back */
531f5c2187cSTrond Myklebust 	if (list_empty(&cred_unused))
53270534a73SDave Chinner 		return SHRINK_STOP;
53370534a73SDave Chinner 
534bae6746fSTrond Myklebust 	return rpcauth_cache_do_shrink(sc->nr_to_scan);
53570534a73SDave Chinner }
53670534a73SDave Chinner 
53770534a73SDave Chinner static unsigned long
53870534a73SDave Chinner rpcauth_cache_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
53970534a73SDave Chinner 
54070534a73SDave Chinner {
5414c3ffd05SNeilBrown 	return number_cred_unused * sysctl_vfs_cache_pressure / 100;
5421da177e4SLinus Torvalds }
5431da177e4SLinus Torvalds 
544bae6746fSTrond Myklebust static void
545bae6746fSTrond Myklebust rpcauth_cache_enforce_limit(void)
546bae6746fSTrond Myklebust {
547bae6746fSTrond Myklebust 	unsigned long diff;
548bae6746fSTrond Myklebust 	unsigned int nr_to_scan;
549bae6746fSTrond Myklebust 
550bae6746fSTrond Myklebust 	if (number_cred_unused <= auth_max_cred_cachesize)
551bae6746fSTrond Myklebust 		return;
552bae6746fSTrond Myklebust 	diff = number_cred_unused - auth_max_cred_cachesize;
553bae6746fSTrond Myklebust 	nr_to_scan = 100;
554bae6746fSTrond Myklebust 	if (diff < nr_to_scan)
555bae6746fSTrond Myklebust 		nr_to_scan = diff;
556bae6746fSTrond Myklebust 	rpcauth_cache_do_shrink(nr_to_scan);
557bae6746fSTrond Myklebust }
558bae6746fSTrond Myklebust 
5591da177e4SLinus Torvalds /*
5601da177e4SLinus Torvalds  * Look up a process' credentials in the authentication cache
5611da177e4SLinus Torvalds  */
5621da177e4SLinus Torvalds struct rpc_cred *
5631da177e4SLinus Torvalds rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
5643c6e0bc8SJeff Layton 		int flags, gfp_t gfp)
5651da177e4SLinus Torvalds {
566e092bdcdSTrond Myklebust 	LIST_HEAD(free);
5671da177e4SLinus Torvalds 	struct rpc_cred_cache *cache = auth->au_credcache;
56831be5bf1STrond Myklebust 	struct rpc_cred	*cred = NULL,
56931be5bf1STrond Myklebust 			*entry, *new;
57025337fdcSTrond Myklebust 	unsigned int nr;
57125337fdcSTrond Myklebust 
57266cbd4baSFrank Sorenson 	nr = auth->au_ops->hash_cred(acred, cache->hashbits);
5731da177e4SLinus Torvalds 
57431be5bf1STrond Myklebust 	rcu_read_lock();
575b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) {
57631be5bf1STrond Myklebust 		if (!entry->cr_ops->crmatch(acred, entry, flags))
57731be5bf1STrond Myklebust 			continue;
57831be5bf1STrond Myklebust 		cred = get_rpccred(entry);
57907d02a67STrond Myklebust 		if (cred)
58031be5bf1STrond Myklebust 			break;
58131be5bf1STrond Myklebust 	}
58231be5bf1STrond Myklebust 	rcu_read_unlock();
58331be5bf1STrond Myklebust 
5849499b434STrond Myklebust 	if (cred != NULL)
58531be5bf1STrond Myklebust 		goto found;
58631be5bf1STrond Myklebust 
5873c6e0bc8SJeff Layton 	new = auth->au_ops->crcreate(auth, acred, flags, gfp);
58831be5bf1STrond Myklebust 	if (IS_ERR(new)) {
58931be5bf1STrond Myklebust 		cred = new;
59031be5bf1STrond Myklebust 		goto out;
59131be5bf1STrond Myklebust 	}
59231be5bf1STrond Myklebust 
5939499b434STrond Myklebust 	spin_lock(&cache->lock);
594b67bfe0dSSasha Levin 	hlist_for_each_entry(entry, &cache->hashtable[nr], cr_hash) {
595e092bdcdSTrond Myklebust 		if (!entry->cr_ops->crmatch(acred, entry, flags))
596e092bdcdSTrond Myklebust 			continue;
597e092bdcdSTrond Myklebust 		cred = get_rpccred(entry);
59807d02a67STrond Myklebust 		if (cred)
5991da177e4SLinus Torvalds 			break;
6001da177e4SLinus Torvalds 	}
60131be5bf1STrond Myklebust 	if (cred == NULL) {
60231be5bf1STrond Myklebust 		cred = new;
60331be5bf1STrond Myklebust 		set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags);
60479b18181STrond Myklebust 		refcount_inc(&cred->cr_count);
60531be5bf1STrond Myklebust 		hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]);
60631be5bf1STrond Myklebust 	} else
607e092bdcdSTrond Myklebust 		list_add_tail(&new->cr_lru, &free);
6089499b434STrond Myklebust 	spin_unlock(&cache->lock);
609bae6746fSTrond Myklebust 	rpcauth_cache_enforce_limit();
61031be5bf1STrond Myklebust found:
611f64f9e71SJoe Perches 	if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) &&
612f64f9e71SJoe Perches 	    cred->cr_ops->cr_init != NULL &&
613f64f9e71SJoe Perches 	    !(flags & RPCAUTH_LOOKUP_NEW)) {
614fba3bad4STrond Myklebust 		int res = cred->cr_ops->cr_init(auth, cred);
615fba3bad4STrond Myklebust 		if (res < 0) {
616fba3bad4STrond Myklebust 			put_rpccred(cred);
617fba3bad4STrond Myklebust 			cred = ERR_PTR(res);
618fba3bad4STrond Myklebust 		}
6191da177e4SLinus Torvalds 	}
62031be5bf1STrond Myklebust 	rpcauth_destroy_credlist(&free);
62131be5bf1STrond Myklebust out:
62231be5bf1STrond Myklebust 	return cred;
6231da177e4SLinus Torvalds }
624e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache);
6251da177e4SLinus Torvalds 
6261da177e4SLinus Torvalds struct rpc_cred *
6278a317760STrond Myklebust rpcauth_lookupcred(struct rpc_auth *auth, int flags)
6281da177e4SLinus Torvalds {
62986a264abSDavid Howells 	struct auth_cred acred;
6301da177e4SLinus Torvalds 	struct rpc_cred *ret;
63186a264abSDavid Howells 	const struct cred *cred = current_cred();
6321da177e4SLinus Torvalds 
63386a264abSDavid Howells 	memset(&acred, 0, sizeof(acred));
63497f68c6bSNeilBrown 	acred.cred = cred;
6358a317760STrond Myklebust 	ret = auth->au_ops->lookup_cred(auth, &acred, flags);
6361da177e4SLinus Torvalds 	return ret;
6371da177e4SLinus Torvalds }
63866b06860SAndy Adamson EXPORT_SYMBOL_GPL(rpcauth_lookupcred);
6391da177e4SLinus Torvalds 
6405fe4755eSTrond Myklebust void
6415fe4755eSTrond Myklebust rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred,
6425fe4755eSTrond Myklebust 		  struct rpc_auth *auth, const struct rpc_credops *ops)
6435fe4755eSTrond Myklebust {
6445fe4755eSTrond Myklebust 	INIT_HLIST_NODE(&cred->cr_hash);
645e092bdcdSTrond Myklebust 	INIT_LIST_HEAD(&cred->cr_lru);
64679b18181STrond Myklebust 	refcount_set(&cred->cr_count, 1);
6475fe4755eSTrond Myklebust 	cred->cr_auth = auth;
6482edd8d74SNeilBrown 	cred->cr_flags = 0;
6495fe4755eSTrond Myklebust 	cred->cr_ops = ops;
6505fe4755eSTrond Myklebust 	cred->cr_expire = jiffies;
65197f68c6bSNeilBrown 	cred->cr_cred = get_cred(acred->cred);
6525fe4755eSTrond Myklebust }
653e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_cred);
6545fe4755eSTrond Myklebust 
6558572b8e2STrond Myklebust static struct rpc_cred *
6565d351754STrond Myklebust rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags)
6571da177e4SLinus Torvalds {
6581be27f36STrond Myklebust 	struct rpc_auth *auth = task->tk_client->cl_auth;
6591da177e4SLinus Torvalds 	struct auth_cred acred = {
66097f68c6bSNeilBrown 		.cred = get_task_cred(&init_task),
6611da177e4SLinus Torvalds 	};
66297f68c6bSNeilBrown 	struct rpc_cred *ret;
6631da177e4SLinus Torvalds 
66497f68c6bSNeilBrown 	ret = auth->au_ops->lookup_cred(auth, &acred, lookupflags);
66597f68c6bSNeilBrown 	put_cred(acred.cred);
66697f68c6bSNeilBrown 	return ret;
667af093835STrond Myklebust }
668af093835STrond Myklebust 
6698572b8e2STrond Myklebust static struct rpc_cred *
6705e16923bSNeilBrown rpcauth_bind_machine_cred(struct rpc_task *task, int lookupflags)
6715e16923bSNeilBrown {
6725e16923bSNeilBrown 	struct rpc_auth *auth = task->tk_client->cl_auth;
6735e16923bSNeilBrown 	struct auth_cred acred = {
6745e16923bSNeilBrown 		.principal = task->tk_client->cl_principal,
6755e16923bSNeilBrown 		.cred = init_task.cred,
6765e16923bSNeilBrown 	};
6775e16923bSNeilBrown 
6785e16923bSNeilBrown 	if (!acred.principal)
6795e16923bSNeilBrown 		return NULL;
6805e16923bSNeilBrown 	return auth->au_ops->lookup_cred(auth, &acred, lookupflags);
6815e16923bSNeilBrown }
6825e16923bSNeilBrown 
6835e16923bSNeilBrown static struct rpc_cred *
6845d351754STrond Myklebust rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags)
685af093835STrond Myklebust {
686af093835STrond Myklebust 	struct rpc_auth *auth = task->tk_client->cl_auth;
687af093835STrond Myklebust 
6888572b8e2STrond Myklebust 	return rpcauth_lookupcred(auth, lookupflags);
6891da177e4SLinus Torvalds }
6901da177e4SLinus Torvalds 
691a17c2153STrond Myklebust static int
692a52458b4SNeilBrown rpcauth_bindcred(struct rpc_task *task, const struct cred *cred, int flags)
6931da177e4SLinus Torvalds {
694a17c2153STrond Myklebust 	struct rpc_rqst *req = task->tk_rqstp;
6955e16923bSNeilBrown 	struct rpc_cred *new = NULL;
6965d351754STrond Myklebust 	int lookupflags = 0;
697a52458b4SNeilBrown 	struct rpc_auth *auth = task->tk_client->cl_auth;
698a52458b4SNeilBrown 	struct auth_cred acred = {
699a52458b4SNeilBrown 		.cred = cred,
700a52458b4SNeilBrown 	};
7015d351754STrond Myklebust 
7025d351754STrond Myklebust 	if (flags & RPC_TASK_ASYNC)
7035d351754STrond Myklebust 		lookupflags |= RPCAUTH_LOOKUP_NEW;
7041de7eea9SNeilBrown 	if (task->tk_op_cred)
7051de7eea9SNeilBrown 		/* Task must use exactly this rpc_cred */
706d6efccd9SNeilBrown 		new = get_rpccred(task->tk_op_cred);
7071de7eea9SNeilBrown 	else if (cred != NULL && cred != &machine_cred)
708a52458b4SNeilBrown 		new = auth->au_ops->lookup_cred(auth, &acred, lookupflags);
7095e16923bSNeilBrown 	else if (cred == &machine_cred)
7105e16923bSNeilBrown 		new = rpcauth_bind_machine_cred(task, lookupflags);
7115e16923bSNeilBrown 
7125e16923bSNeilBrown 	/* If machine cred couldn't be bound, try a root cred */
7135e16923bSNeilBrown 	if (new)
7145e16923bSNeilBrown 		;
7155e16923bSNeilBrown 	else if (cred == &machine_cred || (flags & RPC_TASK_ROOTCREDS))
7168572b8e2STrond Myklebust 		new = rpcauth_bind_root_cred(task, lookupflags);
717a68a72e1SNeilBrown 	else if (flags & RPC_TASK_NULLCREDS)
718a68a72e1SNeilBrown 		new = authnull_ops.lookup_cred(NULL, NULL, 0);
7194ccda2cdSTrond Myklebust 	else
7208572b8e2STrond Myklebust 		new = rpcauth_bind_new_cred(task, lookupflags);
7218572b8e2STrond Myklebust 	if (IS_ERR(new))
7228572b8e2STrond Myklebust 		return PTR_ERR(new);
723a17c2153STrond Myklebust 	put_rpccred(req->rq_cred);
724a17c2153STrond Myklebust 	req->rq_cred = new;
7258572b8e2STrond Myklebust 	return 0;
7261da177e4SLinus Torvalds }
7271da177e4SLinus Torvalds 
7281da177e4SLinus Torvalds void
7291da177e4SLinus Torvalds put_rpccred(struct rpc_cred *cred)
7301da177e4SLinus Torvalds {
7319a8f6b5eSTrond Myklebust 	if (cred == NULL)
7329a8f6b5eSTrond Myklebust 		return;
73395cd6232STrond Myklebust 	rcu_read_lock();
73479b18181STrond Myklebust 	if (refcount_dec_and_test(&cred->cr_count))
73595cd6232STrond Myklebust 		goto destroy;
73679b18181STrond Myklebust 	if (refcount_read(&cred->cr_count) != 1 ||
73795cd6232STrond Myklebust 	    !test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
73895cd6232STrond Myklebust 		goto out;
739f0380f3dSTrond Myklebust 	if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) {
740e092bdcdSTrond Myklebust 		cred->cr_expire = jiffies;
74195cd6232STrond Myklebust 		rpcauth_lru_add(cred);
74295cd6232STrond Myklebust 		/* Race breaker */
74395cd6232STrond Myklebust 		if (unlikely(!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags)))
74495cd6232STrond Myklebust 			rpcauth_lru_remove(cred);
74595cd6232STrond Myklebust 	} else if (rpcauth_unhash_cred(cred)) {
74695cd6232STrond Myklebust 		rpcauth_lru_remove(cred);
74779b18181STrond Myklebust 		if (refcount_dec_and_test(&cred->cr_count))
74895cd6232STrond Myklebust 			goto destroy;
749f0380f3dSTrond Myklebust 	}
75095cd6232STrond Myklebust out:
75195cd6232STrond Myklebust 	rcu_read_unlock();
752f0380f3dSTrond Myklebust 	return;
75395cd6232STrond Myklebust destroy:
75495cd6232STrond Myklebust 	rcu_read_unlock();
75595cd6232STrond Myklebust 	cred->cr_ops->crdestroy(cred);
7561da177e4SLinus Torvalds }
757e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(put_rpccred);
7581da177e4SLinus Torvalds 
759*e8680a24SChuck Lever /**
760*e8680a24SChuck Lever  * rpcauth_marshcred - Append RPC credential to end of @xdr
761*e8680a24SChuck Lever  * @task: controlling RPC task
762*e8680a24SChuck Lever  * @xdr: xdr_stream containing initial portion of RPC Call header
763*e8680a24SChuck Lever  *
764*e8680a24SChuck Lever  * On success, an appropriate verifier is added to @xdr, @xdr is
765*e8680a24SChuck Lever  * updated to point past the verifier, and zero is returned.
766*e8680a24SChuck Lever  * Otherwise, @xdr is in an undefined state and a negative errno
767*e8680a24SChuck Lever  * is returned.
768*e8680a24SChuck Lever  */
769*e8680a24SChuck Lever int rpcauth_marshcred(struct rpc_task *task, struct xdr_stream *xdr)
7701da177e4SLinus Torvalds {
771*e8680a24SChuck Lever 	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
7721da177e4SLinus Torvalds 
773*e8680a24SChuck Lever 	return ops->crmarshal(task, xdr);
7741da177e4SLinus Torvalds }
7751da177e4SLinus Torvalds 
776d8ed029dSAlexey Dobriyan __be32 *
777d8ed029dSAlexey Dobriyan rpcauth_checkverf(struct rpc_task *task, __be32 *p)
7781da177e4SLinus Torvalds {
779a17c2153STrond Myklebust 	struct rpc_cred	*cred = task->tk_rqstp->rq_cred;
7801da177e4SLinus Torvalds 
7811da177e4SLinus Torvalds 	return cred->cr_ops->crvalidate(task, p);
7821da177e4SLinus Torvalds }
7831da177e4SLinus Torvalds 
784*e8680a24SChuck Lever /**
785*e8680a24SChuck Lever  * rpcauth_wrap_req_encode - XDR encode the RPC procedure
786*e8680a24SChuck Lever  * @task: controlling RPC task
787*e8680a24SChuck Lever  * @xdr: stream where on-the-wire bytes are to be marshalled
788*e8680a24SChuck Lever  *
789*e8680a24SChuck Lever  * On success, @xdr contains the encoded and wrapped message.
790*e8680a24SChuck Lever  * Otherwise, @xdr is in an undefined state.
791*e8680a24SChuck Lever  */
792*e8680a24SChuck Lever int rpcauth_wrap_req_encode(struct rpc_task *task, struct xdr_stream *xdr)
7939f06c719SChuck Lever {
794*e8680a24SChuck Lever 	kxdreproc_t encode = task->tk_msg.rpc_proc->p_encode;
7959f06c719SChuck Lever 
796*e8680a24SChuck Lever 	encode(task->tk_rqstp, xdr, task->tk_msg.rpc_argp);
7979f06c719SChuck Lever 	return 0;
7981da177e4SLinus Torvalds }
799*e8680a24SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_wrap_req_encode);
800*e8680a24SChuck Lever 
801*e8680a24SChuck Lever /**
802*e8680a24SChuck Lever  * rpcauth_wrap_req - XDR encode and wrap the RPC procedure
803*e8680a24SChuck Lever  * @task: controlling RPC task
804*e8680a24SChuck Lever  * @xdr: stream where on-the-wire bytes are to be marshalled
805*e8680a24SChuck Lever  *
806*e8680a24SChuck Lever  * On success, @xdr contains the encoded and wrapped message,
807*e8680a24SChuck Lever  * and zero is returned. Otherwise, @xdr is in an undefined
808*e8680a24SChuck Lever  * state and a negative errno is returned.
809*e8680a24SChuck Lever  */
810*e8680a24SChuck Lever int rpcauth_wrap_req(struct rpc_task *task, struct xdr_stream *xdr)
811*e8680a24SChuck Lever {
812*e8680a24SChuck Lever 	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
813*e8680a24SChuck Lever 
814*e8680a24SChuck Lever 	return ops->crwrap_req(task, xdr);
815*e8680a24SChuck Lever }
8161da177e4SLinus Torvalds 
817bf269551SChuck Lever static int
818bf269551SChuck Lever rpcauth_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp,
819bf269551SChuck Lever 			  __be32 *data, void *obj)
820bf269551SChuck Lever {
821bf269551SChuck Lever 	struct xdr_stream xdr;
822bf269551SChuck Lever 
8230ccc61b1SChuck Lever 	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, data, rqstp);
824bf269551SChuck Lever 	return decode(rqstp, &xdr, obj);
825bf269551SChuck Lever }
826bf269551SChuck Lever 
8271da177e4SLinus Torvalds int
828bf269551SChuck Lever rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp,
829d8ed029dSAlexey Dobriyan 		__be32 *data, void *obj)
8301da177e4SLinus Torvalds {
831a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
8321da177e4SLinus Torvalds 
8331da177e4SLinus Torvalds 	if (cred->cr_ops->crunwrap_resp)
8341da177e4SLinus Torvalds 		return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
8351da177e4SLinus Torvalds 						   data, obj);
8361da177e4SLinus Torvalds 	/* By default, we decode the arguments normally. */
837bf269551SChuck Lever 	return rpcauth_unwrap_req_decode(decode, rqstp, data, obj);
8381da177e4SLinus Torvalds }
8391da177e4SLinus Torvalds 
8403021a5bbSTrond Myklebust bool
8413021a5bbSTrond Myklebust rpcauth_xmit_need_reencode(struct rpc_task *task)
8423021a5bbSTrond Myklebust {
8433021a5bbSTrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
8443021a5bbSTrond Myklebust 
8453021a5bbSTrond Myklebust 	if (!cred || !cred->cr_ops->crneed_reencode)
8463021a5bbSTrond Myklebust 		return false;
8473021a5bbSTrond Myklebust 	return cred->cr_ops->crneed_reencode(task);
8483021a5bbSTrond Myklebust }
8493021a5bbSTrond Myklebust 
8501da177e4SLinus Torvalds int
8511da177e4SLinus Torvalds rpcauth_refreshcred(struct rpc_task *task)
8521da177e4SLinus Torvalds {
8539a84d380STrond Myklebust 	struct rpc_cred	*cred;
8541da177e4SLinus Torvalds 	int err;
8551da177e4SLinus Torvalds 
856a17c2153STrond Myklebust 	cred = task->tk_rqstp->rq_cred;
857a17c2153STrond Myklebust 	if (cred == NULL) {
858a17c2153STrond Myklebust 		err = rpcauth_bindcred(task, task->tk_msg.rpc_cred, task->tk_flags);
859a17c2153STrond Myklebust 		if (err < 0)
860a17c2153STrond Myklebust 			goto out;
861a17c2153STrond Myklebust 		cred = task->tk_rqstp->rq_cred;
862f81c6224SJoe Perches 	}
8630bbacc40SChuck Lever 
8641da177e4SLinus Torvalds 	err = cred->cr_ops->crrefresh(task);
865a17c2153STrond Myklebust out:
8661da177e4SLinus Torvalds 	if (err < 0)
8671da177e4SLinus Torvalds 		task->tk_status = err;
8681da177e4SLinus Torvalds 	return err;
8691da177e4SLinus Torvalds }
8701da177e4SLinus Torvalds 
8711da177e4SLinus Torvalds void
8721da177e4SLinus Torvalds rpcauth_invalcred(struct rpc_task *task)
8731da177e4SLinus Torvalds {
874a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
875fc432dd9STrond Myklebust 
876fc432dd9STrond Myklebust 	if (cred)
877fc432dd9STrond Myklebust 		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
8781da177e4SLinus Torvalds }
8791da177e4SLinus Torvalds 
8801da177e4SLinus Torvalds int
8811da177e4SLinus Torvalds rpcauth_uptodatecred(struct rpc_task *task)
8821da177e4SLinus Torvalds {
883a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
884fc432dd9STrond Myklebust 
885fc432dd9STrond Myklebust 	return cred == NULL ||
886fc432dd9STrond Myklebust 		test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0;
8871da177e4SLinus Torvalds }
888f5c2187cSTrond Myklebust 
8898e1f936bSRusty Russell static struct shrinker rpc_cred_shrinker = {
89070534a73SDave Chinner 	.count_objects = rpcauth_cache_shrink_count,
89170534a73SDave Chinner 	.scan_objects = rpcauth_cache_shrink_scan,
8928e1f936bSRusty Russell 	.seeks = DEFAULT_SEEKS,
8938e1f936bSRusty Russell };
894f5c2187cSTrond Myklebust 
8955d8d9a4dSTrond Myklebust int __init rpcauth_init_module(void)
896f5c2187cSTrond Myklebust {
8975d8d9a4dSTrond Myklebust 	int err;
8985d8d9a4dSTrond Myklebust 
8995d8d9a4dSTrond Myklebust 	err = rpc_init_authunix();
9005d8d9a4dSTrond Myklebust 	if (err < 0)
9015d8d9a4dSTrond Myklebust 		goto out1;
9022864486bSKinglong Mee 	err = register_shrinker(&rpc_cred_shrinker);
9032864486bSKinglong Mee 	if (err < 0)
90489a4f758SNeilBrown 		goto out2;
9055d8d9a4dSTrond Myklebust 	return 0;
9065d8d9a4dSTrond Myklebust out2:
9075d8d9a4dSTrond Myklebust 	rpc_destroy_authunix();
9085d8d9a4dSTrond Myklebust out1:
9095d8d9a4dSTrond Myklebust 	return err;
910f5c2187cSTrond Myklebust }
911f5c2187cSTrond Myklebust 
912c135e84aSStephen Rothwell void rpcauth_remove_module(void)
913f5c2187cSTrond Myklebust {
9145d8d9a4dSTrond Myklebust 	rpc_destroy_authunix();
9158e1f936bSRusty Russell 	unregister_shrinker(&rpc_cred_shrinker);
916f5c2187cSTrond Myklebust }
917