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 33fc1b356fSTrond Myklebust static DEFINE_SPINLOCK(rpc_authflavor_lock); 34f1c0a861STrond Myklebust static const struct rpc_authops *auth_flavors[RPC_AUTH_MAXFLAVOR] = { 351da177e4SLinus Torvalds &authnull_ops, /* AUTH_NULL */ 361da177e4SLinus Torvalds &authunix_ops, /* AUTH_UNIX */ 371da177e4SLinus Torvalds NULL, /* others can be loadable modules */ 381da177e4SLinus Torvalds }; 391da177e4SLinus Torvalds 40e092bdcdSTrond Myklebust static LIST_HEAD(cred_unused); 41f5c2187cSTrond Myklebust static unsigned long number_cred_unused; 42e092bdcdSTrond Myklebust 43db5fe265SMiquel van Smoorenburg #define MAX_HASHTABLE_BITS (14) 448e4e15d4SStephen Rothwell static int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp) 45241269bdSTrond Myklebust { 46241269bdSTrond Myklebust unsigned long num; 47241269bdSTrond Myklebust unsigned int nbits; 48241269bdSTrond Myklebust int ret; 49241269bdSTrond Myklebust 50241269bdSTrond Myklebust if (!val) 51241269bdSTrond Myklebust goto out_inval; 5200cfaa94SDaniel Walter ret = kstrtoul(val, 0, &num); 53241269bdSTrond Myklebust if (ret == -EINVAL) 54241269bdSTrond Myklebust goto out_inval; 5534ae685cSFrank Sorenson nbits = fls(num - 1); 56241269bdSTrond Myklebust if (nbits > MAX_HASHTABLE_BITS || nbits < 2) 57241269bdSTrond Myklebust goto out_inval; 58241269bdSTrond Myklebust *(unsigned int *)kp->arg = nbits; 59241269bdSTrond Myklebust return 0; 60241269bdSTrond Myklebust out_inval: 61241269bdSTrond Myklebust return -EINVAL; 62241269bdSTrond Myklebust } 63241269bdSTrond Myklebust 648e4e15d4SStephen Rothwell static int param_get_hashtbl_sz(char *buffer, const struct kernel_param *kp) 65241269bdSTrond Myklebust { 66241269bdSTrond Myklebust unsigned int nbits; 67241269bdSTrond Myklebust 68241269bdSTrond Myklebust nbits = *(unsigned int *)kp->arg; 69241269bdSTrond Myklebust return sprintf(buffer, "%u", 1U << nbits); 70241269bdSTrond Myklebust } 71241269bdSTrond Myklebust 72241269bdSTrond Myklebust #define param_check_hashtbl_sz(name, p) __param_check(name, p, unsigned int); 73241269bdSTrond Myklebust 749c27847dSLuis R. Rodriguez static const struct kernel_param_ops param_ops_hashtbl_sz = { 758e4e15d4SStephen Rothwell .set = param_set_hashtbl_sz, 768e4e15d4SStephen Rothwell .get = param_get_hashtbl_sz, 778e4e15d4SStephen Rothwell }; 788e4e15d4SStephen Rothwell 79241269bdSTrond Myklebust module_param_named(auth_hashtable_size, auth_hashbits, hashtbl_sz, 0644); 80241269bdSTrond Myklebust MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size"); 81241269bdSTrond Myklebust 82bae6746fSTrond Myklebust static unsigned long auth_max_cred_cachesize = ULONG_MAX; 83bae6746fSTrond Myklebust module_param(auth_max_cred_cachesize, ulong, 0644); 84bae6746fSTrond Myklebust MODULE_PARM_DESC(auth_max_cred_cachesize, "RPC credential maximum total cache size"); 85bae6746fSTrond Myklebust 861da177e4SLinus Torvalds static u32 871da177e4SLinus Torvalds pseudoflavor_to_flavor(u32 flavor) { 881c74a244SChuck Lever if (flavor > RPC_AUTH_MAXFLAVOR) 891da177e4SLinus Torvalds return RPC_AUTH_GSS; 901da177e4SLinus Torvalds return flavor; 911da177e4SLinus Torvalds } 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds int 94f1c0a861STrond Myklebust rpcauth_register(const struct rpc_authops *ops) 951da177e4SLinus Torvalds { 961da177e4SLinus Torvalds rpc_authflavor_t flavor; 97fc1b356fSTrond Myklebust int ret = -EPERM; 981da177e4SLinus Torvalds 991da177e4SLinus Torvalds if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR) 1001da177e4SLinus Torvalds return -EINVAL; 101fc1b356fSTrond Myklebust spin_lock(&rpc_authflavor_lock); 102fc1b356fSTrond Myklebust if (auth_flavors[flavor] == NULL) { 1031da177e4SLinus Torvalds auth_flavors[flavor] = ops; 104fc1b356fSTrond Myklebust ret = 0; 105fc1b356fSTrond Myklebust } 106fc1b356fSTrond Myklebust spin_unlock(&rpc_authflavor_lock); 107fc1b356fSTrond Myklebust return ret; 1081da177e4SLinus Torvalds } 109e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_register); 1101da177e4SLinus Torvalds 1111da177e4SLinus Torvalds int 112f1c0a861STrond Myklebust rpcauth_unregister(const struct rpc_authops *ops) 1131da177e4SLinus Torvalds { 1141da177e4SLinus Torvalds rpc_authflavor_t flavor; 115fc1b356fSTrond Myklebust int ret = -EPERM; 1161da177e4SLinus Torvalds 1171da177e4SLinus Torvalds if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR) 1181da177e4SLinus Torvalds return -EINVAL; 119fc1b356fSTrond Myklebust spin_lock(&rpc_authflavor_lock); 120fc1b356fSTrond Myklebust if (auth_flavors[flavor] == ops) { 1211da177e4SLinus Torvalds auth_flavors[flavor] = NULL; 122fc1b356fSTrond Myklebust ret = 0; 123fc1b356fSTrond Myklebust } 124fc1b356fSTrond Myklebust spin_unlock(&rpc_authflavor_lock); 125fc1b356fSTrond Myklebust return ret; 1261da177e4SLinus Torvalds } 127e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_unregister); 1281da177e4SLinus Torvalds 1296a1a1e34SChuck Lever /** 1309568c5e9SChuck Lever * rpcauth_get_pseudoflavor - check if security flavor is supported 1319568c5e9SChuck Lever * @flavor: a security flavor 1329568c5e9SChuck Lever * @info: a GSS mech OID, quality of protection, and service value 1339568c5e9SChuck Lever * 1349568c5e9SChuck Lever * Verifies that an appropriate kernel module is available or already loaded. 1359568c5e9SChuck Lever * Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is 1369568c5e9SChuck Lever * not supported locally. 1379568c5e9SChuck Lever */ 1389568c5e9SChuck Lever rpc_authflavor_t 1399568c5e9SChuck Lever rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info) 1409568c5e9SChuck Lever { 1419568c5e9SChuck Lever const struct rpc_authops *ops; 1429568c5e9SChuck Lever rpc_authflavor_t pseudoflavor; 1439568c5e9SChuck Lever 1449568c5e9SChuck Lever ops = auth_flavors[flavor]; 1459568c5e9SChuck Lever if (ops == NULL) 1469568c5e9SChuck Lever request_module("rpc-auth-%u", flavor); 1479568c5e9SChuck Lever spin_lock(&rpc_authflavor_lock); 1489568c5e9SChuck Lever ops = auth_flavors[flavor]; 1499568c5e9SChuck Lever if (ops == NULL || !try_module_get(ops->owner)) { 1509568c5e9SChuck Lever spin_unlock(&rpc_authflavor_lock); 1519568c5e9SChuck Lever return RPC_AUTH_MAXFLAVOR; 1529568c5e9SChuck Lever } 1539568c5e9SChuck Lever spin_unlock(&rpc_authflavor_lock); 1549568c5e9SChuck Lever 1559568c5e9SChuck Lever pseudoflavor = flavor; 1569568c5e9SChuck Lever if (ops->info2flavor != NULL) 1579568c5e9SChuck Lever pseudoflavor = ops->info2flavor(info); 1589568c5e9SChuck Lever 1599568c5e9SChuck Lever module_put(ops->owner); 1609568c5e9SChuck Lever return pseudoflavor; 1619568c5e9SChuck Lever } 1629568c5e9SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor); 1639568c5e9SChuck Lever 1649568c5e9SChuck Lever /** 165a77c806fSChuck Lever * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor 166a77c806fSChuck Lever * @pseudoflavor: GSS pseudoflavor to match 167a77c806fSChuck Lever * @info: rpcsec_gss_info structure to fill in 168a77c806fSChuck Lever * 169a77c806fSChuck Lever * Returns zero and fills in "info" if pseudoflavor matches a 170a77c806fSChuck Lever * supported mechanism. 171a77c806fSChuck Lever */ 172a77c806fSChuck Lever int 173a77c806fSChuck Lever rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info) 174a77c806fSChuck Lever { 175a77c806fSChuck Lever rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor); 176a77c806fSChuck Lever const struct rpc_authops *ops; 177a77c806fSChuck Lever int result; 178a77c806fSChuck Lever 1791c74a244SChuck Lever if (flavor >= RPC_AUTH_MAXFLAVOR) 1801c74a244SChuck Lever return -EINVAL; 1811c74a244SChuck Lever 182a77c806fSChuck Lever ops = auth_flavors[flavor]; 183a77c806fSChuck Lever if (ops == NULL) 184a77c806fSChuck Lever request_module("rpc-auth-%u", flavor); 185a77c806fSChuck Lever spin_lock(&rpc_authflavor_lock); 186a77c806fSChuck Lever ops = auth_flavors[flavor]; 187a77c806fSChuck Lever if (ops == NULL || !try_module_get(ops->owner)) { 188a77c806fSChuck Lever spin_unlock(&rpc_authflavor_lock); 189a77c806fSChuck Lever return -ENOENT; 190a77c806fSChuck Lever } 191a77c806fSChuck Lever spin_unlock(&rpc_authflavor_lock); 192a77c806fSChuck Lever 193a77c806fSChuck Lever result = -ENOENT; 194a77c806fSChuck Lever if (ops->flavor2info != NULL) 195a77c806fSChuck Lever result = ops->flavor2info(pseudoflavor, info); 196a77c806fSChuck Lever 197a77c806fSChuck Lever module_put(ops->owner); 198a77c806fSChuck Lever return result; 199a77c806fSChuck Lever } 200a77c806fSChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo); 201a77c806fSChuck Lever 202a77c806fSChuck Lever /** 2036a1a1e34SChuck Lever * rpcauth_list_flavors - discover registered flavors and pseudoflavors 2046a1a1e34SChuck Lever * @array: array to fill in 2056a1a1e34SChuck Lever * @size: size of "array" 2066a1a1e34SChuck Lever * 2076a1a1e34SChuck Lever * Returns the number of array items filled in, or a negative errno. 2086a1a1e34SChuck Lever * 2096a1a1e34SChuck Lever * The returned array is not sorted by any policy. Callers should not 2106a1a1e34SChuck Lever * rely on the order of the items in the returned array. 2116a1a1e34SChuck Lever */ 2126a1a1e34SChuck Lever int 2136a1a1e34SChuck Lever rpcauth_list_flavors(rpc_authflavor_t *array, int size) 2146a1a1e34SChuck Lever { 2156a1a1e34SChuck Lever rpc_authflavor_t flavor; 2166a1a1e34SChuck Lever int result = 0; 2176a1a1e34SChuck Lever 2186a1a1e34SChuck Lever spin_lock(&rpc_authflavor_lock); 2196a1a1e34SChuck Lever for (flavor = 0; flavor < RPC_AUTH_MAXFLAVOR; flavor++) { 2206a1a1e34SChuck Lever const struct rpc_authops *ops = auth_flavors[flavor]; 2216a1a1e34SChuck Lever rpc_authflavor_t pseudos[4]; 2226a1a1e34SChuck Lever int i, len; 2236a1a1e34SChuck Lever 2246a1a1e34SChuck Lever if (result >= size) { 2256a1a1e34SChuck Lever result = -ENOMEM; 2266a1a1e34SChuck Lever break; 2276a1a1e34SChuck Lever } 2286a1a1e34SChuck Lever 2296a1a1e34SChuck Lever if (ops == NULL) 2306a1a1e34SChuck Lever continue; 2316a1a1e34SChuck Lever if (ops->list_pseudoflavors == NULL) { 2326a1a1e34SChuck Lever array[result++] = ops->au_flavor; 2336a1a1e34SChuck Lever continue; 2346a1a1e34SChuck Lever } 2356a1a1e34SChuck Lever len = ops->list_pseudoflavors(pseudos, ARRAY_SIZE(pseudos)); 2366a1a1e34SChuck Lever if (len < 0) { 2376a1a1e34SChuck Lever result = len; 2386a1a1e34SChuck Lever break; 2396a1a1e34SChuck Lever } 2406a1a1e34SChuck Lever for (i = 0; i < len; i++) { 2416a1a1e34SChuck Lever if (result >= size) { 2426a1a1e34SChuck Lever result = -ENOMEM; 2436a1a1e34SChuck Lever break; 2446a1a1e34SChuck Lever } 2456a1a1e34SChuck Lever array[result++] = pseudos[i]; 2466a1a1e34SChuck Lever } 2476a1a1e34SChuck Lever } 2486a1a1e34SChuck Lever spin_unlock(&rpc_authflavor_lock); 2496a1a1e34SChuck Lever 2506a1a1e34SChuck Lever dprintk("RPC: %s returns %d\n", __func__, result); 2516a1a1e34SChuck Lever return result; 2526a1a1e34SChuck Lever } 2536a1a1e34SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_list_flavors); 2546a1a1e34SChuck Lever 2551da177e4SLinus Torvalds struct rpc_auth * 25682b98ca5SSargun Dhillon rpcauth_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) 2571da177e4SLinus Torvalds { 2581da177e4SLinus Torvalds struct rpc_auth *auth; 259f1c0a861STrond Myklebust const struct rpc_authops *ops; 260c2190661STrond Myklebust u32 flavor = pseudoflavor_to_flavor(args->pseudoflavor); 2611da177e4SLinus Torvalds 262f344f6dfSOlaf Kirch auth = ERR_PTR(-EINVAL); 263f344f6dfSOlaf Kirch if (flavor >= RPC_AUTH_MAXFLAVOR) 264f344f6dfSOlaf Kirch goto out; 265f344f6dfSOlaf Kirch 266f344f6dfSOlaf Kirch if ((ops = auth_flavors[flavor]) == NULL) 267f344f6dfSOlaf Kirch request_module("rpc-auth-%u", flavor); 268fc1b356fSTrond Myklebust spin_lock(&rpc_authflavor_lock); 269fc1b356fSTrond Myklebust ops = auth_flavors[flavor]; 270fc1b356fSTrond Myklebust if (ops == NULL || !try_module_get(ops->owner)) { 271fc1b356fSTrond Myklebust spin_unlock(&rpc_authflavor_lock); 272f344f6dfSOlaf Kirch goto out; 273fc1b356fSTrond Myklebust } 274fc1b356fSTrond Myklebust spin_unlock(&rpc_authflavor_lock); 275c2190661STrond Myklebust auth = ops->create(args, clnt); 276fc1b356fSTrond Myklebust module_put(ops->owner); 2776a19275aSJ. Bruce Fields if (IS_ERR(auth)) 2786a19275aSJ. Bruce Fields return auth; 2791da177e4SLinus Torvalds if (clnt->cl_auth) 280de7a8ce3STrond Myklebust rpcauth_release(clnt->cl_auth); 2811da177e4SLinus Torvalds clnt->cl_auth = auth; 282f344f6dfSOlaf Kirch 283f344f6dfSOlaf Kirch out: 2841da177e4SLinus Torvalds return auth; 2851da177e4SLinus Torvalds } 286e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_create); 2871da177e4SLinus Torvalds 2881da177e4SLinus Torvalds void 289de7a8ce3STrond Myklebust rpcauth_release(struct rpc_auth *auth) 2901da177e4SLinus Torvalds { 2911da177e4SLinus Torvalds if (!atomic_dec_and_test(&auth->au_count)) 2921da177e4SLinus Torvalds return; 2931da177e4SLinus Torvalds auth->au_ops->destroy(auth); 2941da177e4SLinus Torvalds } 2951da177e4SLinus Torvalds 2961da177e4SLinus Torvalds static DEFINE_SPINLOCK(rpc_credcache_lock); 2971da177e4SLinus Torvalds 29831be5bf1STrond Myklebust static void 29931be5bf1STrond Myklebust rpcauth_unhash_cred_locked(struct rpc_cred *cred) 30031be5bf1STrond Myklebust { 30131be5bf1STrond Myklebust hlist_del_rcu(&cred->cr_hash); 3024e857c58SPeter Zijlstra smp_mb__before_atomic(); 30331be5bf1STrond Myklebust clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); 30431be5bf1STrond Myklebust } 30531be5bf1STrond Myklebust 306f0380f3dSTrond Myklebust static int 3079499b434STrond Myklebust rpcauth_unhash_cred(struct rpc_cred *cred) 3089499b434STrond Myklebust { 3099499b434STrond Myklebust spinlock_t *cache_lock; 310f0380f3dSTrond Myklebust int ret; 3119499b434STrond Myklebust 3129499b434STrond Myklebust cache_lock = &cred->cr_auth->au_credcache->lock; 3139499b434STrond Myklebust spin_lock(cache_lock); 314f0380f3dSTrond Myklebust ret = atomic_read(&cred->cr_count) == 0; 315f0380f3dSTrond Myklebust if (ret) 3169499b434STrond Myklebust rpcauth_unhash_cred_locked(cred); 3179499b434STrond Myklebust spin_unlock(cache_lock); 318f0380f3dSTrond Myklebust return ret; 3199499b434STrond Myklebust } 3209499b434STrond Myklebust 3211da177e4SLinus Torvalds /* 3221da177e4SLinus Torvalds * Initialize RPC credential cache 3231da177e4SLinus Torvalds */ 3241da177e4SLinus Torvalds int 325f5c2187cSTrond Myklebust rpcauth_init_credcache(struct rpc_auth *auth) 3261da177e4SLinus Torvalds { 3271da177e4SLinus Torvalds struct rpc_cred_cache *new; 328988664a0STrond Myklebust unsigned int hashsize; 3291da177e4SLinus Torvalds 3308b3a7005SKris Katterjohn new = kmalloc(sizeof(*new), GFP_KERNEL); 3311da177e4SLinus Torvalds if (!new) 332241269bdSTrond Myklebust goto out_nocache; 333241269bdSTrond Myklebust new->hashbits = auth_hashbits; 334988664a0STrond Myklebust hashsize = 1U << new->hashbits; 335241269bdSTrond Myklebust new->hashtable = kcalloc(hashsize, sizeof(new->hashtable[0]), GFP_KERNEL); 336241269bdSTrond Myklebust if (!new->hashtable) 337241269bdSTrond Myklebust goto out_nohashtbl; 3389499b434STrond Myklebust spin_lock_init(&new->lock); 3391da177e4SLinus Torvalds auth->au_credcache = new; 3401da177e4SLinus Torvalds return 0; 341241269bdSTrond Myklebust out_nohashtbl: 342241269bdSTrond Myklebust kfree(new); 343241269bdSTrond Myklebust out_nocache: 344241269bdSTrond Myklebust return -ENOMEM; 3451da177e4SLinus Torvalds } 346e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_credcache); 3471da177e4SLinus Torvalds 3481da177e4SLinus Torvalds /* 3494de6caa2SAndy Adamson * Setup a credential key lifetime timeout notification 3504de6caa2SAndy Adamson */ 3514de6caa2SAndy Adamson int 3524de6caa2SAndy Adamson rpcauth_key_timeout_notify(struct rpc_auth *auth, struct rpc_cred *cred) 3534de6caa2SAndy Adamson { 3544de6caa2SAndy Adamson if (!cred->cr_auth->au_ops->key_timeout) 3554de6caa2SAndy Adamson return 0; 3564de6caa2SAndy Adamson return cred->cr_auth->au_ops->key_timeout(auth, cred); 3574de6caa2SAndy Adamson } 3584de6caa2SAndy Adamson EXPORT_SYMBOL_GPL(rpcauth_key_timeout_notify); 3594de6caa2SAndy Adamson 3604de6caa2SAndy Adamson bool 361ce52914eSScott Mayhew rpcauth_cred_key_to_expire(struct rpc_auth *auth, struct rpc_cred *cred) 3624de6caa2SAndy Adamson { 363ce52914eSScott Mayhew if (auth->au_flags & RPCAUTH_AUTH_NO_CRKEY_TIMEOUT) 364ce52914eSScott Mayhew return false; 3654de6caa2SAndy Adamson if (!cred->cr_ops->crkey_to_expire) 3664de6caa2SAndy Adamson return false; 3674de6caa2SAndy Adamson return cred->cr_ops->crkey_to_expire(cred); 3684de6caa2SAndy Adamson } 3694de6caa2SAndy Adamson EXPORT_SYMBOL_GPL(rpcauth_cred_key_to_expire); 3704de6caa2SAndy Adamson 371a0337d1dSJeff Layton char * 372a0337d1dSJeff Layton rpcauth_stringify_acceptor(struct rpc_cred *cred) 373a0337d1dSJeff Layton { 374a0337d1dSJeff Layton if (!cred->cr_ops->crstringify_acceptor) 375a0337d1dSJeff Layton return NULL; 376a0337d1dSJeff Layton return cred->cr_ops->crstringify_acceptor(cred); 377a0337d1dSJeff Layton } 378a0337d1dSJeff Layton EXPORT_SYMBOL_GPL(rpcauth_stringify_acceptor); 379a0337d1dSJeff Layton 3804de6caa2SAndy Adamson /* 3811da177e4SLinus Torvalds * Destroy a list of credentials 3821da177e4SLinus Torvalds */ 3831da177e4SLinus Torvalds static inline 384e092bdcdSTrond Myklebust void rpcauth_destroy_credlist(struct list_head *head) 3851da177e4SLinus Torvalds { 3861da177e4SLinus Torvalds struct rpc_cred *cred; 3871da177e4SLinus Torvalds 388e092bdcdSTrond Myklebust while (!list_empty(head)) { 389e092bdcdSTrond Myklebust cred = list_entry(head->next, struct rpc_cred, cr_lru); 390e092bdcdSTrond Myklebust list_del_init(&cred->cr_lru); 3911da177e4SLinus Torvalds put_rpccred(cred); 3921da177e4SLinus Torvalds } 3931da177e4SLinus Torvalds } 3941da177e4SLinus Torvalds 3951da177e4SLinus Torvalds /* 3961da177e4SLinus Torvalds * Clear the RPC credential cache, and delete those credentials 3971da177e4SLinus Torvalds * that are not referenced. 3981da177e4SLinus Torvalds */ 3991da177e4SLinus Torvalds void 4003ab9bb72STrond Myklebust rpcauth_clear_credcache(struct rpc_cred_cache *cache) 4011da177e4SLinus Torvalds { 402e092bdcdSTrond Myklebust LIST_HEAD(free); 403e092bdcdSTrond Myklebust struct hlist_head *head; 4041da177e4SLinus Torvalds struct rpc_cred *cred; 405988664a0STrond Myklebust unsigned int hashsize = 1U << cache->hashbits; 4061da177e4SLinus Torvalds int i; 4071da177e4SLinus Torvalds 4081da177e4SLinus Torvalds spin_lock(&rpc_credcache_lock); 4099499b434STrond Myklebust spin_lock(&cache->lock); 410988664a0STrond Myklebust for (i = 0; i < hashsize; i++) { 411e092bdcdSTrond Myklebust head = &cache->hashtable[i]; 412e092bdcdSTrond Myklebust while (!hlist_empty(head)) { 413e092bdcdSTrond Myklebust cred = hlist_entry(head->first, struct rpc_cred, cr_hash); 414e092bdcdSTrond Myklebust get_rpccred(cred); 415f5c2187cSTrond Myklebust if (!list_empty(&cred->cr_lru)) { 416f5c2187cSTrond Myklebust list_del(&cred->cr_lru); 417f5c2187cSTrond Myklebust number_cred_unused--; 418f5c2187cSTrond Myklebust } 419f5c2187cSTrond Myklebust list_add_tail(&cred->cr_lru, &free); 42031be5bf1STrond Myklebust rpcauth_unhash_cred_locked(cred); 4211da177e4SLinus Torvalds } 4221da177e4SLinus Torvalds } 4239499b434STrond Myklebust spin_unlock(&cache->lock); 4241da177e4SLinus Torvalds spin_unlock(&rpc_credcache_lock); 4251da177e4SLinus Torvalds rpcauth_destroy_credlist(&free); 4261da177e4SLinus Torvalds } 4271da177e4SLinus Torvalds 4283ab9bb72STrond Myklebust /* 4293ab9bb72STrond Myklebust * Destroy the RPC credential cache 4303ab9bb72STrond Myklebust */ 4313ab9bb72STrond Myklebust void 4323ab9bb72STrond Myklebust rpcauth_destroy_credcache(struct rpc_auth *auth) 4333ab9bb72STrond Myklebust { 4343ab9bb72STrond Myklebust struct rpc_cred_cache *cache = auth->au_credcache; 4353ab9bb72STrond Myklebust 4363ab9bb72STrond Myklebust if (cache) { 4373ab9bb72STrond Myklebust auth->au_credcache = NULL; 4383ab9bb72STrond Myklebust rpcauth_clear_credcache(cache); 439241269bdSTrond Myklebust kfree(cache->hashtable); 4403ab9bb72STrond Myklebust kfree(cache); 4413ab9bb72STrond Myklebust } 4423ab9bb72STrond Myklebust } 443e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_destroy_credcache); 4443ab9bb72STrond Myklebust 445d2b83141STrond Myklebust 446d2b83141STrond Myklebust #define RPC_AUTH_EXPIRY_MORATORIUM (60 * HZ) 447d2b83141STrond Myklebust 4481da177e4SLinus Torvalds /* 4491da177e4SLinus Torvalds * Remove stale credentials. Avoid sleeping inside the loop. 4501da177e4SLinus Torvalds */ 45170534a73SDave Chinner static long 452f5c2187cSTrond Myklebust rpcauth_prune_expired(struct list_head *free, int nr_to_scan) 4531da177e4SLinus Torvalds { 4549499b434STrond Myklebust spinlock_t *cache_lock; 455eac0d18dSTrond Myklebust struct rpc_cred *cred, *next; 456d2b83141STrond Myklebust unsigned long expired = jiffies - RPC_AUTH_EXPIRY_MORATORIUM; 45770534a73SDave Chinner long freed = 0; 4581da177e4SLinus Torvalds 459eac0d18dSTrond Myklebust list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) { 460eac0d18dSTrond Myklebust 46120673406STrond Myklebust if (nr_to_scan-- == 0) 46220673406STrond Myklebust break; 46393a05e65STrond Myklebust /* 46493a05e65STrond Myklebust * Enforce a 60 second garbage collection moratorium 46593a05e65STrond Myklebust * Note that the cred_unused list must be time-ordered. 46693a05e65STrond Myklebust */ 4673d7b0894STrond Myklebust if (time_in_range(cred->cr_expire, expired, jiffies) && 4684c3ffd05SNeilBrown test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) { 4694c3ffd05SNeilBrown freed = SHRINK_STOP; 47070534a73SDave Chinner break; 4714c3ffd05SNeilBrown } 472eac0d18dSTrond Myklebust 473e092bdcdSTrond Myklebust list_del_init(&cred->cr_lru); 474f5c2187cSTrond Myklebust number_cred_unused--; 47570534a73SDave Chinner freed++; 476e092bdcdSTrond Myklebust if (atomic_read(&cred->cr_count) != 0) 477e092bdcdSTrond Myklebust continue; 478eac0d18dSTrond Myklebust 4799499b434STrond Myklebust cache_lock = &cred->cr_auth->au_credcache->lock; 4809499b434STrond Myklebust spin_lock(cache_lock); 4819499b434STrond Myklebust if (atomic_read(&cred->cr_count) == 0) { 482e092bdcdSTrond Myklebust get_rpccred(cred); 483e092bdcdSTrond Myklebust list_add_tail(&cred->cr_lru, free); 48431be5bf1STrond Myklebust rpcauth_unhash_cred_locked(cred); 4851da177e4SLinus Torvalds } 4869499b434STrond Myklebust spin_unlock(cache_lock); 4879499b434STrond Myklebust } 48870534a73SDave Chinner return freed; 4891da177e4SLinus Torvalds } 490e092bdcdSTrond Myklebust 491bae6746fSTrond Myklebust static unsigned long 492bae6746fSTrond Myklebust rpcauth_cache_do_shrink(int nr_to_scan) 493bae6746fSTrond Myklebust { 494bae6746fSTrond Myklebust LIST_HEAD(free); 495bae6746fSTrond Myklebust unsigned long freed; 496bae6746fSTrond Myklebust 497bae6746fSTrond Myklebust spin_lock(&rpc_credcache_lock); 498bae6746fSTrond Myklebust freed = rpcauth_prune_expired(&free, nr_to_scan); 499bae6746fSTrond Myklebust spin_unlock(&rpc_credcache_lock); 500bae6746fSTrond Myklebust rpcauth_destroy_credlist(&free); 501bae6746fSTrond Myklebust 502bae6746fSTrond Myklebust return freed; 503bae6746fSTrond Myklebust } 504bae6746fSTrond Myklebust 505e092bdcdSTrond Myklebust /* 506f5c2187cSTrond Myklebust * Run memory cache shrinker. 507e092bdcdSTrond Myklebust */ 50870534a73SDave Chinner static unsigned long 50970534a73SDave Chinner rpcauth_cache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) 51070534a73SDave Chinner 511e092bdcdSTrond Myklebust { 51270534a73SDave Chinner if ((sc->gfp_mask & GFP_KERNEL) != GFP_KERNEL) 51370534a73SDave Chinner return SHRINK_STOP; 51470534a73SDave Chinner 51570534a73SDave Chinner /* nothing left, don't come back */ 516f5c2187cSTrond Myklebust if (list_empty(&cred_unused)) 51770534a73SDave Chinner return SHRINK_STOP; 51870534a73SDave Chinner 519bae6746fSTrond Myklebust return rpcauth_cache_do_shrink(sc->nr_to_scan); 52070534a73SDave Chinner } 52170534a73SDave Chinner 52270534a73SDave Chinner static unsigned long 52370534a73SDave Chinner rpcauth_cache_shrink_count(struct shrinker *shrink, struct shrink_control *sc) 52470534a73SDave Chinner 52570534a73SDave Chinner { 5264c3ffd05SNeilBrown return number_cred_unused * sysctl_vfs_cache_pressure / 100; 5271da177e4SLinus Torvalds } 5281da177e4SLinus Torvalds 529bae6746fSTrond Myklebust static void 530bae6746fSTrond Myklebust rpcauth_cache_enforce_limit(void) 531bae6746fSTrond Myklebust { 532bae6746fSTrond Myklebust unsigned long diff; 533bae6746fSTrond Myklebust unsigned int nr_to_scan; 534bae6746fSTrond Myklebust 535bae6746fSTrond Myklebust if (number_cred_unused <= auth_max_cred_cachesize) 536bae6746fSTrond Myklebust return; 537bae6746fSTrond Myklebust diff = number_cred_unused - auth_max_cred_cachesize; 538bae6746fSTrond Myklebust nr_to_scan = 100; 539bae6746fSTrond Myklebust if (diff < nr_to_scan) 540bae6746fSTrond Myklebust nr_to_scan = diff; 541bae6746fSTrond Myklebust rpcauth_cache_do_shrink(nr_to_scan); 542bae6746fSTrond Myklebust } 543bae6746fSTrond Myklebust 5441da177e4SLinus Torvalds /* 5451da177e4SLinus Torvalds * Look up a process' credentials in the authentication cache 5461da177e4SLinus Torvalds */ 5471da177e4SLinus Torvalds struct rpc_cred * 5481da177e4SLinus Torvalds rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, 5493c6e0bc8SJeff Layton int flags, gfp_t gfp) 5501da177e4SLinus Torvalds { 551e092bdcdSTrond Myklebust LIST_HEAD(free); 5521da177e4SLinus Torvalds struct rpc_cred_cache *cache = auth->au_credcache; 55331be5bf1STrond Myklebust struct rpc_cred *cred = NULL, 55431be5bf1STrond Myklebust *entry, *new; 55525337fdcSTrond Myklebust unsigned int nr; 55625337fdcSTrond Myklebust 55766cbd4baSFrank Sorenson nr = auth->au_ops->hash_cred(acred, cache->hashbits); 5581da177e4SLinus Torvalds 55931be5bf1STrond Myklebust rcu_read_lock(); 560b67bfe0dSSasha Levin hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) { 56131be5bf1STrond Myklebust if (!entry->cr_ops->crmatch(acred, entry, flags)) 56231be5bf1STrond Myklebust continue; 563bd956080SNeilBrown if (flags & RPCAUTH_LOOKUP_RCU) { 564bd956080SNeilBrown if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) && 565bd956080SNeilBrown !test_bit(RPCAUTH_CRED_NEW, &entry->cr_flags)) 566bd956080SNeilBrown cred = entry; 567bd956080SNeilBrown break; 568bd956080SNeilBrown } 5699499b434STrond Myklebust spin_lock(&cache->lock); 57031be5bf1STrond Myklebust if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) == 0) { 5719499b434STrond Myklebust spin_unlock(&cache->lock); 57231be5bf1STrond Myklebust continue; 57331be5bf1STrond Myklebust } 57431be5bf1STrond Myklebust cred = get_rpccred(entry); 5759499b434STrond Myklebust spin_unlock(&cache->lock); 57631be5bf1STrond Myklebust break; 57731be5bf1STrond Myklebust } 57831be5bf1STrond Myklebust rcu_read_unlock(); 57931be5bf1STrond Myklebust 5809499b434STrond Myklebust if (cred != NULL) 58131be5bf1STrond Myklebust goto found; 58231be5bf1STrond Myklebust 583bd956080SNeilBrown if (flags & RPCAUTH_LOOKUP_RCU) 584bd956080SNeilBrown return ERR_PTR(-ECHILD); 585bd956080SNeilBrown 5863c6e0bc8SJeff Layton new = auth->au_ops->crcreate(auth, acred, flags, gfp); 58731be5bf1STrond Myklebust if (IS_ERR(new)) { 58831be5bf1STrond Myklebust cred = new; 58931be5bf1STrond Myklebust goto out; 59031be5bf1STrond Myklebust } 59131be5bf1STrond Myklebust 5929499b434STrond Myklebust spin_lock(&cache->lock); 593b67bfe0dSSasha Levin hlist_for_each_entry(entry, &cache->hashtable[nr], cr_hash) { 594e092bdcdSTrond Myklebust if (!entry->cr_ops->crmatch(acred, entry, flags)) 595e092bdcdSTrond Myklebust continue; 596e092bdcdSTrond Myklebust cred = get_rpccred(entry); 5971da177e4SLinus Torvalds break; 5981da177e4SLinus Torvalds } 59931be5bf1STrond Myklebust if (cred == NULL) { 60031be5bf1STrond Myklebust cred = new; 60131be5bf1STrond Myklebust set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); 60231be5bf1STrond Myklebust hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]); 60331be5bf1STrond Myklebust } else 604e092bdcdSTrond Myklebust list_add_tail(&new->cr_lru, &free); 6059499b434STrond Myklebust spin_unlock(&cache->lock); 606bae6746fSTrond Myklebust rpcauth_cache_enforce_limit(); 60731be5bf1STrond Myklebust found: 608f64f9e71SJoe Perches if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && 609f64f9e71SJoe Perches cred->cr_ops->cr_init != NULL && 610f64f9e71SJoe Perches !(flags & RPCAUTH_LOOKUP_NEW)) { 611fba3bad4STrond Myklebust int res = cred->cr_ops->cr_init(auth, cred); 612fba3bad4STrond Myklebust if (res < 0) { 613fba3bad4STrond Myklebust put_rpccred(cred); 614fba3bad4STrond Myklebust cred = ERR_PTR(res); 615fba3bad4STrond Myklebust } 6161da177e4SLinus Torvalds } 61731be5bf1STrond Myklebust rpcauth_destroy_credlist(&free); 61831be5bf1STrond Myklebust out: 61931be5bf1STrond Myklebust return cred; 6201da177e4SLinus Torvalds } 621e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache); 6221da177e4SLinus Torvalds 6231da177e4SLinus Torvalds struct rpc_cred * 6248a317760STrond Myklebust rpcauth_lookupcred(struct rpc_auth *auth, int flags) 6251da177e4SLinus Torvalds { 62686a264abSDavid Howells struct auth_cred acred; 6271da177e4SLinus Torvalds struct rpc_cred *ret; 62886a264abSDavid Howells const struct cred *cred = current_cred(); 6291da177e4SLinus Torvalds 6301da177e4SLinus Torvalds dprintk("RPC: looking up %s cred\n", 6311da177e4SLinus Torvalds auth->au_ops->au_name); 63286a264abSDavid Howells 63386a264abSDavid Howells memset(&acred, 0, sizeof(acred)); 63486a264abSDavid Howells acred.uid = cred->fsuid; 63586a264abSDavid Howells acred.gid = cred->fsgid; 636122a8cdaSNeilBrown acred.group_info = cred->group_info; 6378a317760STrond Myklebust ret = auth->au_ops->lookup_cred(auth, &acred, flags); 6381da177e4SLinus Torvalds return ret; 6391da177e4SLinus Torvalds } 64066b06860SAndy Adamson EXPORT_SYMBOL_GPL(rpcauth_lookupcred); 6411da177e4SLinus Torvalds 6425fe4755eSTrond Myklebust void 6435fe4755eSTrond Myklebust rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred, 6445fe4755eSTrond Myklebust struct rpc_auth *auth, const struct rpc_credops *ops) 6455fe4755eSTrond Myklebust { 6465fe4755eSTrond Myklebust INIT_HLIST_NODE(&cred->cr_hash); 647e092bdcdSTrond Myklebust INIT_LIST_HEAD(&cred->cr_lru); 6485fe4755eSTrond Myklebust atomic_set(&cred->cr_count, 1); 6495fe4755eSTrond Myklebust cred->cr_auth = auth; 6505fe4755eSTrond Myklebust cred->cr_ops = ops; 6515fe4755eSTrond Myklebust cred->cr_expire = jiffies; 6525fe4755eSTrond Myklebust cred->cr_uid = acred->uid; 6535fe4755eSTrond Myklebust } 654e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_cred); 6555fe4755eSTrond Myklebust 6568572b8e2STrond Myklebust struct rpc_cred * 6575d351754STrond Myklebust rpcauth_generic_bind_cred(struct rpc_task *task, struct rpc_cred *cred, int lookupflags) 6584ccda2cdSTrond Myklebust { 6594ccda2cdSTrond Myklebust dprintk("RPC: %5u holding %s cred %p\n", task->tk_pid, 6604ccda2cdSTrond Myklebust cred->cr_auth->au_ops->au_name, cred); 6618572b8e2STrond Myklebust return get_rpccred(cred); 6624ccda2cdSTrond Myklebust } 6635c691044STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_generic_bind_cred); 6644ccda2cdSTrond Myklebust 6658572b8e2STrond Myklebust static struct rpc_cred * 6665d351754STrond Myklebust rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags) 6671da177e4SLinus Torvalds { 6681be27f36STrond Myklebust struct rpc_auth *auth = task->tk_client->cl_auth; 6691da177e4SLinus Torvalds struct auth_cred acred = { 670bf37f794SEric W. Biederman .uid = GLOBAL_ROOT_UID, 671bf37f794SEric W. Biederman .gid = GLOBAL_ROOT_GID, 6721da177e4SLinus Torvalds }; 6731da177e4SLinus Torvalds 67446121cf7SChuck Lever dprintk("RPC: %5u looking up %s cred\n", 6751be27f36STrond Myklebust task->tk_pid, task->tk_client->cl_auth->au_ops->au_name); 6768572b8e2STrond Myklebust return auth->au_ops->lookup_cred(auth, &acred, lookupflags); 677af093835STrond Myklebust } 678af093835STrond Myklebust 6798572b8e2STrond Myklebust static struct rpc_cred * 6805d351754STrond Myklebust rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags) 681af093835STrond Myklebust { 682af093835STrond Myklebust struct rpc_auth *auth = task->tk_client->cl_auth; 683af093835STrond Myklebust 684af093835STrond Myklebust dprintk("RPC: %5u looking up %s cred\n", 685af093835STrond Myklebust task->tk_pid, auth->au_ops->au_name); 6868572b8e2STrond Myklebust return rpcauth_lookupcred(auth, lookupflags); 6871da177e4SLinus Torvalds } 6881da177e4SLinus Torvalds 689a17c2153STrond Myklebust static int 6904ccda2cdSTrond Myklebust rpcauth_bindcred(struct rpc_task *task, struct rpc_cred *cred, int flags) 6911da177e4SLinus Torvalds { 692a17c2153STrond Myklebust struct rpc_rqst *req = task->tk_rqstp; 6938572b8e2STrond Myklebust struct rpc_cred *new; 6945d351754STrond Myklebust int lookupflags = 0; 6955d351754STrond Myklebust 6965d351754STrond Myklebust if (flags & RPC_TASK_ASYNC) 6975d351754STrond Myklebust lookupflags |= RPCAUTH_LOOKUP_NEW; 6984ccda2cdSTrond Myklebust if (cred != NULL) 6998572b8e2STrond Myklebust new = cred->cr_ops->crbind(task, cred, lookupflags); 7004ccda2cdSTrond Myklebust else if (flags & RPC_TASK_ROOTCREDS) 7018572b8e2STrond Myklebust new = rpcauth_bind_root_cred(task, lookupflags); 7024ccda2cdSTrond Myklebust else 7038572b8e2STrond Myklebust new = rpcauth_bind_new_cred(task, lookupflags); 7048572b8e2STrond Myklebust if (IS_ERR(new)) 7058572b8e2STrond Myklebust return PTR_ERR(new); 706a17c2153STrond Myklebust put_rpccred(req->rq_cred); 707a17c2153STrond Myklebust req->rq_cred = new; 7088572b8e2STrond Myklebust return 0; 7091da177e4SLinus Torvalds } 7101da177e4SLinus Torvalds 7111da177e4SLinus Torvalds void 7121da177e4SLinus Torvalds put_rpccred(struct rpc_cred *cred) 7131da177e4SLinus Torvalds { 7149a8f6b5eSTrond Myklebust if (cred == NULL) 7159a8f6b5eSTrond Myklebust return; 716e092bdcdSTrond Myklebust /* Fast path for unhashed credentials */ 717f0380f3dSTrond Myklebust if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) == 0) { 718f0380f3dSTrond Myklebust if (atomic_dec_and_test(&cred->cr_count)) 719f0380f3dSTrond Myklebust cred->cr_ops->crdestroy(cred); 7201da177e4SLinus Torvalds return; 721f0380f3dSTrond Myklebust } 722f0380f3dSTrond Myklebust 723e092bdcdSTrond Myklebust if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock)) 724e092bdcdSTrond Myklebust return; 725f5c2187cSTrond Myklebust if (!list_empty(&cred->cr_lru)) { 726f5c2187cSTrond Myklebust number_cred_unused--; 727e092bdcdSTrond Myklebust list_del_init(&cred->cr_lru); 728f5c2187cSTrond Myklebust } 7295f707eb4STrond Myklebust if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) { 730f0380f3dSTrond Myklebust if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) { 731e092bdcdSTrond Myklebust cred->cr_expire = jiffies; 732e092bdcdSTrond Myklebust list_add_tail(&cred->cr_lru, &cred_unused); 733f5c2187cSTrond Myklebust number_cred_unused++; 734f0380f3dSTrond Myklebust goto out_nodestroy; 735f0380f3dSTrond Myklebust } 736f0380f3dSTrond Myklebust if (!rpcauth_unhash_cred(cred)) { 737f0380f3dSTrond Myklebust /* We were hashed and someone looked us up... */ 738f0380f3dSTrond Myklebust goto out_nodestroy; 739f0380f3dSTrond Myklebust } 740e092bdcdSTrond Myklebust } 741e092bdcdSTrond Myklebust spin_unlock(&rpc_credcache_lock); 7421da177e4SLinus Torvalds cred->cr_ops->crdestroy(cred); 743f0380f3dSTrond Myklebust return; 744f0380f3dSTrond Myklebust out_nodestroy: 745f0380f3dSTrond Myklebust spin_unlock(&rpc_credcache_lock); 7461da177e4SLinus Torvalds } 747e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(put_rpccred); 7481da177e4SLinus Torvalds 749d8ed029dSAlexey Dobriyan __be32 * 750d8ed029dSAlexey Dobriyan rpcauth_marshcred(struct rpc_task *task, __be32 *p) 7511da177e4SLinus Torvalds { 752a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 7531da177e4SLinus Torvalds 75446121cf7SChuck Lever dprintk("RPC: %5u marshaling %s cred %p\n", 7551be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 7560bbacc40SChuck Lever 7571da177e4SLinus Torvalds return cred->cr_ops->crmarshal(task, p); 7581da177e4SLinus Torvalds } 7591da177e4SLinus Torvalds 760d8ed029dSAlexey Dobriyan __be32 * 761d8ed029dSAlexey Dobriyan rpcauth_checkverf(struct rpc_task *task, __be32 *p) 7621da177e4SLinus Torvalds { 763a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 7641da177e4SLinus Torvalds 76546121cf7SChuck Lever dprintk("RPC: %5u validating %s cred %p\n", 7661be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 7670bbacc40SChuck Lever 7681da177e4SLinus Torvalds return cred->cr_ops->crvalidate(task, p); 7691da177e4SLinus Torvalds } 7701da177e4SLinus Torvalds 7719f06c719SChuck Lever static void rpcauth_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp, 7729f06c719SChuck Lever __be32 *data, void *obj) 7739f06c719SChuck Lever { 7749f06c719SChuck Lever struct xdr_stream xdr; 7759f06c719SChuck Lever 7769f06c719SChuck Lever xdr_init_encode(&xdr, &rqstp->rq_snd_buf, data); 7779f06c719SChuck Lever encode(rqstp, &xdr, obj); 7789f06c719SChuck Lever } 7799f06c719SChuck Lever 7801da177e4SLinus Torvalds int 7819f06c719SChuck Lever rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp, 782d8ed029dSAlexey Dobriyan __be32 *data, void *obj) 7831da177e4SLinus Torvalds { 784a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 7851da177e4SLinus Torvalds 78646121cf7SChuck Lever dprintk("RPC: %5u using %s cred %p to wrap rpc data\n", 7871da177e4SLinus Torvalds task->tk_pid, cred->cr_ops->cr_name, cred); 7881da177e4SLinus Torvalds if (cred->cr_ops->crwrap_req) 7891da177e4SLinus Torvalds return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); 7901da177e4SLinus Torvalds /* By default, we encode the arguments normally. */ 7919f06c719SChuck Lever rpcauth_wrap_req_encode(encode, rqstp, data, obj); 7929f06c719SChuck Lever return 0; 7931da177e4SLinus Torvalds } 7941da177e4SLinus Torvalds 795bf269551SChuck Lever static int 796bf269551SChuck Lever rpcauth_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp, 797bf269551SChuck Lever __be32 *data, void *obj) 798bf269551SChuck Lever { 799bf269551SChuck Lever struct xdr_stream xdr; 800bf269551SChuck Lever 801bf269551SChuck Lever xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, data); 802bf269551SChuck Lever return decode(rqstp, &xdr, obj); 803bf269551SChuck Lever } 804bf269551SChuck Lever 8051da177e4SLinus Torvalds int 806bf269551SChuck Lever rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp, 807d8ed029dSAlexey Dobriyan __be32 *data, void *obj) 8081da177e4SLinus Torvalds { 809a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 8101da177e4SLinus Torvalds 81146121cf7SChuck Lever dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n", 8121da177e4SLinus Torvalds task->tk_pid, cred->cr_ops->cr_name, cred); 8131da177e4SLinus Torvalds if (cred->cr_ops->crunwrap_resp) 8141da177e4SLinus Torvalds return cred->cr_ops->crunwrap_resp(task, decode, rqstp, 8151da177e4SLinus Torvalds data, obj); 8161da177e4SLinus Torvalds /* By default, we decode the arguments normally. */ 817bf269551SChuck Lever return rpcauth_unwrap_req_decode(decode, rqstp, data, obj); 8181da177e4SLinus Torvalds } 8191da177e4SLinus Torvalds 8201da177e4SLinus Torvalds int 8211da177e4SLinus Torvalds rpcauth_refreshcred(struct rpc_task *task) 8221da177e4SLinus Torvalds { 8239a84d380STrond Myklebust struct rpc_cred *cred; 8241da177e4SLinus Torvalds int err; 8251da177e4SLinus Torvalds 826a17c2153STrond Myklebust cred = task->tk_rqstp->rq_cred; 827a17c2153STrond Myklebust if (cred == NULL) { 828a17c2153STrond Myklebust err = rpcauth_bindcred(task, task->tk_msg.rpc_cred, task->tk_flags); 829a17c2153STrond Myklebust if (err < 0) 830a17c2153STrond Myklebust goto out; 831a17c2153STrond Myklebust cred = task->tk_rqstp->rq_cred; 832f81c6224SJoe Perches } 83346121cf7SChuck Lever dprintk("RPC: %5u refreshing %s cred %p\n", 8341be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 8350bbacc40SChuck Lever 8361da177e4SLinus Torvalds err = cred->cr_ops->crrefresh(task); 837a17c2153STrond Myklebust out: 8381da177e4SLinus Torvalds if (err < 0) 8391da177e4SLinus Torvalds task->tk_status = err; 8401da177e4SLinus Torvalds return err; 8411da177e4SLinus Torvalds } 8421da177e4SLinus Torvalds 8431da177e4SLinus Torvalds void 8441da177e4SLinus Torvalds rpcauth_invalcred(struct rpc_task *task) 8451da177e4SLinus Torvalds { 846a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 847fc432dd9STrond Myklebust 84846121cf7SChuck Lever dprintk("RPC: %5u invalidating %s cred %p\n", 8491be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 850fc432dd9STrond Myklebust if (cred) 851fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 8521da177e4SLinus Torvalds } 8531da177e4SLinus Torvalds 8541da177e4SLinus Torvalds int 8551da177e4SLinus Torvalds rpcauth_uptodatecred(struct rpc_task *task) 8561da177e4SLinus Torvalds { 857a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 858fc432dd9STrond Myklebust 859fc432dd9STrond Myklebust return cred == NULL || 860fc432dd9STrond Myklebust test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0; 8611da177e4SLinus Torvalds } 862f5c2187cSTrond Myklebust 8638e1f936bSRusty Russell static struct shrinker rpc_cred_shrinker = { 86470534a73SDave Chinner .count_objects = rpcauth_cache_shrink_count, 86570534a73SDave Chinner .scan_objects = rpcauth_cache_shrink_scan, 8668e1f936bSRusty Russell .seeks = DEFAULT_SEEKS, 8678e1f936bSRusty Russell }; 868f5c2187cSTrond Myklebust 8695d8d9a4dSTrond Myklebust int __init rpcauth_init_module(void) 870f5c2187cSTrond Myklebust { 8715d8d9a4dSTrond Myklebust int err; 8725d8d9a4dSTrond Myklebust 8735d8d9a4dSTrond Myklebust err = rpc_init_authunix(); 8745d8d9a4dSTrond Myklebust if (err < 0) 8755d8d9a4dSTrond Myklebust goto out1; 8765d8d9a4dSTrond Myklebust err = rpc_init_generic_auth(); 8775d8d9a4dSTrond Myklebust if (err < 0) 8785d8d9a4dSTrond Myklebust goto out2; 8792864486bSKinglong Mee err = register_shrinker(&rpc_cred_shrinker); 8802864486bSKinglong Mee if (err < 0) 8812864486bSKinglong Mee goto out3; 8825d8d9a4dSTrond Myklebust return 0; 8832864486bSKinglong Mee out3: 8842864486bSKinglong Mee rpc_destroy_generic_auth(); 8855d8d9a4dSTrond Myklebust out2: 8865d8d9a4dSTrond Myklebust rpc_destroy_authunix(); 8875d8d9a4dSTrond Myklebust out1: 8885d8d9a4dSTrond Myklebust return err; 889f5c2187cSTrond Myklebust } 890f5c2187cSTrond Myklebust 891c135e84aSStephen Rothwell void rpcauth_remove_module(void) 892f5c2187cSTrond Myklebust { 8935d8d9a4dSTrond Myklebust rpc_destroy_authunix(); 8945d8d9a4dSTrond Myklebust rpc_destroy_generic_auth(); 8958e1f936bSRusty Russell unregister_shrinker(&rpc_cred_shrinker); 896f5c2187cSTrond Myklebust } 897