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 22fc1b356fSTrond Myklebust static DEFINE_SPINLOCK(rpc_authflavor_lock); 23f1c0a861STrond Myklebust static const struct rpc_authops *auth_flavors[RPC_AUTH_MAXFLAVOR] = { 241da177e4SLinus Torvalds &authnull_ops, /* AUTH_NULL */ 251da177e4SLinus Torvalds &authunix_ops, /* AUTH_UNIX */ 261da177e4SLinus Torvalds NULL, /* others can be loadable modules */ 271da177e4SLinus Torvalds }; 281da177e4SLinus Torvalds 29e092bdcdSTrond Myklebust static LIST_HEAD(cred_unused); 30f5c2187cSTrond Myklebust static unsigned long number_cred_unused; 31e092bdcdSTrond Myklebust 321da177e4SLinus Torvalds static u32 331da177e4SLinus Torvalds pseudoflavor_to_flavor(u32 flavor) { 341da177e4SLinus Torvalds if (flavor >= RPC_AUTH_MAXFLAVOR) 351da177e4SLinus Torvalds return RPC_AUTH_GSS; 361da177e4SLinus Torvalds return flavor; 371da177e4SLinus Torvalds } 381da177e4SLinus Torvalds 391da177e4SLinus Torvalds int 40f1c0a861STrond Myklebust rpcauth_register(const struct rpc_authops *ops) 411da177e4SLinus Torvalds { 421da177e4SLinus Torvalds rpc_authflavor_t flavor; 43fc1b356fSTrond Myklebust int ret = -EPERM; 441da177e4SLinus Torvalds 451da177e4SLinus Torvalds if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR) 461da177e4SLinus Torvalds return -EINVAL; 47fc1b356fSTrond Myklebust spin_lock(&rpc_authflavor_lock); 48fc1b356fSTrond Myklebust if (auth_flavors[flavor] == NULL) { 491da177e4SLinus Torvalds auth_flavors[flavor] = ops; 50fc1b356fSTrond Myklebust ret = 0; 51fc1b356fSTrond Myklebust } 52fc1b356fSTrond Myklebust spin_unlock(&rpc_authflavor_lock); 53fc1b356fSTrond Myklebust return ret; 541da177e4SLinus Torvalds } 55e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_register); 561da177e4SLinus Torvalds 571da177e4SLinus Torvalds int 58f1c0a861STrond Myklebust rpcauth_unregister(const struct rpc_authops *ops) 591da177e4SLinus Torvalds { 601da177e4SLinus Torvalds rpc_authflavor_t flavor; 61fc1b356fSTrond Myklebust int ret = -EPERM; 621da177e4SLinus Torvalds 631da177e4SLinus Torvalds if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR) 641da177e4SLinus Torvalds return -EINVAL; 65fc1b356fSTrond Myklebust spin_lock(&rpc_authflavor_lock); 66fc1b356fSTrond Myklebust if (auth_flavors[flavor] == ops) { 671da177e4SLinus Torvalds auth_flavors[flavor] = NULL; 68fc1b356fSTrond Myklebust ret = 0; 69fc1b356fSTrond Myklebust } 70fc1b356fSTrond Myklebust spin_unlock(&rpc_authflavor_lock); 71fc1b356fSTrond Myklebust return ret; 721da177e4SLinus Torvalds } 73e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_unregister); 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds struct rpc_auth * 761da177e4SLinus Torvalds rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt) 771da177e4SLinus Torvalds { 781da177e4SLinus Torvalds struct rpc_auth *auth; 79f1c0a861STrond Myklebust const struct rpc_authops *ops; 801da177e4SLinus Torvalds u32 flavor = pseudoflavor_to_flavor(pseudoflavor); 811da177e4SLinus Torvalds 82f344f6dfSOlaf Kirch auth = ERR_PTR(-EINVAL); 83f344f6dfSOlaf Kirch if (flavor >= RPC_AUTH_MAXFLAVOR) 84f344f6dfSOlaf Kirch goto out; 85f344f6dfSOlaf Kirch 86f344f6dfSOlaf Kirch if ((ops = auth_flavors[flavor]) == NULL) 87f344f6dfSOlaf Kirch request_module("rpc-auth-%u", flavor); 88fc1b356fSTrond Myklebust spin_lock(&rpc_authflavor_lock); 89fc1b356fSTrond Myklebust ops = auth_flavors[flavor]; 90fc1b356fSTrond Myklebust if (ops == NULL || !try_module_get(ops->owner)) { 91fc1b356fSTrond Myklebust spin_unlock(&rpc_authflavor_lock); 92f344f6dfSOlaf Kirch goto out; 93fc1b356fSTrond Myklebust } 94fc1b356fSTrond Myklebust spin_unlock(&rpc_authflavor_lock); 951da177e4SLinus Torvalds auth = ops->create(clnt, pseudoflavor); 96fc1b356fSTrond Myklebust module_put(ops->owner); 976a19275aSJ. Bruce Fields if (IS_ERR(auth)) 986a19275aSJ. Bruce Fields return auth; 991da177e4SLinus Torvalds if (clnt->cl_auth) 100de7a8ce3STrond Myklebust rpcauth_release(clnt->cl_auth); 1011da177e4SLinus Torvalds clnt->cl_auth = auth; 102f344f6dfSOlaf Kirch 103f344f6dfSOlaf Kirch out: 1041da177e4SLinus Torvalds return auth; 1051da177e4SLinus Torvalds } 106e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_create); 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds void 109de7a8ce3STrond Myklebust rpcauth_release(struct rpc_auth *auth) 1101da177e4SLinus Torvalds { 1111da177e4SLinus Torvalds if (!atomic_dec_and_test(&auth->au_count)) 1121da177e4SLinus Torvalds return; 1131da177e4SLinus Torvalds auth->au_ops->destroy(auth); 1141da177e4SLinus Torvalds } 1151da177e4SLinus Torvalds 1161da177e4SLinus Torvalds static DEFINE_SPINLOCK(rpc_credcache_lock); 1171da177e4SLinus Torvalds 11831be5bf1STrond Myklebust static void 11931be5bf1STrond Myklebust rpcauth_unhash_cred_locked(struct rpc_cred *cred) 12031be5bf1STrond Myklebust { 12131be5bf1STrond Myklebust hlist_del_rcu(&cred->cr_hash); 12231be5bf1STrond Myklebust smp_mb__before_clear_bit(); 12331be5bf1STrond Myklebust clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); 12431be5bf1STrond Myklebust } 12531be5bf1STrond Myklebust 126f0380f3dSTrond Myklebust static int 1279499b434STrond Myklebust rpcauth_unhash_cred(struct rpc_cred *cred) 1289499b434STrond Myklebust { 1299499b434STrond Myklebust spinlock_t *cache_lock; 130f0380f3dSTrond Myklebust int ret; 1319499b434STrond Myklebust 1329499b434STrond Myklebust cache_lock = &cred->cr_auth->au_credcache->lock; 1339499b434STrond Myklebust spin_lock(cache_lock); 134f0380f3dSTrond Myklebust ret = atomic_read(&cred->cr_count) == 0; 135f0380f3dSTrond Myklebust if (ret) 1369499b434STrond Myklebust rpcauth_unhash_cred_locked(cred); 1379499b434STrond Myklebust spin_unlock(cache_lock); 138f0380f3dSTrond Myklebust return ret; 1399499b434STrond Myklebust } 1409499b434STrond Myklebust 1411da177e4SLinus Torvalds /* 1421da177e4SLinus Torvalds * Initialize RPC credential cache 1431da177e4SLinus Torvalds */ 1441da177e4SLinus Torvalds int 145f5c2187cSTrond Myklebust rpcauth_init_credcache(struct rpc_auth *auth) 1461da177e4SLinus Torvalds { 1471da177e4SLinus Torvalds struct rpc_cred_cache *new; 148988664a0STrond Myklebust unsigned int hashsize; 1491da177e4SLinus Torvalds int i; 1501da177e4SLinus Torvalds 1518b3a7005SKris Katterjohn new = kmalloc(sizeof(*new), GFP_KERNEL); 1521da177e4SLinus Torvalds if (!new) 1531da177e4SLinus Torvalds return -ENOMEM; 154988664a0STrond Myklebust new->hashbits = RPC_CREDCACHE_HASHBITS; 155988664a0STrond Myklebust hashsize = 1U << new->hashbits; 156988664a0STrond Myklebust for (i = 0; i < hashsize; i++) 1571da177e4SLinus Torvalds INIT_HLIST_HEAD(&new->hashtable[i]); 1589499b434STrond Myklebust spin_lock_init(&new->lock); 1591da177e4SLinus Torvalds auth->au_credcache = new; 1601da177e4SLinus Torvalds return 0; 1611da177e4SLinus Torvalds } 162e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_credcache); 1631da177e4SLinus Torvalds 1641da177e4SLinus Torvalds /* 1651da177e4SLinus Torvalds * Destroy a list of credentials 1661da177e4SLinus Torvalds */ 1671da177e4SLinus Torvalds static inline 168e092bdcdSTrond Myklebust void rpcauth_destroy_credlist(struct list_head *head) 1691da177e4SLinus Torvalds { 1701da177e4SLinus Torvalds struct rpc_cred *cred; 1711da177e4SLinus Torvalds 172e092bdcdSTrond Myklebust while (!list_empty(head)) { 173e092bdcdSTrond Myklebust cred = list_entry(head->next, struct rpc_cred, cr_lru); 174e092bdcdSTrond Myklebust list_del_init(&cred->cr_lru); 1751da177e4SLinus Torvalds put_rpccred(cred); 1761da177e4SLinus Torvalds } 1771da177e4SLinus Torvalds } 1781da177e4SLinus Torvalds 1791da177e4SLinus Torvalds /* 1801da177e4SLinus Torvalds * Clear the RPC credential cache, and delete those credentials 1811da177e4SLinus Torvalds * that are not referenced. 1821da177e4SLinus Torvalds */ 1831da177e4SLinus Torvalds void 1843ab9bb72STrond Myklebust rpcauth_clear_credcache(struct rpc_cred_cache *cache) 1851da177e4SLinus Torvalds { 186e092bdcdSTrond Myklebust LIST_HEAD(free); 187e092bdcdSTrond Myklebust struct hlist_head *head; 1881da177e4SLinus Torvalds struct rpc_cred *cred; 189988664a0STrond Myklebust unsigned int hashsize = 1U << cache->hashbits; 1901da177e4SLinus Torvalds int i; 1911da177e4SLinus Torvalds 1921da177e4SLinus Torvalds spin_lock(&rpc_credcache_lock); 1939499b434STrond Myklebust spin_lock(&cache->lock); 194988664a0STrond Myklebust for (i = 0; i < hashsize; i++) { 195e092bdcdSTrond Myklebust head = &cache->hashtable[i]; 196e092bdcdSTrond Myklebust while (!hlist_empty(head)) { 197e092bdcdSTrond Myklebust cred = hlist_entry(head->first, struct rpc_cred, cr_hash); 198e092bdcdSTrond Myklebust get_rpccred(cred); 199f5c2187cSTrond Myklebust if (!list_empty(&cred->cr_lru)) { 200f5c2187cSTrond Myklebust list_del(&cred->cr_lru); 201f5c2187cSTrond Myklebust number_cred_unused--; 202f5c2187cSTrond Myklebust } 203f5c2187cSTrond Myklebust list_add_tail(&cred->cr_lru, &free); 20431be5bf1STrond Myklebust rpcauth_unhash_cred_locked(cred); 2051da177e4SLinus Torvalds } 2061da177e4SLinus Torvalds } 2079499b434STrond Myklebust spin_unlock(&cache->lock); 2081da177e4SLinus Torvalds spin_unlock(&rpc_credcache_lock); 2091da177e4SLinus Torvalds rpcauth_destroy_credlist(&free); 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds 2123ab9bb72STrond Myklebust /* 2133ab9bb72STrond Myklebust * Destroy the RPC credential cache 2143ab9bb72STrond Myklebust */ 2153ab9bb72STrond Myklebust void 2163ab9bb72STrond Myklebust rpcauth_destroy_credcache(struct rpc_auth *auth) 2173ab9bb72STrond Myklebust { 2183ab9bb72STrond Myklebust struct rpc_cred_cache *cache = auth->au_credcache; 2193ab9bb72STrond Myklebust 2203ab9bb72STrond Myklebust if (cache) { 2213ab9bb72STrond Myklebust auth->au_credcache = NULL; 2223ab9bb72STrond Myklebust rpcauth_clear_credcache(cache); 2233ab9bb72STrond Myklebust kfree(cache); 2243ab9bb72STrond Myklebust } 2253ab9bb72STrond Myklebust } 226e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_destroy_credcache); 2273ab9bb72STrond Myklebust 228d2b83141STrond Myklebust 229d2b83141STrond Myklebust #define RPC_AUTH_EXPIRY_MORATORIUM (60 * HZ) 230d2b83141STrond Myklebust 2311da177e4SLinus Torvalds /* 2321da177e4SLinus Torvalds * Remove stale credentials. Avoid sleeping inside the loop. 2331da177e4SLinus Torvalds */ 234f5c2187cSTrond Myklebust static int 235f5c2187cSTrond Myklebust rpcauth_prune_expired(struct list_head *free, int nr_to_scan) 2361da177e4SLinus Torvalds { 2379499b434STrond Myklebust spinlock_t *cache_lock; 238eac0d18dSTrond Myklebust struct rpc_cred *cred, *next; 239d2b83141STrond Myklebust unsigned long expired = jiffies - RPC_AUTH_EXPIRY_MORATORIUM; 2401da177e4SLinus Torvalds 241eac0d18dSTrond Myklebust list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) { 242eac0d18dSTrond Myklebust 24320673406STrond Myklebust if (nr_to_scan-- == 0) 24420673406STrond Myklebust break; 24593a05e65STrond Myklebust /* 24693a05e65STrond Myklebust * Enforce a 60 second garbage collection moratorium 24793a05e65STrond Myklebust * Note that the cred_unused list must be time-ordered. 24893a05e65STrond Myklebust */ 2493d7b0894STrond Myklebust if (time_in_range(cred->cr_expire, expired, jiffies) && 250eac0d18dSTrond Myklebust test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) 25193a05e65STrond Myklebust return 0; 252eac0d18dSTrond Myklebust 253e092bdcdSTrond Myklebust list_del_init(&cred->cr_lru); 254f5c2187cSTrond Myklebust number_cred_unused--; 255e092bdcdSTrond Myklebust if (atomic_read(&cred->cr_count) != 0) 256e092bdcdSTrond Myklebust continue; 257eac0d18dSTrond Myklebust 2589499b434STrond Myklebust cache_lock = &cred->cr_auth->au_credcache->lock; 2599499b434STrond Myklebust spin_lock(cache_lock); 2609499b434STrond Myklebust if (atomic_read(&cred->cr_count) == 0) { 261e092bdcdSTrond Myklebust get_rpccred(cred); 262e092bdcdSTrond Myklebust list_add_tail(&cred->cr_lru, free); 26331be5bf1STrond Myklebust rpcauth_unhash_cred_locked(cred); 2641da177e4SLinus Torvalds } 2659499b434STrond Myklebust spin_unlock(cache_lock); 2669499b434STrond Myklebust } 26793a05e65STrond Myklebust return (number_cred_unused / 100) * sysctl_vfs_cache_pressure; 2681da177e4SLinus Torvalds } 269e092bdcdSTrond Myklebust 270e092bdcdSTrond Myklebust /* 271f5c2187cSTrond Myklebust * Run memory cache shrinker. 272e092bdcdSTrond Myklebust */ 273f5c2187cSTrond Myklebust static int 274f5c2187cSTrond Myklebust rpcauth_cache_shrinker(int nr_to_scan, gfp_t gfp_mask) 275e092bdcdSTrond Myklebust { 276f5c2187cSTrond Myklebust LIST_HEAD(free); 277f5c2187cSTrond Myklebust int res; 278f5c2187cSTrond Myklebust 279d300a41eSTrond Myklebust if ((gfp_mask & GFP_KERNEL) != GFP_KERNEL) 280d300a41eSTrond Myklebust return (nr_to_scan == 0) ? 0 : -1; 281f5c2187cSTrond Myklebust if (list_empty(&cred_unused)) 282f5c2187cSTrond Myklebust return 0; 28331be5bf1STrond Myklebust spin_lock(&rpc_credcache_lock); 28493a05e65STrond Myklebust res = rpcauth_prune_expired(&free, nr_to_scan); 28531be5bf1STrond Myklebust spin_unlock(&rpc_credcache_lock); 286f5c2187cSTrond Myklebust rpcauth_destroy_credlist(&free); 287f5c2187cSTrond Myklebust return res; 2881da177e4SLinus Torvalds } 2891da177e4SLinus Torvalds 2901da177e4SLinus Torvalds /* 2911da177e4SLinus Torvalds * Look up a process' credentials in the authentication cache 2921da177e4SLinus Torvalds */ 2931da177e4SLinus Torvalds struct rpc_cred * 2941da177e4SLinus Torvalds rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred, 2958a317760STrond Myklebust int flags) 2961da177e4SLinus Torvalds { 297e092bdcdSTrond Myklebust LIST_HEAD(free); 2981da177e4SLinus Torvalds struct rpc_cred_cache *cache = auth->au_credcache; 299e092bdcdSTrond Myklebust struct hlist_node *pos; 30031be5bf1STrond Myklebust struct rpc_cred *cred = NULL, 30131be5bf1STrond Myklebust *entry, *new; 30225337fdcSTrond Myklebust unsigned int nr; 30325337fdcSTrond Myklebust 304988664a0STrond Myklebust nr = hash_long(acred->uid, cache->hashbits); 3051da177e4SLinus Torvalds 30631be5bf1STrond Myklebust rcu_read_lock(); 30731be5bf1STrond Myklebust hlist_for_each_entry_rcu(entry, pos, &cache->hashtable[nr], cr_hash) { 30831be5bf1STrond Myklebust if (!entry->cr_ops->crmatch(acred, entry, flags)) 30931be5bf1STrond Myklebust continue; 3109499b434STrond Myklebust spin_lock(&cache->lock); 31131be5bf1STrond Myklebust if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) == 0) { 3129499b434STrond Myklebust spin_unlock(&cache->lock); 31331be5bf1STrond Myklebust continue; 31431be5bf1STrond Myklebust } 31531be5bf1STrond Myklebust cred = get_rpccred(entry); 3169499b434STrond Myklebust spin_unlock(&cache->lock); 31731be5bf1STrond Myklebust break; 31831be5bf1STrond Myklebust } 31931be5bf1STrond Myklebust rcu_read_unlock(); 32031be5bf1STrond Myklebust 3219499b434STrond Myklebust if (cred != NULL) 32231be5bf1STrond Myklebust goto found; 32331be5bf1STrond Myklebust 32431be5bf1STrond Myklebust new = auth->au_ops->crcreate(auth, acred, flags); 32531be5bf1STrond Myklebust if (IS_ERR(new)) { 32631be5bf1STrond Myklebust cred = new; 32731be5bf1STrond Myklebust goto out; 32831be5bf1STrond Myklebust } 32931be5bf1STrond Myklebust 3309499b434STrond Myklebust spin_lock(&cache->lock); 331e092bdcdSTrond Myklebust hlist_for_each_entry(entry, pos, &cache->hashtable[nr], cr_hash) { 332e092bdcdSTrond Myklebust if (!entry->cr_ops->crmatch(acred, entry, flags)) 333e092bdcdSTrond Myklebust continue; 334e092bdcdSTrond Myklebust cred = get_rpccred(entry); 3351da177e4SLinus Torvalds break; 3361da177e4SLinus Torvalds } 33731be5bf1STrond Myklebust if (cred == NULL) { 33831be5bf1STrond Myklebust cred = new; 33931be5bf1STrond Myklebust set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags); 34031be5bf1STrond Myklebust hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]); 34131be5bf1STrond Myklebust } else 342e092bdcdSTrond Myklebust list_add_tail(&new->cr_lru, &free); 3439499b434STrond Myklebust spin_unlock(&cache->lock); 34431be5bf1STrond Myklebust found: 345f64f9e71SJoe Perches if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) && 346f64f9e71SJoe Perches cred->cr_ops->cr_init != NULL && 347f64f9e71SJoe Perches !(flags & RPCAUTH_LOOKUP_NEW)) { 348fba3bad4STrond Myklebust int res = cred->cr_ops->cr_init(auth, cred); 349fba3bad4STrond Myklebust if (res < 0) { 350fba3bad4STrond Myklebust put_rpccred(cred); 351fba3bad4STrond Myklebust cred = ERR_PTR(res); 352fba3bad4STrond Myklebust } 3531da177e4SLinus Torvalds } 35431be5bf1STrond Myklebust rpcauth_destroy_credlist(&free); 35531be5bf1STrond Myklebust out: 35631be5bf1STrond Myklebust return cred; 3571da177e4SLinus Torvalds } 358e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache); 3591da177e4SLinus Torvalds 3601da177e4SLinus Torvalds struct rpc_cred * 3618a317760STrond Myklebust rpcauth_lookupcred(struct rpc_auth *auth, int flags) 3621da177e4SLinus Torvalds { 36386a264abSDavid Howells struct auth_cred acred; 3641da177e4SLinus Torvalds struct rpc_cred *ret; 36586a264abSDavid Howells const struct cred *cred = current_cred(); 3661da177e4SLinus Torvalds 3671da177e4SLinus Torvalds dprintk("RPC: looking up %s cred\n", 3681da177e4SLinus Torvalds auth->au_ops->au_name); 36986a264abSDavid Howells 37086a264abSDavid Howells memset(&acred, 0, sizeof(acred)); 37186a264abSDavid Howells acred.uid = cred->fsuid; 37286a264abSDavid Howells acred.gid = cred->fsgid; 37386a264abSDavid Howells acred.group_info = get_group_info(((struct cred *)cred)->group_info); 37486a264abSDavid Howells 3758a317760STrond Myklebust ret = auth->au_ops->lookup_cred(auth, &acred, flags); 3761da177e4SLinus Torvalds put_group_info(acred.group_info); 3771da177e4SLinus Torvalds return ret; 3781da177e4SLinus Torvalds } 3791da177e4SLinus Torvalds 3805fe4755eSTrond Myklebust void 3815fe4755eSTrond Myklebust rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred, 3825fe4755eSTrond Myklebust struct rpc_auth *auth, const struct rpc_credops *ops) 3835fe4755eSTrond Myklebust { 3845fe4755eSTrond Myklebust INIT_HLIST_NODE(&cred->cr_hash); 385e092bdcdSTrond Myklebust INIT_LIST_HEAD(&cred->cr_lru); 3865fe4755eSTrond Myklebust atomic_set(&cred->cr_count, 1); 3875fe4755eSTrond Myklebust cred->cr_auth = auth; 3885fe4755eSTrond Myklebust cred->cr_ops = ops; 3895fe4755eSTrond Myklebust cred->cr_expire = jiffies; 3905fe4755eSTrond Myklebust #ifdef RPC_DEBUG 3915fe4755eSTrond Myklebust cred->cr_magic = RPCAUTH_CRED_MAGIC; 3925fe4755eSTrond Myklebust #endif 3935fe4755eSTrond Myklebust cred->cr_uid = acred->uid; 3945fe4755eSTrond Myklebust } 395e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_init_cred); 3965fe4755eSTrond Myklebust 3975c691044STrond Myklebust void 3985d351754STrond Myklebust rpcauth_generic_bind_cred(struct rpc_task *task, struct rpc_cred *cred, int lookupflags) 3994ccda2cdSTrond Myklebust { 4004ccda2cdSTrond Myklebust task->tk_msg.rpc_cred = get_rpccred(cred); 4014ccda2cdSTrond Myklebust dprintk("RPC: %5u holding %s cred %p\n", task->tk_pid, 4024ccda2cdSTrond Myklebust cred->cr_auth->au_ops->au_name, cred); 4034ccda2cdSTrond Myklebust } 4045c691044STrond Myklebust EXPORT_SYMBOL_GPL(rpcauth_generic_bind_cred); 4054ccda2cdSTrond Myklebust 4064ccda2cdSTrond Myklebust static void 4075d351754STrond Myklebust rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags) 4081da177e4SLinus Torvalds { 4091be27f36STrond Myklebust struct rpc_auth *auth = task->tk_client->cl_auth; 4101da177e4SLinus Torvalds struct auth_cred acred = { 411af093835STrond Myklebust .uid = 0, 412af093835STrond Myklebust .gid = 0, 4131da177e4SLinus Torvalds }; 4141da177e4SLinus Torvalds struct rpc_cred *ret; 4151da177e4SLinus Torvalds 41646121cf7SChuck Lever dprintk("RPC: %5u looking up %s cred\n", 4171be27f36STrond Myklebust task->tk_pid, task->tk_client->cl_auth->au_ops->au_name); 4185d351754STrond Myklebust ret = auth->au_ops->lookup_cred(auth, &acred, lookupflags); 4191da177e4SLinus Torvalds if (!IS_ERR(ret)) 4201da177e4SLinus Torvalds task->tk_msg.rpc_cred = ret; 4211da177e4SLinus Torvalds else 4221da177e4SLinus Torvalds task->tk_status = PTR_ERR(ret); 423af093835STrond Myklebust } 424af093835STrond Myklebust 4254ccda2cdSTrond Myklebust static void 4265d351754STrond Myklebust rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags) 427af093835STrond Myklebust { 428af093835STrond Myklebust struct rpc_auth *auth = task->tk_client->cl_auth; 429af093835STrond Myklebust struct rpc_cred *ret; 430af093835STrond Myklebust 431af093835STrond Myklebust dprintk("RPC: %5u looking up %s cred\n", 432af093835STrond Myklebust task->tk_pid, auth->au_ops->au_name); 4335d351754STrond Myklebust ret = rpcauth_lookupcred(auth, lookupflags); 434af093835STrond Myklebust if (!IS_ERR(ret)) 435af093835STrond Myklebust task->tk_msg.rpc_cred = ret; 436af093835STrond Myklebust else 437af093835STrond Myklebust task->tk_status = PTR_ERR(ret); 4381da177e4SLinus Torvalds } 4391da177e4SLinus Torvalds 4401da177e4SLinus Torvalds void 4414ccda2cdSTrond Myklebust rpcauth_bindcred(struct rpc_task *task, struct rpc_cred *cred, int flags) 4421da177e4SLinus Torvalds { 4435d351754STrond Myklebust int lookupflags = 0; 4445d351754STrond Myklebust 4455d351754STrond Myklebust if (flags & RPC_TASK_ASYNC) 4465d351754STrond Myklebust lookupflags |= RPCAUTH_LOOKUP_NEW; 4474ccda2cdSTrond Myklebust if (cred != NULL) 4485d351754STrond Myklebust cred->cr_ops->crbind(task, cred, lookupflags); 4494ccda2cdSTrond Myklebust else if (flags & RPC_TASK_ROOTCREDS) 4505d351754STrond Myklebust rpcauth_bind_root_cred(task, lookupflags); 4514ccda2cdSTrond Myklebust else 4525d351754STrond Myklebust rpcauth_bind_new_cred(task, lookupflags); 4531da177e4SLinus Torvalds } 4541da177e4SLinus Torvalds 4551da177e4SLinus Torvalds void 4561da177e4SLinus Torvalds put_rpccred(struct rpc_cred *cred) 4571da177e4SLinus Torvalds { 458e092bdcdSTrond Myklebust /* Fast path for unhashed credentials */ 459f0380f3dSTrond Myklebust if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) == 0) { 460f0380f3dSTrond Myklebust if (atomic_dec_and_test(&cred->cr_count)) 461f0380f3dSTrond Myklebust cred->cr_ops->crdestroy(cred); 4621da177e4SLinus Torvalds return; 463f0380f3dSTrond Myklebust } 464f0380f3dSTrond Myklebust 465e092bdcdSTrond Myklebust if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock)) 466e092bdcdSTrond Myklebust return; 467f5c2187cSTrond Myklebust if (!list_empty(&cred->cr_lru)) { 468f5c2187cSTrond Myklebust number_cred_unused--; 469e092bdcdSTrond Myklebust list_del_init(&cred->cr_lru); 470f5c2187cSTrond Myklebust } 4715f707eb4STrond Myklebust if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) { 472f0380f3dSTrond Myklebust if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) { 473e092bdcdSTrond Myklebust cred->cr_expire = jiffies; 474e092bdcdSTrond Myklebust list_add_tail(&cred->cr_lru, &cred_unused); 475f5c2187cSTrond Myklebust number_cred_unused++; 476f0380f3dSTrond Myklebust goto out_nodestroy; 477f0380f3dSTrond Myklebust } 478f0380f3dSTrond Myklebust if (!rpcauth_unhash_cred(cred)) { 479f0380f3dSTrond Myklebust /* We were hashed and someone looked us up... */ 480f0380f3dSTrond Myklebust goto out_nodestroy; 481f0380f3dSTrond Myklebust } 482e092bdcdSTrond Myklebust } 483e092bdcdSTrond Myklebust spin_unlock(&rpc_credcache_lock); 4841da177e4SLinus Torvalds cred->cr_ops->crdestroy(cred); 485f0380f3dSTrond Myklebust return; 486f0380f3dSTrond Myklebust out_nodestroy: 487f0380f3dSTrond Myklebust spin_unlock(&rpc_credcache_lock); 4881da177e4SLinus Torvalds } 489e8914c65STrond Myklebust EXPORT_SYMBOL_GPL(put_rpccred); 4901da177e4SLinus Torvalds 4911da177e4SLinus Torvalds void 4921da177e4SLinus Torvalds rpcauth_unbindcred(struct rpc_task *task) 4931da177e4SLinus Torvalds { 4941da177e4SLinus Torvalds struct rpc_cred *cred = task->tk_msg.rpc_cred; 4951da177e4SLinus Torvalds 49646121cf7SChuck Lever dprintk("RPC: %5u releasing %s cred %p\n", 4971be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 4981da177e4SLinus Torvalds 4991da177e4SLinus Torvalds put_rpccred(cred); 5001da177e4SLinus Torvalds task->tk_msg.rpc_cred = NULL; 5011da177e4SLinus Torvalds } 5021da177e4SLinus Torvalds 503d8ed029dSAlexey Dobriyan __be32 * 504d8ed029dSAlexey Dobriyan rpcauth_marshcred(struct rpc_task *task, __be32 *p) 5051da177e4SLinus Torvalds { 5061da177e4SLinus Torvalds struct rpc_cred *cred = task->tk_msg.rpc_cred; 5071da177e4SLinus Torvalds 50846121cf7SChuck Lever dprintk("RPC: %5u marshaling %s cred %p\n", 5091be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 5100bbacc40SChuck Lever 5111da177e4SLinus Torvalds return cred->cr_ops->crmarshal(task, p); 5121da177e4SLinus Torvalds } 5131da177e4SLinus Torvalds 514d8ed029dSAlexey Dobriyan __be32 * 515d8ed029dSAlexey Dobriyan rpcauth_checkverf(struct rpc_task *task, __be32 *p) 5161da177e4SLinus Torvalds { 5171da177e4SLinus Torvalds struct rpc_cred *cred = task->tk_msg.rpc_cred; 5181da177e4SLinus Torvalds 51946121cf7SChuck Lever dprintk("RPC: %5u validating %s cred %p\n", 5201be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 5210bbacc40SChuck Lever 5221da177e4SLinus Torvalds return cred->cr_ops->crvalidate(task, p); 5231da177e4SLinus Torvalds } 5241da177e4SLinus Torvalds 5251da177e4SLinus Torvalds int 5261da177e4SLinus Torvalds rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp, 527d8ed029dSAlexey Dobriyan __be32 *data, void *obj) 5281da177e4SLinus Torvalds { 5291da177e4SLinus Torvalds struct rpc_cred *cred = task->tk_msg.rpc_cred; 5301da177e4SLinus Torvalds 53146121cf7SChuck Lever dprintk("RPC: %5u using %s cred %p to wrap rpc data\n", 5321da177e4SLinus Torvalds task->tk_pid, cred->cr_ops->cr_name, cred); 5331da177e4SLinus Torvalds if (cred->cr_ops->crwrap_req) 5341da177e4SLinus Torvalds return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj); 5351da177e4SLinus Torvalds /* By default, we encode the arguments normally. */ 53688a9fe8cSTrond Myklebust return encode(rqstp, data, obj); 5371da177e4SLinus Torvalds } 5381da177e4SLinus Torvalds 5391da177e4SLinus Torvalds int 5401da177e4SLinus Torvalds rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp, 541d8ed029dSAlexey Dobriyan __be32 *data, void *obj) 5421da177e4SLinus Torvalds { 5431da177e4SLinus Torvalds struct rpc_cred *cred = task->tk_msg.rpc_cred; 5441da177e4SLinus Torvalds 54546121cf7SChuck Lever dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n", 5461da177e4SLinus Torvalds task->tk_pid, cred->cr_ops->cr_name, cred); 5471da177e4SLinus Torvalds if (cred->cr_ops->crunwrap_resp) 5481da177e4SLinus Torvalds return cred->cr_ops->crunwrap_resp(task, decode, rqstp, 5491da177e4SLinus Torvalds data, obj); 5501da177e4SLinus Torvalds /* By default, we decode the arguments normally. */ 55188a9fe8cSTrond Myklebust return decode(rqstp, data, obj); 5521da177e4SLinus Torvalds } 5531da177e4SLinus Torvalds 5541da177e4SLinus Torvalds int 5551da177e4SLinus Torvalds rpcauth_refreshcred(struct rpc_task *task) 5561da177e4SLinus Torvalds { 5571da177e4SLinus Torvalds struct rpc_cred *cred = task->tk_msg.rpc_cred; 5581da177e4SLinus Torvalds int err; 5591da177e4SLinus Torvalds 56046121cf7SChuck Lever dprintk("RPC: %5u refreshing %s cred %p\n", 5611be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 5620bbacc40SChuck Lever 5631da177e4SLinus Torvalds err = cred->cr_ops->crrefresh(task); 5641da177e4SLinus Torvalds if (err < 0) 5651da177e4SLinus Torvalds task->tk_status = err; 5661da177e4SLinus Torvalds return err; 5671da177e4SLinus Torvalds } 5681da177e4SLinus Torvalds 5691da177e4SLinus Torvalds void 5701da177e4SLinus Torvalds rpcauth_invalcred(struct rpc_task *task) 5711da177e4SLinus Torvalds { 572fc432dd9STrond Myklebust struct rpc_cred *cred = task->tk_msg.rpc_cred; 573fc432dd9STrond Myklebust 57446121cf7SChuck Lever dprintk("RPC: %5u invalidating %s cred %p\n", 5751be27f36STrond Myklebust task->tk_pid, cred->cr_auth->au_ops->au_name, cred); 576fc432dd9STrond Myklebust if (cred) 577fc432dd9STrond Myklebust clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags); 5781da177e4SLinus Torvalds } 5791da177e4SLinus Torvalds 5801da177e4SLinus Torvalds int 5811da177e4SLinus Torvalds rpcauth_uptodatecred(struct rpc_task *task) 5821da177e4SLinus Torvalds { 583fc432dd9STrond Myklebust struct rpc_cred *cred = task->tk_msg.rpc_cred; 584fc432dd9STrond Myklebust 585fc432dd9STrond Myklebust return cred == NULL || 586fc432dd9STrond Myklebust test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0; 5871da177e4SLinus Torvalds } 588f5c2187cSTrond Myklebust 5898e1f936bSRusty Russell static struct shrinker rpc_cred_shrinker = { 5908e1f936bSRusty Russell .shrink = rpcauth_cache_shrinker, 5918e1f936bSRusty Russell .seeks = DEFAULT_SEEKS, 5928e1f936bSRusty Russell }; 593f5c2187cSTrond Myklebust 5945d8d9a4dSTrond Myklebust int __init rpcauth_init_module(void) 595f5c2187cSTrond Myklebust { 5965d8d9a4dSTrond Myklebust int err; 5975d8d9a4dSTrond Myklebust 5985d8d9a4dSTrond Myklebust err = rpc_init_authunix(); 5995d8d9a4dSTrond Myklebust if (err < 0) 6005d8d9a4dSTrond Myklebust goto out1; 6015d8d9a4dSTrond Myklebust err = rpc_init_generic_auth(); 6025d8d9a4dSTrond Myklebust if (err < 0) 6035d8d9a4dSTrond Myklebust goto out2; 6048e1f936bSRusty Russell register_shrinker(&rpc_cred_shrinker); 6055d8d9a4dSTrond Myklebust return 0; 6065d8d9a4dSTrond Myklebust out2: 6075d8d9a4dSTrond Myklebust rpc_destroy_authunix(); 6085d8d9a4dSTrond Myklebust out1: 6095d8d9a4dSTrond Myklebust return err; 610f5c2187cSTrond Myklebust } 611f5c2187cSTrond Myklebust 612f5c2187cSTrond Myklebust void __exit rpcauth_remove_module(void) 613f5c2187cSTrond Myklebust { 6145d8d9a4dSTrond Myklebust rpc_destroy_authunix(); 6155d8d9a4dSTrond Myklebust rpc_destroy_generic_auth(); 6168e1f936bSRusty Russell unregister_shrinker(&rpc_cred_shrinker); 617f5c2187cSTrond Myklebust } 618