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