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> 111da177e4SLinus Torvalds #include <linux/module.h> 121da177e4SLinus Torvalds #include <linux/slab.h> 131da177e4SLinus Torvalds #include <linux/errno.h> 1425337fdcSTrond Myklebust #include <linux/hash.h> 151da177e4SLinus Torvalds #include <linux/sunrpc/clnt.h> 161da177e4SLinus Torvalds #include <linux/spinlock.h> 171da177e4SLinus Torvalds 181da177e4SLinus Torvalds #ifdef RPC_DEBUG 191da177e4SLinus Torvalds # define RPCDBG_FACILITY RPCDBG_AUTH 201da177e4SLinus Torvalds #endif 211da177e4SLinus Torvalds 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 31fc1b356fSTrond Myklebust static DEFINE_SPINLOCK(rpc_authflavor_lock); 32f1c0a861STrond Myklebust static const struct rpc_authops *auth_flavors[RPC_AUTH_MAXFLAVOR] = { 331da177e4SLinus Torvalds &authnull_ops, /* AUTH_NULL */ 341da177e4SLinus Torvalds &authunix_ops, /* AUTH_UNIX */ 351da177e4SLinus Torvalds NULL, /* others can be loadable modules */ 361da177e4SLinus Torvalds }; 371da177e4SLinus Torvalds 38e092bdcdSTrond Myklebust static LIST_HEAD(cred_unused); 39f5c2187cSTrond Myklebust static unsigned long number_cred_unused; 40e092bdcdSTrond Myklebust 41241269bdSTrond Myklebust #define MAX_HASHTABLE_BITS (10) 428e4e15d4SStephen Rothwell static int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp) 43241269bdSTrond Myklebust { 44241269bdSTrond Myklebust unsigned long num; 45241269bdSTrond Myklebust unsigned int nbits; 46241269bdSTrond Myklebust int ret; 47241269bdSTrond Myklebust 48241269bdSTrond Myklebust if (!val) 49241269bdSTrond Myklebust goto out_inval; 50241269bdSTrond Myklebust ret = strict_strtoul(val, 0, &num); 51241269bdSTrond Myklebust if (ret == -EINVAL) 52241269bdSTrond Myklebust goto out_inval; 53241269bdSTrond Myklebust nbits = fls(num); 54241269bdSTrond Myklebust if (num > (1U << nbits)) 55241269bdSTrond Myklebust nbits++; 56241269bdSTrond Myklebust if (nbits > MAX_HASHTABLE_BITS || nbits < 2) 57241269bdSTrond Myklebust goto out_inval; 58241269bdSTrond Myklebust *(unsigned int *)kp->arg = nbits; 59241269bdSTrond Myklebust return 0; 60241269bdSTrond Myklebust out_inval: 61241269bdSTrond Myklebust return -EINVAL; 62241269bdSTrond Myklebust } 63241269bdSTrond Myklebust 648e4e15d4SStephen Rothwell static int param_get_hashtbl_sz(char *buffer, const struct kernel_param *kp) 65241269bdSTrond Myklebust { 66241269bdSTrond Myklebust unsigned int nbits; 67241269bdSTrond Myklebust 68241269bdSTrond Myklebust nbits = *(unsigned int *)kp->arg; 69241269bdSTrond Myklebust return sprintf(buffer, "%u", 1U << nbits); 70241269bdSTrond Myklebust } 71241269bdSTrond Myklebust 72241269bdSTrond Myklebust #define param_check_hashtbl_sz(name, p) __param_check(name, p, unsigned int); 73241269bdSTrond Myklebust 748e4e15d4SStephen Rothwell static struct kernel_param_ops param_ops_hashtbl_sz = { 758e4e15d4SStephen Rothwell .set = param_set_hashtbl_sz, 768e4e15d4SStephen Rothwell .get = param_get_hashtbl_sz, 778e4e15d4SStephen Rothwell }; 788e4e15d4SStephen Rothwell 79241269bdSTrond Myklebust module_param_named(auth_hashtable_size, auth_hashbits, hashtbl_sz, 0644); 80241269bdSTrond Myklebust MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size"); 81241269bdSTrond Myklebust 821da177e4SLinus Torvalds static u32 831da177e4SLinus Torvalds pseudoflavor_to_flavor(u32 flavor) { 841da177e4SLinus Torvalds if (flavor >= RPC_AUTH_MAXFLAVOR) 851da177e4SLinus Torvalds return RPC_AUTH_GSS; 861da177e4SLinus Torvalds return flavor; 871da177e4SLinus Torvalds } 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds int 90f1c0a861STrond Myklebust rpcauth_register(const struct rpc_authops *ops) 911da177e4SLinus Torvalds { 921da177e4SLinus Torvalds rpc_authflavor_t flavor; 93fc1b356fSTrond Myklebust int ret = -EPERM; 941da177e4SLinus Torvalds 951da177e4SLinus Torvalds if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR) 961da177e4SLinus Torvalds return -EINVAL; 97fc1b356fSTrond Myklebust spin_lock(&rpc_authflavor_lock); 98fc1b356fSTrond Myklebust if (auth_flavors[flavor] == NULL) { 991da177e4SLinus Torvalds auth_flavors[flavor] = ops; 100fc1b356fSTrond Myklebust ret = 0; 101fc1b356fSTrond Myklebust } 102fc1b356fSTrond Myklebust spin_unlock(&rpc_authflavor_lock); 103fc1b356fSTrond Myklebust return ret; 1041da177e4SLinus Torvalds } 105e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_register); 1061da177e4SLinus Torvalds 1071da177e4SLinus Torvalds int 108f1c0a861STrond Myklebust rpcauth_unregister(const struct rpc_authops *ops) 1091da177e4SLinus Torvalds { 1101da177e4SLinus Torvalds rpc_authflavor_t flavor; 111fc1b356fSTrond Myklebust int ret = -EPERM; 1121da177e4SLinus Torvalds 1131da177e4SLinus Torvalds if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR) 1141da177e4SLinus Torvalds return -EINVAL; 115fc1b356fSTrond Myklebust spin_lock(&rpc_authflavor_lock); 116fc1b356fSTrond Myklebust if (auth_flavors[flavor] == ops) { 1171da177e4SLinus Torvalds auth_flavors[flavor] = NULL; 118fc1b356fSTrond Myklebust ret = 0; 119fc1b356fSTrond Myklebust } 120fc1b356fSTrond Myklebust spin_unlock(&rpc_authflavor_lock); 121fc1b356fSTrond Myklebust return ret; 1221da177e4SLinus Torvalds } 123e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_unregister); 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds struct rpc_auth * 1261da177e4SLinus Torvalds rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt) 1271da177e4SLinus Torvalds { 1281da177e4SLinus Torvalds struct rpc_auth *auth; 129f1c0a861STrond Myklebust const struct rpc_authops *ops; 1301da177e4SLinus Torvalds u32 flavor = pseudoflavor_to_flavor(pseudoflavor); 1311da177e4SLinus Torvalds 132f344f6dfSOlaf Kirch auth = ERR_PTR(-EINVAL); 133f344f6dfSOlaf Kirch if (flavor >= RPC_AUTH_MAXFLAVOR) 134f344f6dfSOlaf Kirch goto out; 135f344f6dfSOlaf Kirch 136f344f6dfSOlaf Kirch if ((ops = auth_flavors[flavor]) == NULL) 137f344f6dfSOlaf Kirch request_module("rpc-auth-%u", flavor); 138fc1b356fSTrond Myklebust spin_lock(&rpc_authflavor_lock); 139fc1b356fSTrond Myklebust ops = auth_flavors[flavor]; 140fc1b356fSTrond Myklebust if (ops == NULL || !try_module_get(ops->owner)) { 141fc1b356fSTrond Myklebust spin_unlock(&rpc_authflavor_lock); 142f344f6dfSOlaf Kirch goto out; 143fc1b356fSTrond Myklebust } 144fc1b356fSTrond Myklebust spin_unlock(&rpc_authflavor_lock); 1451da177e4SLinus Torvalds auth = ops->create(clnt, pseudoflavor); 146fc1b356fSTrond Myklebust module_put(ops->owner); 1476a19275aSJ. Bruce Fields if (IS_ERR(auth)) 1486a19275aSJ. Bruce Fields return auth; 1491da177e4SLinus Torvalds if (clnt->cl_auth) 150de7a8ce3STrond Myklebust rpcauth_release(clnt->cl_auth); 1511da177e4SLinus Torvalds clnt->cl_auth = auth; 152f344f6dfSOlaf Kirch 153f344f6dfSOlaf Kirch out: 1541da177e4SLinus Torvalds return auth; 1551da177e4SLinus Torvalds } 156e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_create); 1571da177e4SLinus Torvalds 1581da177e4SLinus Torvalds void 159de7a8ce3STrond Myklebust rpcauth_release(struct rpc_auth *auth) 1601da177e4SLinus Torvalds { 1611da177e4SLinus Torvalds if (!atomic_dec_and_test(&auth->au_count)) 1621da177e4SLinus Torvalds return; 1631da177e4SLinus Torvalds auth->au_ops->destroy(auth); 1641da177e4SLinus Torvalds } 1651da177e4SLinus Torvalds 1661da177e4SLinus Torvalds static DEFINE_SPINLOCK(rpc_credcache_lock); 1671da177e4SLinus Torvalds 16831be5bf1STrond Myklebust static void 16931be5bf1STrond Myklebust rpcauth_unhash_cred_locked(struct rpc_cred *cred) 17031be5bf1STrond Myklebust { 17131be5bf1STrond Myklebust hlist_del_rcu(&cred->cr_hash); 17231be5bf1STrond Myklebust smp_mb__before_clear_bit(); 17331be5bf1STrond Myklebust clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); 17431be5bf1STrond Myklebust } 17531be5bf1STrond Myklebust 176f0380f3dSTrond Myklebust static int 1779499b434STrond Myklebust rpcauth_unhash_cred(struct rpc_cred *cred) 1789499b434STrond Myklebust { 1799499b434STrond Myklebust spinlock_t *cache_lock; 180f0380f3dSTrond Myklebust int ret; 1819499b434STrond Myklebust 1829499b434STrond Myklebust cache_lock = &cred->cr_auth->au_credcache->lock; 1839499b434STrond Myklebust spin_lock(cache_lock); 184f0380f3dSTrond Myklebust ret = atomic_read(&cred->cr_count) == 0; 185f0380f3dSTrond Myklebust if (ret) 1869499b434STrond Myklebust rpcauth_unhash_cred_locked(cred); 1879499b434STrond Myklebust spin_unlock(cache_lock); 188f0380f3dSTrond Myklebust return ret; 1899499b434STrond Myklebust } 1909499b434STrond Myklebust 1911da177e4SLinus Torvalds /* 1921da177e4SLinus Torvalds * Initialize RPC credential cache 1931da177e4SLinus Torvalds */ 1941da177e4SLinus Torvalds int 195f5c2187cSTrond Myklebust rpcauth_init_credcache(struct rpc_auth *auth) 1961da177e4SLinus Torvalds { 1971da177e4SLinus Torvalds struct rpc_cred_cache *new; 198988664a0STrond Myklebust unsigned int hashsize; 1991da177e4SLinus Torvalds 2008b3a7005SKris Katterjohn new = kmalloc(sizeof(*new), GFP_KERNEL); 2011da177e4SLinus Torvalds if (!new) 202241269bdSTrond Myklebust goto out_nocache; 203241269bdSTrond Myklebust new->hashbits = auth_hashbits; 204988664a0STrond Myklebust hashsize = 1U << new->hashbits; 205241269bdSTrond Myklebust new->hashtable = kcalloc(hashsize, sizeof(new->hashtable[0]), GFP_KERNEL); 206241269bdSTrond Myklebust if (!new->hashtable) 207241269bdSTrond Myklebust goto out_nohashtbl; 2089499b434STrond Myklebust spin_lock_init(&new->lock); 2091da177e4SLinus Torvalds auth->au_credcache = new; 2101da177e4SLinus Torvalds return 0; 211241269bdSTrond Myklebust out_nohashtbl: 212241269bdSTrond Myklebust kfree(new); 213241269bdSTrond Myklebust out_nocache: 214241269bdSTrond Myklebust return -ENOMEM; 2151da177e4SLinus Torvalds } 216e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_credcache); 2171da177e4SLinus Torvalds 2181da177e4SLinus Torvalds /* 2191da177e4SLinus Torvalds * Destroy a list of credentials 2201da177e4SLinus Torvalds */ 2211da177e4SLinus Torvalds static inline 222e092bdcdSTrond Myklebust void rpcauth_destroy_credlist(struct list_head *head) 2231da177e4SLinus Torvalds { 2241da177e4SLinus Torvalds struct rpc_cred *cred; 2251da177e4SLinus Torvalds 226e092bdcdSTrond Myklebust while (!list_empty(head)) { 227e092bdcdSTrond Myklebust cred = list_entry(head->next, struct rpc_cred, cr_lru); 228e092bdcdSTrond Myklebust list_del_init(&cred->cr_lru); 2291da177e4SLinus Torvalds put_rpccred(cred); 2301da177e4SLinus Torvalds } 2311da177e4SLinus Torvalds } 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds /* 2341da177e4SLinus Torvalds * Clear the RPC credential cache, and delete those credentials 2351da177e4SLinus Torvalds * that are not referenced. 2361da177e4SLinus Torvalds */ 2371da177e4SLinus Torvalds void 2383ab9bb72STrond Myklebust rpcauth_clear_credcache(struct rpc_cred_cache *cache) 2391da177e4SLinus Torvalds { 240e092bdcdSTrond Myklebust LIST_HEAD(free); 241e092bdcdSTrond Myklebust struct hlist_head *head; 2421da177e4SLinus Torvalds struct rpc_cred *cred; 243988664a0STrond Myklebust unsigned int hashsize = 1U << cache->hashbits; 2441da177e4SLinus Torvalds int i; 2451da177e4SLinus Torvalds 2461da177e4SLinus Torvalds spin_lock(&rpc_credcache_lock); 2479499b434STrond Myklebust spin_lock(&cache->lock); 248988664a0STrond Myklebust for (i = 0; i < hashsize; i++) { 249e092bdcdSTrond Myklebust head = &cache->hashtable[i]; 250e092bdcdSTrond Myklebust while (!hlist_empty(head)) { 251e092bdcdSTrond Myklebust cred = hlist_entry(head->first, struct rpc_cred, cr_hash); 252e092bdcdSTrond Myklebust get_rpccred(cred); 253f5c2187cSTrond Myklebust if (!list_empty(&cred->cr_lru)) { 254f5c2187cSTrond Myklebust list_del(&cred->cr_lru); 255f5c2187cSTrond Myklebust number_cred_unused--; 256f5c2187cSTrond Myklebust } 257f5c2187cSTrond Myklebust list_add_tail(&cred->cr_lru, &free); 25831be5bf1STrond Myklebust rpcauth_unhash_cred_locked(cred); 2591da177e4SLinus Torvalds } 2601da177e4SLinus Torvalds } 2619499b434STrond Myklebust spin_unlock(&cache->lock); 2621da177e4SLinus Torvalds spin_unlock(&rpc_credcache_lock); 2631da177e4SLinus Torvalds rpcauth_destroy_credlist(&free); 2641da177e4SLinus Torvalds } 2651da177e4SLinus Torvalds 2663ab9bb72STrond Myklebust /* 2673ab9bb72STrond Myklebust * Destroy the RPC credential cache 2683ab9bb72STrond Myklebust */ 2693ab9bb72STrond Myklebust void 2703ab9bb72STrond Myklebust rpcauth_destroy_credcache(struct rpc_auth *auth) 2713ab9bb72STrond Myklebust { 2723ab9bb72STrond Myklebust struct rpc_cred_cache *cache = auth->au_credcache; 2733ab9bb72STrond Myklebust 2743ab9bb72STrond Myklebust if (cache) { 2753ab9bb72STrond Myklebust auth->au_credcache = NULL; 2763ab9bb72STrond Myklebust rpcauth_clear_credcache(cache); 277241269bdSTrond Myklebust kfree(cache->hashtable); 2783ab9bb72STrond Myklebust kfree(cache); 2793ab9bb72STrond Myklebust } 2803ab9bb72STrond Myklebust } 281e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_destroy_credcache); 2823ab9bb72STrond Myklebust 283d2b83141STrond Myklebust 284d2b83141STrond Myklebust #define RPC_AUTH_EXPIRY_MORATORIUM (60 * HZ) 285d2b83141STrond Myklebust 2861da177e4SLinus Torvalds /* 2871da177e4SLinus Torvalds * Remove stale credentials. Avoid sleeping inside the loop. 2881da177e4SLinus Torvalds */ 289f5c2187cSTrond Myklebust static int 290f5c2187cSTrond Myklebust rpcauth_prune_expired(struct list_head *free, int nr_to_scan) 2911da177e4SLinus Torvalds { 2929499b434STrond Myklebust spinlock_t *cache_lock; 293eac0d18dSTrond Myklebust struct rpc_cred *cred, *next; 294d2b83141STrond Myklebust unsigned long expired = jiffies - RPC_AUTH_EXPIRY_MORATORIUM; 2951da177e4SLinus Torvalds 296eac0d18dSTrond Myklebust list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) { 297eac0d18dSTrond Myklebust 29820673406STrond Myklebust if (nr_to_scan-- == 0) 29920673406STrond Myklebust break; 30093a05e65STrond Myklebust /* 30193a05e65STrond Myklebust * Enforce a 60 second garbage collection moratorium 30293a05e65STrond Myklebust * Note that the cred_unused list must be time-ordered. 30393a05e65STrond Myklebust */ 3043d7b0894STrond Myklebust if (time_in_range(cred->cr_expire, expired, jiffies) && 305eac0d18dSTrond Myklebust test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) 30693a05e65STrond Myklebust return 0; 307eac0d18dSTrond Myklebust 308e092bdcdSTrond Myklebust list_del_init(&cred->cr_lru); 309f5c2187cSTrond Myklebust number_cred_unused--; 310e092bdcdSTrond Myklebust if (atomic_read(&cred->cr_count) != 0) 311e092bdcdSTrond Myklebust continue; 312eac0d18dSTrond Myklebust 3139499b434STrond Myklebust cache_lock = &cred->cr_auth->au_credcache->lock; 3149499b434STrond Myklebust spin_lock(cache_lock); 3159499b434STrond Myklebust if (atomic_read(&cred->cr_count) == 0) { 316e092bdcdSTrond Myklebust get_rpccred(cred); 317e092bdcdSTrond Myklebust list_add_tail(&cred->cr_lru, free); 31831be5bf1STrond Myklebust rpcauth_unhash_cred_locked(cred); 3191da177e4SLinus Torvalds } 3209499b434STrond Myklebust spin_unlock(cache_lock); 3219499b434STrond Myklebust } 32293a05e65STrond Myklebust return (number_cred_unused / 100) * sysctl_vfs_cache_pressure; 3231da177e4SLinus Torvalds } 324e092bdcdSTrond Myklebust 325e092bdcdSTrond Myklebust /* 326f5c2187cSTrond Myklebust * Run memory cache shrinker. 327e092bdcdSTrond Myklebust */ 328f5c2187cSTrond Myklebust static int 329567c7b0eSDave Chinner rpcauth_cache_shrinker(struct shrinker *shrink, int nr_to_scan, gfp_t gfp_mask) 330e092bdcdSTrond Myklebust { 331f5c2187cSTrond Myklebust LIST_HEAD(free); 332f5c2187cSTrond Myklebust int res; 333f5c2187cSTrond Myklebust 334d300a41eSTrond Myklebust if ((gfp_mask & GFP_KERNEL) != GFP_KERNEL) 335d300a41eSTrond Myklebust return (nr_to_scan == 0) ? 0 : -1; 336f5c2187cSTrond Myklebust if (list_empty(&cred_unused)) 337f5c2187cSTrond Myklebust return 0; 33831be5bf1STrond Myklebust spin_lock(&rpc_credcache_lock); 33993a05e65STrond Myklebust res = rpcauth_prune_expired(&free, nr_to_scan); 34031be5bf1STrond Myklebust spin_unlock(&rpc_credcache_lock); 341f5c2187cSTrond Myklebust rpcauth_destroy_credlist(&free); 342f5c2187cSTrond Myklebust return res; 3431da177e4SLinus Torvalds } 3441da177e4SLinus Torvalds 3451da177e4SLinus Torvalds /* 3461da177e4SLinus Torvalds * Look up a process' credentials in the authentication cache 3471da177e4SLinus Torvalds */ 3481da177e4SLinus Torvalds struct rpc_cred * 3491da177e4SLinus Torvalds rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, 3508a317760STrond Myklebust int flags) 3511da177e4SLinus Torvalds { 352e092bdcdSTrond Myklebust LIST_HEAD(free); 3531da177e4SLinus Torvalds struct rpc_cred_cache *cache = auth->au_credcache; 354e092bdcdSTrond Myklebust struct hlist_node *pos; 35531be5bf1STrond Myklebust struct rpc_cred *cred = NULL, 35631be5bf1STrond Myklebust *entry, *new; 35725337fdcSTrond Myklebust unsigned int nr; 35825337fdcSTrond Myklebust 359988664a0STrond Myklebust nr = hash_long(acred->uid, cache->hashbits); 3601da177e4SLinus Torvalds 36131be5bf1STrond Myklebust rcu_read_lock(); 36231be5bf1STrond Myklebust hlist_for_each_entry_rcu(entry, pos, &cache->hashtable[nr], cr_hash) { 36331be5bf1STrond Myklebust if (!entry->cr_ops->crmatch(acred, entry, flags)) 36431be5bf1STrond Myklebust continue; 3659499b434STrond Myklebust spin_lock(&cache->lock); 36631be5bf1STrond Myklebust if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) == 0) { 3679499b434STrond Myklebust spin_unlock(&cache->lock); 36831be5bf1STrond Myklebust continue; 36931be5bf1STrond Myklebust } 37031be5bf1STrond Myklebust cred = get_rpccred(entry); 3719499b434STrond Myklebust spin_unlock(&cache->lock); 37231be5bf1STrond Myklebust break; 37331be5bf1STrond Myklebust } 37431be5bf1STrond Myklebust rcu_read_unlock(); 37531be5bf1STrond Myklebust 3769499b434STrond Myklebust if (cred != NULL) 37731be5bf1STrond Myklebust goto found; 37831be5bf1STrond Myklebust 37931be5bf1STrond Myklebust new = auth->au_ops->crcreate(auth, acred, flags); 38031be5bf1STrond Myklebust if (IS_ERR(new)) { 38131be5bf1STrond Myklebust cred = new; 38231be5bf1STrond Myklebust goto out; 38331be5bf1STrond Myklebust } 38431be5bf1STrond Myklebust 3859499b434STrond Myklebust spin_lock(&cache->lock); 386e092bdcdSTrond Myklebust hlist_for_each_entry(entry, pos, &cache->hashtable[nr], cr_hash) { 387e092bdcdSTrond Myklebust if (!entry->cr_ops->crmatch(acred, entry, flags)) 388e092bdcdSTrond Myklebust continue; 389e092bdcdSTrond Myklebust cred = get_rpccred(entry); 3901da177e4SLinus Torvalds break; 3911da177e4SLinus Torvalds } 39231be5bf1STrond Myklebust if (cred == NULL) { 39331be5bf1STrond Myklebust cred = new; 39431be5bf1STrond Myklebust set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); 39531be5bf1STrond Myklebust hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]); 39631be5bf1STrond Myklebust } else 397e092bdcdSTrond Myklebust list_add_tail(&new->cr_lru, &free); 3989499b434STrond Myklebust spin_unlock(&cache->lock); 39931be5bf1STrond Myklebust found: 400f64f9e71SJoe Perches if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && 401f64f9e71SJoe Perches cred->cr_ops->cr_init != NULL && 402f64f9e71SJoe Perches !(flags & RPCAUTH_LOOKUP_NEW)) { 403fba3bad4STrond Myklebust int res = cred->cr_ops->cr_init(auth, cred); 404fba3bad4STrond Myklebust if (res < 0) { 405fba3bad4STrond Myklebust put_rpccred(cred); 406fba3bad4STrond Myklebust cred = ERR_PTR(res); 407fba3bad4STrond Myklebust } 4081da177e4SLinus Torvalds } 40931be5bf1STrond Myklebust rpcauth_destroy_credlist(&free); 41031be5bf1STrond Myklebust out: 41131be5bf1STrond Myklebust return cred; 4121da177e4SLinus Torvalds } 413e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache); 4141da177e4SLinus Torvalds 4151da177e4SLinus Torvalds struct rpc_cred * 4168a317760STrond Myklebust rpcauth_lookupcred(struct rpc_auth *auth, int flags) 4171da177e4SLinus Torvalds { 41886a264abSDavid Howells struct auth_cred acred; 4191da177e4SLinus Torvalds struct rpc_cred *ret; 42086a264abSDavid Howells const struct cred *cred = current_cred(); 4211da177e4SLinus Torvalds 4221da177e4SLinus Torvalds dprintk("RPC: looking up %s cred\n", 4231da177e4SLinus Torvalds auth->au_ops->au_name); 42486a264abSDavid Howells 42586a264abSDavid Howells memset(&acred, 0, sizeof(acred)); 42686a264abSDavid Howells acred.uid = cred->fsuid; 42786a264abSDavid Howells acred.gid = cred->fsgid; 42886a264abSDavid Howells acred.group_info = get_group_info(((struct cred *)cred)->group_info); 42986a264abSDavid Howells 4308a317760STrond Myklebust ret = auth->au_ops->lookup_cred(auth, &acred, flags); 4311da177e4SLinus Torvalds put_group_info(acred.group_info); 4321da177e4SLinus Torvalds return ret; 4331da177e4SLinus Torvalds } 4341da177e4SLinus Torvalds 4355fe4755eSTrond Myklebust void 4365fe4755eSTrond Myklebust rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred, 4375fe4755eSTrond Myklebust struct rpc_auth *auth, const struct rpc_credops *ops) 4385fe4755eSTrond Myklebust { 4395fe4755eSTrond Myklebust INIT_HLIST_NODE(&cred->cr_hash); 440e092bdcdSTrond Myklebust INIT_LIST_HEAD(&cred->cr_lru); 4415fe4755eSTrond Myklebust atomic_set(&cred->cr_count, 1); 4425fe4755eSTrond Myklebust cred->cr_auth = auth; 4435fe4755eSTrond Myklebust cred->cr_ops = ops; 4445fe4755eSTrond Myklebust cred->cr_expire = jiffies; 4455fe4755eSTrond Myklebust #ifdef RPC_DEBUG 4465fe4755eSTrond Myklebust cred->cr_magic = RPCAUTH_CRED_MAGIC; 4475fe4755eSTrond Myklebust #endif 4485fe4755eSTrond Myklebust cred->cr_uid = acred->uid; 4495fe4755eSTrond Myklebust } 450e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_cred); 4515fe4755eSTrond Myklebust 4528572b8e2STrond Myklebust struct rpc_cred * 4535d351754STrond Myklebust rpcauth_generic_bind_cred(struct rpc_task *task, struct rpc_cred *cred, int lookupflags) 4544ccda2cdSTrond Myklebust { 4554ccda2cdSTrond Myklebust dprintk("RPC: %5u holding %s cred %p\n", task->tk_pid, 4564ccda2cdSTrond Myklebust cred->cr_auth->au_ops->au_name, cred); 4578572b8e2STrond Myklebust return get_rpccred(cred); 4584ccda2cdSTrond Myklebust } 4595c691044STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_generic_bind_cred); 4604ccda2cdSTrond Myklebust 4618572b8e2STrond Myklebust static struct rpc_cred * 4625d351754STrond Myklebust rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags) 4631da177e4SLinus Torvalds { 4641be27f36STrond Myklebust struct rpc_auth *auth = task->tk_client->cl_auth; 4651da177e4SLinus Torvalds struct auth_cred acred = { 466af093835STrond Myklebust .uid = 0, 467af093835STrond Myklebust .gid = 0, 4681da177e4SLinus Torvalds }; 4691da177e4SLinus Torvalds 47046121cf7SChuck Lever dprintk("RPC: %5u looking up %s cred\n", 4711be27f36STrond Myklebust task->tk_pid, task->tk_client->cl_auth->au_ops->au_name); 4728572b8e2STrond Myklebust return auth->au_ops->lookup_cred(auth, &acred, lookupflags); 473af093835STrond Myklebust } 474af093835STrond Myklebust 4758572b8e2STrond Myklebust static struct rpc_cred * 4765d351754STrond Myklebust rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags) 477af093835STrond Myklebust { 478af093835STrond Myklebust struct rpc_auth *auth = task->tk_client->cl_auth; 479af093835STrond Myklebust 480af093835STrond Myklebust dprintk("RPC: %5u looking up %s cred\n", 481af093835STrond Myklebust task->tk_pid, auth->au_ops->au_name); 4828572b8e2STrond Myklebust return rpcauth_lookupcred(auth, lookupflags); 4831da177e4SLinus Torvalds } 4841da177e4SLinus Torvalds 485a17c2153STrond Myklebust static int 4864ccda2cdSTrond Myklebust rpcauth_bindcred(struct rpc_task *task, struct rpc_cred *cred, int flags) 4871da177e4SLinus Torvalds { 488a17c2153STrond Myklebust struct rpc_rqst *req = task->tk_rqstp; 4898572b8e2STrond Myklebust struct rpc_cred *new; 4905d351754STrond Myklebust int lookupflags = 0; 4915d351754STrond Myklebust 4925d351754STrond Myklebust if (flags & RPC_TASK_ASYNC) 4935d351754STrond Myklebust lookupflags |= RPCAUTH_LOOKUP_NEW; 4944ccda2cdSTrond Myklebust if (cred != NULL) 4958572b8e2STrond Myklebust new = cred->cr_ops->crbind(task, cred, lookupflags); 4964ccda2cdSTrond Myklebust else if (flags & RPC_TASK_ROOTCREDS) 4978572b8e2STrond Myklebust new = rpcauth_bind_root_cred(task, lookupflags); 4984ccda2cdSTrond Myklebust else 4998572b8e2STrond Myklebust new = rpcauth_bind_new_cred(task, lookupflags); 5008572b8e2STrond Myklebust if (IS_ERR(new)) 5018572b8e2STrond Myklebust return PTR_ERR(new); 502a17c2153STrond Myklebust if (req->rq_cred != NULL) 503a17c2153STrond Myklebust put_rpccred(req->rq_cred); 504a17c2153STrond Myklebust req->rq_cred = new; 5058572b8e2STrond Myklebust return 0; 5061da177e4SLinus Torvalds } 5071da177e4SLinus Torvalds 5081da177e4SLinus Torvalds void 5091da177e4SLinus Torvalds put_rpccred(struct rpc_cred *cred) 5101da177e4SLinus Torvalds { 511e092bdcdSTrond Myklebust /* Fast path for unhashed credentials */ 512f0380f3dSTrond Myklebust if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) == 0) { 513f0380f3dSTrond Myklebust if (atomic_dec_and_test(&cred->cr_count)) 514f0380f3dSTrond Myklebust cred->cr_ops->crdestroy(cred); 5151da177e4SLinus Torvalds return; 516f0380f3dSTrond Myklebust } 517f0380f3dSTrond Myklebust 518e092bdcdSTrond Myklebust if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock)) 519e092bdcdSTrond Myklebust return; 520f5c2187cSTrond Myklebust if (!list_empty(&cred->cr_lru)) { 521f5c2187cSTrond Myklebust number_cred_unused--; 522e092bdcdSTrond Myklebust list_del_init(&cred->cr_lru); 523f5c2187cSTrond Myklebust } 5245f707eb4STrond Myklebust if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) { 525f0380f3dSTrond Myklebust if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) { 526e092bdcdSTrond Myklebust cred->cr_expire = jiffies; 527e092bdcdSTrond Myklebust list_add_tail(&cred->cr_lru, &cred_unused); 528f5c2187cSTrond Myklebust number_cred_unused++; 529f0380f3dSTrond Myklebust goto out_nodestroy; 530f0380f3dSTrond Myklebust } 531f0380f3dSTrond Myklebust if (!rpcauth_unhash_cred(cred)) { 532f0380f3dSTrond Myklebust /* We were hashed and someone looked us up... */ 533f0380f3dSTrond Myklebust goto out_nodestroy; 534f0380f3dSTrond Myklebust } 535e092bdcdSTrond Myklebust } 536e092bdcdSTrond Myklebust spin_unlock(&rpc_credcache_lock); 5371da177e4SLinus Torvalds cred->cr_ops->crdestroy(cred); 538f0380f3dSTrond Myklebust return; 539f0380f3dSTrond Myklebust out_nodestroy: 540f0380f3dSTrond Myklebust spin_unlock(&rpc_credcache_lock); 5411da177e4SLinus Torvalds } 542e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(put_rpccred); 5431da177e4SLinus Torvalds 544d8ed029dSAlexey Dobriyan __be32 * 545d8ed029dSAlexey Dobriyan rpcauth_marshcred(struct rpc_task *task, __be32 *p) 5461da177e4SLinus Torvalds { 547a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 5481da177e4SLinus Torvalds 54946121cf7SChuck Lever dprintk("RPC: %5u marshaling %s cred %p\n", 5501be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 5510bbacc40SChuck Lever 5521da177e4SLinus Torvalds return cred->cr_ops->crmarshal(task, p); 5531da177e4SLinus Torvalds } 5541da177e4SLinus Torvalds 555d8ed029dSAlexey Dobriyan __be32 * 556d8ed029dSAlexey Dobriyan rpcauth_checkverf(struct rpc_task *task, __be32 *p) 5571da177e4SLinus Torvalds { 558a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 5591da177e4SLinus Torvalds 56046121cf7SChuck Lever dprintk("RPC: %5u validating %s cred %p\n", 5611be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 5620bbacc40SChuck Lever 5631da177e4SLinus Torvalds return cred->cr_ops->crvalidate(task, p); 5641da177e4SLinus Torvalds } 5651da177e4SLinus Torvalds 5661da177e4SLinus Torvalds int 5671da177e4SLinus Torvalds rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, 568d8ed029dSAlexey Dobriyan __be32 *data, void *obj) 5691da177e4SLinus Torvalds { 570a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 5711da177e4SLinus Torvalds 57246121cf7SChuck Lever dprintk("RPC: %5u using %s cred %p to wrap rpc data\n", 5731da177e4SLinus Torvalds task->tk_pid, cred->cr_ops->cr_name, cred); 5741da177e4SLinus Torvalds if (cred->cr_ops->crwrap_req) 5751da177e4SLinus Torvalds return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); 5761da177e4SLinus Torvalds /* By default, we encode the arguments normally. */ 57788a9fe8cSTrond Myklebust return encode(rqstp, data, obj); 5781da177e4SLinus Torvalds } 5791da177e4SLinus Torvalds 5801da177e4SLinus Torvalds int 5811da177e4SLinus Torvalds rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, 582d8ed029dSAlexey Dobriyan __be32 *data, void *obj) 5831da177e4SLinus Torvalds { 584a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 5851da177e4SLinus Torvalds 58646121cf7SChuck Lever dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n", 5871da177e4SLinus Torvalds task->tk_pid, cred->cr_ops->cr_name, cred); 5881da177e4SLinus Torvalds if (cred->cr_ops->crunwrap_resp) 5891da177e4SLinus Torvalds return cred->cr_ops->crunwrap_resp(task, decode, rqstp, 5901da177e4SLinus Torvalds data, obj); 5911da177e4SLinus Torvalds /* By default, we decode the arguments normally. */ 59288a9fe8cSTrond Myklebust return decode(rqstp, data, obj); 5931da177e4SLinus Torvalds } 5941da177e4SLinus Torvalds 5951da177e4SLinus Torvalds int 5961da177e4SLinus Torvalds rpcauth_refreshcred(struct rpc_task *task) 5971da177e4SLinus Torvalds { 598a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 5991da177e4SLinus Torvalds int err; 6001da177e4SLinus Torvalds 601a17c2153STrond Myklebust cred = task->tk_rqstp->rq_cred; 602a17c2153STrond Myklebust if (cred == NULL) { 603a17c2153STrond Myklebust err = rpcauth_bindcred(task, task->tk_msg.rpc_cred, task->tk_flags); 604a17c2153STrond Myklebust if (err < 0) 605a17c2153STrond Myklebust goto out; 606a17c2153STrond Myklebust cred = task->tk_rqstp->rq_cred; 607a17c2153STrond Myklebust }; 60846121cf7SChuck Lever dprintk("RPC: %5u refreshing %s cred %p\n", 6091be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 6100bbacc40SChuck Lever 6111da177e4SLinus Torvalds err = cred->cr_ops->crrefresh(task); 612a17c2153STrond Myklebust out: 6131da177e4SLinus Torvalds if (err < 0) 6141da177e4SLinus Torvalds task->tk_status = err; 6151da177e4SLinus Torvalds return err; 6161da177e4SLinus Torvalds } 6171da177e4SLinus Torvalds 6181da177e4SLinus Torvalds void 6191da177e4SLinus Torvalds rpcauth_invalcred(struct rpc_task *task) 6201da177e4SLinus Torvalds { 621a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 622fc432dd9STrond Myklebust 62346121cf7SChuck Lever dprintk("RPC: %5u invalidating %s cred %p\n", 6241be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 625fc432dd9STrond Myklebust if (cred) 626fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 6271da177e4SLinus Torvalds } 6281da177e4SLinus Torvalds 6291da177e4SLinus Torvalds int 6301da177e4SLinus Torvalds rpcauth_uptodatecred(struct rpc_task *task) 6311da177e4SLinus Torvalds { 632a17c2153STrond Myklebust struct rpc_cred *cred = task->tk_rqstp->rq_cred; 633fc432dd9STrond Myklebust 634fc432dd9STrond Myklebust return cred == NULL || 635fc432dd9STrond Myklebust test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0; 6361da177e4SLinus Torvalds } 637f5c2187cSTrond Myklebust 6388e1f936bSRusty Russell static struct shrinker rpc_cred_shrinker = { 6398e1f936bSRusty Russell .shrink = rpcauth_cache_shrinker, 6408e1f936bSRusty Russell .seeks = DEFAULT_SEEKS, 6418e1f936bSRusty Russell }; 642f5c2187cSTrond Myklebust 6435d8d9a4dSTrond Myklebust int __init rpcauth_init_module(void) 644f5c2187cSTrond Myklebust { 6455d8d9a4dSTrond Myklebust int err; 6465d8d9a4dSTrond Myklebust 6475d8d9a4dSTrond Myklebust err = rpc_init_authunix(); 6485d8d9a4dSTrond Myklebust if (err < 0) 6495d8d9a4dSTrond Myklebust goto out1; 6505d8d9a4dSTrond Myklebust err = rpc_init_generic_auth(); 6515d8d9a4dSTrond Myklebust if (err < 0) 6525d8d9a4dSTrond Myklebust goto out2; 6538e1f936bSRusty Russell register_shrinker(&rpc_cred_shrinker); 6545d8d9a4dSTrond Myklebust return 0; 6555d8d9a4dSTrond Myklebust out2: 6565d8d9a4dSTrond Myklebust rpc_destroy_authunix(); 6575d8d9a4dSTrond Myklebust out1: 6585d8d9a4dSTrond Myklebust return err; 659f5c2187cSTrond Myklebust } 660f5c2187cSTrond Myklebust 661f5c2187cSTrond Myklebust void __exit rpcauth_remove_module(void) 662f5c2187cSTrond Myklebust { 6635d8d9a4dSTrond Myklebust rpc_destroy_authunix(); 6645d8d9a4dSTrond Myklebust rpc_destroy_generic_auth(); 6658e1f936bSRusty Russell unregister_shrinker(&rpc_cred_shrinker); 666f5c2187cSTrond Myklebust } 667