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 42db5fe265SMiquel van Smoorenburg #define MAX_HASHTABLE_BITS (14) 438e4e15d4SStephen Rothwell static int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp) 44241269bdSTrond Myklebust { 45241269bdSTrond Myklebust unsigned long num; 46241269bdSTrond Myklebust unsigned int nbits; 47241269bdSTrond Myklebust int ret; 48241269bdSTrond Myklebust 49241269bdSTrond Myklebust if (!val) 50241269bdSTrond Myklebust goto out_inval; 5100cfaa94SDaniel Walter ret = kstrtoul(val, 0, &num); 521a54c0cfSDan Carpenter if (ret) 53241269bdSTrond Myklebust goto out_inval; 5434ae685cSFrank Sorenson nbits = fls(num - 1); 55241269bdSTrond Myklebust if (nbits > MAX_HASHTABLE_BITS || nbits < 2) 56241269bdSTrond Myklebust goto out_inval; 57241269bdSTrond Myklebust *(unsigned int *)kp->arg = nbits; 58241269bdSTrond Myklebust return 0; 59241269bdSTrond Myklebust out_inval: 60241269bdSTrond Myklebust return -EINVAL; 61241269bdSTrond Myklebust } 62241269bdSTrond Myklebust 638e4e15d4SStephen Rothwell static int param_get_hashtbl_sz(char *buffer, const struct kernel_param *kp) 64241269bdSTrond Myklebust { 65241269bdSTrond Myklebust unsigned int nbits; 66241269bdSTrond Myklebust 67241269bdSTrond Myklebust nbits = *(unsigned int *)kp->arg; 68241269bdSTrond Myklebust return sprintf(buffer, "%u", 1U << nbits); 69241269bdSTrond Myklebust } 70241269bdSTrond Myklebust 71241269bdSTrond Myklebust #define param_check_hashtbl_sz(name, p) __param_check(name, p, unsigned int); 72241269bdSTrond Myklebust 739c27847dSLuis R. Rodriguez static const struct kernel_param_ops param_ops_hashtbl_sz = { 748e4e15d4SStephen Rothwell .set = param_set_hashtbl_sz, 758e4e15d4SStephen Rothwell .get = param_get_hashtbl_sz, 768e4e15d4SStephen Rothwell }; 778e4e15d4SStephen Rothwell 78241269bdSTrond Myklebust module_param_named(auth_hashtable_size, auth_hashbits, hashtbl_sz, 0644); 79241269bdSTrond Myklebust MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size"); 80241269bdSTrond Myklebust 81bae6746fSTrond Myklebust static unsigned long auth_max_cred_cachesize = ULONG_MAX; 82bae6746fSTrond Myklebust module_param(auth_max_cred_cachesize, ulong, 0644); 83bae6746fSTrond Myklebust MODULE_PARM_DESC(auth_max_cred_cachesize, "RPC credential maximum total cache size"); 84bae6746fSTrond Myklebust 851da177e4SLinus Torvalds static u32 861da177e4SLinus Torvalds pseudoflavor_to_flavor(u32 flavor) { 871c74a244SChuck Lever if (flavor > RPC_AUTH_MAXFLAVOR) 881da177e4SLinus Torvalds return RPC_AUTH_GSS; 891da177e4SLinus Torvalds return flavor; 901da177e4SLinus Torvalds } 911da177e4SLinus Torvalds 921da177e4SLinus Torvalds int 93f1c0a861STrond Myklebust rpcauth_register(const struct rpc_authops *ops) 941da177e4SLinus Torvalds { 954e4c3befSTrond Myklebust const struct rpc_authops *old; 961da177e4SLinus Torvalds rpc_authflavor_t flavor; 971da177e4SLinus Torvalds 981da177e4SLinus Torvalds if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR) 991da177e4SLinus Torvalds return -EINVAL; 1004e4c3befSTrond Myklebust old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], NULL, ops); 1014e4c3befSTrond Myklebust if (old == NULL || old == ops) 1024e4c3befSTrond Myklebust return 0; 1034e4c3befSTrond Myklebust return -EPERM; 1041da177e4SLinus Torvalds } 105e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_register); 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds int 108f1c0a861STrond Myklebust rpcauth_unregister(const struct rpc_authops *ops) 1091da177e4SLinus Torvalds { 1104e4c3befSTrond Myklebust const struct rpc_authops *old; 1111da177e4SLinus Torvalds rpc_authflavor_t flavor; 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR) 1141da177e4SLinus Torvalds return -EINVAL; 1154e4c3befSTrond Myklebust 1164e4c3befSTrond Myklebust old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], ops, NULL); 1174e4c3befSTrond Myklebust if (old == ops || old == NULL) 1184e4c3befSTrond Myklebust return 0; 1194e4c3befSTrond Myklebust return -EPERM; 1201da177e4SLinus Torvalds } 121e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_unregister); 1221da177e4SLinus Torvalds 1234e4c3befSTrond Myklebust static const struct rpc_authops * 1244e4c3befSTrond Myklebust rpcauth_get_authops(rpc_authflavor_t flavor) 1254e4c3befSTrond Myklebust { 1264e4c3befSTrond Myklebust const struct rpc_authops *ops; 1274e4c3befSTrond Myklebust 1284e4c3befSTrond Myklebust if (flavor >= RPC_AUTH_MAXFLAVOR) 1294e4c3befSTrond Myklebust return NULL; 1304e4c3befSTrond Myklebust 1314e4c3befSTrond Myklebust rcu_read_lock(); 1324e4c3befSTrond Myklebust ops = rcu_dereference(auth_flavors[flavor]); 1334e4c3befSTrond Myklebust if (ops == NULL) { 1344e4c3befSTrond Myklebust rcu_read_unlock(); 1354e4c3befSTrond Myklebust request_module("rpc-auth-%u", flavor); 1364e4c3befSTrond Myklebust rcu_read_lock(); 1374e4c3befSTrond Myklebust ops = rcu_dereference(auth_flavors[flavor]); 1384e4c3befSTrond Myklebust if (ops == NULL) 1394e4c3befSTrond Myklebust goto out; 1404e4c3befSTrond Myklebust } 1414e4c3befSTrond Myklebust if (!try_module_get(ops->owner)) 1424e4c3befSTrond Myklebust ops = NULL; 1434e4c3befSTrond Myklebust out: 1444e4c3befSTrond Myklebust rcu_read_unlock(); 1454e4c3befSTrond Myklebust return ops; 1464e4c3befSTrond Myklebust } 1474e4c3befSTrond Myklebust 1484e4c3befSTrond Myklebust static void 1494e4c3befSTrond Myklebust rpcauth_put_authops(const struct rpc_authops *ops) 1504e4c3befSTrond Myklebust { 1514e4c3befSTrond Myklebust module_put(ops->owner); 1524e4c3befSTrond Myklebust } 1534e4c3befSTrond Myklebust 1546a1a1e34SChuck Lever /** 1559568c5e9SChuck Lever * rpcauth_get_pseudoflavor - check if security flavor is supported 1569568c5e9SChuck Lever * @flavor: a security flavor 1579568c5e9SChuck Lever * @info: a GSS mech OID, quality of protection, and service value 1589568c5e9SChuck Lever * 1599568c5e9SChuck Lever * Verifies that an appropriate kernel module is available or already loaded. 1609568c5e9SChuck Lever * Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is 1619568c5e9SChuck Lever * not supported locally. 1629568c5e9SChuck Lever */ 1639568c5e9SChuck Lever rpc_authflavor_t 1649568c5e9SChuck Lever rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info) 1659568c5e9SChuck Lever { 1664e4c3befSTrond Myklebust const struct rpc_authops *ops = rpcauth_get_authops(flavor); 1679568c5e9SChuck Lever rpc_authflavor_t pseudoflavor; 1689568c5e9SChuck Lever 1694e4c3befSTrond Myklebust if (!ops) 1709568c5e9SChuck Lever return RPC_AUTH_MAXFLAVOR; 1719568c5e9SChuck Lever pseudoflavor = flavor; 1729568c5e9SChuck Lever if (ops->info2flavor != NULL) 1739568c5e9SChuck Lever pseudoflavor = ops->info2flavor(info); 1749568c5e9SChuck Lever 1754e4c3befSTrond Myklebust rpcauth_put_authops(ops); 1769568c5e9SChuck Lever return pseudoflavor; 1779568c5e9SChuck Lever } 1789568c5e9SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor); 1799568c5e9SChuck Lever 1809568c5e9SChuck Lever /** 181a77c806fSChuck Lever * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor 182a77c806fSChuck Lever * @pseudoflavor: GSS pseudoflavor to match 183a77c806fSChuck Lever * @info: rpcsec_gss_info structure to fill in 184a77c806fSChuck Lever * 185a77c806fSChuck Lever * Returns zero and fills in "info" if pseudoflavor matches a 186a77c806fSChuck Lever * supported mechanism. 187a77c806fSChuck Lever */ 188a77c806fSChuck Lever int 189a77c806fSChuck Lever rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info) 190a77c806fSChuck Lever { 191a77c806fSChuck Lever rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor); 192a77c806fSChuck Lever const struct rpc_authops *ops; 193a77c806fSChuck Lever int result; 194a77c806fSChuck Lever 1954e4c3befSTrond Myklebust ops = rpcauth_get_authops(flavor); 196a77c806fSChuck Lever if (ops == NULL) 197a77c806fSChuck Lever return -ENOENT; 198a77c806fSChuck Lever 199a77c806fSChuck Lever result = -ENOENT; 200a77c806fSChuck Lever if (ops->flavor2info != NULL) 201a77c806fSChuck Lever result = ops->flavor2info(pseudoflavor, info); 202a77c806fSChuck Lever 2034e4c3befSTrond Myklebust rpcauth_put_authops(ops); 204a77c806fSChuck Lever return result; 205a77c806fSChuck Lever } 206a77c806fSChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo); 207a77c806fSChuck Lever 208a77c806fSChuck Lever /** 2096a1a1e34SChuck Lever * rpcauth_list_flavors - discover registered flavors and pseudoflavors 2106a1a1e34SChuck Lever * @array: array to fill in 2116a1a1e34SChuck Lever * @size: size of "array" 2126a1a1e34SChuck Lever * 2136a1a1e34SChuck Lever * Returns the number of array items filled in, or a negative errno. 2146a1a1e34SChuck Lever * 2156a1a1e34SChuck Lever * The returned array is not sorted by any policy. Callers should not 2166a1a1e34SChuck Lever * rely on the order of the items in the returned array. 2176a1a1e34SChuck Lever */ 2186a1a1e34SChuck Lever int 2196a1a1e34SChuck Lever rpcauth_list_flavors(rpc_authflavor_t *array, int size) 2206a1a1e34SChuck Lever { 2214e4c3befSTrond Myklebust const struct rpc_authops *ops; 2224e4c3befSTrond Myklebust rpc_authflavor_t flavor, pseudos[4]; 2234e4c3befSTrond Myklebust int i, len, result = 0; 2246a1a1e34SChuck Lever 2254e4c3befSTrond Myklebust rcu_read_lock(); 2266a1a1e34SChuck Lever for (flavor = 0; flavor < RPC_AUTH_MAXFLAVOR; flavor++) { 2274e4c3befSTrond Myklebust ops = rcu_dereference(auth_flavors[flavor]); 2286a1a1e34SChuck Lever if (result >= size) { 2296a1a1e34SChuck Lever result = -ENOMEM; 2306a1a1e34SChuck Lever break; 2316a1a1e34SChuck Lever } 2326a1a1e34SChuck Lever 2336a1a1e34SChuck Lever if (ops == NULL) 2346a1a1e34SChuck Lever continue; 2356a1a1e34SChuck Lever if (ops->list_pseudoflavors == NULL) { 2366a1a1e34SChuck Lever array[result++] = ops->au_flavor; 2376a1a1e34SChuck Lever continue; 2386a1a1e34SChuck Lever } 2396a1a1e34SChuck Lever len = ops->list_pseudoflavors(pseudos, ARRAY_SIZE(pseudos)); 2406a1a1e34SChuck Lever if (len < 0) { 2416a1a1e34SChuck Lever result = len; 2426a1a1e34SChuck Lever break; 2436a1a1e34SChuck Lever } 2446a1a1e34SChuck Lever for (i = 0; i < len; i++) { 2456a1a1e34SChuck Lever if (result >= size) { 2466a1a1e34SChuck Lever result = -ENOMEM; 2476a1a1e34SChuck Lever break; 2486a1a1e34SChuck Lever } 2496a1a1e34SChuck Lever array[result++] = pseudos[i]; 2506a1a1e34SChuck Lever } 2516a1a1e34SChuck Lever } 2524e4c3befSTrond Myklebust rcu_read_unlock(); 2536a1a1e34SChuck Lever 2546a1a1e34SChuck Lever dprintk("RPC: %s returns %d\n", __func__, result); 2556a1a1e34SChuck Lever return result; 2566a1a1e34SChuck Lever } 2576a1a1e34SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_list_flavors); 2586a1a1e34SChuck Lever 2591da177e4SLinus Torvalds struct rpc_auth * 26082b98ca5SSargun Dhillon rpcauth_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) 2611da177e4SLinus Torvalds { 2624e4c3befSTrond Myklebust struct rpc_auth *auth = ERR_PTR(-EINVAL); 263f1c0a861STrond Myklebust const struct rpc_authops *ops; 264c2190661STrond Myklebust u32 flavor = pseudoflavor_to_flavor(args->pseudoflavor); 2651da177e4SLinus Torvalds 2664e4c3befSTrond Myklebust ops = rpcauth_get_authops(flavor); 2674e4c3befSTrond Myklebust if (ops == NULL) 268f344f6dfSOlaf Kirch goto out; 269f344f6dfSOlaf Kirch 270c2190661STrond Myklebust auth = ops->create(args, clnt); 2714e4c3befSTrond Myklebust 2724e4c3befSTrond Myklebust rpcauth_put_authops(ops); 2736a19275aSJ. Bruce Fields if (IS_ERR(auth)) 2746a19275aSJ. Bruce Fields return auth; 2751da177e4SLinus Torvalds if (clnt->cl_auth) 276de7a8ce3STrond Myklebust rpcauth_release(clnt->cl_auth); 2771da177e4SLinus Torvalds clnt->cl_auth = auth; 278f344f6dfSOlaf Kirch 279f344f6dfSOlaf Kirch out: 2801da177e4SLinus Torvalds return auth; 2811da177e4SLinus Torvalds } 282e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_create); 2831da177e4SLinus Torvalds 2841da177e4SLinus Torvalds void 285de7a8ce3STrond Myklebust rpcauth_release(struct rpc_auth *auth) 2861da177e4SLinus Torvalds { 2871da177e4SLinus Torvalds if (!atomic_dec_and_test(&auth->au_count)) 2881da177e4SLinus Torvalds return; 2891da177e4SLinus Torvalds auth->au_ops->destroy(auth); 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds 2921da177e4SLinus Torvalds static DEFINE_SPINLOCK(rpc_credcache_lock); 2931da177e4SLinus Torvalds 29495cd6232STrond Myklebust /* 29595cd6232STrond Myklebust * On success, the caller is responsible for freeing the reference 29695cd6232STrond Myklebust * held by the hashtable 29795cd6232STrond Myklebust */ 29895cd6232STrond Myklebust static bool 29931be5bf1STrond Myklebust rpcauth_unhash_cred_locked(struct rpc_cred *cred) 30031be5bf1STrond Myklebust { 30195cd6232STrond Myklebust if (!test_and_clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags)) 30295cd6232STrond Myklebust return false; 30331be5bf1STrond Myklebust hlist_del_rcu(&cred->cr_hash); 30495cd6232STrond Myklebust return true; 30531be5bf1STrond Myklebust } 30631be5bf1STrond Myklebust 30795cd6232STrond Myklebust static bool 3089499b434STrond Myklebust rpcauth_unhash_cred(struct rpc_cred *cred) 3099499b434STrond Myklebust { 3109499b434STrond Myklebust spinlock_t *cache_lock; 31195cd6232STrond Myklebust bool ret; 3129499b434STrond Myklebust 31395cd6232STrond Myklebust if (!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags)) 31495cd6232STrond Myklebust return false; 3159499b434STrond Myklebust cache_lock = &cred->cr_auth->au_credcache->lock; 3169499b434STrond Myklebust spin_lock(cache_lock); 31795cd6232STrond Myklebust ret = rpcauth_unhash_cred_locked(cred); 3189499b434STrond Myklebust spin_unlock(cache_lock); 319f0380f3dSTrond Myklebust return ret; 3209499b434STrond Myklebust } 3219499b434STrond Myklebust 3221da177e4SLinus Torvalds /* 3231da177e4SLinus Torvalds * Initialize RPC credential cache 3241da177e4SLinus Torvalds */ 3251da177e4SLinus Torvalds int 326f5c2187cSTrond Myklebust rpcauth_init_credcache(struct rpc_auth *auth) 3271da177e4SLinus Torvalds { 3281da177e4SLinus Torvalds struct rpc_cred_cache *new; 329988664a0STrond Myklebust unsigned int hashsize; 3301da177e4SLinus Torvalds 3318b3a7005SKris Katterjohn new = kmalloc(sizeof(*new), GFP_KERNEL); 3321da177e4SLinus Torvalds if (!new) 333241269bdSTrond Myklebust goto out_nocache; 334241269bdSTrond Myklebust new->hashbits = auth_hashbits; 335988664a0STrond Myklebust hashsize = 1U << new->hashbits; 336241269bdSTrond Myklebust new->hashtable = kcalloc(hashsize, sizeof(new->hashtable[0]), GFP_KERNEL); 337241269bdSTrond Myklebust if (!new->hashtable) 338241269bdSTrond Myklebust goto out_nohashtbl; 3399499b434STrond Myklebust spin_lock_init(&new->lock); 3401da177e4SLinus Torvalds auth->au_credcache = new; 3411da177e4SLinus Torvalds return 0; 342241269bdSTrond Myklebust out_nohashtbl: 343241269bdSTrond Myklebust kfree(new); 344241269bdSTrond Myklebust out_nocache: 345241269bdSTrond Myklebust return -ENOMEM; 3461da177e4SLinus Torvalds } 347e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_credcache); 3481da177e4SLinus Torvalds 3491da177e4SLinus Torvalds /* 3504de6caa2SAndy Adamson * Setup a credential key lifetime timeout notification 3514de6caa2SAndy Adamson */ 3524de6caa2SAndy Adamson int 3534de6caa2SAndy Adamson rpcauth_key_timeout_notify(struct rpc_auth *auth, struct rpc_cred *cred) 3544de6caa2SAndy Adamson { 3554de6caa2SAndy Adamson if (!cred->cr_auth->au_ops->key_timeout) 3564de6caa2SAndy Adamson return 0; 3574de6caa2SAndy Adamson return cred->cr_auth->au_ops->key_timeout(auth, cred); 3584de6caa2SAndy Adamson } 3594de6caa2SAndy Adamson EXPORT_SYMBOL_GPL(rpcauth_key_timeout_notify); 3604de6caa2SAndy Adamson 3614de6caa2SAndy Adamson bool 362ce52914eSScott Mayhew rpcauth_cred_key_to_expire(struct rpc_auth *auth, struct rpc_cred *cred) 3634de6caa2SAndy Adamson { 364ce52914eSScott Mayhew if (auth->au_flags & RPCAUTH_AUTH_NO_CRKEY_TIMEOUT) 365ce52914eSScott Mayhew return false; 3664de6caa2SAndy Adamson if (!cred->cr_ops->crkey_to_expire) 3674de6caa2SAndy Adamson return false; 3684de6caa2SAndy Adamson return cred->cr_ops->crkey_to_expire(cred); 3694de6caa2SAndy Adamson } 3704de6caa2SAndy Adamson EXPORT_SYMBOL_GPL(rpcauth_cred_key_to_expire); 3714de6caa2SAndy Adamson 372a0337d1dSJeff Layton char * 373a0337d1dSJeff Layton rpcauth_stringify_acceptor(struct rpc_cred *cred) 374a0337d1dSJeff Layton { 375a0337d1dSJeff Layton if (!cred->cr_ops->crstringify_acceptor) 376a0337d1dSJeff Layton return NULL; 377a0337d1dSJeff Layton return cred->cr_ops->crstringify_acceptor(cred); 378a0337d1dSJeff Layton } 379a0337d1dSJeff Layton EXPORT_SYMBOL_GPL(rpcauth_stringify_acceptor); 380a0337d1dSJeff Layton 3814de6caa2SAndy Adamson /* 3821da177e4SLinus Torvalds * Destroy a list of credentials 3831da177e4SLinus Torvalds */ 3841da177e4SLinus Torvalds static inline 385e092bdcdSTrond Myklebust void rpcauth_destroy_credlist(struct list_head *head) 3861da177e4SLinus Torvalds { 3871da177e4SLinus Torvalds struct rpc_cred *cred; 3881da177e4SLinus Torvalds 389e092bdcdSTrond Myklebust while (!list_empty(head)) { 390e092bdcdSTrond Myklebust cred = list_entry(head->next, struct rpc_cred, cr_lru); 391e092bdcdSTrond Myklebust list_del_init(&cred->cr_lru); 3921da177e4SLinus Torvalds put_rpccred(cred); 3931da177e4SLinus Torvalds } 3941da177e4SLinus Torvalds } 3951da177e4SLinus Torvalds 39695cd6232STrond Myklebust static void 39795cd6232STrond Myklebust rpcauth_lru_add_locked(struct rpc_cred *cred) 39895cd6232STrond Myklebust { 39995cd6232STrond Myklebust if (!list_empty(&cred->cr_lru)) 40095cd6232STrond Myklebust return; 40195cd6232STrond Myklebust number_cred_unused++; 40295cd6232STrond Myklebust list_add_tail(&cred->cr_lru, &cred_unused); 40395cd6232STrond Myklebust } 40495cd6232STrond Myklebust 40595cd6232STrond Myklebust static void 40695cd6232STrond Myklebust rpcauth_lru_add(struct rpc_cred *cred) 40795cd6232STrond Myklebust { 40895cd6232STrond Myklebust if (!list_empty(&cred->cr_lru)) 40995cd6232STrond Myklebust return; 41095cd6232STrond Myklebust spin_lock(&rpc_credcache_lock); 41195cd6232STrond Myklebust rpcauth_lru_add_locked(cred); 41295cd6232STrond Myklebust spin_unlock(&rpc_credcache_lock); 41395cd6232STrond Myklebust } 41495cd6232STrond Myklebust 41595cd6232STrond Myklebust static void 41695cd6232STrond Myklebust rpcauth_lru_remove_locked(struct rpc_cred *cred) 41795cd6232STrond Myklebust { 41895cd6232STrond Myklebust if (list_empty(&cred->cr_lru)) 41995cd6232STrond Myklebust return; 42095cd6232STrond Myklebust number_cred_unused--; 42195cd6232STrond Myklebust list_del_init(&cred->cr_lru); 42295cd6232STrond Myklebust } 42395cd6232STrond Myklebust 42495cd6232STrond Myklebust static void 42595cd6232STrond Myklebust rpcauth_lru_remove(struct rpc_cred *cred) 42695cd6232STrond Myklebust { 42795cd6232STrond Myklebust if (list_empty(&cred->cr_lru)) 42895cd6232STrond Myklebust return; 42995cd6232STrond Myklebust spin_lock(&rpc_credcache_lock); 43095cd6232STrond Myklebust rpcauth_lru_remove_locked(cred); 43195cd6232STrond Myklebust spin_unlock(&rpc_credcache_lock); 43295cd6232STrond Myklebust } 43395cd6232STrond Myklebust 4341da177e4SLinus Torvalds /* 4351da177e4SLinus Torvalds * Clear the RPC credential cache, and delete those credentials 4361da177e4SLinus Torvalds * that are not referenced. 4371da177e4SLinus Torvalds */ 4381da177e4SLinus Torvalds void 4393ab9bb72STrond Myklebust rpcauth_clear_credcache(struct rpc_cred_cache *cache) 4401da177e4SLinus Torvalds { 441e092bdcdSTrond Myklebust LIST_HEAD(free); 442e092bdcdSTrond Myklebust struct hlist_head *head; 4431da177e4SLinus Torvalds struct rpc_cred *cred; 444988664a0STrond Myklebust unsigned int hashsize = 1U << cache->hashbits; 4451da177e4SLinus Torvalds int i; 4461da177e4SLinus Torvalds 4471da177e4SLinus Torvalds spin_lock(&rpc_credcache_lock); 4489499b434STrond Myklebust spin_lock(&cache->lock); 449988664a0STrond Myklebust for (i = 0; i < hashsize; i++) { 450e092bdcdSTrond Myklebust head = &cache->hashtable[i]; 451e092bdcdSTrond Myklebust while (!hlist_empty(head)) { 452e092bdcdSTrond Myklebust cred = hlist_entry(head->first, struct rpc_cred, cr_hash); 45331be5bf1STrond Myklebust rpcauth_unhash_cred_locked(cred); 45495cd6232STrond Myklebust /* Note: We now hold a reference to cred */ 45595cd6232STrond Myklebust rpcauth_lru_remove_locked(cred); 45695cd6232STrond Myklebust list_add_tail(&cred->cr_lru, &free); 4571da177e4SLinus Torvalds } 4581da177e4SLinus Torvalds } 4599499b434STrond Myklebust spin_unlock(&cache->lock); 4601da177e4SLinus Torvalds spin_unlock(&rpc_credcache_lock); 4611da177e4SLinus Torvalds rpcauth_destroy_credlist(&free); 4621da177e4SLinus Torvalds } 4631da177e4SLinus Torvalds 4643ab9bb72STrond Myklebust /* 4653ab9bb72STrond Myklebust * Destroy the RPC credential cache 4663ab9bb72STrond Myklebust */ 4673ab9bb72STrond Myklebust void 4683ab9bb72STrond Myklebust rpcauth_destroy_credcache(struct rpc_auth *auth) 4693ab9bb72STrond Myklebust { 4703ab9bb72STrond Myklebust struct rpc_cred_cache *cache = auth->au_credcache; 4713ab9bb72STrond Myklebust 4723ab9bb72STrond Myklebust if (cache) { 4733ab9bb72STrond Myklebust auth->au_credcache = NULL; 4743ab9bb72STrond Myklebust rpcauth_clear_credcache(cache); 475241269bdSTrond Myklebust kfree(cache->hashtable); 4763ab9bb72STrond Myklebust kfree(cache); 4773ab9bb72STrond Myklebust } 4783ab9bb72STrond Myklebust } 479e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_destroy_credcache); 4803ab9bb72STrond Myklebust 481d2b83141STrond Myklebust 482d2b83141STrond Myklebust #define RPC_AUTH_EXPIRY_MORATORIUM (60 * HZ) 483d2b83141STrond Myklebust 4841da177e4SLinus Torvalds /* 4851da177e4SLinus Torvalds * Remove stale credentials. Avoid sleeping inside the loop. 4861da177e4SLinus Torvalds */ 48770534a73SDave Chinner static long 488f5c2187cSTrond Myklebust rpcauth_prune_expired(struct list_head *free, int nr_to_scan) 4891da177e4SLinus Torvalds { 490eac0d18dSTrond Myklebust struct rpc_cred *cred, *next; 491d2b83141STrond Myklebust unsigned long expired = jiffies - RPC_AUTH_EXPIRY_MORATORIUM; 49270534a73SDave Chinner long freed = 0; 4931da177e4SLinus Torvalds 494eac0d18dSTrond Myklebust list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) { 495eac0d18dSTrond Myklebust 49620673406STrond Myklebust if (nr_to_scan-- == 0) 49720673406STrond Myklebust break; 49895cd6232STrond Myklebust if (atomic_read(&cred->cr_count) > 1) { 49995cd6232STrond Myklebust rpcauth_lru_remove_locked(cred); 50095cd6232STrond Myklebust continue; 50195cd6232STrond Myklebust } 50293a05e65STrond Myklebust /* 50393a05e65STrond Myklebust * Enforce a 60 second garbage collection moratorium 50493a05e65STrond Myklebust * Note that the cred_unused list must be time-ordered. 50593a05e65STrond Myklebust */ 50695cd6232STrond Myklebust if (!time_in_range(cred->cr_expire, expired, jiffies)) 50795cd6232STrond Myklebust continue; 50895cd6232STrond Myklebust if (!rpcauth_unhash_cred(cred)) 509e092bdcdSTrond Myklebust continue; 510eac0d18dSTrond Myklebust 51195cd6232STrond Myklebust rpcauth_lru_remove_locked(cred); 51295cd6232STrond Myklebust freed++; 513e092bdcdSTrond Myklebust list_add_tail(&cred->cr_lru, free); 5141da177e4SLinus Torvalds } 51595cd6232STrond Myklebust return freed ? freed : SHRINK_STOP; 5161da177e4SLinus Torvalds } 517e092bdcdSTrond Myklebust 518bae6746fSTrond Myklebust static unsigned long 519bae6746fSTrond Myklebust rpcauth_cache_do_shrink(int nr_to_scan) 520bae6746fSTrond Myklebust { 521bae6746fSTrond Myklebust LIST_HEAD(free); 522bae6746fSTrond Myklebust unsigned long freed; 523bae6746fSTrond Myklebust 524bae6746fSTrond Myklebust spin_lock(&rpc_credcache_lock); 525bae6746fSTrond Myklebust freed = rpcauth_prune_expired(&free, nr_to_scan); 526bae6746fSTrond Myklebust spin_unlock(&rpc_credcache_lock); 527bae6746fSTrond Myklebust rpcauth_destroy_credlist(&free); 528bae6746fSTrond Myklebust 529bae6746fSTrond Myklebust return freed; 530bae6746fSTrond Myklebust } 531bae6746fSTrond Myklebust 532e092bdcdSTrond Myklebust /* 533f5c2187cSTrond Myklebust * Run memory cache shrinker. 534e092bdcdSTrond Myklebust */ 53570534a73SDave Chinner static unsigned long 53670534a73SDave Chinner rpcauth_cache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) 53770534a73SDave Chinner 538e092bdcdSTrond Myklebust { 53970534a73SDave Chinner if ((sc->gfp_mask & GFP_KERNEL) != GFP_KERNEL) 54070534a73SDave Chinner return SHRINK_STOP; 54170534a73SDave Chinner 54270534a73SDave Chinner /* nothing left, don't come back */ 543f5c2187cSTrond Myklebust if (list_empty(&cred_unused)) 54470534a73SDave Chinner return SHRINK_STOP; 54570534a73SDave Chinner 546bae6746fSTrond Myklebust return rpcauth_cache_do_shrink(sc->nr_to_scan); 54770534a73SDave Chinner } 54870534a73SDave Chinner 54970534a73SDave Chinner static unsigned long 55070534a73SDave Chinner rpcauth_cache_shrink_count(struct shrinker *shrink, struct shrink_control *sc) 55170534a73SDave Chinner 55270534a73SDave Chinner { 5534c3ffd05SNeilBrown return number_cred_unused * sysctl_vfs_cache_pressure / 100; 5541da177e4SLinus Torvalds } 5551da177e4SLinus Torvalds 556bae6746fSTrond Myklebust static void 557bae6746fSTrond Myklebust rpcauth_cache_enforce_limit(void) 558bae6746fSTrond Myklebust { 559bae6746fSTrond Myklebust unsigned long diff; 560bae6746fSTrond Myklebust unsigned int nr_to_scan; 561bae6746fSTrond Myklebust 562bae6746fSTrond Myklebust if (number_cred_unused <= auth_max_cred_cachesize) 563bae6746fSTrond Myklebust return; 564bae6746fSTrond Myklebust diff = number_cred_unused - auth_max_cred_cachesize; 565bae6746fSTrond Myklebust nr_to_scan = 100; 566bae6746fSTrond Myklebust if (diff < nr_to_scan) 567bae6746fSTrond Myklebust nr_to_scan = diff; 568bae6746fSTrond Myklebust rpcauth_cache_do_shrink(nr_to_scan); 569bae6746fSTrond Myklebust } 570bae6746fSTrond Myklebust 5711da177e4SLinus Torvalds /* 5721da177e4SLinus Torvalds * Look up a process' credentials in the authentication cache 5731da177e4SLinus Torvalds */ 5741da177e4SLinus Torvalds struct rpc_cred * 5751da177e4SLinus Torvalds rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, 5763c6e0bc8SJeff Layton int flags, gfp_t gfp) 5771da177e4SLinus Torvalds { 578e092bdcdSTrond Myklebust LIST_HEAD(free); 5791da177e4SLinus Torvalds struct rpc_cred_cache *cache = auth->au_credcache; 58031be5bf1STrond Myklebust struct rpc_cred *cred = NULL, 58131be5bf1STrond Myklebust *entry, *new; 58225337fdcSTrond Myklebust unsigned int nr; 58325337fdcSTrond Myklebust 58466cbd4baSFrank Sorenson nr = auth->au_ops->hash_cred(acred, cache->hashbits); 5851da177e4SLinus Torvalds 58631be5bf1STrond Myklebust rcu_read_lock(); 587b67bfe0dSSasha Levin hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) { 58831be5bf1STrond Myklebust if (!entry->cr_ops->crmatch(acred, entry, flags)) 58931be5bf1STrond Myklebust continue; 590bd956080SNeilBrown if (flags & RPCAUTH_LOOKUP_RCU) { 59107d02a67STrond Myklebust if (test_bit(RPCAUTH_CRED_NEW, &entry->cr_flags) || 59207d02a67STrond Myklebust atomic_read(&entry->cr_count) == 0) 59307d02a67STrond Myklebust continue; 594bd956080SNeilBrown cred = entry; 595bd956080SNeilBrown break; 596bd956080SNeilBrown } 59731be5bf1STrond Myklebust cred = get_rpccred(entry); 59807d02a67STrond Myklebust if (cred) 59931be5bf1STrond Myklebust break; 60031be5bf1STrond Myklebust } 60131be5bf1STrond Myklebust rcu_read_unlock(); 60231be5bf1STrond Myklebust 6039499b434STrond Myklebust if (cred != NULL) 60431be5bf1STrond Myklebust goto found; 60531be5bf1STrond Myklebust 606bd956080SNeilBrown if (flags & RPCAUTH_LOOKUP_RCU) 607bd956080SNeilBrown return ERR_PTR(-ECHILD); 608bd956080SNeilBrown 6093c6e0bc8SJeff Layton new = auth->au_ops->crcreate(auth, acred, flags, gfp); 61031be5bf1STrond Myklebust if (IS_ERR(new)) { 61131be5bf1STrond Myklebust cred = new; 61231be5bf1STrond Myklebust goto out; 61331be5bf1STrond Myklebust } 61431be5bf1STrond Myklebust 6159499b434STrond Myklebust spin_lock(&cache->lock); 616b67bfe0dSSasha Levin hlist_for_each_entry(entry, &cache->hashtable[nr], cr_hash) { 617e092bdcdSTrond Myklebust if (!entry->cr_ops->crmatch(acred, entry, flags)) 618e092bdcdSTrond Myklebust continue; 619e092bdcdSTrond Myklebust cred = get_rpccred(entry); 62007d02a67STrond Myklebust if (cred) 6211da177e4SLinus Torvalds break; 6221da177e4SLinus Torvalds } 62331be5bf1STrond Myklebust if (cred == NULL) { 62431be5bf1STrond Myklebust cred = new; 62531be5bf1STrond Myklebust set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); 62695cd6232STrond Myklebust atomic_inc(&cred->cr_count); 62731be5bf1STrond Myklebust hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]); 62831be5bf1STrond Myklebust } else 629e092bdcdSTrond Myklebust list_add_tail(&new->cr_lru, &free); 6309499b434STrond Myklebust spin_unlock(&cache->lock); 631bae6746fSTrond Myklebust rpcauth_cache_enforce_limit(); 63231be5bf1STrond Myklebust found: 633f64f9e71SJoe Perches if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && 634f64f9e71SJoe Perches cred->cr_ops->cr_init != NULL && 635f64f9e71SJoe Perches !(flags & RPCAUTH_LOOKUP_NEW)) { 636fba3bad4STrond Myklebust int res = cred->cr_ops->cr_init(auth, cred); 637fba3bad4STrond Myklebust if (res < 0) { 638fba3bad4STrond Myklebust put_rpccred(cred); 639fba3bad4STrond Myklebust cred = ERR_PTR(res); 640fba3bad4STrond Myklebust } 6411da177e4SLinus Torvalds } 64231be5bf1STrond Myklebust rpcauth_destroy_credlist(&free); 64331be5bf1STrond Myklebust out: 64431be5bf1STrond Myklebust return cred; 6451da177e4SLinus Torvalds } 646e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache); 6471da177e4SLinus Torvalds 6481da177e4SLinus Torvalds struct rpc_cred * 6498a317760STrond Myklebust rpcauth_lookupcred(struct rpc_auth *auth, int flags) 6501da177e4SLinus Torvalds { 65186a264abSDavid Howells struct auth_cred acred; 6521da177e4SLinus Torvalds struct rpc_cred *ret; 65386a264abSDavid Howells const struct cred *cred = current_cred(); 6541da177e4SLinus Torvalds 6551da177e4SLinus Torvalds dprintk("RPC: looking up %s cred\n", 6561da177e4SLinus Torvalds auth->au_ops->au_name); 65786a264abSDavid Howells 65886a264abSDavid Howells memset(&acred, 0, sizeof(acred)); 65986a264abSDavid Howells acred.uid = cred->fsuid; 66086a264abSDavid Howells acred.gid = cred->fsgid; 661122a8cdaSNeilBrown acred.group_info = cred->group_info; 6628a317760STrond Myklebust ret = auth->au_ops->lookup_cred(auth, &acred, flags); 6631da177e4SLinus Torvalds return ret; 6641da177e4SLinus Torvalds } 66566b06860SAndy Adamson EXPORT_SYMBOL_GPL(rpcauth_lookupcred); 6661da177e4SLinus Torvalds 6675fe4755eSTrond Myklebust void 6685fe4755eSTrond Myklebust rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred, 6695fe4755eSTrond Myklebust struct rpc_auth *auth, const struct rpc_credops *ops) 6705fe4755eSTrond Myklebust { 6715fe4755eSTrond Myklebust INIT_HLIST_NODE(&cred->cr_hash); 672e092bdcdSTrond Myklebust INIT_LIST_HEAD(&cred->cr_lru); 6735fe4755eSTrond Myklebust atomic_set(&cred->cr_count, 1); 6745fe4755eSTrond Myklebust cred->cr_auth = auth; 6755fe4755eSTrond Myklebust cred->cr_ops = ops; 6765fe4755eSTrond Myklebust cred->cr_expire = jiffies; 6775fe4755eSTrond Myklebust cred->cr_uid = acred->uid; 6785fe4755eSTrond Myklebust } 679e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_cred); 6805fe4755eSTrond Myklebust 6818572b8e2STrond Myklebust struct rpc_cred * 6825d351754STrond Myklebust rpcauth_generic_bind_cred(struct rpc_task *task, struct rpc_cred *cred, int lookupflags) 6834ccda2cdSTrond Myklebust { 6844ccda2cdSTrond Myklebust dprintk("RPC: %5u holding %s cred %p\n", task->tk_pid, 6854ccda2cdSTrond Myklebust cred->cr_auth->au_ops->au_name, cred); 6868572b8e2STrond Myklebust return get_rpccred(cred); 6874ccda2cdSTrond Myklebust } 6885c691044STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_generic_bind_cred); 6894ccda2cdSTrond Myklebust 6908572b8e2STrond Myklebust static struct rpc_cred * 6915d351754STrond Myklebust rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags) 6921da177e4SLinus Torvalds { 6931be27f36STrond Myklebust struct rpc_auth *auth = task->tk_client->cl_auth; 6941da177e4SLinus Torvalds struct auth_cred acred = { 695bf37f794SEric W. Biederman .uid = GLOBAL_ROOT_UID, 696bf37f794SEric W. Biederman .gid = GLOBAL_ROOT_GID, 6971da177e4SLinus Torvalds }; 6981da177e4SLinus Torvalds 69946121cf7SChuck Lever dprintk("RPC: %5u looking up %s cred\n", 7001be27f36STrond Myklebust task->tk_pid, task->tk_client->cl_auth->au_ops->au_name); 7018572b8e2STrond Myklebust return auth->au_ops->lookup_cred(auth, &acred, lookupflags); 702af093835STrond Myklebust } 703af093835STrond Myklebust 7048572b8e2STrond Myklebust static struct rpc_cred * 7055d351754STrond Myklebust rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags) 706af093835STrond Myklebust { 707af093835STrond Myklebust struct rpc_auth *auth = task->tk_client->cl_auth; 708af093835STrond Myklebust 709af093835STrond Myklebust dprintk("RPC: %5u looking up %s cred\n", 710af093835STrond Myklebust task->tk_pid, auth->au_ops->au_name); 7118572b8e2STrond Myklebust return rpcauth_lookupcred(auth, lookupflags); 7121da177e4SLinus Torvalds } 7131da177e4SLinus Torvalds 714a17c2153STrond Myklebust static int 7154ccda2cdSTrond Myklebust rpcauth_bindcred(struct rpc_task *task, struct rpc_cred *cred, int flags) 7161da177e4SLinus Torvalds { 717a17c2153STrond Myklebust struct rpc_rqst *req = task->tk_rqstp; 7188572b8e2STrond Myklebust struct rpc_cred *new; 7195d351754STrond Myklebust int lookupflags = 0; 7205d351754STrond Myklebust 7215d351754STrond Myklebust if (flags & RPC_TASK_ASYNC) 7225d351754STrond Myklebust lookupflags |= RPCAUTH_LOOKUP_NEW; 7234ccda2cdSTrond Myklebust if (cred != NULL) 7248572b8e2STrond Myklebust new = cred->cr_ops->crbind(task, cred, lookupflags); 7254ccda2cdSTrond Myklebust else if (flags & RPC_TASK_ROOTCREDS) 7268572b8e2STrond Myklebust new = rpcauth_bind_root_cred(task, lookupflags); 7274ccda2cdSTrond Myklebust else 7288572b8e2STrond Myklebust new = rpcauth_bind_new_cred(task, lookupflags); 7298572b8e2STrond Myklebust if (IS_ERR(new)) 7308572b8e2STrond Myklebust return PTR_ERR(new); 731a17c2153STrond Myklebust put_rpccred(req->rq_cred); 732a17c2153STrond Myklebust req->rq_cred = new; 7338572b8e2STrond Myklebust return 0; 7341da177e4SLinus Torvalds } 7351da177e4SLinus Torvalds 7361da177e4SLinus Torvalds void 7371da177e4SLinus Torvalds put_rpccred(struct rpc_cred *cred) 7381da177e4SLinus Torvalds { 7399a8f6b5eSTrond Myklebust if (cred == NULL) 7409a8f6b5eSTrond Myklebust return; 74195cd6232STrond Myklebust rcu_read_lock(); 742f0380f3dSTrond Myklebust if (atomic_dec_and_test(&cred->cr_count)) 74395cd6232STrond Myklebust goto destroy; 74495cd6232STrond Myklebust if (atomic_read(&cred->cr_count) != 1 || 74595cd6232STrond Myklebust !test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags)) 74695cd6232STrond Myklebust goto out; 747f0380f3dSTrond Myklebust if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) { 748e092bdcdSTrond Myklebust cred->cr_expire = jiffies; 74995cd6232STrond Myklebust rpcauth_lru_add(cred); 75095cd6232STrond Myklebust /* Race breaker */ 75195cd6232STrond Myklebust if (unlikely(!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))) 75295cd6232STrond Myklebust rpcauth_lru_remove(cred); 75395cd6232STrond Myklebust } else if (rpcauth_unhash_cred(cred)) { 75495cd6232STrond Myklebust rpcauth_lru_remove(cred); 75595cd6232STrond Myklebust if (atomic_dec_and_test(&cred->cr_count)) 75695cd6232STrond Myklebust goto destroy; 757f0380f3dSTrond Myklebust } 75895cd6232STrond Myklebust out: 75995cd6232STrond Myklebust rcu_read_unlock(); 760f0380f3dSTrond Myklebust return; 76195cd6232STrond Myklebust destroy: 76295cd6232STrond Myklebust rcu_read_unlock(); 76395cd6232STrond Myklebust cred->cr_ops->crdestroy(cred); 7641da177e4SLinus Torvalds } 765e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(put_rpccred); 7661da177e4SLinus Torvalds 767d8ed029dSAlexey Dobriyan __be32 * 768d8ed029dSAlexey Dobriyan rpcauth_marshcred(struct rpc_task *task, __be32 *p) 7691da177e4SLinus Torvalds { 770a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 7711da177e4SLinus Torvalds 77246121cf7SChuck Lever dprintk("RPC: %5u marshaling %s cred %p\n", 7731be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 7740bbacc40SChuck Lever 7751da177e4SLinus Torvalds return cred->cr_ops->crmarshal(task, p); 7761da177e4SLinus Torvalds } 7771da177e4SLinus Torvalds 778d8ed029dSAlexey Dobriyan __be32 * 779d8ed029dSAlexey Dobriyan rpcauth_checkverf(struct rpc_task *task, __be32 *p) 7801da177e4SLinus Torvalds { 781a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 7821da177e4SLinus Torvalds 78346121cf7SChuck Lever dprintk("RPC: %5u validating %s cred %p\n", 7841be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 7850bbacc40SChuck Lever 7861da177e4SLinus Torvalds return cred->cr_ops->crvalidate(task, p); 7871da177e4SLinus Torvalds } 7881da177e4SLinus Torvalds 7899f06c719SChuck Lever static void rpcauth_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp, 7909f06c719SChuck Lever __be32 *data, void *obj) 7919f06c719SChuck Lever { 7929f06c719SChuck Lever struct xdr_stream xdr; 7939f06c719SChuck Lever 7949f06c719SChuck Lever xdr_init_encode(&xdr, &rqstp->rq_snd_buf, data); 7959f06c719SChuck Lever encode(rqstp, &xdr, obj); 7969f06c719SChuck Lever } 7979f06c719SChuck Lever 7981da177e4SLinus Torvalds int 7999f06c719SChuck Lever rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp, 800d8ed029dSAlexey Dobriyan __be32 *data, void *obj) 8011da177e4SLinus Torvalds { 802a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 8031da177e4SLinus Torvalds 80446121cf7SChuck Lever dprintk("RPC: %5u using %s cred %p to wrap rpc data\n", 8051da177e4SLinus Torvalds task->tk_pid, cred->cr_ops->cr_name, cred); 8061da177e4SLinus Torvalds if (cred->cr_ops->crwrap_req) 8071da177e4SLinus Torvalds return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); 8081da177e4SLinus Torvalds /* By default, we encode the arguments normally. */ 8099f06c719SChuck Lever rpcauth_wrap_req_encode(encode, rqstp, data, obj); 8109f06c719SChuck Lever return 0; 8111da177e4SLinus Torvalds } 8121da177e4SLinus Torvalds 813bf269551SChuck Lever static int 814bf269551SChuck Lever rpcauth_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp, 815bf269551SChuck Lever __be32 *data, void *obj) 816bf269551SChuck Lever { 817bf269551SChuck Lever struct xdr_stream xdr; 818bf269551SChuck Lever 819bf269551SChuck Lever xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, data); 820bf269551SChuck Lever return decode(rqstp, &xdr, obj); 821bf269551SChuck Lever } 822bf269551SChuck Lever 8231da177e4SLinus Torvalds int 824bf269551SChuck Lever rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp, 825d8ed029dSAlexey Dobriyan __be32 *data, void *obj) 8261da177e4SLinus Torvalds { 827a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 8281da177e4SLinus Torvalds 82946121cf7SChuck Lever dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n", 8301da177e4SLinus Torvalds task->tk_pid, cred->cr_ops->cr_name, cred); 8311da177e4SLinus Torvalds if (cred->cr_ops->crunwrap_resp) 8321da177e4SLinus Torvalds return cred->cr_ops->crunwrap_resp(task, decode, rqstp, 8331da177e4SLinus Torvalds data, obj); 8341da177e4SLinus Torvalds /* By default, we decode the arguments normally. */ 835bf269551SChuck Lever return rpcauth_unwrap_req_decode(decode, rqstp, data, obj); 8361da177e4SLinus Torvalds } 8371da177e4SLinus Torvalds 8383021a5bbSTrond Myklebust bool 8393021a5bbSTrond Myklebust rpcauth_xmit_need_reencode(struct rpc_task *task) 8403021a5bbSTrond Myklebust { 8413021a5bbSTrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 8423021a5bbSTrond Myklebust 8433021a5bbSTrond Myklebust if (!cred || !cred->cr_ops->crneed_reencode) 8443021a5bbSTrond Myklebust return false; 8453021a5bbSTrond Myklebust return cred->cr_ops->crneed_reencode(task); 8463021a5bbSTrond Myklebust } 8473021a5bbSTrond Myklebust 8481da177e4SLinus Torvalds int 8491da177e4SLinus Torvalds rpcauth_refreshcred(struct rpc_task *task) 8501da177e4SLinus Torvalds { 8519a84d380STrond Myklebust struct rpc_cred *cred; 8521da177e4SLinus Torvalds int err; 8531da177e4SLinus Torvalds 854a17c2153STrond Myklebust cred = task->tk_rqstp->rq_cred; 855a17c2153STrond Myklebust if (cred == NULL) { 856a17c2153STrond Myklebust err = rpcauth_bindcred(task, task->tk_msg.rpc_cred, task->tk_flags); 857a17c2153STrond Myklebust if (err < 0) 858a17c2153STrond Myklebust goto out; 859a17c2153STrond Myklebust cred = task->tk_rqstp->rq_cred; 860f81c6224SJoe Perches } 86146121cf7SChuck Lever dprintk("RPC: %5u refreshing %s cred %p\n", 8621be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 8630bbacc40SChuck Lever 8641da177e4SLinus Torvalds err = cred->cr_ops->crrefresh(task); 865a17c2153STrond Myklebust out: 8661da177e4SLinus Torvalds if (err < 0) 8671da177e4SLinus Torvalds task->tk_status = err; 8681da177e4SLinus Torvalds return err; 8691da177e4SLinus Torvalds } 8701da177e4SLinus Torvalds 8711da177e4SLinus Torvalds void 8721da177e4SLinus Torvalds rpcauth_invalcred(struct rpc_task *task) 8731da177e4SLinus Torvalds { 874a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 875fc432dd9STrond Myklebust 87646121cf7SChuck Lever dprintk("RPC: %5u invalidating %s cred %p\n", 8771be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 878fc432dd9STrond Myklebust if (cred) 879fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 8801da177e4SLinus Torvalds } 8811da177e4SLinus Torvalds 8821da177e4SLinus Torvalds int 8831da177e4SLinus Torvalds rpcauth_uptodatecred(struct rpc_task *task) 8841da177e4SLinus Torvalds { 885a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 886fc432dd9STrond Myklebust 887fc432dd9STrond Myklebust return cred == NULL || 888fc432dd9STrond Myklebust test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0; 8891da177e4SLinus Torvalds } 890f5c2187cSTrond Myklebust 8918e1f936bSRusty Russell static struct shrinker rpc_cred_shrinker = { 89270534a73SDave Chinner .count_objects = rpcauth_cache_shrink_count, 89370534a73SDave Chinner .scan_objects = rpcauth_cache_shrink_scan, 8948e1f936bSRusty Russell .seeks = DEFAULT_SEEKS, 8958e1f936bSRusty Russell }; 896f5c2187cSTrond Myklebust 8975d8d9a4dSTrond Myklebust int __init rpcauth_init_module(void) 898f5c2187cSTrond Myklebust { 8995d8d9a4dSTrond Myklebust int err; 9005d8d9a4dSTrond Myklebust 9015d8d9a4dSTrond Myklebust err = rpc_init_authunix(); 9025d8d9a4dSTrond Myklebust if (err < 0) 9035d8d9a4dSTrond Myklebust goto out1; 9045d8d9a4dSTrond Myklebust err = rpc_init_generic_auth(); 9055d8d9a4dSTrond Myklebust if (err < 0) 9065d8d9a4dSTrond Myklebust goto out2; 9072864486bSKinglong Mee err = register_shrinker(&rpc_cred_shrinker); 9082864486bSKinglong Mee if (err < 0) 9092864486bSKinglong Mee goto out3; 9105d8d9a4dSTrond Myklebust return 0; 9112864486bSKinglong Mee out3: 9122864486bSKinglong Mee rpc_destroy_generic_auth(); 9135d8d9a4dSTrond Myklebust out2: 9145d8d9a4dSTrond Myklebust rpc_destroy_authunix(); 9155d8d9a4dSTrond Myklebust out1: 9165d8d9a4dSTrond Myklebust return err; 917f5c2187cSTrond Myklebust } 918f5c2187cSTrond Myklebust 919c135e84aSStephen Rothwell void rpcauth_remove_module(void) 920f5c2187cSTrond Myklebust { 9215d8d9a4dSTrond Myklebust rpc_destroy_authunix(); 9225d8d9a4dSTrond Myklebust rpc_destroy_generic_auth(); 9238e1f936bSRusty Russell unregister_shrinker(&rpc_cred_shrinker); 924f5c2187cSTrond Myklebust } 925