xref: /linux/net/sunrpc/auth.c (revision a0584ee9aed805446b044ce855e67264f0dc619e)
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 
20*a0584ee9SChuck Lever #include <trace/events/sunrpc.h>
21*a0584ee9SChuck Lever 
22241269bdSTrond Myklebust #define RPC_CREDCACHE_DEFAULT_HASHBITS	(4)
23241269bdSTrond Myklebust struct rpc_cred_cache {
24241269bdSTrond Myklebust 	struct hlist_head	*hashtable;
25241269bdSTrond Myklebust 	unsigned int		hashbits;
26241269bdSTrond Myklebust 	spinlock_t		lock;
27241269bdSTrond Myklebust };
28241269bdSTrond Myklebust 
29241269bdSTrond Myklebust static unsigned int auth_hashbits = RPC_CREDCACHE_DEFAULT_HASHBITS;
30241269bdSTrond Myklebust 
314e4c3befSTrond Myklebust static const struct rpc_authops __rcu *auth_flavors[RPC_AUTH_MAXFLAVOR] = {
324e4c3befSTrond Myklebust 	[RPC_AUTH_NULL] = (const struct rpc_authops __force __rcu *)&authnull_ops,
334e4c3befSTrond Myklebust 	[RPC_AUTH_UNIX] = (const struct rpc_authops __force __rcu *)&authunix_ops,
341da177e4SLinus Torvalds 	NULL,			/* others can be loadable modules */
351da177e4SLinus Torvalds };
361da177e4SLinus Torvalds 
37e092bdcdSTrond Myklebust static LIST_HEAD(cred_unused);
38f5c2187cSTrond Myklebust static unsigned long number_cred_unused;
39e092bdcdSTrond Myklebust 
40a52458b4SNeilBrown static struct cred machine_cred = {
41a52458b4SNeilBrown 	.usage = ATOMIC_INIT(1),
42e7f45099SSantosh kumar pradhan #ifdef CONFIG_DEBUG_CREDENTIALS
43e7f45099SSantosh kumar pradhan 	.magic = CRED_MAGIC,
44e7f45099SSantosh kumar pradhan #endif
455e16923bSNeilBrown };
465e16923bSNeilBrown 
475e16923bSNeilBrown /*
485e16923bSNeilBrown  * Return the machine_cred pointer to be used whenever
495e16923bSNeilBrown  * the a generic machine credential is needed.
505e16923bSNeilBrown  */
51a52458b4SNeilBrown const struct cred *rpc_machine_cred(void)
525e16923bSNeilBrown {
535e16923bSNeilBrown 	return &machine_cred;
545e16923bSNeilBrown }
555e16923bSNeilBrown EXPORT_SYMBOL_GPL(rpc_machine_cred);
565e16923bSNeilBrown 
57db5fe265SMiquel van Smoorenburg #define MAX_HASHTABLE_BITS (14)
588e4e15d4SStephen Rothwell static int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp)
59241269bdSTrond Myklebust {
60241269bdSTrond Myklebust 	unsigned long num;
61241269bdSTrond Myklebust 	unsigned int nbits;
62241269bdSTrond Myklebust 	int ret;
63241269bdSTrond Myklebust 
64241269bdSTrond Myklebust 	if (!val)
65241269bdSTrond Myklebust 		goto out_inval;
6600cfaa94SDaniel Walter 	ret = kstrtoul(val, 0, &num);
671a54c0cfSDan Carpenter 	if (ret)
68241269bdSTrond Myklebust 		goto out_inval;
6934ae685cSFrank Sorenson 	nbits = fls(num - 1);
70241269bdSTrond Myklebust 	if (nbits > MAX_HASHTABLE_BITS || nbits < 2)
71241269bdSTrond Myklebust 		goto out_inval;
72241269bdSTrond Myklebust 	*(unsigned int *)kp->arg = nbits;
73241269bdSTrond Myklebust 	return 0;
74241269bdSTrond Myklebust out_inval:
75241269bdSTrond Myklebust 	return -EINVAL;
76241269bdSTrond Myklebust }
77241269bdSTrond Myklebust 
788e4e15d4SStephen Rothwell static int param_get_hashtbl_sz(char *buffer, const struct kernel_param *kp)
79241269bdSTrond Myklebust {
80241269bdSTrond Myklebust 	unsigned int nbits;
81241269bdSTrond Myklebust 
82241269bdSTrond Myklebust 	nbits = *(unsigned int *)kp->arg;
83241269bdSTrond Myklebust 	return sprintf(buffer, "%u", 1U << nbits);
84241269bdSTrond Myklebust }
85241269bdSTrond Myklebust 
86241269bdSTrond Myklebust #define param_check_hashtbl_sz(name, p) __param_check(name, p, unsigned int);
87241269bdSTrond Myklebust 
889c27847dSLuis R. Rodriguez static const struct kernel_param_ops param_ops_hashtbl_sz = {
898e4e15d4SStephen Rothwell 	.set = param_set_hashtbl_sz,
908e4e15d4SStephen Rothwell 	.get = param_get_hashtbl_sz,
918e4e15d4SStephen Rothwell };
928e4e15d4SStephen Rothwell 
93241269bdSTrond Myklebust module_param_named(auth_hashtable_size, auth_hashbits, hashtbl_sz, 0644);
94241269bdSTrond Myklebust MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size");
95241269bdSTrond Myklebust 
96bae6746fSTrond Myklebust static unsigned long auth_max_cred_cachesize = ULONG_MAX;
97bae6746fSTrond Myklebust module_param(auth_max_cred_cachesize, ulong, 0644);
98bae6746fSTrond Myklebust MODULE_PARM_DESC(auth_max_cred_cachesize, "RPC credential maximum total cache size");
99bae6746fSTrond Myklebust 
1001da177e4SLinus Torvalds static u32
1011da177e4SLinus Torvalds pseudoflavor_to_flavor(u32 flavor) {
1021c74a244SChuck Lever 	if (flavor > RPC_AUTH_MAXFLAVOR)
1031da177e4SLinus Torvalds 		return RPC_AUTH_GSS;
1041da177e4SLinus Torvalds 	return flavor;
1051da177e4SLinus Torvalds }
1061da177e4SLinus Torvalds 
1071da177e4SLinus Torvalds int
108f1c0a861STrond Myklebust rpcauth_register(const struct rpc_authops *ops)
1091da177e4SLinus Torvalds {
1104e4c3befSTrond Myklebust 	const struct rpc_authops *old;
1111da177e4SLinus Torvalds 	rpc_authflavor_t flavor;
1121da177e4SLinus Torvalds 
1131da177e4SLinus Torvalds 	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
1141da177e4SLinus Torvalds 		return -EINVAL;
1154e4c3befSTrond Myklebust 	old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], NULL, ops);
1164e4c3befSTrond Myklebust 	if (old == NULL || old == ops)
1174e4c3befSTrond Myklebust 		return 0;
1184e4c3befSTrond Myklebust 	return -EPERM;
1191da177e4SLinus Torvalds }
120e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_register);
1211da177e4SLinus Torvalds 
1221da177e4SLinus Torvalds int
123f1c0a861STrond Myklebust rpcauth_unregister(const struct rpc_authops *ops)
1241da177e4SLinus Torvalds {
1254e4c3befSTrond Myklebust 	const struct rpc_authops *old;
1261da177e4SLinus Torvalds 	rpc_authflavor_t flavor;
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
1291da177e4SLinus Torvalds 		return -EINVAL;
1304e4c3befSTrond Myklebust 
1314e4c3befSTrond Myklebust 	old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], ops, NULL);
1324e4c3befSTrond Myklebust 	if (old == ops || old == NULL)
1334e4c3befSTrond Myklebust 		return 0;
1344e4c3befSTrond Myklebust 	return -EPERM;
1351da177e4SLinus Torvalds }
136e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_unregister);
1371da177e4SLinus Torvalds 
1384e4c3befSTrond Myklebust static const struct rpc_authops *
1394e4c3befSTrond Myklebust rpcauth_get_authops(rpc_authflavor_t flavor)
1404e4c3befSTrond Myklebust {
1414e4c3befSTrond Myklebust 	const struct rpc_authops *ops;
1424e4c3befSTrond Myklebust 
1434e4c3befSTrond Myklebust 	if (flavor >= RPC_AUTH_MAXFLAVOR)
1444e4c3befSTrond Myklebust 		return NULL;
1454e4c3befSTrond Myklebust 
1464e4c3befSTrond Myklebust 	rcu_read_lock();
1474e4c3befSTrond Myklebust 	ops = rcu_dereference(auth_flavors[flavor]);
1484e4c3befSTrond Myklebust 	if (ops == NULL) {
1494e4c3befSTrond Myklebust 		rcu_read_unlock();
1504e4c3befSTrond Myklebust 		request_module("rpc-auth-%u", flavor);
1514e4c3befSTrond Myklebust 		rcu_read_lock();
1524e4c3befSTrond Myklebust 		ops = rcu_dereference(auth_flavors[flavor]);
1534e4c3befSTrond Myklebust 		if (ops == NULL)
1544e4c3befSTrond Myklebust 			goto out;
1554e4c3befSTrond Myklebust 	}
1564e4c3befSTrond Myklebust 	if (!try_module_get(ops->owner))
1574e4c3befSTrond Myklebust 		ops = NULL;
1584e4c3befSTrond Myklebust out:
1594e4c3befSTrond Myklebust 	rcu_read_unlock();
1604e4c3befSTrond Myklebust 	return ops;
1614e4c3befSTrond Myklebust }
1624e4c3befSTrond Myklebust 
1634e4c3befSTrond Myklebust static void
1644e4c3befSTrond Myklebust rpcauth_put_authops(const struct rpc_authops *ops)
1654e4c3befSTrond Myklebust {
1664e4c3befSTrond Myklebust 	module_put(ops->owner);
1674e4c3befSTrond Myklebust }
1684e4c3befSTrond Myklebust 
1696a1a1e34SChuck Lever /**
1709568c5e9SChuck Lever  * rpcauth_get_pseudoflavor - check if security flavor is supported
1719568c5e9SChuck Lever  * @flavor: a security flavor
1729568c5e9SChuck Lever  * @info: a GSS mech OID, quality of protection, and service value
1739568c5e9SChuck Lever  *
1749568c5e9SChuck Lever  * Verifies that an appropriate kernel module is available or already loaded.
1759568c5e9SChuck Lever  * Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is
1769568c5e9SChuck Lever  * not supported locally.
1779568c5e9SChuck Lever  */
1789568c5e9SChuck Lever rpc_authflavor_t
1799568c5e9SChuck Lever rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
1809568c5e9SChuck Lever {
1814e4c3befSTrond Myklebust 	const struct rpc_authops *ops = rpcauth_get_authops(flavor);
1829568c5e9SChuck Lever 	rpc_authflavor_t pseudoflavor;
1839568c5e9SChuck Lever 
1844e4c3befSTrond Myklebust 	if (!ops)
1859568c5e9SChuck Lever 		return RPC_AUTH_MAXFLAVOR;
1869568c5e9SChuck Lever 	pseudoflavor = flavor;
1879568c5e9SChuck Lever 	if (ops->info2flavor != NULL)
1889568c5e9SChuck Lever 		pseudoflavor = ops->info2flavor(info);
1899568c5e9SChuck Lever 
1904e4c3befSTrond Myklebust 	rpcauth_put_authops(ops);
1919568c5e9SChuck Lever 	return pseudoflavor;
1929568c5e9SChuck Lever }
1939568c5e9SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);
1949568c5e9SChuck Lever 
1959568c5e9SChuck Lever /**
196a77c806fSChuck Lever  * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor
197a77c806fSChuck Lever  * @pseudoflavor: GSS pseudoflavor to match
198a77c806fSChuck Lever  * @info: rpcsec_gss_info structure to fill in
199a77c806fSChuck Lever  *
200a77c806fSChuck Lever  * Returns zero and fills in "info" if pseudoflavor matches a
201a77c806fSChuck Lever  * supported mechanism.
202a77c806fSChuck Lever  */
203a77c806fSChuck Lever int
204a77c806fSChuck Lever rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
205a77c806fSChuck Lever {
206a77c806fSChuck Lever 	rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor);
207a77c806fSChuck Lever 	const struct rpc_authops *ops;
208a77c806fSChuck Lever 	int result;
209a77c806fSChuck Lever 
2104e4c3befSTrond Myklebust 	ops = rpcauth_get_authops(flavor);
211a77c806fSChuck Lever 	if (ops == NULL)
212a77c806fSChuck Lever 		return -ENOENT;
213a77c806fSChuck Lever 
214a77c806fSChuck Lever 	result = -ENOENT;
215a77c806fSChuck Lever 	if (ops->flavor2info != NULL)
216a77c806fSChuck Lever 		result = ops->flavor2info(pseudoflavor, info);
217a77c806fSChuck Lever 
2184e4c3befSTrond Myklebust 	rpcauth_put_authops(ops);
219a77c806fSChuck Lever 	return result;
220a77c806fSChuck Lever }
221a77c806fSChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);
222a77c806fSChuck Lever 
223a77c806fSChuck Lever /**
2246a1a1e34SChuck Lever  * rpcauth_list_flavors - discover registered flavors and pseudoflavors
2256a1a1e34SChuck Lever  * @array: array to fill in
2266a1a1e34SChuck Lever  * @size: size of "array"
2276a1a1e34SChuck Lever  *
2286a1a1e34SChuck Lever  * Returns the number of array items filled in, or a negative errno.
2296a1a1e34SChuck Lever  *
2306a1a1e34SChuck Lever  * The returned array is not sorted by any policy.  Callers should not
2316a1a1e34SChuck Lever  * rely on the order of the items in the returned array.
2326a1a1e34SChuck Lever  */
2336a1a1e34SChuck Lever int
2346a1a1e34SChuck Lever rpcauth_list_flavors(rpc_authflavor_t *array, int size)
2356a1a1e34SChuck Lever {
2364e4c3befSTrond Myklebust 	const struct rpc_authops *ops;
2374e4c3befSTrond Myklebust 	rpc_authflavor_t flavor, pseudos[4];
2384e4c3befSTrond Myklebust 	int i, len, result = 0;
2396a1a1e34SChuck Lever 
2404e4c3befSTrond Myklebust 	rcu_read_lock();
2416a1a1e34SChuck Lever 	for (flavor = 0; flavor < RPC_AUTH_MAXFLAVOR; flavor++) {
2424e4c3befSTrond Myklebust 		ops = rcu_dereference(auth_flavors[flavor]);
2436a1a1e34SChuck Lever 		if (result >= size) {
2446a1a1e34SChuck Lever 			result = -ENOMEM;
2456a1a1e34SChuck Lever 			break;
2466a1a1e34SChuck Lever 		}
2476a1a1e34SChuck Lever 
2486a1a1e34SChuck Lever 		if (ops == NULL)
2496a1a1e34SChuck Lever 			continue;
2506a1a1e34SChuck Lever 		if (ops->list_pseudoflavors == NULL) {
2516a1a1e34SChuck Lever 			array[result++] = ops->au_flavor;
2526a1a1e34SChuck Lever 			continue;
2536a1a1e34SChuck Lever 		}
2546a1a1e34SChuck Lever 		len = ops->list_pseudoflavors(pseudos, ARRAY_SIZE(pseudos));
2556a1a1e34SChuck Lever 		if (len < 0) {
2566a1a1e34SChuck Lever 			result = len;
2576a1a1e34SChuck Lever 			break;
2586a1a1e34SChuck Lever 		}
2596a1a1e34SChuck Lever 		for (i = 0; i < len; i++) {
2606a1a1e34SChuck Lever 			if (result >= size) {
2616a1a1e34SChuck Lever 				result = -ENOMEM;
2626a1a1e34SChuck Lever 				break;
2636a1a1e34SChuck Lever 			}
2646a1a1e34SChuck Lever 			array[result++] = pseudos[i];
2656a1a1e34SChuck Lever 		}
2666a1a1e34SChuck Lever 	}
2674e4c3befSTrond Myklebust 	rcu_read_unlock();
2686a1a1e34SChuck Lever 	return result;
2696a1a1e34SChuck Lever }
2706a1a1e34SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_list_flavors);
2716a1a1e34SChuck Lever 
2721da177e4SLinus Torvalds struct rpc_auth *
27382b98ca5SSargun Dhillon rpcauth_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
2741da177e4SLinus Torvalds {
2754e4c3befSTrond Myklebust 	struct rpc_auth	*auth = ERR_PTR(-EINVAL);
276f1c0a861STrond Myklebust 	const struct rpc_authops *ops;
277c2190661STrond Myklebust 	u32 flavor = pseudoflavor_to_flavor(args->pseudoflavor);
2781da177e4SLinus Torvalds 
2794e4c3befSTrond Myklebust 	ops = rpcauth_get_authops(flavor);
2804e4c3befSTrond Myklebust 	if (ops == NULL)
281f344f6dfSOlaf Kirch 		goto out;
282f344f6dfSOlaf Kirch 
283c2190661STrond Myklebust 	auth = ops->create(args, clnt);
2844e4c3befSTrond Myklebust 
2854e4c3befSTrond Myklebust 	rpcauth_put_authops(ops);
2866a19275aSJ. Bruce Fields 	if (IS_ERR(auth))
2876a19275aSJ. Bruce Fields 		return auth;
2881da177e4SLinus Torvalds 	if (clnt->cl_auth)
289de7a8ce3STrond Myklebust 		rpcauth_release(clnt->cl_auth);
2901da177e4SLinus Torvalds 	clnt->cl_auth = auth;
291f344f6dfSOlaf Kirch 
292f344f6dfSOlaf Kirch out:
2931da177e4SLinus Torvalds 	return auth;
2941da177e4SLinus Torvalds }
295e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_create);
2961da177e4SLinus Torvalds 
2971da177e4SLinus Torvalds void
298de7a8ce3STrond Myklebust rpcauth_release(struct rpc_auth *auth)
2991da177e4SLinus Torvalds {
300331bc71cSTrond Myklebust 	if (!refcount_dec_and_test(&auth->au_count))
3011da177e4SLinus Torvalds 		return;
3021da177e4SLinus Torvalds 	auth->au_ops->destroy(auth);
3031da177e4SLinus Torvalds }
3041da177e4SLinus Torvalds 
3051da177e4SLinus Torvalds static DEFINE_SPINLOCK(rpc_credcache_lock);
3061da177e4SLinus Torvalds 
30795cd6232STrond Myklebust /*
30895cd6232STrond Myklebust  * On success, the caller is responsible for freeing the reference
30995cd6232STrond Myklebust  * held by the hashtable
31095cd6232STrond Myklebust  */
31195cd6232STrond Myklebust static bool
31231be5bf1STrond Myklebust rpcauth_unhash_cred_locked(struct rpc_cred *cred)
31331be5bf1STrond Myklebust {
31495cd6232STrond Myklebust 	if (!test_and_clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
31595cd6232STrond Myklebust 		return false;
31631be5bf1STrond Myklebust 	hlist_del_rcu(&cred->cr_hash);
31795cd6232STrond Myklebust 	return true;
31831be5bf1STrond Myklebust }
31931be5bf1STrond Myklebust 
32095cd6232STrond Myklebust static bool
3219499b434STrond Myklebust rpcauth_unhash_cred(struct rpc_cred *cred)
3229499b434STrond Myklebust {
3239499b434STrond Myklebust 	spinlock_t *cache_lock;
32495cd6232STrond Myklebust 	bool ret;
3259499b434STrond Myklebust 
32695cd6232STrond Myklebust 	if (!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
32795cd6232STrond Myklebust 		return false;
3289499b434STrond Myklebust 	cache_lock = &cred->cr_auth->au_credcache->lock;
3299499b434STrond Myklebust 	spin_lock(cache_lock);
33095cd6232STrond Myklebust 	ret = rpcauth_unhash_cred_locked(cred);
3319499b434STrond Myklebust 	spin_unlock(cache_lock);
332f0380f3dSTrond Myklebust 	return ret;
3339499b434STrond Myklebust }
3349499b434STrond Myklebust 
3351da177e4SLinus Torvalds /*
3361da177e4SLinus Torvalds  * Initialize RPC credential cache
3371da177e4SLinus Torvalds  */
3381da177e4SLinus Torvalds int
339f5c2187cSTrond Myklebust rpcauth_init_credcache(struct rpc_auth *auth)
3401da177e4SLinus Torvalds {
3411da177e4SLinus Torvalds 	struct rpc_cred_cache *new;
342988664a0STrond Myklebust 	unsigned int hashsize;
3431da177e4SLinus Torvalds 
3448b3a7005SKris Katterjohn 	new = kmalloc(sizeof(*new), GFP_KERNEL);
3451da177e4SLinus Torvalds 	if (!new)
346241269bdSTrond Myklebust 		goto out_nocache;
347241269bdSTrond Myklebust 	new->hashbits = auth_hashbits;
348988664a0STrond Myklebust 	hashsize = 1U << new->hashbits;
349241269bdSTrond Myklebust 	new->hashtable = kcalloc(hashsize, sizeof(new->hashtable[0]), GFP_KERNEL);
350241269bdSTrond Myklebust 	if (!new->hashtable)
351241269bdSTrond Myklebust 		goto out_nohashtbl;
3529499b434STrond Myklebust 	spin_lock_init(&new->lock);
3531da177e4SLinus Torvalds 	auth->au_credcache = new;
3541da177e4SLinus Torvalds 	return 0;
355241269bdSTrond Myklebust out_nohashtbl:
356241269bdSTrond Myklebust 	kfree(new);
357241269bdSTrond Myklebust out_nocache:
358241269bdSTrond Myklebust 	return -ENOMEM;
3591da177e4SLinus Torvalds }
360e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_credcache);
3611da177e4SLinus Torvalds 
362a0337d1dSJeff Layton char *
363a0337d1dSJeff Layton rpcauth_stringify_acceptor(struct rpc_cred *cred)
364a0337d1dSJeff Layton {
365a0337d1dSJeff Layton 	if (!cred->cr_ops->crstringify_acceptor)
366a0337d1dSJeff Layton 		return NULL;
367a0337d1dSJeff Layton 	return cred->cr_ops->crstringify_acceptor(cred);
368a0337d1dSJeff Layton }
369a0337d1dSJeff Layton EXPORT_SYMBOL_GPL(rpcauth_stringify_acceptor);
370a0337d1dSJeff Layton 
3714de6caa2SAndy Adamson /*
3721da177e4SLinus Torvalds  * Destroy a list of credentials
3731da177e4SLinus Torvalds  */
3741da177e4SLinus Torvalds static inline
375e092bdcdSTrond Myklebust void rpcauth_destroy_credlist(struct list_head *head)
3761da177e4SLinus Torvalds {
3771da177e4SLinus Torvalds 	struct rpc_cred *cred;
3781da177e4SLinus Torvalds 
379e092bdcdSTrond Myklebust 	while (!list_empty(head)) {
380e092bdcdSTrond Myklebust 		cred = list_entry(head->next, struct rpc_cred, cr_lru);
381e092bdcdSTrond Myklebust 		list_del_init(&cred->cr_lru);
3821da177e4SLinus Torvalds 		put_rpccred(cred);
3831da177e4SLinus Torvalds 	}
3841da177e4SLinus Torvalds }
3851da177e4SLinus Torvalds 
38695cd6232STrond Myklebust static void
38795cd6232STrond Myklebust rpcauth_lru_add_locked(struct rpc_cred *cred)
38895cd6232STrond Myklebust {
38995cd6232STrond Myklebust 	if (!list_empty(&cred->cr_lru))
39095cd6232STrond Myklebust 		return;
39195cd6232STrond Myklebust 	number_cred_unused++;
39295cd6232STrond Myklebust 	list_add_tail(&cred->cr_lru, &cred_unused);
39395cd6232STrond Myklebust }
39495cd6232STrond Myklebust 
39595cd6232STrond Myklebust static void
39695cd6232STrond Myklebust rpcauth_lru_add(struct rpc_cred *cred)
39795cd6232STrond Myklebust {
39895cd6232STrond Myklebust 	if (!list_empty(&cred->cr_lru))
39995cd6232STrond Myklebust 		return;
40095cd6232STrond Myklebust 	spin_lock(&rpc_credcache_lock);
40195cd6232STrond Myklebust 	rpcauth_lru_add_locked(cred);
40295cd6232STrond Myklebust 	spin_unlock(&rpc_credcache_lock);
40395cd6232STrond Myklebust }
40495cd6232STrond Myklebust 
40595cd6232STrond Myklebust static void
40695cd6232STrond Myklebust rpcauth_lru_remove_locked(struct rpc_cred *cred)
40795cd6232STrond Myklebust {
40895cd6232STrond Myklebust 	if (list_empty(&cred->cr_lru))
40995cd6232STrond Myklebust 		return;
41095cd6232STrond Myklebust 	number_cred_unused--;
41195cd6232STrond Myklebust 	list_del_init(&cred->cr_lru);
41295cd6232STrond Myklebust }
41395cd6232STrond Myklebust 
41495cd6232STrond Myklebust static void
41595cd6232STrond Myklebust rpcauth_lru_remove(struct rpc_cred *cred)
41695cd6232STrond Myklebust {
41795cd6232STrond Myklebust 	if (list_empty(&cred->cr_lru))
41895cd6232STrond Myklebust 		return;
41995cd6232STrond Myklebust 	spin_lock(&rpc_credcache_lock);
42095cd6232STrond Myklebust 	rpcauth_lru_remove_locked(cred);
42195cd6232STrond Myklebust 	spin_unlock(&rpc_credcache_lock);
42295cd6232STrond Myklebust }
42395cd6232STrond Myklebust 
4241da177e4SLinus Torvalds /*
4251da177e4SLinus Torvalds  * Clear the RPC credential cache, and delete those credentials
4261da177e4SLinus Torvalds  * that are not referenced.
4271da177e4SLinus Torvalds  */
4281da177e4SLinus Torvalds void
4293ab9bb72STrond Myklebust rpcauth_clear_credcache(struct rpc_cred_cache *cache)
4301da177e4SLinus Torvalds {
431e092bdcdSTrond Myklebust 	LIST_HEAD(free);
432e092bdcdSTrond Myklebust 	struct hlist_head *head;
4331da177e4SLinus Torvalds 	struct rpc_cred	*cred;
434988664a0STrond Myklebust 	unsigned int hashsize = 1U << cache->hashbits;
4351da177e4SLinus Torvalds 	int		i;
4361da177e4SLinus Torvalds 
4371da177e4SLinus Torvalds 	spin_lock(&rpc_credcache_lock);
4389499b434STrond Myklebust 	spin_lock(&cache->lock);
439988664a0STrond Myklebust 	for (i = 0; i < hashsize; i++) {
440e092bdcdSTrond Myklebust 		head = &cache->hashtable[i];
441e092bdcdSTrond Myklebust 		while (!hlist_empty(head)) {
442e092bdcdSTrond Myklebust 			cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
44331be5bf1STrond Myklebust 			rpcauth_unhash_cred_locked(cred);
44495cd6232STrond Myklebust 			/* Note: We now hold a reference to cred */
44595cd6232STrond Myklebust 			rpcauth_lru_remove_locked(cred);
44695cd6232STrond Myklebust 			list_add_tail(&cred->cr_lru, &free);
4471da177e4SLinus Torvalds 		}
4481da177e4SLinus Torvalds 	}
4499499b434STrond Myklebust 	spin_unlock(&cache->lock);
4501da177e4SLinus Torvalds 	spin_unlock(&rpc_credcache_lock);
4511da177e4SLinus Torvalds 	rpcauth_destroy_credlist(&free);
4521da177e4SLinus Torvalds }
4531da177e4SLinus Torvalds 
4543ab9bb72STrond Myklebust /*
4553ab9bb72STrond Myklebust  * Destroy the RPC credential cache
4563ab9bb72STrond Myklebust  */
4573ab9bb72STrond Myklebust void
4583ab9bb72STrond Myklebust rpcauth_destroy_credcache(struct rpc_auth *auth)
4593ab9bb72STrond Myklebust {
4603ab9bb72STrond Myklebust 	struct rpc_cred_cache *cache = auth->au_credcache;
4613ab9bb72STrond Myklebust 
4623ab9bb72STrond Myklebust 	if (cache) {
4633ab9bb72STrond Myklebust 		auth->au_credcache = NULL;
4643ab9bb72STrond Myklebust 		rpcauth_clear_credcache(cache);
465241269bdSTrond Myklebust 		kfree(cache->hashtable);
4663ab9bb72STrond Myklebust 		kfree(cache);
4673ab9bb72STrond Myklebust 	}
4683ab9bb72STrond Myklebust }
469e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_destroy_credcache);
4703ab9bb72STrond Myklebust 
471d2b83141STrond Myklebust 
472d2b83141STrond Myklebust #define RPC_AUTH_EXPIRY_MORATORIUM (60 * HZ)
473d2b83141STrond Myklebust 
4741da177e4SLinus Torvalds /*
4751da177e4SLinus Torvalds  * Remove stale credentials. Avoid sleeping inside the loop.
4761da177e4SLinus Torvalds  */
47770534a73SDave Chinner static long
478f5c2187cSTrond Myklebust rpcauth_prune_expired(struct list_head *free, int nr_to_scan)
4791da177e4SLinus Torvalds {
480eac0d18dSTrond Myklebust 	struct rpc_cred *cred, *next;
481d2b83141STrond Myklebust 	unsigned long expired = jiffies - RPC_AUTH_EXPIRY_MORATORIUM;
48270534a73SDave Chinner 	long freed = 0;
4831da177e4SLinus Torvalds 
484eac0d18dSTrond Myklebust 	list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) {
485eac0d18dSTrond Myklebust 
48620673406STrond Myklebust 		if (nr_to_scan-- == 0)
48720673406STrond Myklebust 			break;
48879b18181STrond Myklebust 		if (refcount_read(&cred->cr_count) > 1) {
48995cd6232STrond Myklebust 			rpcauth_lru_remove_locked(cred);
49095cd6232STrond Myklebust 			continue;
49195cd6232STrond Myklebust 		}
49293a05e65STrond Myklebust 		/*
49393a05e65STrond Myklebust 		 * Enforce a 60 second garbage collection moratorium
49493a05e65STrond Myklebust 		 * Note that the cred_unused list must be time-ordered.
49593a05e65STrond Myklebust 		 */
49695cd6232STrond Myklebust 		if (!time_in_range(cred->cr_expire, expired, jiffies))
49795cd6232STrond Myklebust 			continue;
49895cd6232STrond Myklebust 		if (!rpcauth_unhash_cred(cred))
499e092bdcdSTrond Myklebust 			continue;
500eac0d18dSTrond Myklebust 
50195cd6232STrond Myklebust 		rpcauth_lru_remove_locked(cred);
50295cd6232STrond Myklebust 		freed++;
503e092bdcdSTrond Myklebust 		list_add_tail(&cred->cr_lru, free);
5041da177e4SLinus Torvalds 	}
50595cd6232STrond Myklebust 	return freed ? freed : SHRINK_STOP;
5061da177e4SLinus Torvalds }
507e092bdcdSTrond Myklebust 
508bae6746fSTrond Myklebust static unsigned long
509bae6746fSTrond Myklebust rpcauth_cache_do_shrink(int nr_to_scan)
510bae6746fSTrond Myklebust {
511bae6746fSTrond Myklebust 	LIST_HEAD(free);
512bae6746fSTrond Myklebust 	unsigned long freed;
513bae6746fSTrond Myklebust 
514bae6746fSTrond Myklebust 	spin_lock(&rpc_credcache_lock);
515bae6746fSTrond Myklebust 	freed = rpcauth_prune_expired(&free, nr_to_scan);
516bae6746fSTrond Myklebust 	spin_unlock(&rpc_credcache_lock);
517bae6746fSTrond Myklebust 	rpcauth_destroy_credlist(&free);
518bae6746fSTrond Myklebust 
519bae6746fSTrond Myklebust 	return freed;
520bae6746fSTrond Myklebust }
521bae6746fSTrond Myklebust 
522e092bdcdSTrond Myklebust /*
523f5c2187cSTrond Myklebust  * Run memory cache shrinker.
524e092bdcdSTrond Myklebust  */
52570534a73SDave Chinner static unsigned long
52670534a73SDave Chinner rpcauth_cache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
52770534a73SDave Chinner 
528e092bdcdSTrond Myklebust {
52970534a73SDave Chinner 	if ((sc->gfp_mask & GFP_KERNEL) != GFP_KERNEL)
53070534a73SDave Chinner 		return SHRINK_STOP;
53170534a73SDave Chinner 
53270534a73SDave Chinner 	/* nothing left, don't come back */
533f5c2187cSTrond Myklebust 	if (list_empty(&cred_unused))
53470534a73SDave Chinner 		return SHRINK_STOP;
53570534a73SDave Chinner 
536bae6746fSTrond Myklebust 	return rpcauth_cache_do_shrink(sc->nr_to_scan);
53770534a73SDave Chinner }
53870534a73SDave Chinner 
53970534a73SDave Chinner static unsigned long
54070534a73SDave Chinner rpcauth_cache_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
54170534a73SDave Chinner 
54270534a73SDave Chinner {
5434c3ffd05SNeilBrown 	return number_cred_unused * sysctl_vfs_cache_pressure / 100;
5441da177e4SLinus Torvalds }
5451da177e4SLinus Torvalds 
546bae6746fSTrond Myklebust static void
547bae6746fSTrond Myklebust rpcauth_cache_enforce_limit(void)
548bae6746fSTrond Myklebust {
549bae6746fSTrond Myklebust 	unsigned long diff;
550bae6746fSTrond Myklebust 	unsigned int nr_to_scan;
551bae6746fSTrond Myklebust 
552bae6746fSTrond Myklebust 	if (number_cred_unused <= auth_max_cred_cachesize)
553bae6746fSTrond Myklebust 		return;
554bae6746fSTrond Myklebust 	diff = number_cred_unused - auth_max_cred_cachesize;
555bae6746fSTrond Myklebust 	nr_to_scan = 100;
556bae6746fSTrond Myklebust 	if (diff < nr_to_scan)
557bae6746fSTrond Myklebust 		nr_to_scan = diff;
558bae6746fSTrond Myklebust 	rpcauth_cache_do_shrink(nr_to_scan);
559bae6746fSTrond Myklebust }
560bae6746fSTrond Myklebust 
5611da177e4SLinus Torvalds /*
5621da177e4SLinus Torvalds  * Look up a process' credentials in the authentication cache
5631da177e4SLinus Torvalds  */
5641da177e4SLinus Torvalds struct rpc_cred *
5651da177e4SLinus Torvalds rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
5663c6e0bc8SJeff Layton 		int flags, gfp_t gfp)
5671da177e4SLinus Torvalds {
568e092bdcdSTrond Myklebust 	LIST_HEAD(free);
5691da177e4SLinus Torvalds 	struct rpc_cred_cache *cache = auth->au_credcache;
57031be5bf1STrond Myklebust 	struct rpc_cred	*cred = NULL,
57131be5bf1STrond Myklebust 			*entry, *new;
57225337fdcSTrond Myklebust 	unsigned int nr;
57325337fdcSTrond Myklebust 
57466cbd4baSFrank Sorenson 	nr = auth->au_ops->hash_cred(acred, cache->hashbits);
5751da177e4SLinus Torvalds 
57631be5bf1STrond Myklebust 	rcu_read_lock();
577b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) {
57831be5bf1STrond Myklebust 		if (!entry->cr_ops->crmatch(acred, entry, flags))
57931be5bf1STrond Myklebust 			continue;
58031be5bf1STrond Myklebust 		cred = get_rpccred(entry);
58107d02a67STrond Myklebust 		if (cred)
58231be5bf1STrond Myklebust 			break;
58331be5bf1STrond Myklebust 	}
58431be5bf1STrond Myklebust 	rcu_read_unlock();
58531be5bf1STrond Myklebust 
5869499b434STrond Myklebust 	if (cred != NULL)
58731be5bf1STrond Myklebust 		goto found;
58831be5bf1STrond Myklebust 
5893c6e0bc8SJeff Layton 	new = auth->au_ops->crcreate(auth, acred, flags, gfp);
59031be5bf1STrond Myklebust 	if (IS_ERR(new)) {
59131be5bf1STrond Myklebust 		cred = new;
59231be5bf1STrond Myklebust 		goto out;
59331be5bf1STrond Myklebust 	}
59431be5bf1STrond Myklebust 
5959499b434STrond Myklebust 	spin_lock(&cache->lock);
596b67bfe0dSSasha Levin 	hlist_for_each_entry(entry, &cache->hashtable[nr], cr_hash) {
597e092bdcdSTrond Myklebust 		if (!entry->cr_ops->crmatch(acred, entry, flags))
598e092bdcdSTrond Myklebust 			continue;
599e092bdcdSTrond Myklebust 		cred = get_rpccred(entry);
60007d02a67STrond Myklebust 		if (cred)
6011da177e4SLinus Torvalds 			break;
6021da177e4SLinus Torvalds 	}
60331be5bf1STrond Myklebust 	if (cred == NULL) {
60431be5bf1STrond Myklebust 		cred = new;
60531be5bf1STrond Myklebust 		set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags);
60679b18181STrond Myklebust 		refcount_inc(&cred->cr_count);
60731be5bf1STrond Myklebust 		hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]);
60831be5bf1STrond Myklebust 	} else
609e092bdcdSTrond Myklebust 		list_add_tail(&new->cr_lru, &free);
6109499b434STrond Myklebust 	spin_unlock(&cache->lock);
611bae6746fSTrond Myklebust 	rpcauth_cache_enforce_limit();
61231be5bf1STrond Myklebust found:
613f64f9e71SJoe Perches 	if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) &&
614f64f9e71SJoe Perches 	    cred->cr_ops->cr_init != NULL &&
615f64f9e71SJoe Perches 	    !(flags & RPCAUTH_LOOKUP_NEW)) {
616fba3bad4STrond Myklebust 		int res = cred->cr_ops->cr_init(auth, cred);
617fba3bad4STrond Myklebust 		if (res < 0) {
618fba3bad4STrond Myklebust 			put_rpccred(cred);
619fba3bad4STrond Myklebust 			cred = ERR_PTR(res);
620fba3bad4STrond Myklebust 		}
6211da177e4SLinus Torvalds 	}
62231be5bf1STrond Myklebust 	rpcauth_destroy_credlist(&free);
62331be5bf1STrond Myklebust out:
62431be5bf1STrond Myklebust 	return cred;
6251da177e4SLinus Torvalds }
626e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache);
6271da177e4SLinus Torvalds 
6281da177e4SLinus Torvalds struct rpc_cred *
6298a317760STrond Myklebust rpcauth_lookupcred(struct rpc_auth *auth, int flags)
6301da177e4SLinus Torvalds {
63186a264abSDavid Howells 	struct auth_cred acred;
6321da177e4SLinus Torvalds 	struct rpc_cred *ret;
63386a264abSDavid Howells 	const struct cred *cred = current_cred();
6341da177e4SLinus Torvalds 
63586a264abSDavid Howells 	memset(&acred, 0, sizeof(acred));
63697f68c6bSNeilBrown 	acred.cred = cred;
6378a317760STrond Myklebust 	ret = auth->au_ops->lookup_cred(auth, &acred, flags);
6381da177e4SLinus Torvalds 	return ret;
6391da177e4SLinus Torvalds }
64066b06860SAndy Adamson EXPORT_SYMBOL_GPL(rpcauth_lookupcred);
6411da177e4SLinus Torvalds 
6425fe4755eSTrond Myklebust void
6435fe4755eSTrond Myklebust rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred,
6445fe4755eSTrond Myklebust 		  struct rpc_auth *auth, const struct rpc_credops *ops)
6455fe4755eSTrond Myklebust {
6465fe4755eSTrond Myklebust 	INIT_HLIST_NODE(&cred->cr_hash);
647e092bdcdSTrond Myklebust 	INIT_LIST_HEAD(&cred->cr_lru);
64879b18181STrond Myklebust 	refcount_set(&cred->cr_count, 1);
6495fe4755eSTrond Myklebust 	cred->cr_auth = auth;
6502edd8d74SNeilBrown 	cred->cr_flags = 0;
6515fe4755eSTrond Myklebust 	cred->cr_ops = ops;
6525fe4755eSTrond Myklebust 	cred->cr_expire = jiffies;
65397f68c6bSNeilBrown 	cred->cr_cred = get_cred(acred->cred);
6545fe4755eSTrond Myklebust }
655e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_cred);
6565fe4755eSTrond Myklebust 
6578572b8e2STrond Myklebust static struct rpc_cred *
6585d351754STrond Myklebust rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags)
6591da177e4SLinus Torvalds {
6601be27f36STrond Myklebust 	struct rpc_auth *auth = task->tk_client->cl_auth;
6611da177e4SLinus Torvalds 	struct auth_cred acred = {
66297f68c6bSNeilBrown 		.cred = get_task_cred(&init_task),
6631da177e4SLinus Torvalds 	};
66497f68c6bSNeilBrown 	struct rpc_cred *ret;
6651da177e4SLinus Torvalds 
66697f68c6bSNeilBrown 	ret = auth->au_ops->lookup_cred(auth, &acred, lookupflags);
66797f68c6bSNeilBrown 	put_cred(acred.cred);
66897f68c6bSNeilBrown 	return ret;
669af093835STrond Myklebust }
670af093835STrond Myklebust 
6718572b8e2STrond Myklebust static struct rpc_cred *
6725e16923bSNeilBrown rpcauth_bind_machine_cred(struct rpc_task *task, int lookupflags)
6735e16923bSNeilBrown {
6745e16923bSNeilBrown 	struct rpc_auth *auth = task->tk_client->cl_auth;
6755e16923bSNeilBrown 	struct auth_cred acred = {
6765e16923bSNeilBrown 		.principal = task->tk_client->cl_principal,
6775e16923bSNeilBrown 		.cred = init_task.cred,
6785e16923bSNeilBrown 	};
6795e16923bSNeilBrown 
6805e16923bSNeilBrown 	if (!acred.principal)
6815e16923bSNeilBrown 		return NULL;
6825e16923bSNeilBrown 	return auth->au_ops->lookup_cred(auth, &acred, lookupflags);
6835e16923bSNeilBrown }
6845e16923bSNeilBrown 
6855e16923bSNeilBrown static struct rpc_cred *
6865d351754STrond Myklebust rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags)
687af093835STrond Myklebust {
688af093835STrond Myklebust 	struct rpc_auth *auth = task->tk_client->cl_auth;
689af093835STrond Myklebust 
6908572b8e2STrond Myklebust 	return rpcauth_lookupcred(auth, lookupflags);
6911da177e4SLinus Torvalds }
6921da177e4SLinus Torvalds 
693a17c2153STrond Myklebust static int
694a52458b4SNeilBrown rpcauth_bindcred(struct rpc_task *task, const struct cred *cred, int flags)
6951da177e4SLinus Torvalds {
696a17c2153STrond Myklebust 	struct rpc_rqst *req = task->tk_rqstp;
6975e16923bSNeilBrown 	struct rpc_cred *new = NULL;
6985d351754STrond Myklebust 	int lookupflags = 0;
699a52458b4SNeilBrown 	struct rpc_auth *auth = task->tk_client->cl_auth;
700a52458b4SNeilBrown 	struct auth_cred acred = {
701a52458b4SNeilBrown 		.cred = cred,
702a52458b4SNeilBrown 	};
7035d351754STrond Myklebust 
7045d351754STrond Myklebust 	if (flags & RPC_TASK_ASYNC)
7055d351754STrond Myklebust 		lookupflags |= RPCAUTH_LOOKUP_NEW;
7061de7eea9SNeilBrown 	if (task->tk_op_cred)
7071de7eea9SNeilBrown 		/* Task must use exactly this rpc_cred */
708d6efccd9SNeilBrown 		new = get_rpccred(task->tk_op_cred);
7091de7eea9SNeilBrown 	else if (cred != NULL && cred != &machine_cred)
710a52458b4SNeilBrown 		new = auth->au_ops->lookup_cred(auth, &acred, lookupflags);
7115e16923bSNeilBrown 	else if (cred == &machine_cred)
7125e16923bSNeilBrown 		new = rpcauth_bind_machine_cred(task, lookupflags);
7135e16923bSNeilBrown 
7145e16923bSNeilBrown 	/* If machine cred couldn't be bound, try a root cred */
7155e16923bSNeilBrown 	if (new)
7165e16923bSNeilBrown 		;
7175e16923bSNeilBrown 	else if (cred == &machine_cred || (flags & RPC_TASK_ROOTCREDS))
7188572b8e2STrond Myklebust 		new = rpcauth_bind_root_cred(task, lookupflags);
719a68a72e1SNeilBrown 	else if (flags & RPC_TASK_NULLCREDS)
720a68a72e1SNeilBrown 		new = authnull_ops.lookup_cred(NULL, NULL, 0);
7214ccda2cdSTrond Myklebust 	else
7228572b8e2STrond Myklebust 		new = rpcauth_bind_new_cred(task, lookupflags);
7238572b8e2STrond Myklebust 	if (IS_ERR(new))
7248572b8e2STrond Myklebust 		return PTR_ERR(new);
725a17c2153STrond Myklebust 	put_rpccred(req->rq_cred);
726a17c2153STrond Myklebust 	req->rq_cred = new;
7278572b8e2STrond Myklebust 	return 0;
7281da177e4SLinus Torvalds }
7291da177e4SLinus Torvalds 
7301da177e4SLinus Torvalds void
7311da177e4SLinus Torvalds put_rpccred(struct rpc_cred *cred)
7321da177e4SLinus Torvalds {
7339a8f6b5eSTrond Myklebust 	if (cred == NULL)
7349a8f6b5eSTrond Myklebust 		return;
73595cd6232STrond Myklebust 	rcu_read_lock();
73679b18181STrond Myklebust 	if (refcount_dec_and_test(&cred->cr_count))
73795cd6232STrond Myklebust 		goto destroy;
73879b18181STrond Myklebust 	if (refcount_read(&cred->cr_count) != 1 ||
73995cd6232STrond Myklebust 	    !test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
74095cd6232STrond Myklebust 		goto out;
741f0380f3dSTrond Myklebust 	if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) {
742e092bdcdSTrond Myklebust 		cred->cr_expire = jiffies;
74395cd6232STrond Myklebust 		rpcauth_lru_add(cred);
74495cd6232STrond Myklebust 		/* Race breaker */
74595cd6232STrond Myklebust 		if (unlikely(!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags)))
74695cd6232STrond Myklebust 			rpcauth_lru_remove(cred);
74795cd6232STrond Myklebust 	} else if (rpcauth_unhash_cred(cred)) {
74895cd6232STrond Myklebust 		rpcauth_lru_remove(cred);
74979b18181STrond Myklebust 		if (refcount_dec_and_test(&cred->cr_count))
75095cd6232STrond Myklebust 			goto destroy;
751f0380f3dSTrond Myklebust 	}
75295cd6232STrond Myklebust out:
75395cd6232STrond Myklebust 	rcu_read_unlock();
754f0380f3dSTrond Myklebust 	return;
75595cd6232STrond Myklebust destroy:
75695cd6232STrond Myklebust 	rcu_read_unlock();
75795cd6232STrond Myklebust 	cred->cr_ops->crdestroy(cred);
7581da177e4SLinus Torvalds }
759e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(put_rpccred);
7601da177e4SLinus Torvalds 
761e8680a24SChuck Lever /**
762e8680a24SChuck Lever  * rpcauth_marshcred - Append RPC credential to end of @xdr
763e8680a24SChuck Lever  * @task: controlling RPC task
764e8680a24SChuck Lever  * @xdr: xdr_stream containing initial portion of RPC Call header
765e8680a24SChuck Lever  *
766e8680a24SChuck Lever  * On success, an appropriate verifier is added to @xdr, @xdr is
767e8680a24SChuck Lever  * updated to point past the verifier, and zero is returned.
768e8680a24SChuck Lever  * Otherwise, @xdr is in an undefined state and a negative errno
769e8680a24SChuck Lever  * is returned.
770e8680a24SChuck Lever  */
771e8680a24SChuck Lever int rpcauth_marshcred(struct rpc_task *task, struct xdr_stream *xdr)
7721da177e4SLinus Torvalds {
773e8680a24SChuck Lever 	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
7741da177e4SLinus Torvalds 
775e8680a24SChuck Lever 	return ops->crmarshal(task, xdr);
7761da177e4SLinus Torvalds }
7771da177e4SLinus Torvalds 
778e8680a24SChuck Lever /**
779e8680a24SChuck Lever  * rpcauth_wrap_req_encode - XDR encode the RPC procedure
780e8680a24SChuck Lever  * @task: controlling RPC task
781e8680a24SChuck Lever  * @xdr: stream where on-the-wire bytes are to be marshalled
782e8680a24SChuck Lever  *
783e8680a24SChuck Lever  * On success, @xdr contains the encoded and wrapped message.
784e8680a24SChuck Lever  * Otherwise, @xdr is in an undefined state.
785e8680a24SChuck Lever  */
786e8680a24SChuck Lever int rpcauth_wrap_req_encode(struct rpc_task *task, struct xdr_stream *xdr)
7879f06c719SChuck Lever {
788e8680a24SChuck Lever 	kxdreproc_t encode = task->tk_msg.rpc_proc->p_encode;
7899f06c719SChuck Lever 
790e8680a24SChuck Lever 	encode(task->tk_rqstp, xdr, task->tk_msg.rpc_argp);
7919f06c719SChuck Lever 	return 0;
7921da177e4SLinus Torvalds }
793e8680a24SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_wrap_req_encode);
794e8680a24SChuck Lever 
795e8680a24SChuck Lever /**
796e8680a24SChuck Lever  * rpcauth_wrap_req - XDR encode and wrap the RPC procedure
797e8680a24SChuck Lever  * @task: controlling RPC task
798e8680a24SChuck Lever  * @xdr: stream where on-the-wire bytes are to be marshalled
799e8680a24SChuck Lever  *
800e8680a24SChuck Lever  * On success, @xdr contains the encoded and wrapped message,
801e8680a24SChuck Lever  * and zero is returned. Otherwise, @xdr is in an undefined
802e8680a24SChuck Lever  * state and a negative errno is returned.
803e8680a24SChuck Lever  */
804e8680a24SChuck Lever int rpcauth_wrap_req(struct rpc_task *task, struct xdr_stream *xdr)
805e8680a24SChuck Lever {
806e8680a24SChuck Lever 	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
807e8680a24SChuck Lever 
808e8680a24SChuck Lever 	return ops->crwrap_req(task, xdr);
809e8680a24SChuck Lever }
8101da177e4SLinus Torvalds 
811*a0584ee9SChuck Lever /**
812*a0584ee9SChuck Lever  * rpcauth_checkverf - Validate verifier in RPC Reply header
813*a0584ee9SChuck Lever  * @task: controlling RPC task
814*a0584ee9SChuck Lever  * @xdr: xdr_stream containing RPC Reply header
815*a0584ee9SChuck Lever  *
816*a0584ee9SChuck Lever  * On success, @xdr is updated to point past the verifier and
817*a0584ee9SChuck Lever  * zero is returned. Otherwise, @xdr is in an undefined state
818*a0584ee9SChuck Lever  * and a negative errno is returned.
819*a0584ee9SChuck Lever  */
820*a0584ee9SChuck Lever int
821*a0584ee9SChuck Lever rpcauth_checkverf(struct rpc_task *task, struct xdr_stream *xdr)
822bf269551SChuck Lever {
823*a0584ee9SChuck Lever 	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
824bf269551SChuck Lever 
825*a0584ee9SChuck Lever 	return ops->crvalidate(task, xdr);
826bf269551SChuck Lever }
827bf269551SChuck Lever 
828*a0584ee9SChuck Lever /**
829*a0584ee9SChuck Lever  * rpcauth_unwrap_resp_decode - Invoke XDR decode function
830*a0584ee9SChuck Lever  * @task: controlling RPC task
831*a0584ee9SChuck Lever  * @xdr: stream where the Reply message resides
832*a0584ee9SChuck Lever  *
833*a0584ee9SChuck Lever  * Returns zero on success; otherwise a negative errno is returned.
834*a0584ee9SChuck Lever  */
8351da177e4SLinus Torvalds int
836*a0584ee9SChuck Lever rpcauth_unwrap_resp_decode(struct rpc_task *task, struct xdr_stream *xdr)
8371da177e4SLinus Torvalds {
838*a0584ee9SChuck Lever 	kxdrdproc_t decode = task->tk_msg.rpc_proc->p_decode;
8391da177e4SLinus Torvalds 
840*a0584ee9SChuck Lever 	return decode(task->tk_rqstp, xdr, task->tk_msg.rpc_resp);
841*a0584ee9SChuck Lever }
842*a0584ee9SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_unwrap_resp_decode);
843*a0584ee9SChuck Lever 
844*a0584ee9SChuck Lever /**
845*a0584ee9SChuck Lever  * rpcauth_unwrap_resp - Invoke unwrap and decode function for the cred
846*a0584ee9SChuck Lever  * @task: controlling RPC task
847*a0584ee9SChuck Lever  * @xdr: stream where the Reply message resides
848*a0584ee9SChuck Lever  *
849*a0584ee9SChuck Lever  * Returns zero on success; otherwise a negative errno is returned.
850*a0584ee9SChuck Lever  */
851*a0584ee9SChuck Lever int
852*a0584ee9SChuck Lever rpcauth_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr)
853*a0584ee9SChuck Lever {
854*a0584ee9SChuck Lever 	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
855*a0584ee9SChuck Lever 
856*a0584ee9SChuck Lever 	return ops->crunwrap_resp(task, xdr);
8571da177e4SLinus Torvalds }
8581da177e4SLinus Torvalds 
8593021a5bbSTrond Myklebust bool
8603021a5bbSTrond Myklebust rpcauth_xmit_need_reencode(struct rpc_task *task)
8613021a5bbSTrond Myklebust {
8623021a5bbSTrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
8633021a5bbSTrond Myklebust 
8643021a5bbSTrond Myklebust 	if (!cred || !cred->cr_ops->crneed_reencode)
8653021a5bbSTrond Myklebust 		return false;
8663021a5bbSTrond Myklebust 	return cred->cr_ops->crneed_reencode(task);
8673021a5bbSTrond Myklebust }
8683021a5bbSTrond Myklebust 
8691da177e4SLinus Torvalds int
8701da177e4SLinus Torvalds rpcauth_refreshcred(struct rpc_task *task)
8711da177e4SLinus Torvalds {
8729a84d380STrond Myklebust 	struct rpc_cred	*cred;
8731da177e4SLinus Torvalds 	int err;
8741da177e4SLinus Torvalds 
875a17c2153STrond Myklebust 	cred = task->tk_rqstp->rq_cred;
876a17c2153STrond Myklebust 	if (cred == NULL) {
877a17c2153STrond Myklebust 		err = rpcauth_bindcred(task, task->tk_msg.rpc_cred, task->tk_flags);
878a17c2153STrond Myklebust 		if (err < 0)
879a17c2153STrond Myklebust 			goto out;
880a17c2153STrond Myklebust 		cred = task->tk_rqstp->rq_cred;
881f81c6224SJoe Perches 	}
8820bbacc40SChuck Lever 
8831da177e4SLinus Torvalds 	err = cred->cr_ops->crrefresh(task);
884a17c2153STrond Myklebust out:
8851da177e4SLinus Torvalds 	if (err < 0)
8861da177e4SLinus Torvalds 		task->tk_status = err;
8871da177e4SLinus Torvalds 	return err;
8881da177e4SLinus Torvalds }
8891da177e4SLinus Torvalds 
8901da177e4SLinus Torvalds void
8911da177e4SLinus Torvalds rpcauth_invalcred(struct rpc_task *task)
8921da177e4SLinus Torvalds {
893a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
894fc432dd9STrond Myklebust 
895fc432dd9STrond Myklebust 	if (cred)
896fc432dd9STrond Myklebust 		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
8971da177e4SLinus Torvalds }
8981da177e4SLinus Torvalds 
8991da177e4SLinus Torvalds int
9001da177e4SLinus Torvalds rpcauth_uptodatecred(struct rpc_task *task)
9011da177e4SLinus Torvalds {
902a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
903fc432dd9STrond Myklebust 
904fc432dd9STrond Myklebust 	return cred == NULL ||
905fc432dd9STrond Myklebust 		test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0;
9061da177e4SLinus Torvalds }
907f5c2187cSTrond Myklebust 
9088e1f936bSRusty Russell static struct shrinker rpc_cred_shrinker = {
90970534a73SDave Chinner 	.count_objects = rpcauth_cache_shrink_count,
91070534a73SDave Chinner 	.scan_objects = rpcauth_cache_shrink_scan,
9118e1f936bSRusty Russell 	.seeks = DEFAULT_SEEKS,
9128e1f936bSRusty Russell };
913f5c2187cSTrond Myklebust 
9145d8d9a4dSTrond Myklebust int __init rpcauth_init_module(void)
915f5c2187cSTrond Myklebust {
9165d8d9a4dSTrond Myklebust 	int err;
9175d8d9a4dSTrond Myklebust 
9185d8d9a4dSTrond Myklebust 	err = rpc_init_authunix();
9195d8d9a4dSTrond Myklebust 	if (err < 0)
9205d8d9a4dSTrond Myklebust 		goto out1;
9212864486bSKinglong Mee 	err = register_shrinker(&rpc_cred_shrinker);
9222864486bSKinglong Mee 	if (err < 0)
92389a4f758SNeilBrown 		goto out2;
9245d8d9a4dSTrond Myklebust 	return 0;
9255d8d9a4dSTrond Myklebust out2:
9265d8d9a4dSTrond Myklebust 	rpc_destroy_authunix();
9275d8d9a4dSTrond Myklebust out1:
9285d8d9a4dSTrond Myklebust 	return err;
929f5c2187cSTrond Myklebust }
930f5c2187cSTrond Myklebust 
931c135e84aSStephen Rothwell void rpcauth_remove_module(void)
932f5c2187cSTrond Myklebust {
9335d8d9a4dSTrond Myklebust 	rpc_destroy_authunix();
9348e1f936bSRusty Russell 	unregister_shrinker(&rpc_cred_shrinker);
935f5c2187cSTrond Myklebust }
936