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