18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * linux/net/sunrpc/auth.c
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Generic RPC client authentication API.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/types.h>
118c2ecf20Sopenharmony_ci#include <linux/sched.h>
128c2ecf20Sopenharmony_ci#include <linux/cred.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/errno.h>
168c2ecf20Sopenharmony_ci#include <linux/hash.h>
178c2ecf20Sopenharmony_ci#include <linux/sunrpc/clnt.h>
188c2ecf20Sopenharmony_ci#include <linux/sunrpc/gss_api.h>
198c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <trace/events/sunrpc.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#define RPC_CREDCACHE_DEFAULT_HASHBITS	(4)
248c2ecf20Sopenharmony_cistruct rpc_cred_cache {
258c2ecf20Sopenharmony_ci	struct hlist_head	*hashtable;
268c2ecf20Sopenharmony_ci	unsigned int		hashbits;
278c2ecf20Sopenharmony_ci	spinlock_t		lock;
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic unsigned int auth_hashbits = RPC_CREDCACHE_DEFAULT_HASHBITS;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic const struct rpc_authops __rcu *auth_flavors[RPC_AUTH_MAXFLAVOR] = {
338c2ecf20Sopenharmony_ci	[RPC_AUTH_NULL] = (const struct rpc_authops __force __rcu *)&authnull_ops,
348c2ecf20Sopenharmony_ci	[RPC_AUTH_UNIX] = (const struct rpc_authops __force __rcu *)&authunix_ops,
358c2ecf20Sopenharmony_ci	NULL,			/* others can be loadable modules */
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic LIST_HEAD(cred_unused);
398c2ecf20Sopenharmony_cistatic unsigned long number_cred_unused;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic struct cred machine_cred = {
428c2ecf20Sopenharmony_ci	.usage = ATOMIC_INIT(1),
438c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_CREDENTIALS
448c2ecf20Sopenharmony_ci	.magic = CRED_MAGIC,
458c2ecf20Sopenharmony_ci#endif
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/*
498c2ecf20Sopenharmony_ci * Return the machine_cred pointer to be used whenever
508c2ecf20Sopenharmony_ci * the a generic machine credential is needed.
518c2ecf20Sopenharmony_ci */
528c2ecf20Sopenharmony_ciconst struct cred *rpc_machine_cred(void)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	return &machine_cred;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpc_machine_cred);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci#define MAX_HASHTABLE_BITS (14)
598c2ecf20Sopenharmony_cistatic int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	unsigned long num;
628c2ecf20Sopenharmony_ci	unsigned int nbits;
638c2ecf20Sopenharmony_ci	int ret;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (!val)
668c2ecf20Sopenharmony_ci		goto out_inval;
678c2ecf20Sopenharmony_ci	ret = kstrtoul(val, 0, &num);
688c2ecf20Sopenharmony_ci	if (ret)
698c2ecf20Sopenharmony_ci		goto out_inval;
708c2ecf20Sopenharmony_ci	nbits = fls(num - 1);
718c2ecf20Sopenharmony_ci	if (nbits > MAX_HASHTABLE_BITS || nbits < 2)
728c2ecf20Sopenharmony_ci		goto out_inval;
738c2ecf20Sopenharmony_ci	*(unsigned int *)kp->arg = nbits;
748c2ecf20Sopenharmony_ci	return 0;
758c2ecf20Sopenharmony_ciout_inval:
768c2ecf20Sopenharmony_ci	return -EINVAL;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic int param_get_hashtbl_sz(char *buffer, const struct kernel_param *kp)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	unsigned int nbits;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	nbits = *(unsigned int *)kp->arg;
848c2ecf20Sopenharmony_ci	return sprintf(buffer, "%u\n", 1U << nbits);
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#define param_check_hashtbl_sz(name, p) __param_check(name, p, unsigned int);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_cistatic const struct kernel_param_ops param_ops_hashtbl_sz = {
908c2ecf20Sopenharmony_ci	.set = param_set_hashtbl_sz,
918c2ecf20Sopenharmony_ci	.get = param_get_hashtbl_sz,
928c2ecf20Sopenharmony_ci};
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cimodule_param_named(auth_hashtable_size, auth_hashbits, hashtbl_sz, 0644);
958c2ecf20Sopenharmony_ciMODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size");
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic unsigned long auth_max_cred_cachesize = ULONG_MAX;
988c2ecf20Sopenharmony_cimodule_param(auth_max_cred_cachesize, ulong, 0644);
998c2ecf20Sopenharmony_ciMODULE_PARM_DESC(auth_max_cred_cachesize, "RPC credential maximum total cache size");
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic u32
1028c2ecf20Sopenharmony_cipseudoflavor_to_flavor(u32 flavor) {
1038c2ecf20Sopenharmony_ci	if (flavor > RPC_AUTH_MAXFLAVOR)
1048c2ecf20Sopenharmony_ci		return RPC_AUTH_GSS;
1058c2ecf20Sopenharmony_ci	return flavor;
1068c2ecf20Sopenharmony_ci}
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ciint
1098c2ecf20Sopenharmony_cirpcauth_register(const struct rpc_authops *ops)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci	const struct rpc_authops *old;
1128c2ecf20Sopenharmony_ci	rpc_authflavor_t flavor;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
1158c2ecf20Sopenharmony_ci		return -EINVAL;
1168c2ecf20Sopenharmony_ci	old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], NULL, ops);
1178c2ecf20Sopenharmony_ci	if (old == NULL || old == ops)
1188c2ecf20Sopenharmony_ci		return 0;
1198c2ecf20Sopenharmony_ci	return -EPERM;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpcauth_register);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ciint
1248c2ecf20Sopenharmony_cirpcauth_unregister(const struct rpc_authops *ops)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	const struct rpc_authops *old;
1278c2ecf20Sopenharmony_ci	rpc_authflavor_t flavor;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
1308c2ecf20Sopenharmony_ci		return -EINVAL;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], ops, NULL);
1338c2ecf20Sopenharmony_ci	if (old == ops || old == NULL)
1348c2ecf20Sopenharmony_ci		return 0;
1358c2ecf20Sopenharmony_ci	return -EPERM;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpcauth_unregister);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic const struct rpc_authops *
1408c2ecf20Sopenharmony_cirpcauth_get_authops(rpc_authflavor_t flavor)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	const struct rpc_authops *ops;
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	if (flavor >= RPC_AUTH_MAXFLAVOR)
1458c2ecf20Sopenharmony_ci		return NULL;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	rcu_read_lock();
1488c2ecf20Sopenharmony_ci	ops = rcu_dereference(auth_flavors[flavor]);
1498c2ecf20Sopenharmony_ci	if (ops == NULL) {
1508c2ecf20Sopenharmony_ci		rcu_read_unlock();
1518c2ecf20Sopenharmony_ci		request_module("rpc-auth-%u", flavor);
1528c2ecf20Sopenharmony_ci		rcu_read_lock();
1538c2ecf20Sopenharmony_ci		ops = rcu_dereference(auth_flavors[flavor]);
1548c2ecf20Sopenharmony_ci		if (ops == NULL)
1558c2ecf20Sopenharmony_ci			goto out;
1568c2ecf20Sopenharmony_ci	}
1578c2ecf20Sopenharmony_ci	if (!try_module_get(ops->owner))
1588c2ecf20Sopenharmony_ci		ops = NULL;
1598c2ecf20Sopenharmony_ciout:
1608c2ecf20Sopenharmony_ci	rcu_read_unlock();
1618c2ecf20Sopenharmony_ci	return ops;
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_cistatic void
1658c2ecf20Sopenharmony_cirpcauth_put_authops(const struct rpc_authops *ops)
1668c2ecf20Sopenharmony_ci{
1678c2ecf20Sopenharmony_ci	module_put(ops->owner);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/**
1718c2ecf20Sopenharmony_ci * rpcauth_get_pseudoflavor - check if security flavor is supported
1728c2ecf20Sopenharmony_ci * @flavor: a security flavor
1738c2ecf20Sopenharmony_ci * @info: a GSS mech OID, quality of protection, and service value
1748c2ecf20Sopenharmony_ci *
1758c2ecf20Sopenharmony_ci * Verifies that an appropriate kernel module is available or already loaded.
1768c2ecf20Sopenharmony_ci * Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is
1778c2ecf20Sopenharmony_ci * not supported locally.
1788c2ecf20Sopenharmony_ci */
1798c2ecf20Sopenharmony_cirpc_authflavor_t
1808c2ecf20Sopenharmony_cirpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	const struct rpc_authops *ops = rpcauth_get_authops(flavor);
1838c2ecf20Sopenharmony_ci	rpc_authflavor_t pseudoflavor;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	if (!ops)
1868c2ecf20Sopenharmony_ci		return RPC_AUTH_MAXFLAVOR;
1878c2ecf20Sopenharmony_ci	pseudoflavor = flavor;
1888c2ecf20Sopenharmony_ci	if (ops->info2flavor != NULL)
1898c2ecf20Sopenharmony_ci		pseudoflavor = ops->info2flavor(info);
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	rpcauth_put_authops(ops);
1928c2ecf20Sopenharmony_ci	return pseudoflavor;
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci/**
1978c2ecf20Sopenharmony_ci * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor
1988c2ecf20Sopenharmony_ci * @pseudoflavor: GSS pseudoflavor to match
1998c2ecf20Sopenharmony_ci * @info: rpcsec_gss_info structure to fill in
2008c2ecf20Sopenharmony_ci *
2018c2ecf20Sopenharmony_ci * Returns zero and fills in "info" if pseudoflavor matches a
2028c2ecf20Sopenharmony_ci * supported mechanism.
2038c2ecf20Sopenharmony_ci */
2048c2ecf20Sopenharmony_ciint
2058c2ecf20Sopenharmony_cirpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor);
2088c2ecf20Sopenharmony_ci	const struct rpc_authops *ops;
2098c2ecf20Sopenharmony_ci	int result;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	ops = rpcauth_get_authops(flavor);
2128c2ecf20Sopenharmony_ci	if (ops == NULL)
2138c2ecf20Sopenharmony_ci		return -ENOENT;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci	result = -ENOENT;
2168c2ecf20Sopenharmony_ci	if (ops->flavor2info != NULL)
2178c2ecf20Sopenharmony_ci		result = ops->flavor2info(pseudoflavor, info);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	rpcauth_put_authops(ops);
2208c2ecf20Sopenharmony_ci	return result;
2218c2ecf20Sopenharmony_ci}
2228c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistruct rpc_auth *
2258c2ecf20Sopenharmony_cirpcauth_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt)
2268c2ecf20Sopenharmony_ci{
2278c2ecf20Sopenharmony_ci	struct rpc_auth	*auth = ERR_PTR(-EINVAL);
2288c2ecf20Sopenharmony_ci	const struct rpc_authops *ops;
2298c2ecf20Sopenharmony_ci	u32 flavor = pseudoflavor_to_flavor(args->pseudoflavor);
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	ops = rpcauth_get_authops(flavor);
2328c2ecf20Sopenharmony_ci	if (ops == NULL)
2338c2ecf20Sopenharmony_ci		goto out;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	auth = ops->create(args, clnt);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	rpcauth_put_authops(ops);
2388c2ecf20Sopenharmony_ci	if (IS_ERR(auth))
2398c2ecf20Sopenharmony_ci		return auth;
2408c2ecf20Sopenharmony_ci	if (clnt->cl_auth)
2418c2ecf20Sopenharmony_ci		rpcauth_release(clnt->cl_auth);
2428c2ecf20Sopenharmony_ci	clnt->cl_auth = auth;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ciout:
2458c2ecf20Sopenharmony_ci	return auth;
2468c2ecf20Sopenharmony_ci}
2478c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpcauth_create);
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_civoid
2508c2ecf20Sopenharmony_cirpcauth_release(struct rpc_auth *auth)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	if (!refcount_dec_and_test(&auth->au_count))
2538c2ecf20Sopenharmony_ci		return;
2548c2ecf20Sopenharmony_ci	auth->au_ops->destroy(auth);
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(rpc_credcache_lock);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci/*
2608c2ecf20Sopenharmony_ci * On success, the caller is responsible for freeing the reference
2618c2ecf20Sopenharmony_ci * held by the hashtable
2628c2ecf20Sopenharmony_ci */
2638c2ecf20Sopenharmony_cistatic bool
2648c2ecf20Sopenharmony_cirpcauth_unhash_cred_locked(struct rpc_cred *cred)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	if (!test_and_clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
2678c2ecf20Sopenharmony_ci		return false;
2688c2ecf20Sopenharmony_ci	hlist_del_rcu(&cred->cr_hash);
2698c2ecf20Sopenharmony_ci	return true;
2708c2ecf20Sopenharmony_ci}
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_cistatic bool
2738c2ecf20Sopenharmony_cirpcauth_unhash_cred(struct rpc_cred *cred)
2748c2ecf20Sopenharmony_ci{
2758c2ecf20Sopenharmony_ci	spinlock_t *cache_lock;
2768c2ecf20Sopenharmony_ci	bool ret;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	if (!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
2798c2ecf20Sopenharmony_ci		return false;
2808c2ecf20Sopenharmony_ci	cache_lock = &cred->cr_auth->au_credcache->lock;
2818c2ecf20Sopenharmony_ci	spin_lock(cache_lock);
2828c2ecf20Sopenharmony_ci	ret = rpcauth_unhash_cred_locked(cred);
2838c2ecf20Sopenharmony_ci	spin_unlock(cache_lock);
2848c2ecf20Sopenharmony_ci	return ret;
2858c2ecf20Sopenharmony_ci}
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci/*
2888c2ecf20Sopenharmony_ci * Initialize RPC credential cache
2898c2ecf20Sopenharmony_ci */
2908c2ecf20Sopenharmony_ciint
2918c2ecf20Sopenharmony_cirpcauth_init_credcache(struct rpc_auth *auth)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	struct rpc_cred_cache *new;
2948c2ecf20Sopenharmony_ci	unsigned int hashsize;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	new = kmalloc(sizeof(*new), GFP_KERNEL);
2978c2ecf20Sopenharmony_ci	if (!new)
2988c2ecf20Sopenharmony_ci		goto out_nocache;
2998c2ecf20Sopenharmony_ci	new->hashbits = auth_hashbits;
3008c2ecf20Sopenharmony_ci	hashsize = 1U << new->hashbits;
3018c2ecf20Sopenharmony_ci	new->hashtable = kcalloc(hashsize, sizeof(new->hashtable[0]), GFP_KERNEL);
3028c2ecf20Sopenharmony_ci	if (!new->hashtable)
3038c2ecf20Sopenharmony_ci		goto out_nohashtbl;
3048c2ecf20Sopenharmony_ci	spin_lock_init(&new->lock);
3058c2ecf20Sopenharmony_ci	auth->au_credcache = new;
3068c2ecf20Sopenharmony_ci	return 0;
3078c2ecf20Sopenharmony_ciout_nohashtbl:
3088c2ecf20Sopenharmony_ci	kfree(new);
3098c2ecf20Sopenharmony_ciout_nocache:
3108c2ecf20Sopenharmony_ci	return -ENOMEM;
3118c2ecf20Sopenharmony_ci}
3128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpcauth_init_credcache);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cichar *
3158c2ecf20Sopenharmony_cirpcauth_stringify_acceptor(struct rpc_cred *cred)
3168c2ecf20Sopenharmony_ci{
3178c2ecf20Sopenharmony_ci	if (!cred->cr_ops->crstringify_acceptor)
3188c2ecf20Sopenharmony_ci		return NULL;
3198c2ecf20Sopenharmony_ci	return cred->cr_ops->crstringify_acceptor(cred);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpcauth_stringify_acceptor);
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci/*
3248c2ecf20Sopenharmony_ci * Destroy a list of credentials
3258c2ecf20Sopenharmony_ci */
3268c2ecf20Sopenharmony_cistatic inline
3278c2ecf20Sopenharmony_civoid rpcauth_destroy_credlist(struct list_head *head)
3288c2ecf20Sopenharmony_ci{
3298c2ecf20Sopenharmony_ci	struct rpc_cred *cred;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	while (!list_empty(head)) {
3328c2ecf20Sopenharmony_ci		cred = list_entry(head->next, struct rpc_cred, cr_lru);
3338c2ecf20Sopenharmony_ci		list_del_init(&cred->cr_lru);
3348c2ecf20Sopenharmony_ci		put_rpccred(cred);
3358c2ecf20Sopenharmony_ci	}
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic void
3398c2ecf20Sopenharmony_cirpcauth_lru_add_locked(struct rpc_cred *cred)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	if (!list_empty(&cred->cr_lru))
3428c2ecf20Sopenharmony_ci		return;
3438c2ecf20Sopenharmony_ci	number_cred_unused++;
3448c2ecf20Sopenharmony_ci	list_add_tail(&cred->cr_lru, &cred_unused);
3458c2ecf20Sopenharmony_ci}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_cistatic void
3488c2ecf20Sopenharmony_cirpcauth_lru_add(struct rpc_cred *cred)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	if (!list_empty(&cred->cr_lru))
3518c2ecf20Sopenharmony_ci		return;
3528c2ecf20Sopenharmony_ci	spin_lock(&rpc_credcache_lock);
3538c2ecf20Sopenharmony_ci	rpcauth_lru_add_locked(cred);
3548c2ecf20Sopenharmony_ci	spin_unlock(&rpc_credcache_lock);
3558c2ecf20Sopenharmony_ci}
3568c2ecf20Sopenharmony_ci
3578c2ecf20Sopenharmony_cistatic void
3588c2ecf20Sopenharmony_cirpcauth_lru_remove_locked(struct rpc_cred *cred)
3598c2ecf20Sopenharmony_ci{
3608c2ecf20Sopenharmony_ci	if (list_empty(&cred->cr_lru))
3618c2ecf20Sopenharmony_ci		return;
3628c2ecf20Sopenharmony_ci	number_cred_unused--;
3638c2ecf20Sopenharmony_ci	list_del_init(&cred->cr_lru);
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic void
3678c2ecf20Sopenharmony_cirpcauth_lru_remove(struct rpc_cred *cred)
3688c2ecf20Sopenharmony_ci{
3698c2ecf20Sopenharmony_ci	if (list_empty(&cred->cr_lru))
3708c2ecf20Sopenharmony_ci		return;
3718c2ecf20Sopenharmony_ci	spin_lock(&rpc_credcache_lock);
3728c2ecf20Sopenharmony_ci	rpcauth_lru_remove_locked(cred);
3738c2ecf20Sopenharmony_ci	spin_unlock(&rpc_credcache_lock);
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci/*
3778c2ecf20Sopenharmony_ci * Clear the RPC credential cache, and delete those credentials
3788c2ecf20Sopenharmony_ci * that are not referenced.
3798c2ecf20Sopenharmony_ci */
3808c2ecf20Sopenharmony_civoid
3818c2ecf20Sopenharmony_cirpcauth_clear_credcache(struct rpc_cred_cache *cache)
3828c2ecf20Sopenharmony_ci{
3838c2ecf20Sopenharmony_ci	LIST_HEAD(free);
3848c2ecf20Sopenharmony_ci	struct hlist_head *head;
3858c2ecf20Sopenharmony_ci	struct rpc_cred	*cred;
3868c2ecf20Sopenharmony_ci	unsigned int hashsize = 1U << cache->hashbits;
3878c2ecf20Sopenharmony_ci	int		i;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	spin_lock(&rpc_credcache_lock);
3908c2ecf20Sopenharmony_ci	spin_lock(&cache->lock);
3918c2ecf20Sopenharmony_ci	for (i = 0; i < hashsize; i++) {
3928c2ecf20Sopenharmony_ci		head = &cache->hashtable[i];
3938c2ecf20Sopenharmony_ci		while (!hlist_empty(head)) {
3948c2ecf20Sopenharmony_ci			cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
3958c2ecf20Sopenharmony_ci			rpcauth_unhash_cred_locked(cred);
3968c2ecf20Sopenharmony_ci			/* Note: We now hold a reference to cred */
3978c2ecf20Sopenharmony_ci			rpcauth_lru_remove_locked(cred);
3988c2ecf20Sopenharmony_ci			list_add_tail(&cred->cr_lru, &free);
3998c2ecf20Sopenharmony_ci		}
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci	spin_unlock(&cache->lock);
4028c2ecf20Sopenharmony_ci	spin_unlock(&rpc_credcache_lock);
4038c2ecf20Sopenharmony_ci	rpcauth_destroy_credlist(&free);
4048c2ecf20Sopenharmony_ci}
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci/*
4078c2ecf20Sopenharmony_ci * Destroy the RPC credential cache
4088c2ecf20Sopenharmony_ci */
4098c2ecf20Sopenharmony_civoid
4108c2ecf20Sopenharmony_cirpcauth_destroy_credcache(struct rpc_auth *auth)
4118c2ecf20Sopenharmony_ci{
4128c2ecf20Sopenharmony_ci	struct rpc_cred_cache *cache = auth->au_credcache;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	if (cache) {
4158c2ecf20Sopenharmony_ci		auth->au_credcache = NULL;
4168c2ecf20Sopenharmony_ci		rpcauth_clear_credcache(cache);
4178c2ecf20Sopenharmony_ci		kfree(cache->hashtable);
4188c2ecf20Sopenharmony_ci		kfree(cache);
4198c2ecf20Sopenharmony_ci	}
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpcauth_destroy_credcache);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci
4248c2ecf20Sopenharmony_ci#define RPC_AUTH_EXPIRY_MORATORIUM (60 * HZ)
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci/*
4278c2ecf20Sopenharmony_ci * Remove stale credentials. Avoid sleeping inside the loop.
4288c2ecf20Sopenharmony_ci */
4298c2ecf20Sopenharmony_cistatic long
4308c2ecf20Sopenharmony_cirpcauth_prune_expired(struct list_head *free, int nr_to_scan)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	struct rpc_cred *cred, *next;
4338c2ecf20Sopenharmony_ci	unsigned long expired = jiffies - RPC_AUTH_EXPIRY_MORATORIUM;
4348c2ecf20Sopenharmony_ci	long freed = 0;
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ci	list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) {
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci		if (nr_to_scan-- == 0)
4398c2ecf20Sopenharmony_ci			break;
4408c2ecf20Sopenharmony_ci		if (refcount_read(&cred->cr_count) > 1) {
4418c2ecf20Sopenharmony_ci			rpcauth_lru_remove_locked(cred);
4428c2ecf20Sopenharmony_ci			continue;
4438c2ecf20Sopenharmony_ci		}
4448c2ecf20Sopenharmony_ci		/*
4458c2ecf20Sopenharmony_ci		 * Enforce a 60 second garbage collection moratorium
4468c2ecf20Sopenharmony_ci		 * Note that the cred_unused list must be time-ordered.
4478c2ecf20Sopenharmony_ci		 */
4488c2ecf20Sopenharmony_ci		if (time_in_range(cred->cr_expire, expired, jiffies))
4498c2ecf20Sopenharmony_ci			continue;
4508c2ecf20Sopenharmony_ci		if (!rpcauth_unhash_cred(cred))
4518c2ecf20Sopenharmony_ci			continue;
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci		rpcauth_lru_remove_locked(cred);
4548c2ecf20Sopenharmony_ci		freed++;
4558c2ecf20Sopenharmony_ci		list_add_tail(&cred->cr_lru, free);
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci	return freed ? freed : SHRINK_STOP;
4588c2ecf20Sopenharmony_ci}
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic unsigned long
4618c2ecf20Sopenharmony_cirpcauth_cache_do_shrink(int nr_to_scan)
4628c2ecf20Sopenharmony_ci{
4638c2ecf20Sopenharmony_ci	LIST_HEAD(free);
4648c2ecf20Sopenharmony_ci	unsigned long freed;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	spin_lock(&rpc_credcache_lock);
4678c2ecf20Sopenharmony_ci	freed = rpcauth_prune_expired(&free, nr_to_scan);
4688c2ecf20Sopenharmony_ci	spin_unlock(&rpc_credcache_lock);
4698c2ecf20Sopenharmony_ci	rpcauth_destroy_credlist(&free);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ci	return freed;
4728c2ecf20Sopenharmony_ci}
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci/*
4758c2ecf20Sopenharmony_ci * Run memory cache shrinker.
4768c2ecf20Sopenharmony_ci */
4778c2ecf20Sopenharmony_cistatic unsigned long
4788c2ecf20Sopenharmony_cirpcauth_cache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc)
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci{
4818c2ecf20Sopenharmony_ci	if ((sc->gfp_mask & GFP_KERNEL) != GFP_KERNEL)
4828c2ecf20Sopenharmony_ci		return SHRINK_STOP;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	/* nothing left, don't come back */
4858c2ecf20Sopenharmony_ci	if (list_empty(&cred_unused))
4868c2ecf20Sopenharmony_ci		return SHRINK_STOP;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	return rpcauth_cache_do_shrink(sc->nr_to_scan);
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_cistatic unsigned long
4928c2ecf20Sopenharmony_cirpcauth_cache_shrink_count(struct shrinker *shrink, struct shrink_control *sc)
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	return number_cred_unused * sysctl_vfs_cache_pressure / 100;
4968c2ecf20Sopenharmony_ci}
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_cistatic void
4998c2ecf20Sopenharmony_cirpcauth_cache_enforce_limit(void)
5008c2ecf20Sopenharmony_ci{
5018c2ecf20Sopenharmony_ci	unsigned long diff;
5028c2ecf20Sopenharmony_ci	unsigned int nr_to_scan;
5038c2ecf20Sopenharmony_ci
5048c2ecf20Sopenharmony_ci	if (number_cred_unused <= auth_max_cred_cachesize)
5058c2ecf20Sopenharmony_ci		return;
5068c2ecf20Sopenharmony_ci	diff = number_cred_unused - auth_max_cred_cachesize;
5078c2ecf20Sopenharmony_ci	nr_to_scan = 100;
5088c2ecf20Sopenharmony_ci	if (diff < nr_to_scan)
5098c2ecf20Sopenharmony_ci		nr_to_scan = diff;
5108c2ecf20Sopenharmony_ci	rpcauth_cache_do_shrink(nr_to_scan);
5118c2ecf20Sopenharmony_ci}
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci/*
5148c2ecf20Sopenharmony_ci * Look up a process' credentials in the authentication cache
5158c2ecf20Sopenharmony_ci */
5168c2ecf20Sopenharmony_cistruct rpc_cred *
5178c2ecf20Sopenharmony_cirpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
5188c2ecf20Sopenharmony_ci		int flags, gfp_t gfp)
5198c2ecf20Sopenharmony_ci{
5208c2ecf20Sopenharmony_ci	LIST_HEAD(free);
5218c2ecf20Sopenharmony_ci	struct rpc_cred_cache *cache = auth->au_credcache;
5228c2ecf20Sopenharmony_ci	struct rpc_cred	*cred = NULL,
5238c2ecf20Sopenharmony_ci			*entry, *new;
5248c2ecf20Sopenharmony_ci	unsigned int nr;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	nr = auth->au_ops->hash_cred(acred, cache->hashbits);
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	rcu_read_lock();
5298c2ecf20Sopenharmony_ci	hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) {
5308c2ecf20Sopenharmony_ci		if (!entry->cr_ops->crmatch(acred, entry, flags))
5318c2ecf20Sopenharmony_ci			continue;
5328c2ecf20Sopenharmony_ci		cred = get_rpccred(entry);
5338c2ecf20Sopenharmony_ci		if (cred)
5348c2ecf20Sopenharmony_ci			break;
5358c2ecf20Sopenharmony_ci	}
5368c2ecf20Sopenharmony_ci	rcu_read_unlock();
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	if (cred != NULL)
5398c2ecf20Sopenharmony_ci		goto found;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	new = auth->au_ops->crcreate(auth, acred, flags, gfp);
5428c2ecf20Sopenharmony_ci	if (IS_ERR(new)) {
5438c2ecf20Sopenharmony_ci		cred = new;
5448c2ecf20Sopenharmony_ci		goto out;
5458c2ecf20Sopenharmony_ci	}
5468c2ecf20Sopenharmony_ci
5478c2ecf20Sopenharmony_ci	spin_lock(&cache->lock);
5488c2ecf20Sopenharmony_ci	hlist_for_each_entry(entry, &cache->hashtable[nr], cr_hash) {
5498c2ecf20Sopenharmony_ci		if (!entry->cr_ops->crmatch(acred, entry, flags))
5508c2ecf20Sopenharmony_ci			continue;
5518c2ecf20Sopenharmony_ci		cred = get_rpccred(entry);
5528c2ecf20Sopenharmony_ci		if (cred)
5538c2ecf20Sopenharmony_ci			break;
5548c2ecf20Sopenharmony_ci	}
5558c2ecf20Sopenharmony_ci	if (cred == NULL) {
5568c2ecf20Sopenharmony_ci		cred = new;
5578c2ecf20Sopenharmony_ci		set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags);
5588c2ecf20Sopenharmony_ci		refcount_inc(&cred->cr_count);
5598c2ecf20Sopenharmony_ci		hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]);
5608c2ecf20Sopenharmony_ci	} else
5618c2ecf20Sopenharmony_ci		list_add_tail(&new->cr_lru, &free);
5628c2ecf20Sopenharmony_ci	spin_unlock(&cache->lock);
5638c2ecf20Sopenharmony_ci	rpcauth_cache_enforce_limit();
5648c2ecf20Sopenharmony_cifound:
5658c2ecf20Sopenharmony_ci	if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) &&
5668c2ecf20Sopenharmony_ci	    cred->cr_ops->cr_init != NULL &&
5678c2ecf20Sopenharmony_ci	    !(flags & RPCAUTH_LOOKUP_NEW)) {
5688c2ecf20Sopenharmony_ci		int res = cred->cr_ops->cr_init(auth, cred);
5698c2ecf20Sopenharmony_ci		if (res < 0) {
5708c2ecf20Sopenharmony_ci			put_rpccred(cred);
5718c2ecf20Sopenharmony_ci			cred = ERR_PTR(res);
5728c2ecf20Sopenharmony_ci		}
5738c2ecf20Sopenharmony_ci	}
5748c2ecf20Sopenharmony_ci	rpcauth_destroy_credlist(&free);
5758c2ecf20Sopenharmony_ciout:
5768c2ecf20Sopenharmony_ci	return cred;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpcauth_lookup_credcache);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_cistruct rpc_cred *
5818c2ecf20Sopenharmony_cirpcauth_lookupcred(struct rpc_auth *auth, int flags)
5828c2ecf20Sopenharmony_ci{
5838c2ecf20Sopenharmony_ci	struct auth_cred acred;
5848c2ecf20Sopenharmony_ci	struct rpc_cred *ret;
5858c2ecf20Sopenharmony_ci	const struct cred *cred = current_cred();
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	memset(&acred, 0, sizeof(acred));
5888c2ecf20Sopenharmony_ci	acred.cred = cred;
5898c2ecf20Sopenharmony_ci	ret = auth->au_ops->lookup_cred(auth, &acred, flags);
5908c2ecf20Sopenharmony_ci	return ret;
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpcauth_lookupcred);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_civoid
5958c2ecf20Sopenharmony_cirpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred,
5968c2ecf20Sopenharmony_ci		  struct rpc_auth *auth, const struct rpc_credops *ops)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	INIT_HLIST_NODE(&cred->cr_hash);
5998c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&cred->cr_lru);
6008c2ecf20Sopenharmony_ci	refcount_set(&cred->cr_count, 1);
6018c2ecf20Sopenharmony_ci	cred->cr_auth = auth;
6028c2ecf20Sopenharmony_ci	cred->cr_flags = 0;
6038c2ecf20Sopenharmony_ci	cred->cr_ops = ops;
6048c2ecf20Sopenharmony_ci	cred->cr_expire = jiffies;
6058c2ecf20Sopenharmony_ci	cred->cr_cred = get_cred(acred->cred);
6068c2ecf20Sopenharmony_ci}
6078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpcauth_init_cred);
6088c2ecf20Sopenharmony_ci
6098c2ecf20Sopenharmony_cistatic struct rpc_cred *
6108c2ecf20Sopenharmony_cirpcauth_bind_root_cred(struct rpc_task *task, int lookupflags)
6118c2ecf20Sopenharmony_ci{
6128c2ecf20Sopenharmony_ci	struct rpc_auth *auth = task->tk_client->cl_auth;
6138c2ecf20Sopenharmony_ci	struct auth_cred acred = {
6148c2ecf20Sopenharmony_ci		.cred = get_task_cred(&init_task),
6158c2ecf20Sopenharmony_ci	};
6168c2ecf20Sopenharmony_ci	struct rpc_cred *ret;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	ret = auth->au_ops->lookup_cred(auth, &acred, lookupflags);
6198c2ecf20Sopenharmony_ci	put_cred(acred.cred);
6208c2ecf20Sopenharmony_ci	return ret;
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistatic struct rpc_cred *
6248c2ecf20Sopenharmony_cirpcauth_bind_machine_cred(struct rpc_task *task, int lookupflags)
6258c2ecf20Sopenharmony_ci{
6268c2ecf20Sopenharmony_ci	struct rpc_auth *auth = task->tk_client->cl_auth;
6278c2ecf20Sopenharmony_ci	struct auth_cred acred = {
6288c2ecf20Sopenharmony_ci		.principal = task->tk_client->cl_principal,
6298c2ecf20Sopenharmony_ci		.cred = init_task.cred,
6308c2ecf20Sopenharmony_ci	};
6318c2ecf20Sopenharmony_ci
6328c2ecf20Sopenharmony_ci	if (!acred.principal)
6338c2ecf20Sopenharmony_ci		return NULL;
6348c2ecf20Sopenharmony_ci	return auth->au_ops->lookup_cred(auth, &acred, lookupflags);
6358c2ecf20Sopenharmony_ci}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_cistatic struct rpc_cred *
6388c2ecf20Sopenharmony_cirpcauth_bind_new_cred(struct rpc_task *task, int lookupflags)
6398c2ecf20Sopenharmony_ci{
6408c2ecf20Sopenharmony_ci	struct rpc_auth *auth = task->tk_client->cl_auth;
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci	return rpcauth_lookupcred(auth, lookupflags);
6438c2ecf20Sopenharmony_ci}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_cistatic int
6468c2ecf20Sopenharmony_cirpcauth_bindcred(struct rpc_task *task, const struct cred *cred, int flags)
6478c2ecf20Sopenharmony_ci{
6488c2ecf20Sopenharmony_ci	struct rpc_rqst *req = task->tk_rqstp;
6498c2ecf20Sopenharmony_ci	struct rpc_cred *new = NULL;
6508c2ecf20Sopenharmony_ci	int lookupflags = 0;
6518c2ecf20Sopenharmony_ci	struct rpc_auth *auth = task->tk_client->cl_auth;
6528c2ecf20Sopenharmony_ci	struct auth_cred acred = {
6538c2ecf20Sopenharmony_ci		.cred = cred,
6548c2ecf20Sopenharmony_ci	};
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci	if (flags & RPC_TASK_ASYNC)
6578c2ecf20Sopenharmony_ci		lookupflags |= RPCAUTH_LOOKUP_NEW;
6588c2ecf20Sopenharmony_ci	if (task->tk_op_cred)
6598c2ecf20Sopenharmony_ci		/* Task must use exactly this rpc_cred */
6608c2ecf20Sopenharmony_ci		new = get_rpccred(task->tk_op_cred);
6618c2ecf20Sopenharmony_ci	else if (cred != NULL && cred != &machine_cred)
6628c2ecf20Sopenharmony_ci		new = auth->au_ops->lookup_cred(auth, &acred, lookupflags);
6638c2ecf20Sopenharmony_ci	else if (cred == &machine_cred)
6648c2ecf20Sopenharmony_ci		new = rpcauth_bind_machine_cred(task, lookupflags);
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	/* If machine cred couldn't be bound, try a root cred */
6678c2ecf20Sopenharmony_ci	if (new)
6688c2ecf20Sopenharmony_ci		;
6698c2ecf20Sopenharmony_ci	else if (cred == &machine_cred || (flags & RPC_TASK_ROOTCREDS))
6708c2ecf20Sopenharmony_ci		new = rpcauth_bind_root_cred(task, lookupflags);
6718c2ecf20Sopenharmony_ci	else if (flags & RPC_TASK_NULLCREDS)
6728c2ecf20Sopenharmony_ci		new = authnull_ops.lookup_cred(NULL, NULL, 0);
6738c2ecf20Sopenharmony_ci	else
6748c2ecf20Sopenharmony_ci		new = rpcauth_bind_new_cred(task, lookupflags);
6758c2ecf20Sopenharmony_ci	if (IS_ERR(new))
6768c2ecf20Sopenharmony_ci		return PTR_ERR(new);
6778c2ecf20Sopenharmony_ci	put_rpccred(req->rq_cred);
6788c2ecf20Sopenharmony_ci	req->rq_cred = new;
6798c2ecf20Sopenharmony_ci	return 0;
6808c2ecf20Sopenharmony_ci}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_civoid
6838c2ecf20Sopenharmony_ciput_rpccred(struct rpc_cred *cred)
6848c2ecf20Sopenharmony_ci{
6858c2ecf20Sopenharmony_ci	if (cred == NULL)
6868c2ecf20Sopenharmony_ci		return;
6878c2ecf20Sopenharmony_ci	rcu_read_lock();
6888c2ecf20Sopenharmony_ci	if (refcount_dec_and_test(&cred->cr_count))
6898c2ecf20Sopenharmony_ci		goto destroy;
6908c2ecf20Sopenharmony_ci	if (refcount_read(&cred->cr_count) != 1 ||
6918c2ecf20Sopenharmony_ci	    !test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))
6928c2ecf20Sopenharmony_ci		goto out;
6938c2ecf20Sopenharmony_ci	if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) {
6948c2ecf20Sopenharmony_ci		cred->cr_expire = jiffies;
6958c2ecf20Sopenharmony_ci		rpcauth_lru_add(cred);
6968c2ecf20Sopenharmony_ci		/* Race breaker */
6978c2ecf20Sopenharmony_ci		if (unlikely(!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags)))
6988c2ecf20Sopenharmony_ci			rpcauth_lru_remove(cred);
6998c2ecf20Sopenharmony_ci	} else if (rpcauth_unhash_cred(cred)) {
7008c2ecf20Sopenharmony_ci		rpcauth_lru_remove(cred);
7018c2ecf20Sopenharmony_ci		if (refcount_dec_and_test(&cred->cr_count))
7028c2ecf20Sopenharmony_ci			goto destroy;
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ciout:
7058c2ecf20Sopenharmony_ci	rcu_read_unlock();
7068c2ecf20Sopenharmony_ci	return;
7078c2ecf20Sopenharmony_cidestroy:
7088c2ecf20Sopenharmony_ci	rcu_read_unlock();
7098c2ecf20Sopenharmony_ci	cred->cr_ops->crdestroy(cred);
7108c2ecf20Sopenharmony_ci}
7118c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(put_rpccred);
7128c2ecf20Sopenharmony_ci
7138c2ecf20Sopenharmony_ci/**
7148c2ecf20Sopenharmony_ci * rpcauth_marshcred - Append RPC credential to end of @xdr
7158c2ecf20Sopenharmony_ci * @task: controlling RPC task
7168c2ecf20Sopenharmony_ci * @xdr: xdr_stream containing initial portion of RPC Call header
7178c2ecf20Sopenharmony_ci *
7188c2ecf20Sopenharmony_ci * On success, an appropriate verifier is added to @xdr, @xdr is
7198c2ecf20Sopenharmony_ci * updated to point past the verifier, and zero is returned.
7208c2ecf20Sopenharmony_ci * Otherwise, @xdr is in an undefined state and a negative errno
7218c2ecf20Sopenharmony_ci * is returned.
7228c2ecf20Sopenharmony_ci */
7238c2ecf20Sopenharmony_ciint rpcauth_marshcred(struct rpc_task *task, struct xdr_stream *xdr)
7248c2ecf20Sopenharmony_ci{
7258c2ecf20Sopenharmony_ci	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci	return ops->crmarshal(task, xdr);
7288c2ecf20Sopenharmony_ci}
7298c2ecf20Sopenharmony_ci
7308c2ecf20Sopenharmony_ci/**
7318c2ecf20Sopenharmony_ci * rpcauth_wrap_req_encode - XDR encode the RPC procedure
7328c2ecf20Sopenharmony_ci * @task: controlling RPC task
7338c2ecf20Sopenharmony_ci * @xdr: stream where on-the-wire bytes are to be marshalled
7348c2ecf20Sopenharmony_ci *
7358c2ecf20Sopenharmony_ci * On success, @xdr contains the encoded and wrapped message.
7368c2ecf20Sopenharmony_ci * Otherwise, @xdr is in an undefined state.
7378c2ecf20Sopenharmony_ci */
7388c2ecf20Sopenharmony_ciint rpcauth_wrap_req_encode(struct rpc_task *task, struct xdr_stream *xdr)
7398c2ecf20Sopenharmony_ci{
7408c2ecf20Sopenharmony_ci	kxdreproc_t encode = task->tk_msg.rpc_proc->p_encode;
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_ci	encode(task->tk_rqstp, xdr, task->tk_msg.rpc_argp);
7438c2ecf20Sopenharmony_ci	return 0;
7448c2ecf20Sopenharmony_ci}
7458c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpcauth_wrap_req_encode);
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci/**
7488c2ecf20Sopenharmony_ci * rpcauth_wrap_req - XDR encode and wrap the RPC procedure
7498c2ecf20Sopenharmony_ci * @task: controlling RPC task
7508c2ecf20Sopenharmony_ci * @xdr: stream where on-the-wire bytes are to be marshalled
7518c2ecf20Sopenharmony_ci *
7528c2ecf20Sopenharmony_ci * On success, @xdr contains the encoded and wrapped message,
7538c2ecf20Sopenharmony_ci * and zero is returned. Otherwise, @xdr is in an undefined
7548c2ecf20Sopenharmony_ci * state and a negative errno is returned.
7558c2ecf20Sopenharmony_ci */
7568c2ecf20Sopenharmony_ciint rpcauth_wrap_req(struct rpc_task *task, struct xdr_stream *xdr)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	return ops->crwrap_req(task, xdr);
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci/**
7648c2ecf20Sopenharmony_ci * rpcauth_checkverf - Validate verifier in RPC Reply header
7658c2ecf20Sopenharmony_ci * @task: controlling RPC task
7668c2ecf20Sopenharmony_ci * @xdr: xdr_stream containing RPC Reply header
7678c2ecf20Sopenharmony_ci *
7688c2ecf20Sopenharmony_ci * On success, @xdr is updated to point past the verifier and
7698c2ecf20Sopenharmony_ci * zero is returned. Otherwise, @xdr is in an undefined state
7708c2ecf20Sopenharmony_ci * and a negative errno is returned.
7718c2ecf20Sopenharmony_ci */
7728c2ecf20Sopenharmony_ciint
7738c2ecf20Sopenharmony_cirpcauth_checkverf(struct rpc_task *task, struct xdr_stream *xdr)
7748c2ecf20Sopenharmony_ci{
7758c2ecf20Sopenharmony_ci	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
7768c2ecf20Sopenharmony_ci
7778c2ecf20Sopenharmony_ci	return ops->crvalidate(task, xdr);
7788c2ecf20Sopenharmony_ci}
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci/**
7818c2ecf20Sopenharmony_ci * rpcauth_unwrap_resp_decode - Invoke XDR decode function
7828c2ecf20Sopenharmony_ci * @task: controlling RPC task
7838c2ecf20Sopenharmony_ci * @xdr: stream where the Reply message resides
7848c2ecf20Sopenharmony_ci *
7858c2ecf20Sopenharmony_ci * Returns zero on success; otherwise a negative errno is returned.
7868c2ecf20Sopenharmony_ci */
7878c2ecf20Sopenharmony_ciint
7888c2ecf20Sopenharmony_cirpcauth_unwrap_resp_decode(struct rpc_task *task, struct xdr_stream *xdr)
7898c2ecf20Sopenharmony_ci{
7908c2ecf20Sopenharmony_ci	kxdrdproc_t decode = task->tk_msg.rpc_proc->p_decode;
7918c2ecf20Sopenharmony_ci
7928c2ecf20Sopenharmony_ci	return decode(task->tk_rqstp, xdr, task->tk_msg.rpc_resp);
7938c2ecf20Sopenharmony_ci}
7948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rpcauth_unwrap_resp_decode);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci/**
7978c2ecf20Sopenharmony_ci * rpcauth_unwrap_resp - Invoke unwrap and decode function for the cred
7988c2ecf20Sopenharmony_ci * @task: controlling RPC task
7998c2ecf20Sopenharmony_ci * @xdr: stream where the Reply message resides
8008c2ecf20Sopenharmony_ci *
8018c2ecf20Sopenharmony_ci * Returns zero on success; otherwise a negative errno is returned.
8028c2ecf20Sopenharmony_ci */
8038c2ecf20Sopenharmony_ciint
8048c2ecf20Sopenharmony_cirpcauth_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr)
8058c2ecf20Sopenharmony_ci{
8068c2ecf20Sopenharmony_ci	const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops;
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ci	return ops->crunwrap_resp(task, xdr);
8098c2ecf20Sopenharmony_ci}
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_cibool
8128c2ecf20Sopenharmony_cirpcauth_xmit_need_reencode(struct rpc_task *task)
8138c2ecf20Sopenharmony_ci{
8148c2ecf20Sopenharmony_ci	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci	if (!cred || !cred->cr_ops->crneed_reencode)
8178c2ecf20Sopenharmony_ci		return false;
8188c2ecf20Sopenharmony_ci	return cred->cr_ops->crneed_reencode(task);
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ciint
8228c2ecf20Sopenharmony_cirpcauth_refreshcred(struct rpc_task *task)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	struct rpc_cred	*cred;
8258c2ecf20Sopenharmony_ci	int err;
8268c2ecf20Sopenharmony_ci
8278c2ecf20Sopenharmony_ci	cred = task->tk_rqstp->rq_cred;
8288c2ecf20Sopenharmony_ci	if (cred == NULL) {
8298c2ecf20Sopenharmony_ci		err = rpcauth_bindcred(task, task->tk_msg.rpc_cred, task->tk_flags);
8308c2ecf20Sopenharmony_ci		if (err < 0)
8318c2ecf20Sopenharmony_ci			goto out;
8328c2ecf20Sopenharmony_ci		cred = task->tk_rqstp->rq_cred;
8338c2ecf20Sopenharmony_ci	}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_ci	err = cred->cr_ops->crrefresh(task);
8368c2ecf20Sopenharmony_ciout:
8378c2ecf20Sopenharmony_ci	if (err < 0)
8388c2ecf20Sopenharmony_ci		task->tk_status = err;
8398c2ecf20Sopenharmony_ci	return err;
8408c2ecf20Sopenharmony_ci}
8418c2ecf20Sopenharmony_ci
8428c2ecf20Sopenharmony_civoid
8438c2ecf20Sopenharmony_cirpcauth_invalcred(struct rpc_task *task)
8448c2ecf20Sopenharmony_ci{
8458c2ecf20Sopenharmony_ci	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
8468c2ecf20Sopenharmony_ci
8478c2ecf20Sopenharmony_ci	if (cred)
8488c2ecf20Sopenharmony_ci		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
8498c2ecf20Sopenharmony_ci}
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ciint
8528c2ecf20Sopenharmony_cirpcauth_uptodatecred(struct rpc_task *task)
8538c2ecf20Sopenharmony_ci{
8548c2ecf20Sopenharmony_ci	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
8558c2ecf20Sopenharmony_ci
8568c2ecf20Sopenharmony_ci	return cred == NULL ||
8578c2ecf20Sopenharmony_ci		test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0;
8588c2ecf20Sopenharmony_ci}
8598c2ecf20Sopenharmony_ci
8608c2ecf20Sopenharmony_cistatic struct shrinker rpc_cred_shrinker = {
8618c2ecf20Sopenharmony_ci	.count_objects = rpcauth_cache_shrink_count,
8628c2ecf20Sopenharmony_ci	.scan_objects = rpcauth_cache_shrink_scan,
8638c2ecf20Sopenharmony_ci	.seeks = DEFAULT_SEEKS,
8648c2ecf20Sopenharmony_ci};
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ciint __init rpcauth_init_module(void)
8678c2ecf20Sopenharmony_ci{
8688c2ecf20Sopenharmony_ci	int err;
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	err = rpc_init_authunix();
8718c2ecf20Sopenharmony_ci	if (err < 0)
8728c2ecf20Sopenharmony_ci		goto out1;
8738c2ecf20Sopenharmony_ci	err = register_shrinker(&rpc_cred_shrinker);
8748c2ecf20Sopenharmony_ci	if (err < 0)
8758c2ecf20Sopenharmony_ci		goto out2;
8768c2ecf20Sopenharmony_ci	return 0;
8778c2ecf20Sopenharmony_ciout2:
8788c2ecf20Sopenharmony_ci	rpc_destroy_authunix();
8798c2ecf20Sopenharmony_ciout1:
8808c2ecf20Sopenharmony_ci	return err;
8818c2ecf20Sopenharmony_ci}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_civoid rpcauth_remove_module(void)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci	rpc_destroy_authunix();
8868c2ecf20Sopenharmony_ci	unregister_shrinker(&rpc_cred_shrinker);
8878c2ecf20Sopenharmony_ci}
888