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 20*a0584ee9SChuck Lever #include <trace/events/sunrpc.h> 21*a0584ee9SChuck Lever 22241269bdSTrond Myklebust #define RPC_CREDCACHE_DEFAULT_HASHBITS (4) 23241269bdSTrond Myklebust struct rpc_cred_cache { 24241269bdSTrond Myklebust struct hlist_head *hashtable; 25241269bdSTrond Myklebust unsigned int hashbits; 26241269bdSTrond Myklebust spinlock_t lock; 27241269bdSTrond Myklebust }; 28241269bdSTrond Myklebust 29241269bdSTrond Myklebust static unsigned int auth_hashbits = RPC_CREDCACHE_DEFAULT_HASHBITS; 30241269bdSTrond Myklebust 314e4c3befSTrond Myklebust static const struct rpc_authops __rcu *auth_flavors[RPC_AUTH_MAXFLAVOR] = { 324e4c3befSTrond Myklebust [RPC_AUTH_NULL] = (const struct rpc_authops __force __rcu *)&authnull_ops, 334e4c3befSTrond Myklebust [RPC_AUTH_UNIX] = (const struct rpc_authops __force __rcu *)&authunix_ops, 341da177e4SLinus Torvalds NULL, /* others can be loadable modules */ 351da177e4SLinus Torvalds }; 361da177e4SLinus Torvalds 37e092bdcdSTrond Myklebust static LIST_HEAD(cred_unused); 38f5c2187cSTrond Myklebust static unsigned long number_cred_unused; 39e092bdcdSTrond Myklebust 40a52458b4SNeilBrown static struct cred machine_cred = { 41a52458b4SNeilBrown .usage = ATOMIC_INIT(1), 42e7f45099SSantosh kumar pradhan #ifdef CONFIG_DEBUG_CREDENTIALS 43e7f45099SSantosh kumar pradhan .magic = CRED_MAGIC, 44e7f45099SSantosh kumar pradhan #endif 455e16923bSNeilBrown }; 465e16923bSNeilBrown 475e16923bSNeilBrown /* 485e16923bSNeilBrown * Return the machine_cred pointer to be used whenever 495e16923bSNeilBrown * the a generic machine credential is needed. 505e16923bSNeilBrown */ 51a52458b4SNeilBrown const struct cred *rpc_machine_cred(void) 525e16923bSNeilBrown { 535e16923bSNeilBrown return &machine_cred; 545e16923bSNeilBrown } 555e16923bSNeilBrown EXPORT_SYMBOL_GPL(rpc_machine_cred); 565e16923bSNeilBrown 57db5fe265SMiquel van Smoorenburg #define MAX_HASHTABLE_BITS (14) 588e4e15d4SStephen Rothwell static int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp) 59241269bdSTrond Myklebust { 60241269bdSTrond Myklebust unsigned long num; 61241269bdSTrond Myklebust unsigned int nbits; 62241269bdSTrond Myklebust int ret; 63241269bdSTrond Myklebust 64241269bdSTrond Myklebust if (!val) 65241269bdSTrond Myklebust goto out_inval; 6600cfaa94SDaniel Walter ret = kstrtoul(val, 0, &num); 671a54c0cfSDan Carpenter if (ret) 68241269bdSTrond Myklebust goto out_inval; 6934ae685cSFrank Sorenson nbits = fls(num - 1); 70241269bdSTrond Myklebust if (nbits > MAX_HASHTABLE_BITS || nbits < 2) 71241269bdSTrond Myklebust goto out_inval; 72241269bdSTrond Myklebust *(unsigned int *)kp->arg = nbits; 73241269bdSTrond Myklebust return 0; 74241269bdSTrond Myklebust out_inval: 75241269bdSTrond Myklebust return -EINVAL; 76241269bdSTrond Myklebust } 77241269bdSTrond Myklebust 788e4e15d4SStephen Rothwell static int param_get_hashtbl_sz(char *buffer, const struct kernel_param *kp) 79241269bdSTrond Myklebust { 80241269bdSTrond Myklebust unsigned int nbits; 81241269bdSTrond Myklebust 82241269bdSTrond Myklebust nbits = *(unsigned int *)kp->arg; 83241269bdSTrond Myklebust return sprintf(buffer, "%u", 1U << nbits); 84241269bdSTrond Myklebust } 85241269bdSTrond Myklebust 86241269bdSTrond Myklebust #define param_check_hashtbl_sz(name, p) __param_check(name, p, unsigned int); 87241269bdSTrond Myklebust 889c27847dSLuis R. Rodriguez static const struct kernel_param_ops param_ops_hashtbl_sz = { 898e4e15d4SStephen Rothwell .set = param_set_hashtbl_sz, 908e4e15d4SStephen Rothwell .get = param_get_hashtbl_sz, 918e4e15d4SStephen Rothwell }; 928e4e15d4SStephen Rothwell 93241269bdSTrond Myklebust module_param_named(auth_hashtable_size, auth_hashbits, hashtbl_sz, 0644); 94241269bdSTrond Myklebust MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size"); 95241269bdSTrond Myklebust 96bae6746fSTrond Myklebust static unsigned long auth_max_cred_cachesize = ULONG_MAX; 97bae6746fSTrond Myklebust module_param(auth_max_cred_cachesize, ulong, 0644); 98bae6746fSTrond Myklebust MODULE_PARM_DESC(auth_max_cred_cachesize, "RPC credential maximum total cache size"); 99bae6746fSTrond Myklebust 1001da177e4SLinus Torvalds static u32 1011da177e4SLinus Torvalds pseudoflavor_to_flavor(u32 flavor) { 1021c74a244SChuck Lever if (flavor > RPC_AUTH_MAXFLAVOR) 1031da177e4SLinus Torvalds return RPC_AUTH_GSS; 1041da177e4SLinus Torvalds return flavor; 1051da177e4SLinus Torvalds } 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds int 108f1c0a861STrond Myklebust rpcauth_register(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 old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], NULL, ops); 1164e4c3befSTrond Myklebust if (old == NULL || old == ops) 1174e4c3befSTrond Myklebust return 0; 1184e4c3befSTrond Myklebust return -EPERM; 1191da177e4SLinus Torvalds } 120e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_register); 1211da177e4SLinus Torvalds 1221da177e4SLinus Torvalds int 123f1c0a861STrond Myklebust rpcauth_unregister(const struct rpc_authops *ops) 1241da177e4SLinus Torvalds { 1254e4c3befSTrond Myklebust const struct rpc_authops *old; 1261da177e4SLinus Torvalds rpc_authflavor_t flavor; 1271da177e4SLinus Torvalds 1281da177e4SLinus Torvalds if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR) 1291da177e4SLinus Torvalds return -EINVAL; 1304e4c3befSTrond Myklebust 1314e4c3befSTrond Myklebust old = cmpxchg((const struct rpc_authops ** __force)&auth_flavors[flavor], ops, NULL); 1324e4c3befSTrond Myklebust if (old == ops || old == NULL) 1334e4c3befSTrond Myklebust return 0; 1344e4c3befSTrond Myklebust return -EPERM; 1351da177e4SLinus Torvalds } 136e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_unregister); 1371da177e4SLinus Torvalds 1384e4c3befSTrond Myklebust static const struct rpc_authops * 1394e4c3befSTrond Myklebust rpcauth_get_authops(rpc_authflavor_t flavor) 1404e4c3befSTrond Myklebust { 1414e4c3befSTrond Myklebust const struct rpc_authops *ops; 1424e4c3befSTrond Myklebust 1434e4c3befSTrond Myklebust if (flavor >= RPC_AUTH_MAXFLAVOR) 1444e4c3befSTrond Myklebust return NULL; 1454e4c3befSTrond Myklebust 1464e4c3befSTrond Myklebust rcu_read_lock(); 1474e4c3befSTrond Myklebust ops = rcu_dereference(auth_flavors[flavor]); 1484e4c3befSTrond Myklebust if (ops == NULL) { 1494e4c3befSTrond Myklebust rcu_read_unlock(); 1504e4c3befSTrond Myklebust request_module("rpc-auth-%u", flavor); 1514e4c3befSTrond Myklebust rcu_read_lock(); 1524e4c3befSTrond Myklebust ops = rcu_dereference(auth_flavors[flavor]); 1534e4c3befSTrond Myklebust if (ops == NULL) 1544e4c3befSTrond Myklebust goto out; 1554e4c3befSTrond Myklebust } 1564e4c3befSTrond Myklebust if (!try_module_get(ops->owner)) 1574e4c3befSTrond Myklebust ops = NULL; 1584e4c3befSTrond Myklebust out: 1594e4c3befSTrond Myklebust rcu_read_unlock(); 1604e4c3befSTrond Myklebust return ops; 1614e4c3befSTrond Myklebust } 1624e4c3befSTrond Myklebust 1634e4c3befSTrond Myklebust static void 1644e4c3befSTrond Myklebust rpcauth_put_authops(const struct rpc_authops *ops) 1654e4c3befSTrond Myklebust { 1664e4c3befSTrond Myklebust module_put(ops->owner); 1674e4c3befSTrond Myklebust } 1684e4c3befSTrond Myklebust 1696a1a1e34SChuck Lever /** 1709568c5e9SChuck Lever * rpcauth_get_pseudoflavor - check if security flavor is supported 1719568c5e9SChuck Lever * @flavor: a security flavor 1729568c5e9SChuck Lever * @info: a GSS mech OID, quality of protection, and service value 1739568c5e9SChuck Lever * 1749568c5e9SChuck Lever * Verifies that an appropriate kernel module is available or already loaded. 1759568c5e9SChuck Lever * Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is 1769568c5e9SChuck Lever * not supported locally. 1779568c5e9SChuck Lever */ 1789568c5e9SChuck Lever rpc_authflavor_t 1799568c5e9SChuck Lever rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info) 1809568c5e9SChuck Lever { 1814e4c3befSTrond Myklebust const struct rpc_authops *ops = rpcauth_get_authops(flavor); 1829568c5e9SChuck Lever rpc_authflavor_t pseudoflavor; 1839568c5e9SChuck Lever 1844e4c3befSTrond Myklebust if (!ops) 1859568c5e9SChuck Lever return RPC_AUTH_MAXFLAVOR; 1869568c5e9SChuck Lever pseudoflavor = flavor; 1879568c5e9SChuck Lever if (ops->info2flavor != NULL) 1889568c5e9SChuck Lever pseudoflavor = ops->info2flavor(info); 1899568c5e9SChuck Lever 1904e4c3befSTrond Myklebust rpcauth_put_authops(ops); 1919568c5e9SChuck Lever return pseudoflavor; 1929568c5e9SChuck Lever } 1939568c5e9SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor); 1949568c5e9SChuck Lever 1959568c5e9SChuck Lever /** 196a77c806fSChuck Lever * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor 197a77c806fSChuck Lever * @pseudoflavor: GSS pseudoflavor to match 198a77c806fSChuck Lever * @info: rpcsec_gss_info structure to fill in 199a77c806fSChuck Lever * 200a77c806fSChuck Lever * Returns zero and fills in "info" if pseudoflavor matches a 201a77c806fSChuck Lever * supported mechanism. 202a77c806fSChuck Lever */ 203a77c806fSChuck Lever int 204a77c806fSChuck Lever rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info) 205a77c806fSChuck Lever { 206a77c806fSChuck Lever rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor); 207a77c806fSChuck Lever const struct rpc_authops *ops; 208a77c806fSChuck Lever int result; 209a77c806fSChuck Lever 2104e4c3befSTrond Myklebust ops = rpcauth_get_authops(flavor); 211a77c806fSChuck Lever if (ops == NULL) 212a77c806fSChuck Lever return -ENOENT; 213a77c806fSChuck Lever 214a77c806fSChuck Lever result = -ENOENT; 215a77c806fSChuck Lever if (ops->flavor2info != NULL) 216a77c806fSChuck Lever result = ops->flavor2info(pseudoflavor, info); 217a77c806fSChuck Lever 2184e4c3befSTrond Myklebust rpcauth_put_authops(ops); 219a77c806fSChuck Lever return result; 220a77c806fSChuck Lever } 221a77c806fSChuck Lever EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo); 222a77c806fSChuck Lever 223a77c806fSChuck Lever /** 2246a1a1e34SChuck Lever * rpcauth_list_flavors - discover registered flavors and pseudoflavors 2256a1a1e34SChuck Lever * @array: array to fill in 2266a1a1e34SChuck Lever * @size: size of "array" 2276a1a1e34SChuck Lever * 2286a1a1e34SChuck Lever * Returns the number of array items filled in, or a negative errno. 2296a1a1e34SChuck Lever * 2306a1a1e34SChuck Lever * The returned array is not sorted by any policy. Callers should not 2316a1a1e34SChuck Lever * rely on the order of the items in the returned array. 2326a1a1e34SChuck Lever */ 2336a1a1e34SChuck Lever int 2346a1a1e34SChuck Lever rpcauth_list_flavors(rpc_authflavor_t *array, int size) 2356a1a1e34SChuck Lever { 2364e4c3befSTrond Myklebust const struct rpc_authops *ops; 2374e4c3befSTrond Myklebust rpc_authflavor_t flavor, pseudos[4]; 2384e4c3befSTrond Myklebust int i, len, result = 0; 2396a1a1e34SChuck Lever 2404e4c3befSTrond Myklebust rcu_read_lock(); 2416a1a1e34SChuck Lever for (flavor = 0; flavor < RPC_AUTH_MAXFLAVOR; flavor++) { 2424e4c3befSTrond Myklebust ops = rcu_dereference(auth_flavors[flavor]); 2436a1a1e34SChuck Lever if (result >= size) { 2446a1a1e34SChuck Lever result = -ENOMEM; 2456a1a1e34SChuck Lever break; 2466a1a1e34SChuck Lever } 2476a1a1e34SChuck Lever 2486a1a1e34SChuck Lever if (ops == NULL) 2496a1a1e34SChuck Lever continue; 2506a1a1e34SChuck Lever if (ops->list_pseudoflavors == NULL) { 2516a1a1e34SChuck Lever array[result++] = ops->au_flavor; 2526a1a1e34SChuck Lever continue; 2536a1a1e34SChuck Lever } 2546a1a1e34SChuck Lever len = ops->list_pseudoflavors(pseudos, ARRAY_SIZE(pseudos)); 2556a1a1e34SChuck Lever if (len < 0) { 2566a1a1e34SChuck Lever result = len; 2576a1a1e34SChuck Lever break; 2586a1a1e34SChuck Lever } 2596a1a1e34SChuck Lever for (i = 0; i < len; i++) { 2606a1a1e34SChuck Lever if (result >= size) { 2616a1a1e34SChuck Lever result = -ENOMEM; 2626a1a1e34SChuck Lever break; 2636a1a1e34SChuck Lever } 2646a1a1e34SChuck Lever array[result++] = pseudos[i]; 2656a1a1e34SChuck Lever } 2666a1a1e34SChuck Lever } 2674e4c3befSTrond Myklebust rcu_read_unlock(); 2686a1a1e34SChuck Lever return result; 2696a1a1e34SChuck Lever } 2706a1a1e34SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_list_flavors); 2716a1a1e34SChuck Lever 2721da177e4SLinus Torvalds struct rpc_auth * 27382b98ca5SSargun Dhillon rpcauth_create(const struct rpc_auth_create_args *args, struct rpc_clnt *clnt) 2741da177e4SLinus Torvalds { 2754e4c3befSTrond Myklebust struct rpc_auth *auth = ERR_PTR(-EINVAL); 276f1c0a861STrond Myklebust const struct rpc_authops *ops; 277c2190661STrond Myklebust u32 flavor = pseudoflavor_to_flavor(args->pseudoflavor); 2781da177e4SLinus Torvalds 2794e4c3befSTrond Myklebust ops = rpcauth_get_authops(flavor); 2804e4c3befSTrond Myklebust if (ops == NULL) 281f344f6dfSOlaf Kirch goto out; 282f344f6dfSOlaf Kirch 283c2190661STrond Myklebust auth = ops->create(args, clnt); 2844e4c3befSTrond Myklebust 2854e4c3befSTrond Myklebust rpcauth_put_authops(ops); 2866a19275aSJ. Bruce Fields if (IS_ERR(auth)) 2876a19275aSJ. Bruce Fields return auth; 2881da177e4SLinus Torvalds if (clnt->cl_auth) 289de7a8ce3STrond Myklebust rpcauth_release(clnt->cl_auth); 2901da177e4SLinus Torvalds clnt->cl_auth = auth; 291f344f6dfSOlaf Kirch 292f344f6dfSOlaf Kirch out: 2931da177e4SLinus Torvalds return auth; 2941da177e4SLinus Torvalds } 295e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_create); 2961da177e4SLinus Torvalds 2971da177e4SLinus Torvalds void 298de7a8ce3STrond Myklebust rpcauth_release(struct rpc_auth *auth) 2991da177e4SLinus Torvalds { 300331bc71cSTrond Myklebust if (!refcount_dec_and_test(&auth->au_count)) 3011da177e4SLinus Torvalds return; 3021da177e4SLinus Torvalds auth->au_ops->destroy(auth); 3031da177e4SLinus Torvalds } 3041da177e4SLinus Torvalds 3051da177e4SLinus Torvalds static DEFINE_SPINLOCK(rpc_credcache_lock); 3061da177e4SLinus Torvalds 30795cd6232STrond Myklebust /* 30895cd6232STrond Myklebust * On success, the caller is responsible for freeing the reference 30995cd6232STrond Myklebust * held by the hashtable 31095cd6232STrond Myklebust */ 31195cd6232STrond Myklebust static bool 31231be5bf1STrond Myklebust rpcauth_unhash_cred_locked(struct rpc_cred *cred) 31331be5bf1STrond Myklebust { 31495cd6232STrond Myklebust if (!test_and_clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags)) 31595cd6232STrond Myklebust return false; 31631be5bf1STrond Myklebust hlist_del_rcu(&cred->cr_hash); 31795cd6232STrond Myklebust return true; 31831be5bf1STrond Myklebust } 31931be5bf1STrond Myklebust 32095cd6232STrond Myklebust static bool 3219499b434STrond Myklebust rpcauth_unhash_cred(struct rpc_cred *cred) 3229499b434STrond Myklebust { 3239499b434STrond Myklebust spinlock_t *cache_lock; 32495cd6232STrond Myklebust bool ret; 3259499b434STrond Myklebust 32695cd6232STrond Myklebust if (!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags)) 32795cd6232STrond Myklebust return false; 3289499b434STrond Myklebust cache_lock = &cred->cr_auth->au_credcache->lock; 3299499b434STrond Myklebust spin_lock(cache_lock); 33095cd6232STrond Myklebust ret = rpcauth_unhash_cred_locked(cred); 3319499b434STrond Myklebust spin_unlock(cache_lock); 332f0380f3dSTrond Myklebust return ret; 3339499b434STrond Myklebust } 3349499b434STrond Myklebust 3351da177e4SLinus Torvalds /* 3361da177e4SLinus Torvalds * Initialize RPC credential cache 3371da177e4SLinus Torvalds */ 3381da177e4SLinus Torvalds int 339f5c2187cSTrond Myklebust rpcauth_init_credcache(struct rpc_auth *auth) 3401da177e4SLinus Torvalds { 3411da177e4SLinus Torvalds struct rpc_cred_cache *new; 342988664a0STrond Myklebust unsigned int hashsize; 3431da177e4SLinus Torvalds 3448b3a7005SKris Katterjohn new = kmalloc(sizeof(*new), GFP_KERNEL); 3451da177e4SLinus Torvalds if (!new) 346241269bdSTrond Myklebust goto out_nocache; 347241269bdSTrond Myklebust new->hashbits = auth_hashbits; 348988664a0STrond Myklebust hashsize = 1U << new->hashbits; 349241269bdSTrond Myklebust new->hashtable = kcalloc(hashsize, sizeof(new->hashtable[0]), GFP_KERNEL); 350241269bdSTrond Myklebust if (!new->hashtable) 351241269bdSTrond Myklebust goto out_nohashtbl; 3529499b434STrond Myklebust spin_lock_init(&new->lock); 3531da177e4SLinus Torvalds auth->au_credcache = new; 3541da177e4SLinus Torvalds return 0; 355241269bdSTrond Myklebust out_nohashtbl: 356241269bdSTrond Myklebust kfree(new); 357241269bdSTrond Myklebust out_nocache: 358241269bdSTrond Myklebust return -ENOMEM; 3591da177e4SLinus Torvalds } 360e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_credcache); 3611da177e4SLinus Torvalds 362a0337d1dSJeff Layton char * 363a0337d1dSJeff Layton rpcauth_stringify_acceptor(struct rpc_cred *cred) 364a0337d1dSJeff Layton { 365a0337d1dSJeff Layton if (!cred->cr_ops->crstringify_acceptor) 366a0337d1dSJeff Layton return NULL; 367a0337d1dSJeff Layton return cred->cr_ops->crstringify_acceptor(cred); 368a0337d1dSJeff Layton } 369a0337d1dSJeff Layton EXPORT_SYMBOL_GPL(rpcauth_stringify_acceptor); 370a0337d1dSJeff Layton 3714de6caa2SAndy Adamson /* 3721da177e4SLinus Torvalds * Destroy a list of credentials 3731da177e4SLinus Torvalds */ 3741da177e4SLinus Torvalds static inline 375e092bdcdSTrond Myklebust void rpcauth_destroy_credlist(struct list_head *head) 3761da177e4SLinus Torvalds { 3771da177e4SLinus Torvalds struct rpc_cred *cred; 3781da177e4SLinus Torvalds 379e092bdcdSTrond Myklebust while (!list_empty(head)) { 380e092bdcdSTrond Myklebust cred = list_entry(head->next, struct rpc_cred, cr_lru); 381e092bdcdSTrond Myklebust list_del_init(&cred->cr_lru); 3821da177e4SLinus Torvalds put_rpccred(cred); 3831da177e4SLinus Torvalds } 3841da177e4SLinus Torvalds } 3851da177e4SLinus Torvalds 38695cd6232STrond Myklebust static void 38795cd6232STrond Myklebust rpcauth_lru_add_locked(struct rpc_cred *cred) 38895cd6232STrond Myklebust { 38995cd6232STrond Myklebust if (!list_empty(&cred->cr_lru)) 39095cd6232STrond Myklebust return; 39195cd6232STrond Myklebust number_cred_unused++; 39295cd6232STrond Myklebust list_add_tail(&cred->cr_lru, &cred_unused); 39395cd6232STrond Myklebust } 39495cd6232STrond Myklebust 39595cd6232STrond Myklebust static void 39695cd6232STrond Myklebust rpcauth_lru_add(struct rpc_cred *cred) 39795cd6232STrond Myklebust { 39895cd6232STrond Myklebust if (!list_empty(&cred->cr_lru)) 39995cd6232STrond Myklebust return; 40095cd6232STrond Myklebust spin_lock(&rpc_credcache_lock); 40195cd6232STrond Myklebust rpcauth_lru_add_locked(cred); 40295cd6232STrond Myklebust spin_unlock(&rpc_credcache_lock); 40395cd6232STrond Myklebust } 40495cd6232STrond Myklebust 40595cd6232STrond Myklebust static void 40695cd6232STrond Myklebust rpcauth_lru_remove_locked(struct rpc_cred *cred) 40795cd6232STrond Myklebust { 40895cd6232STrond Myklebust if (list_empty(&cred->cr_lru)) 40995cd6232STrond Myklebust return; 41095cd6232STrond Myklebust number_cred_unused--; 41195cd6232STrond Myklebust list_del_init(&cred->cr_lru); 41295cd6232STrond Myklebust } 41395cd6232STrond Myklebust 41495cd6232STrond Myklebust static void 41595cd6232STrond Myklebust rpcauth_lru_remove(struct rpc_cred *cred) 41695cd6232STrond Myklebust { 41795cd6232STrond Myklebust if (list_empty(&cred->cr_lru)) 41895cd6232STrond Myklebust return; 41995cd6232STrond Myklebust spin_lock(&rpc_credcache_lock); 42095cd6232STrond Myklebust rpcauth_lru_remove_locked(cred); 42195cd6232STrond Myklebust spin_unlock(&rpc_credcache_lock); 42295cd6232STrond Myklebust } 42395cd6232STrond Myklebust 4241da177e4SLinus Torvalds /* 4251da177e4SLinus Torvalds * Clear the RPC credential cache, and delete those credentials 4261da177e4SLinus Torvalds * that are not referenced. 4271da177e4SLinus Torvalds */ 4281da177e4SLinus Torvalds void 4293ab9bb72STrond Myklebust rpcauth_clear_credcache(struct rpc_cred_cache *cache) 4301da177e4SLinus Torvalds { 431e092bdcdSTrond Myklebust LIST_HEAD(free); 432e092bdcdSTrond Myklebust struct hlist_head *head; 4331da177e4SLinus Torvalds struct rpc_cred *cred; 434988664a0STrond Myklebust unsigned int hashsize = 1U << cache->hashbits; 4351da177e4SLinus Torvalds int i; 4361da177e4SLinus Torvalds 4371da177e4SLinus Torvalds spin_lock(&rpc_credcache_lock); 4389499b434STrond Myklebust spin_lock(&cache->lock); 439988664a0STrond Myklebust for (i = 0; i < hashsize; i++) { 440e092bdcdSTrond Myklebust head = &cache->hashtable[i]; 441e092bdcdSTrond Myklebust while (!hlist_empty(head)) { 442e092bdcdSTrond Myklebust cred = hlist_entry(head->first, struct rpc_cred, cr_hash); 44331be5bf1STrond Myklebust rpcauth_unhash_cred_locked(cred); 44495cd6232STrond Myklebust /* Note: We now hold a reference to cred */ 44595cd6232STrond Myklebust rpcauth_lru_remove_locked(cred); 44695cd6232STrond Myklebust list_add_tail(&cred->cr_lru, &free); 4471da177e4SLinus Torvalds } 4481da177e4SLinus Torvalds } 4499499b434STrond Myklebust spin_unlock(&cache->lock); 4501da177e4SLinus Torvalds spin_unlock(&rpc_credcache_lock); 4511da177e4SLinus Torvalds rpcauth_destroy_credlist(&free); 4521da177e4SLinus Torvalds } 4531da177e4SLinus Torvalds 4543ab9bb72STrond Myklebust /* 4553ab9bb72STrond Myklebust * Destroy the RPC credential cache 4563ab9bb72STrond Myklebust */ 4573ab9bb72STrond Myklebust void 4583ab9bb72STrond Myklebust rpcauth_destroy_credcache(struct rpc_auth *auth) 4593ab9bb72STrond Myklebust { 4603ab9bb72STrond Myklebust struct rpc_cred_cache *cache = auth->au_credcache; 4613ab9bb72STrond Myklebust 4623ab9bb72STrond Myklebust if (cache) { 4633ab9bb72STrond Myklebust auth->au_credcache = NULL; 4643ab9bb72STrond Myklebust rpcauth_clear_credcache(cache); 465241269bdSTrond Myklebust kfree(cache->hashtable); 4663ab9bb72STrond Myklebust kfree(cache); 4673ab9bb72STrond Myklebust } 4683ab9bb72STrond Myklebust } 469e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_destroy_credcache); 4703ab9bb72STrond Myklebust 471d2b83141STrond Myklebust 472d2b83141STrond Myklebust #define RPC_AUTH_EXPIRY_MORATORIUM (60 * HZ) 473d2b83141STrond Myklebust 4741da177e4SLinus Torvalds /* 4751da177e4SLinus Torvalds * Remove stale credentials. Avoid sleeping inside the loop. 4761da177e4SLinus Torvalds */ 47770534a73SDave Chinner static long 478f5c2187cSTrond Myklebust rpcauth_prune_expired(struct list_head *free, int nr_to_scan) 4791da177e4SLinus Torvalds { 480eac0d18dSTrond Myklebust struct rpc_cred *cred, *next; 481d2b83141STrond Myklebust unsigned long expired = jiffies - RPC_AUTH_EXPIRY_MORATORIUM; 48270534a73SDave Chinner long freed = 0; 4831da177e4SLinus Torvalds 484eac0d18dSTrond Myklebust list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) { 485eac0d18dSTrond Myklebust 48620673406STrond Myklebust if (nr_to_scan-- == 0) 48720673406STrond Myklebust break; 48879b18181STrond Myklebust if (refcount_read(&cred->cr_count) > 1) { 48995cd6232STrond Myklebust rpcauth_lru_remove_locked(cred); 49095cd6232STrond Myklebust continue; 49195cd6232STrond Myklebust } 49293a05e65STrond Myklebust /* 49393a05e65STrond Myklebust * Enforce a 60 second garbage collection moratorium 49493a05e65STrond Myklebust * Note that the cred_unused list must be time-ordered. 49593a05e65STrond Myklebust */ 49695cd6232STrond Myklebust if (!time_in_range(cred->cr_expire, expired, jiffies)) 49795cd6232STrond Myklebust continue; 49895cd6232STrond Myklebust if (!rpcauth_unhash_cred(cred)) 499e092bdcdSTrond Myklebust continue; 500eac0d18dSTrond Myklebust 50195cd6232STrond Myklebust rpcauth_lru_remove_locked(cred); 50295cd6232STrond Myklebust freed++; 503e092bdcdSTrond Myklebust list_add_tail(&cred->cr_lru, free); 5041da177e4SLinus Torvalds } 50595cd6232STrond Myklebust return freed ? freed : SHRINK_STOP; 5061da177e4SLinus Torvalds } 507e092bdcdSTrond Myklebust 508bae6746fSTrond Myklebust static unsigned long 509bae6746fSTrond Myklebust rpcauth_cache_do_shrink(int nr_to_scan) 510bae6746fSTrond Myklebust { 511bae6746fSTrond Myklebust LIST_HEAD(free); 512bae6746fSTrond Myklebust unsigned long freed; 513bae6746fSTrond Myklebust 514bae6746fSTrond Myklebust spin_lock(&rpc_credcache_lock); 515bae6746fSTrond Myklebust freed = rpcauth_prune_expired(&free, nr_to_scan); 516bae6746fSTrond Myklebust spin_unlock(&rpc_credcache_lock); 517bae6746fSTrond Myklebust rpcauth_destroy_credlist(&free); 518bae6746fSTrond Myklebust 519bae6746fSTrond Myklebust return freed; 520bae6746fSTrond Myklebust } 521bae6746fSTrond Myklebust 522e092bdcdSTrond Myklebust /* 523f5c2187cSTrond Myklebust * Run memory cache shrinker. 524e092bdcdSTrond Myklebust */ 52570534a73SDave Chinner static unsigned long 52670534a73SDave Chinner rpcauth_cache_shrink_scan(struct shrinker *shrink, struct shrink_control *sc) 52770534a73SDave Chinner 528e092bdcdSTrond Myklebust { 52970534a73SDave Chinner if ((sc->gfp_mask & GFP_KERNEL) != GFP_KERNEL) 53070534a73SDave Chinner return SHRINK_STOP; 53170534a73SDave Chinner 53270534a73SDave Chinner /* nothing left, don't come back */ 533f5c2187cSTrond Myklebust if (list_empty(&cred_unused)) 53470534a73SDave Chinner return SHRINK_STOP; 53570534a73SDave Chinner 536bae6746fSTrond Myklebust return rpcauth_cache_do_shrink(sc->nr_to_scan); 53770534a73SDave Chinner } 53870534a73SDave Chinner 53970534a73SDave Chinner static unsigned long 54070534a73SDave Chinner rpcauth_cache_shrink_count(struct shrinker *shrink, struct shrink_control *sc) 54170534a73SDave Chinner 54270534a73SDave Chinner { 5434c3ffd05SNeilBrown return number_cred_unused * sysctl_vfs_cache_pressure / 100; 5441da177e4SLinus Torvalds } 5451da177e4SLinus Torvalds 546bae6746fSTrond Myklebust static void 547bae6746fSTrond Myklebust rpcauth_cache_enforce_limit(void) 548bae6746fSTrond Myklebust { 549bae6746fSTrond Myklebust unsigned long diff; 550bae6746fSTrond Myklebust unsigned int nr_to_scan; 551bae6746fSTrond Myklebust 552bae6746fSTrond Myklebust if (number_cred_unused <= auth_max_cred_cachesize) 553bae6746fSTrond Myklebust return; 554bae6746fSTrond Myklebust diff = number_cred_unused - auth_max_cred_cachesize; 555bae6746fSTrond Myklebust nr_to_scan = 100; 556bae6746fSTrond Myklebust if (diff < nr_to_scan) 557bae6746fSTrond Myklebust nr_to_scan = diff; 558bae6746fSTrond Myklebust rpcauth_cache_do_shrink(nr_to_scan); 559bae6746fSTrond Myklebust } 560bae6746fSTrond Myklebust 5611da177e4SLinus Torvalds /* 5621da177e4SLinus Torvalds * Look up a process' credentials in the authentication cache 5631da177e4SLinus Torvalds */ 5641da177e4SLinus Torvalds struct rpc_cred * 5651da177e4SLinus Torvalds rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, 5663c6e0bc8SJeff Layton int flags, gfp_t gfp) 5671da177e4SLinus Torvalds { 568e092bdcdSTrond Myklebust LIST_HEAD(free); 5691da177e4SLinus Torvalds struct rpc_cred_cache *cache = auth->au_credcache; 57031be5bf1STrond Myklebust struct rpc_cred *cred = NULL, 57131be5bf1STrond Myklebust *entry, *new; 57225337fdcSTrond Myklebust unsigned int nr; 57325337fdcSTrond Myklebust 57466cbd4baSFrank Sorenson nr = auth->au_ops->hash_cred(acred, cache->hashbits); 5751da177e4SLinus Torvalds 57631be5bf1STrond Myklebust rcu_read_lock(); 577b67bfe0dSSasha Levin hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) { 57831be5bf1STrond Myklebust if (!entry->cr_ops->crmatch(acred, entry, flags)) 57931be5bf1STrond Myklebust continue; 58031be5bf1STrond Myklebust cred = get_rpccred(entry); 58107d02a67STrond Myklebust if (cred) 58231be5bf1STrond Myklebust break; 58331be5bf1STrond Myklebust } 58431be5bf1STrond Myklebust rcu_read_unlock(); 58531be5bf1STrond Myklebust 5869499b434STrond Myklebust if (cred != NULL) 58731be5bf1STrond Myklebust goto found; 58831be5bf1STrond Myklebust 5893c6e0bc8SJeff Layton new = auth->au_ops->crcreate(auth, acred, flags, gfp); 59031be5bf1STrond Myklebust if (IS_ERR(new)) { 59131be5bf1STrond Myklebust cred = new; 59231be5bf1STrond Myklebust goto out; 59331be5bf1STrond Myklebust } 59431be5bf1STrond Myklebust 5959499b434STrond Myklebust spin_lock(&cache->lock); 596b67bfe0dSSasha Levin hlist_for_each_entry(entry, &cache->hashtable[nr], cr_hash) { 597e092bdcdSTrond Myklebust if (!entry->cr_ops->crmatch(acred, entry, flags)) 598e092bdcdSTrond Myklebust continue; 599e092bdcdSTrond Myklebust cred = get_rpccred(entry); 60007d02a67STrond Myklebust if (cred) 6011da177e4SLinus Torvalds break; 6021da177e4SLinus Torvalds } 60331be5bf1STrond Myklebust if (cred == NULL) { 60431be5bf1STrond Myklebust cred = new; 60531be5bf1STrond Myklebust set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); 60679b18181STrond Myklebust refcount_inc(&cred->cr_count); 60731be5bf1STrond Myklebust hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]); 60831be5bf1STrond Myklebust } else 609e092bdcdSTrond Myklebust list_add_tail(&new->cr_lru, &free); 6109499b434STrond Myklebust spin_unlock(&cache->lock); 611bae6746fSTrond Myklebust rpcauth_cache_enforce_limit(); 61231be5bf1STrond Myklebust found: 613f64f9e71SJoe Perches if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && 614f64f9e71SJoe Perches cred->cr_ops->cr_init != NULL && 615f64f9e71SJoe Perches !(flags & RPCAUTH_LOOKUP_NEW)) { 616fba3bad4STrond Myklebust int res = cred->cr_ops->cr_init(auth, cred); 617fba3bad4STrond Myklebust if (res < 0) { 618fba3bad4STrond Myklebust put_rpccred(cred); 619fba3bad4STrond Myklebust cred = ERR_PTR(res); 620fba3bad4STrond Myklebust } 6211da177e4SLinus Torvalds } 62231be5bf1STrond Myklebust rpcauth_destroy_credlist(&free); 62331be5bf1STrond Myklebust out: 62431be5bf1STrond Myklebust return cred; 6251da177e4SLinus Torvalds } 626e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache); 6271da177e4SLinus Torvalds 6281da177e4SLinus Torvalds struct rpc_cred * 6298a317760STrond Myklebust rpcauth_lookupcred(struct rpc_auth *auth, int flags) 6301da177e4SLinus Torvalds { 63186a264abSDavid Howells struct auth_cred acred; 6321da177e4SLinus Torvalds struct rpc_cred *ret; 63386a264abSDavid Howells const struct cred *cred = current_cred(); 6341da177e4SLinus Torvalds 63586a264abSDavid Howells memset(&acred, 0, sizeof(acred)); 63697f68c6bSNeilBrown acred.cred = cred; 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); 64879b18181STrond Myklebust refcount_set(&cred->cr_count, 1); 6495fe4755eSTrond Myklebust cred->cr_auth = auth; 6502edd8d74SNeilBrown cred->cr_flags = 0; 6515fe4755eSTrond Myklebust cred->cr_ops = ops; 6525fe4755eSTrond Myklebust cred->cr_expire = jiffies; 65397f68c6bSNeilBrown cred->cr_cred = get_cred(acred->cred); 6545fe4755eSTrond Myklebust } 655e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_cred); 6565fe4755eSTrond Myklebust 6578572b8e2STrond Myklebust static struct rpc_cred * 6585d351754STrond Myklebust rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags) 6591da177e4SLinus Torvalds { 6601be27f36STrond Myklebust struct rpc_auth *auth = task->tk_client->cl_auth; 6611da177e4SLinus Torvalds struct auth_cred acred = { 66297f68c6bSNeilBrown .cred = get_task_cred(&init_task), 6631da177e4SLinus Torvalds }; 66497f68c6bSNeilBrown struct rpc_cred *ret; 6651da177e4SLinus Torvalds 66697f68c6bSNeilBrown ret = auth->au_ops->lookup_cred(auth, &acred, lookupflags); 66797f68c6bSNeilBrown put_cred(acred.cred); 66897f68c6bSNeilBrown return ret; 669af093835STrond Myklebust } 670af093835STrond Myklebust 6718572b8e2STrond Myklebust static struct rpc_cred * 6725e16923bSNeilBrown rpcauth_bind_machine_cred(struct rpc_task *task, int lookupflags) 6735e16923bSNeilBrown { 6745e16923bSNeilBrown struct rpc_auth *auth = task->tk_client->cl_auth; 6755e16923bSNeilBrown struct auth_cred acred = { 6765e16923bSNeilBrown .principal = task->tk_client->cl_principal, 6775e16923bSNeilBrown .cred = init_task.cred, 6785e16923bSNeilBrown }; 6795e16923bSNeilBrown 6805e16923bSNeilBrown if (!acred.principal) 6815e16923bSNeilBrown return NULL; 6825e16923bSNeilBrown return auth->au_ops->lookup_cred(auth, &acred, lookupflags); 6835e16923bSNeilBrown } 6845e16923bSNeilBrown 6855e16923bSNeilBrown static struct rpc_cred * 6865d351754STrond Myklebust rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags) 687af093835STrond Myklebust { 688af093835STrond Myklebust struct rpc_auth *auth = task->tk_client->cl_auth; 689af093835STrond Myklebust 6908572b8e2STrond Myklebust return rpcauth_lookupcred(auth, lookupflags); 6911da177e4SLinus Torvalds } 6921da177e4SLinus Torvalds 693a17c2153STrond Myklebust static int 694a52458b4SNeilBrown rpcauth_bindcred(struct rpc_task *task, const struct cred *cred, int flags) 6951da177e4SLinus Torvalds { 696a17c2153STrond Myklebust struct rpc_rqst *req = task->tk_rqstp; 6975e16923bSNeilBrown struct rpc_cred *new = NULL; 6985d351754STrond Myklebust int lookupflags = 0; 699a52458b4SNeilBrown struct rpc_auth *auth = task->tk_client->cl_auth; 700a52458b4SNeilBrown struct auth_cred acred = { 701a52458b4SNeilBrown .cred = cred, 702a52458b4SNeilBrown }; 7035d351754STrond Myklebust 7045d351754STrond Myklebust if (flags & RPC_TASK_ASYNC) 7055d351754STrond Myklebust lookupflags |= RPCAUTH_LOOKUP_NEW; 7061de7eea9SNeilBrown if (task->tk_op_cred) 7071de7eea9SNeilBrown /* Task must use exactly this rpc_cred */ 708d6efccd9SNeilBrown new = get_rpccred(task->tk_op_cred); 7091de7eea9SNeilBrown else if (cred != NULL && cred != &machine_cred) 710a52458b4SNeilBrown new = auth->au_ops->lookup_cred(auth, &acred, lookupflags); 7115e16923bSNeilBrown else if (cred == &machine_cred) 7125e16923bSNeilBrown new = rpcauth_bind_machine_cred(task, lookupflags); 7135e16923bSNeilBrown 7145e16923bSNeilBrown /* If machine cred couldn't be bound, try a root cred */ 7155e16923bSNeilBrown if (new) 7165e16923bSNeilBrown ; 7175e16923bSNeilBrown else if (cred == &machine_cred || (flags & RPC_TASK_ROOTCREDS)) 7188572b8e2STrond Myklebust new = rpcauth_bind_root_cred(task, lookupflags); 719a68a72e1SNeilBrown else if (flags & RPC_TASK_NULLCREDS) 720a68a72e1SNeilBrown new = authnull_ops.lookup_cred(NULL, NULL, 0); 7214ccda2cdSTrond Myklebust else 7228572b8e2STrond Myklebust new = rpcauth_bind_new_cred(task, lookupflags); 7238572b8e2STrond Myklebust if (IS_ERR(new)) 7248572b8e2STrond Myklebust return PTR_ERR(new); 725a17c2153STrond Myklebust put_rpccred(req->rq_cred); 726a17c2153STrond Myklebust req->rq_cred = new; 7278572b8e2STrond Myklebust return 0; 7281da177e4SLinus Torvalds } 7291da177e4SLinus Torvalds 7301da177e4SLinus Torvalds void 7311da177e4SLinus Torvalds put_rpccred(struct rpc_cred *cred) 7321da177e4SLinus Torvalds { 7339a8f6b5eSTrond Myklebust if (cred == NULL) 7349a8f6b5eSTrond Myklebust return; 73595cd6232STrond Myklebust rcu_read_lock(); 73679b18181STrond Myklebust if (refcount_dec_and_test(&cred->cr_count)) 73795cd6232STrond Myklebust goto destroy; 73879b18181STrond Myklebust if (refcount_read(&cred->cr_count) != 1 || 73995cd6232STrond Myklebust !test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags)) 74095cd6232STrond Myklebust goto out; 741f0380f3dSTrond Myklebust if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) { 742e092bdcdSTrond Myklebust cred->cr_expire = jiffies; 74395cd6232STrond Myklebust rpcauth_lru_add(cred); 74495cd6232STrond Myklebust /* Race breaker */ 74595cd6232STrond Myklebust if (unlikely(!test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags))) 74695cd6232STrond Myklebust rpcauth_lru_remove(cred); 74795cd6232STrond Myklebust } else if (rpcauth_unhash_cred(cred)) { 74895cd6232STrond Myklebust rpcauth_lru_remove(cred); 74979b18181STrond Myklebust if (refcount_dec_and_test(&cred->cr_count)) 75095cd6232STrond Myklebust goto destroy; 751f0380f3dSTrond Myklebust } 75295cd6232STrond Myklebust out: 75395cd6232STrond Myklebust rcu_read_unlock(); 754f0380f3dSTrond Myklebust return; 75595cd6232STrond Myklebust destroy: 75695cd6232STrond Myklebust rcu_read_unlock(); 75795cd6232STrond Myklebust cred->cr_ops->crdestroy(cred); 7581da177e4SLinus Torvalds } 759e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(put_rpccred); 7601da177e4SLinus Torvalds 761e8680a24SChuck Lever /** 762e8680a24SChuck Lever * rpcauth_marshcred - Append RPC credential to end of @xdr 763e8680a24SChuck Lever * @task: controlling RPC task 764e8680a24SChuck Lever * @xdr: xdr_stream containing initial portion of RPC Call header 765e8680a24SChuck Lever * 766e8680a24SChuck Lever * On success, an appropriate verifier is added to @xdr, @xdr is 767e8680a24SChuck Lever * updated to point past the verifier, and zero is returned. 768e8680a24SChuck Lever * Otherwise, @xdr is in an undefined state and a negative errno 769e8680a24SChuck Lever * is returned. 770e8680a24SChuck Lever */ 771e8680a24SChuck Lever int rpcauth_marshcred(struct rpc_task *task, struct xdr_stream *xdr) 7721da177e4SLinus Torvalds { 773e8680a24SChuck Lever const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops; 7741da177e4SLinus Torvalds 775e8680a24SChuck Lever return ops->crmarshal(task, xdr); 7761da177e4SLinus Torvalds } 7771da177e4SLinus Torvalds 778e8680a24SChuck Lever /** 779e8680a24SChuck Lever * rpcauth_wrap_req_encode - XDR encode the RPC procedure 780e8680a24SChuck Lever * @task: controlling RPC task 781e8680a24SChuck Lever * @xdr: stream where on-the-wire bytes are to be marshalled 782e8680a24SChuck Lever * 783e8680a24SChuck Lever * On success, @xdr contains the encoded and wrapped message. 784e8680a24SChuck Lever * Otherwise, @xdr is in an undefined state. 785e8680a24SChuck Lever */ 786e8680a24SChuck Lever int rpcauth_wrap_req_encode(struct rpc_task *task, struct xdr_stream *xdr) 7879f06c719SChuck Lever { 788e8680a24SChuck Lever kxdreproc_t encode = task->tk_msg.rpc_proc->p_encode; 7899f06c719SChuck Lever 790e8680a24SChuck Lever encode(task->tk_rqstp, xdr, task->tk_msg.rpc_argp); 7919f06c719SChuck Lever return 0; 7921da177e4SLinus Torvalds } 793e8680a24SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_wrap_req_encode); 794e8680a24SChuck Lever 795e8680a24SChuck Lever /** 796e8680a24SChuck Lever * rpcauth_wrap_req - XDR encode and wrap the RPC procedure 797e8680a24SChuck Lever * @task: controlling RPC task 798e8680a24SChuck Lever * @xdr: stream where on-the-wire bytes are to be marshalled 799e8680a24SChuck Lever * 800e8680a24SChuck Lever * On success, @xdr contains the encoded and wrapped message, 801e8680a24SChuck Lever * and zero is returned. Otherwise, @xdr is in an undefined 802e8680a24SChuck Lever * state and a negative errno is returned. 803e8680a24SChuck Lever */ 804e8680a24SChuck Lever int rpcauth_wrap_req(struct rpc_task *task, struct xdr_stream *xdr) 805e8680a24SChuck Lever { 806e8680a24SChuck Lever const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops; 807e8680a24SChuck Lever 808e8680a24SChuck Lever return ops->crwrap_req(task, xdr); 809e8680a24SChuck Lever } 8101da177e4SLinus Torvalds 811*a0584ee9SChuck Lever /** 812*a0584ee9SChuck Lever * rpcauth_checkverf - Validate verifier in RPC Reply header 813*a0584ee9SChuck Lever * @task: controlling RPC task 814*a0584ee9SChuck Lever * @xdr: xdr_stream containing RPC Reply header 815*a0584ee9SChuck Lever * 816*a0584ee9SChuck Lever * On success, @xdr is updated to point past the verifier and 817*a0584ee9SChuck Lever * zero is returned. Otherwise, @xdr is in an undefined state 818*a0584ee9SChuck Lever * and a negative errno is returned. 819*a0584ee9SChuck Lever */ 820*a0584ee9SChuck Lever int 821*a0584ee9SChuck Lever rpcauth_checkverf(struct rpc_task *task, struct xdr_stream *xdr) 822bf269551SChuck Lever { 823*a0584ee9SChuck Lever const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops; 824bf269551SChuck Lever 825*a0584ee9SChuck Lever return ops->crvalidate(task, xdr); 826bf269551SChuck Lever } 827bf269551SChuck Lever 828*a0584ee9SChuck Lever /** 829*a0584ee9SChuck Lever * rpcauth_unwrap_resp_decode - Invoke XDR decode function 830*a0584ee9SChuck Lever * @task: controlling RPC task 831*a0584ee9SChuck Lever * @xdr: stream where the Reply message resides 832*a0584ee9SChuck Lever * 833*a0584ee9SChuck Lever * Returns zero on success; otherwise a negative errno is returned. 834*a0584ee9SChuck Lever */ 8351da177e4SLinus Torvalds int 836*a0584ee9SChuck Lever rpcauth_unwrap_resp_decode(struct rpc_task *task, struct xdr_stream *xdr) 8371da177e4SLinus Torvalds { 838*a0584ee9SChuck Lever kxdrdproc_t decode = task->tk_msg.rpc_proc->p_decode; 8391da177e4SLinus Torvalds 840*a0584ee9SChuck Lever return decode(task->tk_rqstp, xdr, task->tk_msg.rpc_resp); 841*a0584ee9SChuck Lever } 842*a0584ee9SChuck Lever EXPORT_SYMBOL_GPL(rpcauth_unwrap_resp_decode); 843*a0584ee9SChuck Lever 844*a0584ee9SChuck Lever /** 845*a0584ee9SChuck Lever * rpcauth_unwrap_resp - Invoke unwrap and decode function for the cred 846*a0584ee9SChuck Lever * @task: controlling RPC task 847*a0584ee9SChuck Lever * @xdr: stream where the Reply message resides 848*a0584ee9SChuck Lever * 849*a0584ee9SChuck Lever * Returns zero on success; otherwise a negative errno is returned. 850*a0584ee9SChuck Lever */ 851*a0584ee9SChuck Lever int 852*a0584ee9SChuck Lever rpcauth_unwrap_resp(struct rpc_task *task, struct xdr_stream *xdr) 853*a0584ee9SChuck Lever { 854*a0584ee9SChuck Lever const struct rpc_credops *ops = task->tk_rqstp->rq_cred->cr_ops; 855*a0584ee9SChuck Lever 856*a0584ee9SChuck Lever return ops->crunwrap_resp(task, xdr); 8571da177e4SLinus Torvalds } 8581da177e4SLinus Torvalds 8593021a5bbSTrond Myklebust bool 8603021a5bbSTrond Myklebust rpcauth_xmit_need_reencode(struct rpc_task *task) 8613021a5bbSTrond Myklebust { 8623021a5bbSTrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 8633021a5bbSTrond Myklebust 8643021a5bbSTrond Myklebust if (!cred || !cred->cr_ops->crneed_reencode) 8653021a5bbSTrond Myklebust return false; 8663021a5bbSTrond Myklebust return cred->cr_ops->crneed_reencode(task); 8673021a5bbSTrond Myklebust } 8683021a5bbSTrond Myklebust 8691da177e4SLinus Torvalds int 8701da177e4SLinus Torvalds rpcauth_refreshcred(struct rpc_task *task) 8711da177e4SLinus Torvalds { 8729a84d380STrond Myklebust struct rpc_cred *cred; 8731da177e4SLinus Torvalds int err; 8741da177e4SLinus Torvalds 875a17c2153STrond Myklebust cred = task->tk_rqstp->rq_cred; 876a17c2153STrond Myklebust if (cred == NULL) { 877a17c2153STrond Myklebust err = rpcauth_bindcred(task, task->tk_msg.rpc_cred, task->tk_flags); 878a17c2153STrond Myklebust if (err < 0) 879a17c2153STrond Myklebust goto out; 880a17c2153STrond Myklebust cred = task->tk_rqstp->rq_cred; 881f81c6224SJoe Perches } 8820bbacc40SChuck Lever 8831da177e4SLinus Torvalds err = cred->cr_ops->crrefresh(task); 884a17c2153STrond Myklebust out: 8851da177e4SLinus Torvalds if (err < 0) 8861da177e4SLinus Torvalds task->tk_status = err; 8871da177e4SLinus Torvalds return err; 8881da177e4SLinus Torvalds } 8891da177e4SLinus Torvalds 8901da177e4SLinus Torvalds void 8911da177e4SLinus Torvalds rpcauth_invalcred(struct rpc_task *task) 8921da177e4SLinus Torvalds { 893a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 894fc432dd9STrond Myklebust 895fc432dd9STrond Myklebust if (cred) 896fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 8971da177e4SLinus Torvalds } 8981da177e4SLinus Torvalds 8991da177e4SLinus Torvalds int 9001da177e4SLinus Torvalds rpcauth_uptodatecred(struct rpc_task *task) 9011da177e4SLinus Torvalds { 902a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 903fc432dd9STrond Myklebust 904fc432dd9STrond Myklebust return cred == NULL || 905fc432dd9STrond Myklebust test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0; 9061da177e4SLinus Torvalds } 907f5c2187cSTrond Myklebust 9088e1f936bSRusty Russell static struct shrinker rpc_cred_shrinker = { 90970534a73SDave Chinner .count_objects = rpcauth_cache_shrink_count, 91070534a73SDave Chinner .scan_objects = rpcauth_cache_shrink_scan, 9118e1f936bSRusty Russell .seeks = DEFAULT_SEEKS, 9128e1f936bSRusty Russell }; 913f5c2187cSTrond Myklebust 9145d8d9a4dSTrond Myklebust int __init rpcauth_init_module(void) 915f5c2187cSTrond Myklebust { 9165d8d9a4dSTrond Myklebust int err; 9175d8d9a4dSTrond Myklebust 9185d8d9a4dSTrond Myklebust err = rpc_init_authunix(); 9195d8d9a4dSTrond Myklebust if (err < 0) 9205d8d9a4dSTrond Myklebust goto out1; 9212864486bSKinglong Mee err = register_shrinker(&rpc_cred_shrinker); 9222864486bSKinglong Mee if (err < 0) 92389a4f758SNeilBrown goto out2; 9245d8d9a4dSTrond Myklebust return 0; 9255d8d9a4dSTrond Myklebust out2: 9265d8d9a4dSTrond Myklebust rpc_destroy_authunix(); 9275d8d9a4dSTrond Myklebust out1: 9285d8d9a4dSTrond Myklebust return err; 929f5c2187cSTrond Myklebust } 930f5c2187cSTrond Myklebust 931c135e84aSStephen Rothwell void rpcauth_remove_module(void) 932f5c2187cSTrond Myklebust { 9335d8d9a4dSTrond Myklebust rpc_destroy_authunix(); 9348e1f936bSRusty Russell unregister_shrinker(&rpc_cred_shrinker); 935f5c2187cSTrond Myklebust } 936