xref: /openbmc/linux/net/sunrpc/auth.c (revision 5e16923b)
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 
20f895b252SJeff Layton #if IS_ENABLED(CONFIG_SUNRPC_DEBUG)
211da177e4SLinus Torvalds # define RPCDBG_FACILITY	RPCDBG_AUTH
221da177e4SLinus Torvalds #endif
231da177e4SLinus Torvalds 
24241269bdSTrond Myklebust #define RPC_CREDCACHE_DEFAULT_HASHBITS	(4)
25241269bdSTrond Myklebust struct rpc_cred_cache {
26241269bdSTrond Myklebust 	struct hlist_head	*hashtable;
27241269bdSTrond Myklebust 	unsigned int		hashbits;
28241269bdSTrond Myklebust 	spinlock_t		lock;
29241269bdSTrond Myklebust };
30241269bdSTrond Myklebust 
31241269bdSTrond Myklebust static unsigned int auth_hashbits = RPC_CREDCACHE_DEFAULT_HASHBITS;
32241269bdSTrond Myklebust 
334e4c3befSTrond Myklebust static const struct rpc_authops __rcu *auth_flavors[RPC_AUTH_MAXFLAVOR] = {
344e4c3befSTrond Myklebust 	[RPC_AUTH_NULL] = (const struct rpc_authops __force __rcu *)&authnull_ops,
354e4c3befSTrond Myklebust 	[RPC_AUTH_UNIX] = (const struct rpc_authops __force __rcu *)&authunix_ops,
361da177e4SLinus Torvalds 	NULL,			/* others can be loadable modules */
371da177e4SLinus Torvalds };
381da177e4SLinus Torvalds 
39e092bdcdSTrond Myklebust static LIST_HEAD(cred_unused);
40f5c2187cSTrond Myklebust static unsigned long number_cred_unused;
41e092bdcdSTrond Myklebust 
425e16923bSNeilBrown static struct rpc_cred machine_cred = {
435e16923bSNeilBrown 	.cr_count = REFCOUNT_INIT(1),
445e16923bSNeilBrown };
455e16923bSNeilBrown 
465e16923bSNeilBrown /*
475e16923bSNeilBrown  * Return the machine_cred pointer to be used whenever
485e16923bSNeilBrown  * the a generic machine credential is needed.
495e16923bSNeilBrown  */
505e16923bSNeilBrown struct rpc_cred *rpc_machine_cred(void)
515e16923bSNeilBrown {
525e16923bSNeilBrown 	return &machine_cred;
535e16923bSNeilBrown }
545e16923bSNeilBrown EXPORT_SYMBOL_GPL(rpc_machine_cred);
555e16923bSNeilBrown 
56db5fe265SMiquel van Smoorenburg #define MAX_HASHTABLE_BITS (14)
578e4e15d4SStephen Rothwell static int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp)
58241269bdSTrond Myklebust {
59241269bdSTrond Myklebust 	unsigned long num;
60241269bdSTrond Myklebust 	unsigned int nbits;
61241269bdSTrond Myklebust 	int ret;
62241269bdSTrond Myklebust 
63241269bdSTrond Myklebust 	if (!val)
64241269bdSTrond Myklebust 		goto out_inval;
6500cfaa94SDaniel Walter 	ret = kstrtoul(val, 0, &num);
661a54c0cfSDan Carpenter 	if (ret)
67241269bdSTrond Myklebust 		goto out_inval;
6834ae685cSFrank Sorenson 	nbits = fls(num - 1);
69241269bdSTrond Myklebust 	if (nbits > MAX_HASHTABLE_BITS || nbits < 2)
70241269bdSTrond Myklebust 		goto out_inval;
71241269bdSTrond Myklebust 	*(unsigned int *)kp->arg = nbits;
72241269bdSTrond Myklebust 	return 0;
73241269bdSTrond Myklebust out_inval:
74241269bdSTrond Myklebust 	return -EINVAL;
75241269bdSTrond Myklebust }
76241269bdSTrond Myklebust 
778e4e15d4SStephen Rothwell static int param_get_hashtbl_sz(char *buffer, const struct kernel_param *kp)
78241269bdSTrond Myklebust {
79241269bdSTrond Myklebust 	unsigned int nbits;
80241269bdSTrond Myklebust 
81241269bdSTrond Myklebust 	nbits = *(unsigned int *)kp->arg;
82241269bdSTrond Myklebust 	return sprintf(buffer, "%u", 1U << nbits);
83241269bdSTrond Myklebust }
84241269bdSTrond Myklebust 
85241269bdSTrond Myklebust #define param_check_hashtbl_sz(name, p) __param_check(name, p, unsigned int);
86241269bdSTrond Myklebust 
879c27847dSLuis R. Rodriguez static const struct kernel_param_ops param_ops_hashtbl_sz = {
888e4e15d4SStephen Rothwell 	.set = param_set_hashtbl_sz,
898e4e15d4SStephen Rothwell 	.get = param_get_hashtbl_sz,
908e4e15d4SStephen Rothwell };
918e4e15d4SStephen Rothwell 
92241269bdSTrond Myklebust module_param_named(auth_hashtable_size, auth_hashbits, hashtbl_sz, 0644);
93241269bdSTrond Myklebust MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size");
94241269bdSTrond Myklebust 
95bae6746fSTrond Myklebust static unsigned long auth_max_cred_cachesize = ULONG_MAX;
96bae6746fSTrond Myklebust module_param(auth_max_cred_cachesize, ulong, 0644);
97bae6746fSTrond Myklebust MODULE_PARM_DESC(auth_max_cred_cachesize, "RPC credential maximum total cache size");
98bae6746fSTrond Myklebust 
991da177e4SLinus Torvalds static u32
1001da177e4SLinus Torvalds pseudoflavor_to_flavor(u32 flavor) {
1011c74a244SChuck Lever 	if (flavor > RPC_AUTH_MAXFLAVOR)
1021da177e4SLinus Torvalds 		return RPC_AUTH_GSS;
1031da177e4SLinus Torvalds 	return flavor;
1041da177e4SLinus Torvalds }
1051da177e4SLinus Torvalds 
1061da177e4SLinus Torvalds int
107f1c0a861STrond Myklebust rpcauth_register(const struct rpc_authops *ops)
1081da177e4SLinus Torvalds {
1094e4c3befSTrond Myklebust 	const struct rpc_authops *old;
1101da177e4SLinus Torvalds 	rpc_authflavor_t flavor;
1111da177e4SLinus Torvalds 
1121da177e4SLinus Torvalds 	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
1131da177e4SLinus Torvalds 		return -EINVAL;
1144e4c3befSTrond Myklebust 	old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], NULL, ops);
1154e4c3befSTrond Myklebust 	if (old == NULL || old == ops)
1164e4c3befSTrond Myklebust 		return 0;
1174e4c3befSTrond Myklebust 	return -EPERM;
1181da177e4SLinus Torvalds }
119e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_register);
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds int
122f1c0a861STrond Myklebust rpcauth_unregister(const struct rpc_authops *ops)
1231da177e4SLinus Torvalds {
1244e4c3befSTrond Myklebust 	const struct rpc_authops *old;
1251da177e4SLinus Torvalds 	rpc_authflavor_t flavor;
1261da177e4SLinus Torvalds 
1271da177e4SLinus Torvalds 	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
1281da177e4SLinus Torvalds 		return -EINVAL;
1294e4c3befSTrond Myklebust 
1304e4c3befSTrond Myklebust 	old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], ops, NULL);
1314e4c3befSTrond Myklebust 	if (old == ops || old == NULL)
1324e4c3befSTrond Myklebust 		return 0;
1334e4c3befSTrond Myklebust 	return -EPERM;
1341da177e4SLinus Torvalds }
135e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_unregister);
1361da177e4SLinus Torvalds 
1374e4c3befSTrond Myklebust static const struct rpc_authops *
1384e4c3befSTrond Myklebust rpcauth_get_authops(rpc_authflavor_t flavor)
1394e4c3befSTrond Myklebust {
1404e4c3befSTrond Myklebust 	const struct rpc_authops *ops;
1414e4c3befSTrond Myklebust 
1424e4c3befSTrond Myklebust 	if (flavor >= RPC_AUTH_MAXFLAVOR)
1434e4c3befSTrond Myklebust 		return NULL;
1444e4c3befSTrond Myklebust 
1454e4c3befSTrond Myklebust 	rcu_read_lock();
1464e4c3befSTrond Myklebust 	ops = rcu_dereference(auth_flavors[flavor]);
1474e4c3befSTrond Myklebust 	if (ops == NULL) {
1484e4c3befSTrond Myklebust 		rcu_read_unlock();
1494e4c3befSTrond Myklebust 		request_module("rpc-auth-%u", flavor);
1504e4c3befSTrond Myklebust 		rcu_read_lock();
1514e4c3befSTrond Myklebust 		ops = rcu_dereference(auth_flavors[flavor]);
1524e4c3befSTrond Myklebust 		if (ops == NULL)
1534e4c3befSTrond Myklebust 			goto out;
1544e4c3befSTrond Myklebust 	}
1554e4c3befSTrond Myklebust 	if (!try_module_get(ops->owner))
1564e4c3befSTrond Myklebust 		ops = NULL;
1574e4c3befSTrond Myklebust out:
1584e4c3befSTrond Myklebust 	rcu_read_unlock();
1594e4c3befSTrond Myklebust 	return ops;
1604e4c3befSTrond Myklebust }
1614e4c3befSTrond Myklebust 
1624e4c3befSTrond Myklebust static void
1634e4c3befSTrond Myklebust rpcauth_put_authops(const struct rpc_authops *ops)
1644e4c3befSTrond Myklebust {
1654e4c3befSTrond Myklebust 	module_put(ops->owner);
1664e4c3befSTrond Myklebust }
1674e4c3befSTrond Myklebust 
1686a1a1e34SChuck Lever /**
1699568c5e9SChuck Lever  * rpcauth_get_pseudoflavor - check if security flavor is supported
1709568c5e9SChuck Lever  * @flavor: a security flavor
1719568c5e9SChuck Lever  * @info: a GSS mech OID, quality of protection, and service value
1729568c5e9SChuck Lever  *
1739568c5e9SChuck Lever  * Verifies that an appropriate kernel module is available or already loaded.
1749568c5e9SChuck Lever  * Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is
1759568c5e9SChuck Lever  * not supported locally.
1769568c5e9SChuck Lever  */
1779568c5e9SChuck Lever rpc_authflavor_t
1789568c5e9SChuck Lever rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
1799568c5e9SChuck Lever {
1804e4c3befSTrond Myklebust 	const struct rpc_authops *ops = rpcauth_get_authops(flavor);
1819568c5e9SChuck Lever 	rpc_authflavor_t pseudoflavor;
1829568c5e9SChuck Lever 
1834e4c3befSTrond Myklebust 	if (!ops)
1849568c5e9SChuck Lever 		return RPC_AUTH_MAXFLAVOR;
1859568c5e9SChuck Lever 	pseudoflavor = flavor;
1869568c5e9SChuck Lever 	if (ops->info2flavor != NULL)
1879568c5e9SChuck Lever 		pseudoflavor = ops->info2flavor(info);
1889568c5e9SChuck Lever 
1894e4c3befSTrond Myklebust 	rpcauth_put_authops(ops);
1909568c5e9SChuck Lever 	return pseudoflavor;
1919568c5e9SChuck Lever }
1929568c5e9SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);
1939568c5e9SChuck Lever 
1949568c5e9SChuck Lever /**
195a77c806fSChuck Lever  * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor
196a77c806fSChuck Lever  * @pseudoflavor: GSS pseudoflavor to match
197a77c806fSChuck Lever  * @info: rpcsec_gss_info structure to fill in
198a77c806fSChuck Lever  *
199a77c806fSChuck Lever  * Returns zero and fills in "info" if pseudoflavor matches a
200a77c806fSChuck Lever  * supported mechanism.
201a77c806fSChuck Lever  */
202a77c806fSChuck Lever int
203a77c806fSChuck Lever rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
204a77c806fSChuck Lever {
205a77c806fSChuck Lever 	rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor);
206a77c806fSChuck Lever 	const struct rpc_authops *ops;
207a77c806fSChuck Lever 	int result;
208a77c806fSChuck Lever 
2094e4c3befSTrond Myklebust 	ops = rpcauth_get_authops(flavor);
210a77c806fSChuck Lever 	if (ops == NULL)
211a77c806fSChuck Lever 		return -ENOENT;
212a77c806fSChuck Lever 
213a77c806fSChuck Lever 	result = -ENOENT;
214a77c806fSChuck Lever 	if (ops->flavor2info != NULL)
215a77c806fSChuck Lever 		result = ops->flavor2info(pseudoflavor, info);
216a77c806fSChuck Lever 
2174e4c3befSTrond Myklebust 	rpcauth_put_authops(ops);
218a77c806fSChuck Lever 	return result;
219a77c806fSChuck Lever }
220a77c806fSChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);
221a77c806fSChuck Lever 
222a77c806fSChuck Lever /**
2236a1a1e34SChuck Lever  * rpcauth_list_flavors - discover registered flavors and pseudoflavors
2246a1a1e34SChuck Lever  * @array: array to fill in
2256a1a1e34SChuck Lever  * @size: size of "array"
2266a1a1e34SChuck Lever  *
2276a1a1e34SChuck Lever  * Returns the number of array items filled in, or a negative errno.
2286a1a1e34SChuck Lever  *
2296a1a1e34SChuck Lever  * The returned array is not sorted by any policy.  Callers should not
2306a1a1e34SChuck Lever  * rely on the order of the items in the returned array.
2316a1a1e34SChuck Lever  */
2326a1a1e34SChuck Lever int
2336a1a1e34SChuck Lever rpcauth_list_flavors(rpc_authflavor_t *array, int size)
2346a1a1e34SChuck Lever {
2354e4c3befSTrond Myklebust 	const struct rpc_authops *ops;
2364e4c3befSTrond Myklebust 	rpc_authflavor_t flavor, pseudos[4];
2374e4c3befSTrond Myklebust 	int i, len, result = 0;
2386a1a1e34SChuck Lever 
2394e4c3befSTrond Myklebust 	rcu_read_lock();
2406a1a1e34SChuck Lever 	for (flavor = 0; flavor < RPC_AUTH_MAXFLAVOR; flavor++) {
2414e4c3befSTrond Myklebust 		ops = rcu_dereference(auth_flavors[flavor]);
2426a1a1e34SChuck Lever 		if (result >= size) {
2436a1a1e34SChuck Lever 			result = -ENOMEM;
2446a1a1e34SChuck Lever 			break;
2456a1a1e34SChuck Lever 		}
2466a1a1e34SChuck Lever 
2476a1a1e34SChuck Lever 		if (ops == NULL)
2486a1a1e34SChuck Lever 			continue;
2496a1a1e34SChuck Lever 		if (ops->list_pseudoflavors == NULL) {
2506a1a1e34SChuck Lever 			array[result++] = ops->au_flavor;
2516a1a1e34SChuck Lever 			continue;
2526a1a1e34SChuck Lever 		}
2536a1a1e34SChuck Lever 		len = ops->list_pseudoflavors(pseudos, ARRAY_SIZE(pseudos));
2546a1a1e34SChuck Lever 		if (len < 0) {
2556a1a1e34SChuck Lever 			result = len;
2566a1a1e34SChuck Lever 			break;
2576a1a1e34SChuck Lever 		}
2586a1a1e34SChuck Lever 		for (i = 0; i < len; i++) {
2596a1a1e34SChuck Lever 			if (result >= size) {
2606a1a1e34SChuck Lever 				result = -ENOMEM;
2616a1a1e34SChuck Lever 				break;
2626a1a1e34SChuck Lever 			}
2636a1a1e34SChuck Lever 			array[result++] = pseudos[i];
2646a1a1e34SChuck Lever 		}
2656a1a1e34SChuck Lever 	}
2664e4c3befSTrond Myklebust 	rcu_read_unlock();
2676a1a1e34SChuck Lever 
2686a1a1e34SChuck Lever 	dprintk("RPC:       %s returns %d\n", __func__, result);
2696a1a1e34SChuck Lever 	return result;
2706a1a1e34SChuck Lever }
2716a1a1e34SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_list_flavors);
2726a1a1e34SChuck Lever 
2731da177e4SLinus Torvalds struct rpc_auth *
27482b98ca5SSargun Dhillon rpcauth_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
2751da177e4SLinus Torvalds {
2764e4c3befSTrond Myklebust 	struct rpc_auth	*auth = ERR_PTR(-EINVAL);
277f1c0a861STrond Myklebust 	const struct rpc_authops *ops;
278c2190661STrond Myklebust 	u32 flavor = pseudoflavor_to_flavor(args->pseudoflavor);
2791da177e4SLinus Torvalds 
2804e4c3befSTrond Myklebust 	ops = rpcauth_get_authops(flavor);
2814e4c3befSTrond Myklebust 	if (ops == NULL)
282f344f6dfSOlaf Kirch 		goto out;
283f344f6dfSOlaf Kirch 
284c2190661STrond Myklebust 	auth = ops->create(args, clnt);
2854e4c3befSTrond Myklebust 
2864e4c3befSTrond Myklebust 	rpcauth_put_authops(ops);
2876a19275aSJ. Bruce Fields 	if (IS_ERR(auth))
2886a19275aSJ. Bruce Fields 		return auth;
2891da177e4SLinus Torvalds 	if (clnt->cl_auth)
290de7a8ce3STrond Myklebust 		rpcauth_release(clnt->cl_auth);
2911da177e4SLinus Torvalds 	clnt->cl_auth = auth;
292f344f6dfSOlaf Kirch 
293f344f6dfSOlaf Kirch out:
2941da177e4SLinus Torvalds 	return auth;
2951da177e4SLinus Torvalds }
296e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_create);
2971da177e4SLinus Torvalds 
2981da177e4SLinus Torvalds void
299de7a8ce3STrond Myklebust rpcauth_release(struct rpc_auth *auth)
3001da177e4SLinus Torvalds {
301331bc71cSTrond Myklebust 	if (!refcount_dec_and_test(&auth->au_count))
3021da177e4SLinus Torvalds 		return;
3031da177e4SLinus Torvalds 	auth->au_ops->destroy(auth);
3041da177e4SLinus Torvalds }
3051da177e4SLinus Torvalds 
3061da177e4SLinus Torvalds static DEFINE_SPINLOCK(rpc_credcache_lock);
3071da177e4SLinus Torvalds 
30895cd6232STrond Myklebust /*
30995cd6232STrond Myklebust  * On success, the caller is responsible for freeing the reference
31095cd6232STrond Myklebust  * held by the hashtable
31195cd6232STrond Myklebust  */
31295cd6232STrond Myklebust static bool
31331be5bf1STrond Myklebust rpcauth_unhash_cred_locked(struct rpc_cred *cred)
31431be5bf1STrond Myklebust {
31595cd6232STrond Myklebust 	if (!test_and_clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
31695cd6232STrond Myklebust 		return false;
31731be5bf1STrond Myklebust 	hlist_del_rcu(&cred->cr_hash);
31895cd6232STrond Myklebust 	return true;
31931be5bf1STrond Myklebust }
32031be5bf1STrond Myklebust 
32195cd6232STrond Myklebust static bool
3229499b434STrond Myklebust rpcauth_unhash_cred(struct rpc_cred *cred)
3239499b434STrond Myklebust {
3249499b434STrond Myklebust 	spinlock_t *cache_lock;
32595cd6232STrond Myklebust 	bool ret;
3269499b434STrond Myklebust 
32795cd6232STrond Myklebust 	if (!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
32895cd6232STrond Myklebust 		return false;
3299499b434STrond Myklebust 	cache_lock = &cred->cr_auth->au_credcache->lock;
3309499b434STrond Myklebust 	spin_lock(cache_lock);
33195cd6232STrond Myklebust 	ret = rpcauth_unhash_cred_locked(cred);
3329499b434STrond Myklebust 	spin_unlock(cache_lock);
333f0380f3dSTrond Myklebust 	return ret;
3349499b434STrond Myklebust }
3359499b434STrond Myklebust 
3361da177e4SLinus Torvalds /*
3371da177e4SLinus Torvalds  * Initialize RPC credential cache
3381da177e4SLinus Torvalds  */
3391da177e4SLinus Torvalds int
340f5c2187cSTrond Myklebust rpcauth_init_credcache(struct rpc_auth *auth)
3411da177e4SLinus Torvalds {
3421da177e4SLinus Torvalds 	struct rpc_cred_cache *new;
343988664a0STrond Myklebust 	unsigned int hashsize;
3441da177e4SLinus Torvalds 
3458b3a7005SKris Katterjohn 	new = kmalloc(sizeof(*new), GFP_KERNEL);
3461da177e4SLinus Torvalds 	if (!new)
347241269bdSTrond Myklebust 		goto out_nocache;
348241269bdSTrond Myklebust 	new->hashbits = auth_hashbits;
349988664a0STrond Myklebust 	hashsize = 1U << new->hashbits;
350241269bdSTrond Myklebust 	new->hashtable = kcalloc(hashsize, sizeof(new->hashtable[0]), GFP_KERNEL);
351241269bdSTrond Myklebust 	if (!new->hashtable)
352241269bdSTrond Myklebust 		goto out_nohashtbl;
3539499b434STrond Myklebust 	spin_lock_init(&new->lock);
3541da177e4SLinus Torvalds 	auth->au_credcache = new;
3551da177e4SLinus Torvalds 	return 0;
356241269bdSTrond Myklebust out_nohashtbl:
357241269bdSTrond Myklebust 	kfree(new);
358241269bdSTrond Myklebust out_nocache:
359241269bdSTrond Myklebust 	return -ENOMEM;
3601da177e4SLinus Torvalds }
361e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_credcache);
3621da177e4SLinus Torvalds 
3631da177e4SLinus Torvalds /*
3644de6caa2SAndy Adamson  * Setup a credential key lifetime timeout notification
3654de6caa2SAndy Adamson  */
3664de6caa2SAndy Adamson int
3674de6caa2SAndy Adamson rpcauth_key_timeout_notify(struct rpc_auth *auth, struct rpc_cred *cred)
3684de6caa2SAndy Adamson {
3694de6caa2SAndy Adamson 	if (!cred->cr_auth->au_ops->key_timeout)
3704de6caa2SAndy Adamson 		return 0;
3714de6caa2SAndy Adamson 	return cred->cr_auth->au_ops->key_timeout(auth, cred);
3724de6caa2SAndy Adamson }
3734de6caa2SAndy Adamson EXPORT_SYMBOL_GPL(rpcauth_key_timeout_notify);
3744de6caa2SAndy Adamson 
3754de6caa2SAndy Adamson bool
376ce52914eSScott Mayhew rpcauth_cred_key_to_expire(struct rpc_auth *auth, struct rpc_cred *cred)
3774de6caa2SAndy Adamson {
378ce52914eSScott Mayhew 	if (auth->au_flags & RPCAUTH_AUTH_NO_CRKEY_TIMEOUT)
379ce52914eSScott Mayhew 		return false;
3804de6caa2SAndy Adamson 	if (!cred->cr_ops->crkey_to_expire)
3814de6caa2SAndy Adamson 		return false;
3824de6caa2SAndy Adamson 	return cred->cr_ops->crkey_to_expire(cred);
3834de6caa2SAndy Adamson }
3844de6caa2SAndy Adamson EXPORT_SYMBOL_GPL(rpcauth_cred_key_to_expire);
3854de6caa2SAndy Adamson 
386a0337d1dSJeff Layton char *
387a0337d1dSJeff Layton rpcauth_stringify_acceptor(struct rpc_cred *cred)
388a0337d1dSJeff Layton {
389a0337d1dSJeff Layton 	if (!cred->cr_ops->crstringify_acceptor)
390a0337d1dSJeff Layton 		return NULL;
391a0337d1dSJeff Layton 	return cred->cr_ops->crstringify_acceptor(cred);
392a0337d1dSJeff Layton }
393a0337d1dSJeff Layton EXPORT_SYMBOL_GPL(rpcauth_stringify_acceptor);
394a0337d1dSJeff Layton 
3954de6caa2SAndy Adamson /*
3961da177e4SLinus Torvalds  * Destroy a list of credentials
3971da177e4SLinus Torvalds  */
3981da177e4SLinus Torvalds static inline
399e092bdcdSTrond Myklebust void rpcauth_destroy_credlist(struct list_head *head)
4001da177e4SLinus Torvalds {
4011da177e4SLinus Torvalds 	struct rpc_cred *cred;
4021da177e4SLinus Torvalds 
403e092bdcdSTrond Myklebust 	while (!list_empty(head)) {
404e092bdcdSTrond Myklebust 		cred = list_entry(head->next, struct rpc_cred, cr_lru);
405e092bdcdSTrond Myklebust 		list_del_init(&cred->cr_lru);
4061da177e4SLinus Torvalds 		put_rpccred(cred);
4071da177e4SLinus Torvalds 	}
4081da177e4SLinus Torvalds }
4091da177e4SLinus Torvalds 
41095cd6232STrond Myklebust static void
41195cd6232STrond Myklebust rpcauth_lru_add_locked(struct rpc_cred *cred)
41295cd6232STrond Myklebust {
41395cd6232STrond Myklebust 	if (!list_empty(&cred->cr_lru))
41495cd6232STrond Myklebust 		return;
41595cd6232STrond Myklebust 	number_cred_unused++;
41695cd6232STrond Myklebust 	list_add_tail(&cred->cr_lru, &cred_unused);
41795cd6232STrond Myklebust }
41895cd6232STrond Myklebust 
41995cd6232STrond Myklebust static void
42095cd6232STrond Myklebust rpcauth_lru_add(struct rpc_cred *cred)
42195cd6232STrond Myklebust {
42295cd6232STrond Myklebust 	if (!list_empty(&cred->cr_lru))
42395cd6232STrond Myklebust 		return;
42495cd6232STrond Myklebust 	spin_lock(&rpc_credcache_lock);
42595cd6232STrond Myklebust 	rpcauth_lru_add_locked(cred);
42695cd6232STrond Myklebust 	spin_unlock(&rpc_credcache_lock);
42795cd6232STrond Myklebust }
42895cd6232STrond Myklebust 
42995cd6232STrond Myklebust static void
43095cd6232STrond Myklebust rpcauth_lru_remove_locked(struct rpc_cred *cred)
43195cd6232STrond Myklebust {
43295cd6232STrond Myklebust 	if (list_empty(&cred->cr_lru))
43395cd6232STrond Myklebust 		return;
43495cd6232STrond Myklebust 	number_cred_unused--;
43595cd6232STrond Myklebust 	list_del_init(&cred->cr_lru);
43695cd6232STrond Myklebust }
43795cd6232STrond Myklebust 
43895cd6232STrond Myklebust static void
43995cd6232STrond Myklebust rpcauth_lru_remove(struct rpc_cred *cred)
44095cd6232STrond Myklebust {
44195cd6232STrond Myklebust 	if (list_empty(&cred->cr_lru))
44295cd6232STrond Myklebust 		return;
44395cd6232STrond Myklebust 	spin_lock(&rpc_credcache_lock);
44495cd6232STrond Myklebust 	rpcauth_lru_remove_locked(cred);
44595cd6232STrond Myklebust 	spin_unlock(&rpc_credcache_lock);
44695cd6232STrond Myklebust }
44795cd6232STrond Myklebust 
4481da177e4SLinus Torvalds /*
4491da177e4SLinus Torvalds  * Clear the RPC credential cache, and delete those credentials
4501da177e4SLinus Torvalds  * that are not referenced.
4511da177e4SLinus Torvalds  */
4521da177e4SLinus Torvalds void
4533ab9bb72STrond Myklebust rpcauth_clear_credcache(struct rpc_cred_cache *cache)
4541da177e4SLinus Torvalds {
455e092bdcdSTrond Myklebust 	LIST_HEAD(free);
456e092bdcdSTrond Myklebust 	struct hlist_head *head;
4571da177e4SLinus Torvalds 	struct rpc_cred	*cred;
458988664a0STrond Myklebust 	unsigned int hashsize = 1U << cache->hashbits;
4591da177e4SLinus Torvalds 	int		i;
4601da177e4SLinus Torvalds 
4611da177e4SLinus Torvalds 	spin_lock(&rpc_credcache_lock);
4629499b434STrond Myklebust 	spin_lock(&cache->lock);
463988664a0STrond Myklebust 	for (i = 0; i < hashsize; i++) {
464e092bdcdSTrond Myklebust 		head = &cache->hashtable[i];
465e092bdcdSTrond Myklebust 		while (!hlist_empty(head)) {
466e092bdcdSTrond Myklebust 			cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
46731be5bf1STrond Myklebust 			rpcauth_unhash_cred_locked(cred);
46895cd6232STrond Myklebust 			/* Note: We now hold a reference to cred */
46995cd6232STrond Myklebust 			rpcauth_lru_remove_locked(cred);
47095cd6232STrond Myklebust 			list_add_tail(&cred->cr_lru, &free);
4711da177e4SLinus Torvalds 		}
4721da177e4SLinus Torvalds 	}
4739499b434STrond Myklebust 	spin_unlock(&cache->lock);
4741da177e4SLinus Torvalds 	spin_unlock(&rpc_credcache_lock);
4751da177e4SLinus Torvalds 	rpcauth_destroy_credlist(&free);
4761da177e4SLinus Torvalds }
4771da177e4SLinus Torvalds 
4783ab9bb72STrond Myklebust /*
4793ab9bb72STrond Myklebust  * Destroy the RPC credential cache
4803ab9bb72STrond Myklebust  */
4813ab9bb72STrond Myklebust void
4823ab9bb72STrond Myklebust rpcauth_destroy_credcache(struct rpc_auth *auth)
4833ab9bb72STrond Myklebust {
4843ab9bb72STrond Myklebust 	struct rpc_cred_cache *cache = auth->au_credcache;
4853ab9bb72STrond Myklebust 
4863ab9bb72STrond Myklebust 	if (cache) {
4873ab9bb72STrond Myklebust 		auth->au_credcache = NULL;
4883ab9bb72STrond Myklebust 		rpcauth_clear_credcache(cache);
489241269bdSTrond Myklebust 		kfree(cache->hashtable);
4903ab9bb72STrond Myklebust 		kfree(cache);
4913ab9bb72STrond Myklebust 	}
4923ab9bb72STrond Myklebust }
493e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_destroy_credcache);
4943ab9bb72STrond Myklebust 
495d2b83141STrond Myklebust 
496d2b83141STrond Myklebust #define RPC_AUTH_EXPIRY_MORATORIUM (60 * HZ)
497d2b83141STrond Myklebust 
4981da177e4SLinus Torvalds /*
4991da177e4SLinus Torvalds  * Remove stale credentials. Avoid sleeping inside the loop.
5001da177e4SLinus Torvalds  */
50170534a73SDave Chinner static long
502f5c2187cSTrond Myklebust rpcauth_prune_expired(struct list_head *free, int nr_to_scan)
5031da177e4SLinus Torvalds {
504eac0d18dSTrond Myklebust 	struct rpc_cred *cred, *next;
505d2b83141STrond Myklebust 	unsigned long expired = jiffies - RPC_AUTH_EXPIRY_MORATORIUM;
50670534a73SDave Chinner 	long freed = 0;
5071da177e4SLinus Torvalds 
508eac0d18dSTrond Myklebust 	list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) {
509eac0d18dSTrond Myklebust 
51020673406STrond Myklebust 		if (nr_to_scan-- == 0)
51120673406STrond Myklebust 			break;
51279b18181STrond Myklebust 		if (refcount_read(&cred->cr_count) > 1) {
51395cd6232STrond Myklebust 			rpcauth_lru_remove_locked(cred);
51495cd6232STrond Myklebust 			continue;
51595cd6232STrond Myklebust 		}
51693a05e65STrond Myklebust 		/*
51793a05e65STrond Myklebust 		 * Enforce a 60 second garbage collection moratorium
51893a05e65STrond Myklebust 		 * Note that the cred_unused list must be time-ordered.
51993a05e65STrond Myklebust 		 */
52095cd6232STrond Myklebust 		if (!time_in_range(cred->cr_expire, expired, jiffies))
52195cd6232STrond Myklebust 			continue;
52295cd6232STrond Myklebust 		if (!rpcauth_unhash_cred(cred))
523e092bdcdSTrond Myklebust 			continue;
524eac0d18dSTrond Myklebust 
52595cd6232STrond Myklebust 		rpcauth_lru_remove_locked(cred);
52695cd6232STrond Myklebust 		freed++;
527e092bdcdSTrond Myklebust 		list_add_tail(&cred->cr_lru, free);
5281da177e4SLinus Torvalds 	}
52995cd6232STrond Myklebust 	return freed ? freed : SHRINK_STOP;
5301da177e4SLinus Torvalds }
531e092bdcdSTrond Myklebust 
532bae6746fSTrond Myklebust static unsigned long
533bae6746fSTrond Myklebust rpcauth_cache_do_shrink(int nr_to_scan)
534bae6746fSTrond Myklebust {
535bae6746fSTrond Myklebust 	LIST_HEAD(free);
536bae6746fSTrond Myklebust 	unsigned long freed;
537bae6746fSTrond Myklebust 
538bae6746fSTrond Myklebust 	spin_lock(&rpc_credcache_lock);
539bae6746fSTrond Myklebust 	freed = rpcauth_prune_expired(&free, nr_to_scan);
540bae6746fSTrond Myklebust 	spin_unlock(&rpc_credcache_lock);
541bae6746fSTrond Myklebust 	rpcauth_destroy_credlist(&free);
542bae6746fSTrond Myklebust 
543bae6746fSTrond Myklebust 	return freed;
544bae6746fSTrond Myklebust }
545bae6746fSTrond Myklebust 
546e092bdcdSTrond Myklebust /*
547f5c2187cSTrond Myklebust  * Run memory cache shrinker.
548e092bdcdSTrond Myklebust  */
54970534a73SDave Chinner static unsigned long
55070534a73SDave Chinner rpcauth_cache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
55170534a73SDave Chinner 
552e092bdcdSTrond Myklebust {
55370534a73SDave Chinner 	if ((sc->gfp_mask & GFP_KERNEL) != GFP_KERNEL)
55470534a73SDave Chinner 		return SHRINK_STOP;
55570534a73SDave Chinner 
55670534a73SDave Chinner 	/* nothing left, don't come back */
557f5c2187cSTrond Myklebust 	if (list_empty(&cred_unused))
55870534a73SDave Chinner 		return SHRINK_STOP;
55970534a73SDave Chinner 
560bae6746fSTrond Myklebust 	return rpcauth_cache_do_shrink(sc->nr_to_scan);
56170534a73SDave Chinner }
56270534a73SDave Chinner 
56370534a73SDave Chinner static unsigned long
56470534a73SDave Chinner rpcauth_cache_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
56570534a73SDave Chinner 
56670534a73SDave Chinner {
5674c3ffd05SNeilBrown 	return number_cred_unused * sysctl_vfs_cache_pressure / 100;
5681da177e4SLinus Torvalds }
5691da177e4SLinus Torvalds 
570bae6746fSTrond Myklebust static void
571bae6746fSTrond Myklebust rpcauth_cache_enforce_limit(void)
572bae6746fSTrond Myklebust {
573bae6746fSTrond Myklebust 	unsigned long diff;
574bae6746fSTrond Myklebust 	unsigned int nr_to_scan;
575bae6746fSTrond Myklebust 
576bae6746fSTrond Myklebust 	if (number_cred_unused <= auth_max_cred_cachesize)
577bae6746fSTrond Myklebust 		return;
578bae6746fSTrond Myklebust 	diff = number_cred_unused - auth_max_cred_cachesize;
579bae6746fSTrond Myklebust 	nr_to_scan = 100;
580bae6746fSTrond Myklebust 	if (diff < nr_to_scan)
581bae6746fSTrond Myklebust 		nr_to_scan = diff;
582bae6746fSTrond Myklebust 	rpcauth_cache_do_shrink(nr_to_scan);
583bae6746fSTrond Myklebust }
584bae6746fSTrond Myklebust 
5851da177e4SLinus Torvalds /*
5861da177e4SLinus Torvalds  * Look up a process' credentials in the authentication cache
5871da177e4SLinus Torvalds  */
5881da177e4SLinus Torvalds struct rpc_cred *
5891da177e4SLinus Torvalds rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
5903c6e0bc8SJeff Layton 		int flags, gfp_t gfp)
5911da177e4SLinus Torvalds {
592e092bdcdSTrond Myklebust 	LIST_HEAD(free);
5931da177e4SLinus Torvalds 	struct rpc_cred_cache *cache = auth->au_credcache;
59431be5bf1STrond Myklebust 	struct rpc_cred	*cred = NULL,
59531be5bf1STrond Myklebust 			*entry, *new;
59625337fdcSTrond Myklebust 	unsigned int nr;
59725337fdcSTrond Myklebust 
59866cbd4baSFrank Sorenson 	nr = auth->au_ops->hash_cred(acred, cache->hashbits);
5991da177e4SLinus Torvalds 
60031be5bf1STrond Myklebust 	rcu_read_lock();
601b67bfe0dSSasha Levin 	hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) {
60231be5bf1STrond Myklebust 		if (!entry->cr_ops->crmatch(acred, entry, flags))
60331be5bf1STrond Myklebust 			continue;
604bd956080SNeilBrown 		if (flags & RPCAUTH_LOOKUP_RCU) {
60507d02a67STrond Myklebust 			if (test_bit(RPCAUTH_CRED_NEW, &entry->cr_flags) ||
60679b18181STrond Myklebust 			    refcount_read(&entry->cr_count) == 0)
60707d02a67STrond Myklebust 				continue;
608bd956080SNeilBrown 			cred = entry;
609bd956080SNeilBrown 			break;
610bd956080SNeilBrown 		}
61131be5bf1STrond Myklebust 		cred = get_rpccred(entry);
61207d02a67STrond Myklebust 		if (cred)
61331be5bf1STrond Myklebust 			break;
61431be5bf1STrond Myklebust 	}
61531be5bf1STrond Myklebust 	rcu_read_unlock();
61631be5bf1STrond Myklebust 
6179499b434STrond Myklebust 	if (cred != NULL)
61831be5bf1STrond Myklebust 		goto found;
61931be5bf1STrond Myklebust 
620bd956080SNeilBrown 	if (flags & RPCAUTH_LOOKUP_RCU)
621bd956080SNeilBrown 		return ERR_PTR(-ECHILD);
622bd956080SNeilBrown 
6233c6e0bc8SJeff Layton 	new = auth->au_ops->crcreate(auth, acred, flags, gfp);
62431be5bf1STrond Myklebust 	if (IS_ERR(new)) {
62531be5bf1STrond Myklebust 		cred = new;
62631be5bf1STrond Myklebust 		goto out;
62731be5bf1STrond Myklebust 	}
62831be5bf1STrond Myklebust 
6299499b434STrond Myklebust 	spin_lock(&cache->lock);
630b67bfe0dSSasha Levin 	hlist_for_each_entry(entry, &cache->hashtable[nr], cr_hash) {
631e092bdcdSTrond Myklebust 		if (!entry->cr_ops->crmatch(acred, entry, flags))
632e092bdcdSTrond Myklebust 			continue;
633e092bdcdSTrond Myklebust 		cred = get_rpccred(entry);
63407d02a67STrond Myklebust 		if (cred)
6351da177e4SLinus Torvalds 			break;
6361da177e4SLinus Torvalds 	}
63731be5bf1STrond Myklebust 	if (cred == NULL) {
63831be5bf1STrond Myklebust 		cred = new;
63931be5bf1STrond Myklebust 		set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags);
64079b18181STrond Myklebust 		refcount_inc(&cred->cr_count);
64131be5bf1STrond Myklebust 		hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]);
64231be5bf1STrond Myklebust 	} else
643e092bdcdSTrond Myklebust 		list_add_tail(&new->cr_lru, &free);
6449499b434STrond Myklebust 	spin_unlock(&cache->lock);
645bae6746fSTrond Myklebust 	rpcauth_cache_enforce_limit();
64631be5bf1STrond Myklebust found:
647f64f9e71SJoe Perches 	if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) &&
648f64f9e71SJoe Perches 	    cred->cr_ops->cr_init != NULL &&
649f64f9e71SJoe Perches 	    !(flags & RPCAUTH_LOOKUP_NEW)) {
650fba3bad4STrond Myklebust 		int res = cred->cr_ops->cr_init(auth, cred);
651fba3bad4STrond Myklebust 		if (res < 0) {
652fba3bad4STrond Myklebust 			put_rpccred(cred);
653fba3bad4STrond Myklebust 			cred = ERR_PTR(res);
654fba3bad4STrond Myklebust 		}
6551da177e4SLinus Torvalds 	}
65631be5bf1STrond Myklebust 	rpcauth_destroy_credlist(&free);
65731be5bf1STrond Myklebust out:
65831be5bf1STrond Myklebust 	return cred;
6591da177e4SLinus Torvalds }
660e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache);
6611da177e4SLinus Torvalds 
6621da177e4SLinus Torvalds struct rpc_cred *
6638a317760STrond Myklebust rpcauth_lookupcred(struct rpc_auth *auth, int flags)
6641da177e4SLinus Torvalds {
66586a264abSDavid Howells 	struct auth_cred acred;
6661da177e4SLinus Torvalds 	struct rpc_cred *ret;
66786a264abSDavid Howells 	const struct cred *cred = current_cred();
6681da177e4SLinus Torvalds 
6691da177e4SLinus Torvalds 	dprintk("RPC:       looking up %s cred\n",
6701da177e4SLinus Torvalds 		auth->au_ops->au_name);
67186a264abSDavid Howells 
67286a264abSDavid Howells 	memset(&acred, 0, sizeof(acred));
67397f68c6bSNeilBrown 	acred.cred = cred;
6748a317760STrond Myklebust 	ret = auth->au_ops->lookup_cred(auth, &acred, flags);
6751da177e4SLinus Torvalds 	return ret;
6761da177e4SLinus Torvalds }
67766b06860SAndy Adamson EXPORT_SYMBOL_GPL(rpcauth_lookupcred);
6781da177e4SLinus Torvalds 
6795fe4755eSTrond Myklebust void
6805fe4755eSTrond Myklebust rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred,
6815fe4755eSTrond Myklebust 		  struct rpc_auth *auth, const struct rpc_credops *ops)
6825fe4755eSTrond Myklebust {
6835fe4755eSTrond Myklebust 	INIT_HLIST_NODE(&cred->cr_hash);
684e092bdcdSTrond Myklebust 	INIT_LIST_HEAD(&cred->cr_lru);
68579b18181STrond Myklebust 	refcount_set(&cred->cr_count, 1);
6865fe4755eSTrond Myklebust 	cred->cr_auth = auth;
6875fe4755eSTrond Myklebust 	cred->cr_ops = ops;
6885fe4755eSTrond Myklebust 	cred->cr_expire = jiffies;
68997f68c6bSNeilBrown 	cred->cr_cred = get_cred(acred->cred);
6908276c902SNeilBrown 	cred->cr_uid = acred->cred->fsuid;
6915fe4755eSTrond Myklebust }
692e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_cred);
6935fe4755eSTrond Myklebust 
6948572b8e2STrond Myklebust struct rpc_cred *
6955d351754STrond Myklebust rpcauth_generic_bind_cred(struct rpc_task *task, struct rpc_cred *cred, int lookupflags)
6964ccda2cdSTrond Myklebust {
6974ccda2cdSTrond Myklebust 	dprintk("RPC: %5u holding %s cred %p\n", task->tk_pid,
6984ccda2cdSTrond Myklebust 			cred->cr_auth->au_ops->au_name, cred);
6998572b8e2STrond Myklebust 	return get_rpccred(cred);
7004ccda2cdSTrond Myklebust }
7015c691044STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_generic_bind_cred);
7024ccda2cdSTrond Myklebust 
7038572b8e2STrond Myklebust static struct rpc_cred *
7045d351754STrond Myklebust rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags)
7051da177e4SLinus Torvalds {
7061be27f36STrond Myklebust 	struct rpc_auth *auth = task->tk_client->cl_auth;
7071da177e4SLinus Torvalds 	struct auth_cred acred = {
70897f68c6bSNeilBrown 		.cred = get_task_cred(&init_task),
7091da177e4SLinus Torvalds 	};
71097f68c6bSNeilBrown 	struct rpc_cred *ret;
7111da177e4SLinus Torvalds 
71246121cf7SChuck Lever 	dprintk("RPC: %5u looking up %s cred\n",
7131be27f36STrond Myklebust 		task->tk_pid, task->tk_client->cl_auth->au_ops->au_name);
71497f68c6bSNeilBrown 	ret = auth->au_ops->lookup_cred(auth, &acred, lookupflags);
71597f68c6bSNeilBrown 	put_cred(acred.cred);
71697f68c6bSNeilBrown 	return ret;
717af093835STrond Myklebust }
718af093835STrond Myklebust 
7198572b8e2STrond Myklebust static struct rpc_cred *
7205e16923bSNeilBrown rpcauth_bind_machine_cred(struct rpc_task *task, int lookupflags)
7215e16923bSNeilBrown {
7225e16923bSNeilBrown 	struct rpc_auth *auth = task->tk_client->cl_auth;
7235e16923bSNeilBrown 	struct auth_cred acred = {
7245e16923bSNeilBrown 		.principal = task->tk_client->cl_principal,
7255e16923bSNeilBrown 		.cred = init_task.cred,
7265e16923bSNeilBrown 	};
7275e16923bSNeilBrown 
7285e16923bSNeilBrown 	if (!acred.principal)
7295e16923bSNeilBrown 		return NULL;
7305e16923bSNeilBrown 	dprintk("RPC: %5u looking up %s machine cred\n",
7315e16923bSNeilBrown 		task->tk_pid, task->tk_client->cl_auth->au_ops->au_name);
7325e16923bSNeilBrown 	return auth->au_ops->lookup_cred(auth, &acred, lookupflags);
7335e16923bSNeilBrown }
7345e16923bSNeilBrown 
7355e16923bSNeilBrown static struct rpc_cred *
7365d351754STrond Myklebust rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags)
737af093835STrond Myklebust {
738af093835STrond Myklebust 	struct rpc_auth *auth = task->tk_client->cl_auth;
739af093835STrond Myklebust 
740af093835STrond Myklebust 	dprintk("RPC: %5u looking up %s cred\n",
741af093835STrond Myklebust 		task->tk_pid, auth->au_ops->au_name);
7428572b8e2STrond Myklebust 	return rpcauth_lookupcred(auth, lookupflags);
7431da177e4SLinus Torvalds }
7441da177e4SLinus Torvalds 
745a17c2153STrond Myklebust static int
7464ccda2cdSTrond Myklebust rpcauth_bindcred(struct rpc_task *task, struct rpc_cred *cred, int flags)
7471da177e4SLinus Torvalds {
748a17c2153STrond Myklebust 	struct rpc_rqst *req = task->tk_rqstp;
7495e16923bSNeilBrown 	struct rpc_cred *new = NULL;
7505d351754STrond Myklebust 	int lookupflags = 0;
7515d351754STrond Myklebust 
7525d351754STrond Myklebust 	if (flags & RPC_TASK_ASYNC)
7535d351754STrond Myklebust 		lookupflags |= RPCAUTH_LOOKUP_NEW;
7545e16923bSNeilBrown 	if (cred != NULL && cred != &machine_cred)
7558572b8e2STrond Myklebust 		new = cred->cr_ops->crbind(task, cred, lookupflags);
7565e16923bSNeilBrown 	else if (cred == &machine_cred)
7575e16923bSNeilBrown 		new = rpcauth_bind_machine_cred(task, lookupflags);
7585e16923bSNeilBrown 
7595e16923bSNeilBrown 	/* If machine cred couldn't be bound, try a root cred */
7605e16923bSNeilBrown 	if (new)
7615e16923bSNeilBrown 		;
7625e16923bSNeilBrown 	else if (cred == &machine_cred || (flags & RPC_TASK_ROOTCREDS))
7638572b8e2STrond Myklebust 		new = rpcauth_bind_root_cred(task, lookupflags);
7644ccda2cdSTrond Myklebust 	else
7658572b8e2STrond Myklebust 		new = rpcauth_bind_new_cred(task, lookupflags);
7668572b8e2STrond Myklebust 	if (IS_ERR(new))
7678572b8e2STrond Myklebust 		return PTR_ERR(new);
768a17c2153STrond Myklebust 	put_rpccred(req->rq_cred);
769a17c2153STrond Myklebust 	req->rq_cred = new;
7708572b8e2STrond Myklebust 	return 0;
7711da177e4SLinus Torvalds }
7721da177e4SLinus Torvalds 
7731da177e4SLinus Torvalds void
7741da177e4SLinus Torvalds put_rpccred(struct rpc_cred *cred)
7751da177e4SLinus Torvalds {
7769a8f6b5eSTrond Myklebust 	if (cred == NULL)
7779a8f6b5eSTrond Myklebust 		return;
77895cd6232STrond Myklebust 	rcu_read_lock();
77979b18181STrond Myklebust 	if (refcount_dec_and_test(&cred->cr_count))
78095cd6232STrond Myklebust 		goto destroy;
78179b18181STrond Myklebust 	if (refcount_read(&cred->cr_count) != 1 ||
78295cd6232STrond Myklebust 	    !test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
78395cd6232STrond Myklebust 		goto out;
784f0380f3dSTrond Myklebust 	if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) {
785e092bdcdSTrond Myklebust 		cred->cr_expire = jiffies;
78695cd6232STrond Myklebust 		rpcauth_lru_add(cred);
78795cd6232STrond Myklebust 		/* Race breaker */
78895cd6232STrond Myklebust 		if (unlikely(!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags)))
78995cd6232STrond Myklebust 			rpcauth_lru_remove(cred);
79095cd6232STrond Myklebust 	} else if (rpcauth_unhash_cred(cred)) {
79195cd6232STrond Myklebust 		rpcauth_lru_remove(cred);
79279b18181STrond Myklebust 		if (refcount_dec_and_test(&cred->cr_count))
79395cd6232STrond Myklebust 			goto destroy;
794f0380f3dSTrond Myklebust 	}
79595cd6232STrond Myklebust out:
79695cd6232STrond Myklebust 	rcu_read_unlock();
797f0380f3dSTrond Myklebust 	return;
79895cd6232STrond Myklebust destroy:
79995cd6232STrond Myklebust 	rcu_read_unlock();
80095cd6232STrond Myklebust 	cred->cr_ops->crdestroy(cred);
8011da177e4SLinus Torvalds }
802e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(put_rpccred);
8031da177e4SLinus Torvalds 
804d8ed029dSAlexey Dobriyan __be32 *
805d8ed029dSAlexey Dobriyan rpcauth_marshcred(struct rpc_task *task, __be32 *p)
8061da177e4SLinus Torvalds {
807a17c2153STrond Myklebust 	struct rpc_cred	*cred = task->tk_rqstp->rq_cred;
8081da177e4SLinus Torvalds 
80946121cf7SChuck Lever 	dprintk("RPC: %5u marshaling %s cred %p\n",
8101be27f36STrond Myklebust 		task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
8110bbacc40SChuck Lever 
8121da177e4SLinus Torvalds 	return cred->cr_ops->crmarshal(task, p);
8131da177e4SLinus Torvalds }
8141da177e4SLinus Torvalds 
815d8ed029dSAlexey Dobriyan __be32 *
816d8ed029dSAlexey Dobriyan rpcauth_checkverf(struct rpc_task *task, __be32 *p)
8171da177e4SLinus Torvalds {
818a17c2153STrond Myklebust 	struct rpc_cred	*cred = task->tk_rqstp->rq_cred;
8191da177e4SLinus Torvalds 
82046121cf7SChuck Lever 	dprintk("RPC: %5u validating %s cred %p\n",
8211be27f36STrond Myklebust 		task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
8220bbacc40SChuck Lever 
8231da177e4SLinus Torvalds 	return cred->cr_ops->crvalidate(task, p);
8241da177e4SLinus Torvalds }
8251da177e4SLinus Torvalds 
8269f06c719SChuck Lever static void rpcauth_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp,
8279f06c719SChuck Lever 				   __be32 *data, void *obj)
8289f06c719SChuck Lever {
8299f06c719SChuck Lever 	struct xdr_stream xdr;
8309f06c719SChuck Lever 
8319f06c719SChuck Lever 	xdr_init_encode(&xdr, &rqstp->rq_snd_buf, data);
8329f06c719SChuck Lever 	encode(rqstp, &xdr, obj);
8339f06c719SChuck Lever }
8349f06c719SChuck Lever 
8351da177e4SLinus Torvalds int
8369f06c719SChuck Lever rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp,
837d8ed029dSAlexey Dobriyan 		__be32 *data, void *obj)
8381da177e4SLinus Torvalds {
839a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
8401da177e4SLinus Torvalds 
84146121cf7SChuck Lever 	dprintk("RPC: %5u using %s cred %p to wrap rpc data\n",
8421da177e4SLinus Torvalds 			task->tk_pid, cred->cr_ops->cr_name, cred);
8431da177e4SLinus Torvalds 	if (cred->cr_ops->crwrap_req)
8441da177e4SLinus Torvalds 		return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
8451da177e4SLinus Torvalds 	/* By default, we encode the arguments normally. */
8469f06c719SChuck Lever 	rpcauth_wrap_req_encode(encode, rqstp, data, obj);
8479f06c719SChuck Lever 	return 0;
8481da177e4SLinus Torvalds }
8491da177e4SLinus Torvalds 
850bf269551SChuck Lever static int
851bf269551SChuck Lever rpcauth_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp,
852bf269551SChuck Lever 			  __be32 *data, void *obj)
853bf269551SChuck Lever {
854bf269551SChuck Lever 	struct xdr_stream xdr;
855bf269551SChuck Lever 
856bf269551SChuck Lever 	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, data);
857bf269551SChuck Lever 	return decode(rqstp, &xdr, obj);
858bf269551SChuck Lever }
859bf269551SChuck Lever 
8601da177e4SLinus Torvalds int
861bf269551SChuck Lever rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp,
862d8ed029dSAlexey Dobriyan 		__be32 *data, void *obj)
8631da177e4SLinus Torvalds {
864a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
8651da177e4SLinus Torvalds 
86646121cf7SChuck Lever 	dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n",
8671da177e4SLinus Torvalds 			task->tk_pid, cred->cr_ops->cr_name, cred);
8681da177e4SLinus Torvalds 	if (cred->cr_ops->crunwrap_resp)
8691da177e4SLinus Torvalds 		return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
8701da177e4SLinus Torvalds 						   data, obj);
8711da177e4SLinus Torvalds 	/* By default, we decode the arguments normally. */
872bf269551SChuck Lever 	return rpcauth_unwrap_req_decode(decode, rqstp, data, obj);
8731da177e4SLinus Torvalds }
8741da177e4SLinus Torvalds 
8753021a5bbSTrond Myklebust bool
8763021a5bbSTrond Myklebust rpcauth_xmit_need_reencode(struct rpc_task *task)
8773021a5bbSTrond Myklebust {
8783021a5bbSTrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
8793021a5bbSTrond Myklebust 
8803021a5bbSTrond Myklebust 	if (!cred || !cred->cr_ops->crneed_reencode)
8813021a5bbSTrond Myklebust 		return false;
8823021a5bbSTrond Myklebust 	return cred->cr_ops->crneed_reencode(task);
8833021a5bbSTrond Myklebust }
8843021a5bbSTrond Myklebust 
8851da177e4SLinus Torvalds int
8861da177e4SLinus Torvalds rpcauth_refreshcred(struct rpc_task *task)
8871da177e4SLinus Torvalds {
8889a84d380STrond Myklebust 	struct rpc_cred	*cred;
8891da177e4SLinus Torvalds 	int err;
8901da177e4SLinus Torvalds 
891a17c2153STrond Myklebust 	cred = task->tk_rqstp->rq_cred;
892a17c2153STrond Myklebust 	if (cred == NULL) {
893a17c2153STrond Myklebust 		err = rpcauth_bindcred(task, task->tk_msg.rpc_cred, task->tk_flags);
894a17c2153STrond Myklebust 		if (err < 0)
895a17c2153STrond Myklebust 			goto out;
896a17c2153STrond Myklebust 		cred = task->tk_rqstp->rq_cred;
897f81c6224SJoe Perches 	}
89846121cf7SChuck Lever 	dprintk("RPC: %5u refreshing %s cred %p\n",
8991be27f36STrond Myklebust 		task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
9000bbacc40SChuck Lever 
9011da177e4SLinus Torvalds 	err = cred->cr_ops->crrefresh(task);
902a17c2153STrond Myklebust out:
9031da177e4SLinus Torvalds 	if (err < 0)
9041da177e4SLinus Torvalds 		task->tk_status = err;
9051da177e4SLinus Torvalds 	return err;
9061da177e4SLinus Torvalds }
9071da177e4SLinus Torvalds 
9081da177e4SLinus Torvalds void
9091da177e4SLinus Torvalds rpcauth_invalcred(struct rpc_task *task)
9101da177e4SLinus Torvalds {
911a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
912fc432dd9STrond Myklebust 
91346121cf7SChuck Lever 	dprintk("RPC: %5u invalidating %s cred %p\n",
9141be27f36STrond Myklebust 		task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
915fc432dd9STrond Myklebust 	if (cred)
916fc432dd9STrond Myklebust 		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
9171da177e4SLinus Torvalds }
9181da177e4SLinus Torvalds 
9191da177e4SLinus Torvalds int
9201da177e4SLinus Torvalds rpcauth_uptodatecred(struct rpc_task *task)
9211da177e4SLinus Torvalds {
922a17c2153STrond Myklebust 	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
923fc432dd9STrond Myklebust 
924fc432dd9STrond Myklebust 	return cred == NULL ||
925fc432dd9STrond Myklebust 		test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0;
9261da177e4SLinus Torvalds }
927f5c2187cSTrond Myklebust 
9288e1f936bSRusty Russell static struct shrinker rpc_cred_shrinker = {
92970534a73SDave Chinner 	.count_objects = rpcauth_cache_shrink_count,
93070534a73SDave Chinner 	.scan_objects = rpcauth_cache_shrink_scan,
9318e1f936bSRusty Russell 	.seeks = DEFAULT_SEEKS,
9328e1f936bSRusty Russell };
933f5c2187cSTrond Myklebust 
9345d8d9a4dSTrond Myklebust int __init rpcauth_init_module(void)
935f5c2187cSTrond Myklebust {
9365d8d9a4dSTrond Myklebust 	int err;
9375d8d9a4dSTrond Myklebust 
9385d8d9a4dSTrond Myklebust 	err = rpc_init_authunix();
9395d8d9a4dSTrond Myklebust 	if (err < 0)
9405d8d9a4dSTrond Myklebust 		goto out1;
9415d8d9a4dSTrond Myklebust 	err = rpc_init_generic_auth();
9425d8d9a4dSTrond Myklebust 	if (err < 0)
9435d8d9a4dSTrond Myklebust 		goto out2;
9442864486bSKinglong Mee 	err = register_shrinker(&rpc_cred_shrinker);
9452864486bSKinglong Mee 	if (err < 0)
9462864486bSKinglong Mee 		goto out3;
9475d8d9a4dSTrond Myklebust 	return 0;
9482864486bSKinglong Mee out3:
9492864486bSKinglong Mee 	rpc_destroy_generic_auth();
9505d8d9a4dSTrond Myklebust out2:
9515d8d9a4dSTrond Myklebust 	rpc_destroy_authunix();
9525d8d9a4dSTrond Myklebust out1:
9535d8d9a4dSTrond Myklebust 	return err;
954f5c2187cSTrond Myklebust }
955f5c2187cSTrond Myklebust 
956c135e84aSStephen Rothwell void rpcauth_remove_module(void)
957f5c2187cSTrond Myklebust {
9585d8d9a4dSTrond Myklebust 	rpc_destroy_authunix();
9595d8d9a4dSTrond Myklebust 	rpc_destroy_generic_auth();
9608e1f936bSRusty Russell 	unregister_shrinker(&rpc_cred_shrinker);
961f5c2187cSTrond Myklebust }
962