auth.c 19.1 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * linux/net/sunrpc/auth.c
 *
 * Generic RPC client authentication API.
 *
 * Copyright (C) 1996, Olaf Kirch <okir@monad.swb.de>
 */

#include <linux/types.h>
#include <linux/sched.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/errno.h>
14
#include <linux/hash.h>
L
Linus Torvalds 已提交
15
#include <linux/sunrpc/clnt.h>
C
Chuck Lever 已提交
16
#include <linux/sunrpc/gss_api.h>
L
Linus Torvalds 已提交
17 18 19 20 21 22
#include <linux/spinlock.h>

#ifdef RPC_DEBUG
# define RPCDBG_FACILITY	RPCDBG_AUTH
#endif

23 24 25 26 27 28 29 30 31
#define RPC_CREDCACHE_DEFAULT_HASHBITS	(4)
struct rpc_cred_cache {
	struct hlist_head	*hashtable;
	unsigned int		hashbits;
	spinlock_t		lock;
};

static unsigned int auth_hashbits = RPC_CREDCACHE_DEFAULT_HASHBITS;

32
static DEFINE_SPINLOCK(rpc_authflavor_lock);
33
static const struct rpc_authops *auth_flavors[RPC_AUTH_MAXFLAVOR] = {
L
Linus Torvalds 已提交
34 35 36 37 38
	&authnull_ops,		/* AUTH_NULL */
	&authunix_ops,		/* AUTH_UNIX */
	NULL,			/* others can be loadable modules */
};

39
static LIST_HEAD(cred_unused);
40
static unsigned long number_cred_unused;
41

42
#define MAX_HASHTABLE_BITS (14)
43
static int param_set_hashtbl_sz(const char *val, const struct kernel_param *kp)
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
{
	unsigned long num;
	unsigned int nbits;
	int ret;

	if (!val)
		goto out_inval;
	ret = strict_strtoul(val, 0, &num);
	if (ret == -EINVAL)
		goto out_inval;
	nbits = fls(num);
	if (num > (1U << nbits))
		nbits++;
	if (nbits > MAX_HASHTABLE_BITS || nbits < 2)
		goto out_inval;
	*(unsigned int *)kp->arg = nbits;
	return 0;
out_inval:
	return -EINVAL;
}

65
static int param_get_hashtbl_sz(char *buffer, const struct kernel_param *kp)
66 67 68 69 70 71 72 73 74
{
	unsigned int nbits;

	nbits = *(unsigned int *)kp->arg;
	return sprintf(buffer, "%u", 1U << nbits);
}

#define param_check_hashtbl_sz(name, p) __param_check(name, p, unsigned int);

75 76 77 78 79
static struct kernel_param_ops param_ops_hashtbl_sz = {
	.set = param_set_hashtbl_sz,
	.get = param_get_hashtbl_sz,
};

80 81 82
module_param_named(auth_hashtable_size, auth_hashbits, hashtbl_sz, 0644);
MODULE_PARM_DESC(auth_hashtable_size, "RPC credential cache hashtable size");

L
Linus Torvalds 已提交
83 84
static u32
pseudoflavor_to_flavor(u32 flavor) {
85
	if (flavor > RPC_AUTH_MAXFLAVOR)
L
Linus Torvalds 已提交
86 87 88 89 90
		return RPC_AUTH_GSS;
	return flavor;
}

int
91
rpcauth_register(const struct rpc_authops *ops)
L
Linus Torvalds 已提交
92 93
{
	rpc_authflavor_t flavor;
94
	int ret = -EPERM;
L
Linus Torvalds 已提交
95 96 97

	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
		return -EINVAL;
98 99 100 101 102 103 104
	spin_lock(&rpc_authflavor_lock);
	if (auth_flavors[flavor] == NULL) {
		auth_flavors[flavor] = ops;
		ret = 0;
	}
	spin_unlock(&rpc_authflavor_lock);
	return ret;
L
Linus Torvalds 已提交
105
}
106
EXPORT_SYMBOL_GPL(rpcauth_register);
L
Linus Torvalds 已提交
107 108

int
109
rpcauth_unregister(const struct rpc_authops *ops)
L
Linus Torvalds 已提交
110 111
{
	rpc_authflavor_t flavor;
112
	int ret = -EPERM;
L
Linus Torvalds 已提交
113 114 115

	if ((flavor = ops->au_flavor) >= RPC_AUTH_MAXFLAVOR)
		return -EINVAL;
116 117 118 119 120 121 122
	spin_lock(&rpc_authflavor_lock);
	if (auth_flavors[flavor] == ops) {
		auth_flavors[flavor] = NULL;
		ret = 0;
	}
	spin_unlock(&rpc_authflavor_lock);
	return ret;
L
Linus Torvalds 已提交
123
}
124
EXPORT_SYMBOL_GPL(rpcauth_unregister);
L
Linus Torvalds 已提交
125

126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
/**
 * rpcauth_get_pseudoflavor - check if security flavor is supported
 * @flavor: a security flavor
 * @info: a GSS mech OID, quality of protection, and service value
 *
 * Verifies that an appropriate kernel module is available or already loaded.
 * Returns an equivalent pseudoflavor, or RPC_AUTH_MAXFLAVOR if "flavor" is
 * not supported locally.
 */
rpc_authflavor_t
rpcauth_get_pseudoflavor(rpc_authflavor_t flavor, struct rpcsec_gss_info *info)
{
	const struct rpc_authops *ops;
	rpc_authflavor_t pseudoflavor;

	ops = auth_flavors[flavor];
	if (ops == NULL)
		request_module("rpc-auth-%u", flavor);
	spin_lock(&rpc_authflavor_lock);
	ops = auth_flavors[flavor];
	if (ops == NULL || !try_module_get(ops->owner)) {
		spin_unlock(&rpc_authflavor_lock);
		return RPC_AUTH_MAXFLAVOR;
	}
	spin_unlock(&rpc_authflavor_lock);

	pseudoflavor = flavor;
	if (ops->info2flavor != NULL)
		pseudoflavor = ops->info2flavor(info);

	module_put(ops->owner);
	return pseudoflavor;
}
EXPORT_SYMBOL_GPL(rpcauth_get_pseudoflavor);

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
/**
 * rpcauth_get_gssinfo - find GSS tuple matching a GSS pseudoflavor
 * @pseudoflavor: GSS pseudoflavor to match
 * @info: rpcsec_gss_info structure to fill in
 *
 * Returns zero and fills in "info" if pseudoflavor matches a
 * supported mechanism.
 */
int
rpcauth_get_gssinfo(rpc_authflavor_t pseudoflavor, struct rpcsec_gss_info *info)
{
	rpc_authflavor_t flavor = pseudoflavor_to_flavor(pseudoflavor);
	const struct rpc_authops *ops;
	int result;

176 177 178
	if (flavor >= RPC_AUTH_MAXFLAVOR)
		return -EINVAL;

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
	ops = auth_flavors[flavor];
	if (ops == NULL)
		request_module("rpc-auth-%u", flavor);
	spin_lock(&rpc_authflavor_lock);
	ops = auth_flavors[flavor];
	if (ops == NULL || !try_module_get(ops->owner)) {
		spin_unlock(&rpc_authflavor_lock);
		return -ENOENT;
	}
	spin_unlock(&rpc_authflavor_lock);

	result = -ENOENT;
	if (ops->flavor2info != NULL)
		result = ops->flavor2info(pseudoflavor, info);

	module_put(ops->owner);
	return result;
}
EXPORT_SYMBOL_GPL(rpcauth_get_gssinfo);

C
Chuck Lever 已提交
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
/**
 * rpcauth_list_flavors - discover registered flavors and pseudoflavors
 * @array: array to fill in
 * @size: size of "array"
 *
 * Returns the number of array items filled in, or a negative errno.
 *
 * The returned array is not sorted by any policy.  Callers should not
 * rely on the order of the items in the returned array.
 */
int
rpcauth_list_flavors(rpc_authflavor_t *array, int size)
{
	rpc_authflavor_t flavor;
	int result = 0;

	spin_lock(&rpc_authflavor_lock);
	for (flavor = 0; flavor < RPC_AUTH_MAXFLAVOR; flavor++) {
		const struct rpc_authops *ops = auth_flavors[flavor];
		rpc_authflavor_t pseudos[4];
		int i, len;

		if (result >= size) {
			result = -ENOMEM;
			break;
		}

		if (ops == NULL)
			continue;
		if (ops->list_pseudoflavors == NULL) {
			array[result++] = ops->au_flavor;
			continue;
		}
		len = ops->list_pseudoflavors(pseudos, ARRAY_SIZE(pseudos));
		if (len < 0) {
			result = len;
			break;
		}
		for (i = 0; i < len; i++) {
			if (result >= size) {
				result = -ENOMEM;
				break;
			}
			array[result++] = pseudos[i];
		}
	}
	spin_unlock(&rpc_authflavor_lock);

	dprintk("RPC:       %s returns %d\n", __func__, result);
	return result;
}
EXPORT_SYMBOL_GPL(rpcauth_list_flavors);

L
Linus Torvalds 已提交
252 253 254 255
struct rpc_auth *
rpcauth_create(rpc_authflavor_t pseudoflavor, struct rpc_clnt *clnt)
{
	struct rpc_auth		*auth;
256
	const struct rpc_authops *ops;
L
Linus Torvalds 已提交
257 258
	u32			flavor = pseudoflavor_to_flavor(pseudoflavor);

259 260 261 262 263 264
	auth = ERR_PTR(-EINVAL);
	if (flavor >= RPC_AUTH_MAXFLAVOR)
		goto out;

	if ((ops = auth_flavors[flavor]) == NULL)
		request_module("rpc-auth-%u", flavor);
265 266 267 268
	spin_lock(&rpc_authflavor_lock);
	ops = auth_flavors[flavor];
	if (ops == NULL || !try_module_get(ops->owner)) {
		spin_unlock(&rpc_authflavor_lock);
269
		goto out;
270 271
	}
	spin_unlock(&rpc_authflavor_lock);
L
Linus Torvalds 已提交
272
	auth = ops->create(clnt, pseudoflavor);
273
	module_put(ops->owner);
274 275
	if (IS_ERR(auth))
		return auth;
L
Linus Torvalds 已提交
276
	if (clnt->cl_auth)
277
		rpcauth_release(clnt->cl_auth);
L
Linus Torvalds 已提交
278
	clnt->cl_auth = auth;
279 280

out:
L
Linus Torvalds 已提交
281 282
	return auth;
}
283
EXPORT_SYMBOL_GPL(rpcauth_create);
L
Linus Torvalds 已提交
284 285

void
286
rpcauth_release(struct rpc_auth *auth)
L
Linus Torvalds 已提交
287 288 289 290 291 292 293 294
{
	if (!atomic_dec_and_test(&auth->au_count))
		return;
	auth->au_ops->destroy(auth);
}

static DEFINE_SPINLOCK(rpc_credcache_lock);

295 296 297 298 299 300 301 302
static void
rpcauth_unhash_cred_locked(struct rpc_cred *cred)
{
	hlist_del_rcu(&cred->cr_hash);
	smp_mb__before_clear_bit();
	clear_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags);
}

303
static int
304 305 306
rpcauth_unhash_cred(struct rpc_cred *cred)
{
	spinlock_t *cache_lock;
307
	int ret;
308 309 310

	cache_lock = &cred->cr_auth->au_credcache->lock;
	spin_lock(cache_lock);
311 312
	ret = atomic_read(&cred->cr_count) == 0;
	if (ret)
313 314
		rpcauth_unhash_cred_locked(cred);
	spin_unlock(cache_lock);
315
	return ret;
316 317
}

L
Linus Torvalds 已提交
318 319 320 321
/*
 * Initialize RPC credential cache
 */
int
322
rpcauth_init_credcache(struct rpc_auth *auth)
L
Linus Torvalds 已提交
323 324
{
	struct rpc_cred_cache *new;
325
	unsigned int hashsize;
L
Linus Torvalds 已提交
326

327
	new = kmalloc(sizeof(*new), GFP_KERNEL);
L
Linus Torvalds 已提交
328
	if (!new)
329 330
		goto out_nocache;
	new->hashbits = auth_hashbits;
331
	hashsize = 1U << new->hashbits;
332 333 334
	new->hashtable = kcalloc(hashsize, sizeof(new->hashtable[0]), GFP_KERNEL);
	if (!new->hashtable)
		goto out_nohashtbl;
335
	spin_lock_init(&new->lock);
L
Linus Torvalds 已提交
336 337
	auth->au_credcache = new;
	return 0;
338 339 340 341
out_nohashtbl:
	kfree(new);
out_nocache:
	return -ENOMEM;
L
Linus Torvalds 已提交
342
}
343
EXPORT_SYMBOL_GPL(rpcauth_init_credcache);
L
Linus Torvalds 已提交
344 345 346 347 348

/*
 * Destroy a list of credentials
 */
static inline
349
void rpcauth_destroy_credlist(struct list_head *head)
L
Linus Torvalds 已提交
350 351 352
{
	struct rpc_cred *cred;

353 354 355
	while (!list_empty(head)) {
		cred = list_entry(head->next, struct rpc_cred, cr_lru);
		list_del_init(&cred->cr_lru);
L
Linus Torvalds 已提交
356 357 358 359 360 361 362 363 364
		put_rpccred(cred);
	}
}

/*
 * Clear the RPC credential cache, and delete those credentials
 * that are not referenced.
 */
void
365
rpcauth_clear_credcache(struct rpc_cred_cache *cache)
L
Linus Torvalds 已提交
366
{
367 368
	LIST_HEAD(free);
	struct hlist_head *head;
L
Linus Torvalds 已提交
369
	struct rpc_cred	*cred;
370
	unsigned int hashsize = 1U << cache->hashbits;
L
Linus Torvalds 已提交
371 372 373
	int		i;

	spin_lock(&rpc_credcache_lock);
374
	spin_lock(&cache->lock);
375
	for (i = 0; i < hashsize; i++) {
376 377 378 379
		head = &cache->hashtable[i];
		while (!hlist_empty(head)) {
			cred = hlist_entry(head->first, struct rpc_cred, cr_hash);
			get_rpccred(cred);
380 381 382 383 384
			if (!list_empty(&cred->cr_lru)) {
				list_del(&cred->cr_lru);
				number_cred_unused--;
			}
			list_add_tail(&cred->cr_lru, &free);
385
			rpcauth_unhash_cred_locked(cred);
L
Linus Torvalds 已提交
386 387
		}
	}
388
	spin_unlock(&cache->lock);
L
Linus Torvalds 已提交
389 390 391 392
	spin_unlock(&rpc_credcache_lock);
	rpcauth_destroy_credlist(&free);
}

393 394 395 396 397 398 399 400 401 402 403
/*
 * Destroy the RPC credential cache
 */
void
rpcauth_destroy_credcache(struct rpc_auth *auth)
{
	struct rpc_cred_cache *cache = auth->au_credcache;

	if (cache) {
		auth->au_credcache = NULL;
		rpcauth_clear_credcache(cache);
404
		kfree(cache->hashtable);
405 406 407
		kfree(cache);
	}
}
408
EXPORT_SYMBOL_GPL(rpcauth_destroy_credcache);
409

410 411 412

#define RPC_AUTH_EXPIRY_MORATORIUM (60 * HZ)

413 414 415
/*
 * Remove stale credentials. Avoid sleeping inside the loop.
 */
416 417
static int
rpcauth_prune_expired(struct list_head *free, int nr_to_scan)
L
Linus Torvalds 已提交
418
{
419
	spinlock_t *cache_lock;
420
	struct rpc_cred *cred, *next;
421
	unsigned long expired = jiffies - RPC_AUTH_EXPIRY_MORATORIUM;
422

423 424
	list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) {

425 426
		if (nr_to_scan-- == 0)
			break;
427 428 429 430
		/*
		 * Enforce a 60 second garbage collection moratorium
		 * Note that the cred_unused list must be time-ordered.
		 */
431
		if (time_in_range(cred->cr_expire, expired, jiffies) &&
432
		    test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0)
433
			return 0;
434

435
		list_del_init(&cred->cr_lru);
436
		number_cred_unused--;
437 438
		if (atomic_read(&cred->cr_count) != 0)
			continue;
439

440 441 442 443 444 445 446 447
		cache_lock = &cred->cr_auth->au_credcache->lock;
		spin_lock(cache_lock);
		if (atomic_read(&cred->cr_count) == 0) {
			get_rpccred(cred);
			list_add_tail(&cred->cr_lru, free);
			rpcauth_unhash_cred_locked(cred);
		}
		spin_unlock(cache_lock);
L
Linus Torvalds 已提交
448
	}
449
	return (number_cred_unused / 100) * sysctl_vfs_cache_pressure;
L
Linus Torvalds 已提交
450 451 452
}

/*
453
 * Run memory cache shrinker.
L
Linus Torvalds 已提交
454
 */
455
static int
456
rpcauth_cache_shrinker(struct shrinker *shrink, struct shrink_control *sc)
L
Linus Torvalds 已提交
457
{
458 459
	LIST_HEAD(free);
	int res;
460 461
	int nr_to_scan = sc->nr_to_scan;
	gfp_t gfp_mask = sc->gfp_mask;
462

463 464
	if ((gfp_mask & GFP_KERNEL) != GFP_KERNEL)
		return (nr_to_scan == 0) ? 0 : -1;
465 466
	if (list_empty(&cred_unused))
		return 0;
467
	spin_lock(&rpc_credcache_lock);
468
	res = rpcauth_prune_expired(&free, nr_to_scan);
469
	spin_unlock(&rpc_credcache_lock);
470 471
	rpcauth_destroy_credlist(&free);
	return res;
L
Linus Torvalds 已提交
472 473 474 475 476 477 478
}

/*
 * Look up a process' credentials in the authentication cache
 */
struct rpc_cred *
rpcauth_lookup_credcache(struct rpc_auth *auth, struct auth_cred * acred,
479
		int flags)
L
Linus Torvalds 已提交
480
{
481
	LIST_HEAD(free);
L
Linus Torvalds 已提交
482
	struct rpc_cred_cache *cache = auth->au_credcache;
483 484
	struct rpc_cred	*cred = NULL,
			*entry, *new;
485 486
	unsigned int nr;

487
	nr = hash_long(from_kuid(&init_user_ns, acred->uid), cache->hashbits);
L
Linus Torvalds 已提交
488

489
	rcu_read_lock();
490
	hlist_for_each_entry_rcu(entry, &cache->hashtable[nr], cr_hash) {
491 492
		if (!entry->cr_ops->crmatch(acred, entry, flags))
			continue;
493
		spin_lock(&cache->lock);
494
		if (test_bit(RPCAUTH_CRED_HASHED, &entry->cr_flags) == 0) {
495
			spin_unlock(&cache->lock);
496 497
			continue;
		}
498
		cred = get_rpccred(entry);
499
		spin_unlock(&cache->lock);
500
		break;
L
Linus Torvalds 已提交
501
	}
502 503
	rcu_read_unlock();

504
	if (cred != NULL)
505
		goto found;
L
Linus Torvalds 已提交
506

507 508 509 510 511
	new = auth->au_ops->crcreate(auth, acred, flags);
	if (IS_ERR(new)) {
		cred = new;
		goto out;
	}
L
Linus Torvalds 已提交
512

513
	spin_lock(&cache->lock);
514
	hlist_for_each_entry(entry, &cache->hashtable[nr], cr_hash) {
515 516 517 518 519 520
		if (!entry->cr_ops->crmatch(acred, entry, flags))
			continue;
		cred = get_rpccred(entry);
		break;
	}
	if (cred == NULL) {
521
		cred = new;
522 523 524 525
		set_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags);
		hlist_add_head_rcu(&cred->cr_hash, &cache->hashtable[nr]);
	} else
		list_add_tail(&new->cr_lru, &free);
526
	spin_unlock(&cache->lock);
527
found:
528 529 530
	if (test_bit(RPCAUTH_CRED_NEW, &cred->cr_flags) &&
	    cred->cr_ops->cr_init != NULL &&
	    !(flags & RPCAUTH_LOOKUP_NEW)) {
531 532 533 534 535
		int res = cred->cr_ops->cr_init(auth, cred);
		if (res < 0) {
			put_rpccred(cred);
			cred = ERR_PTR(res);
		}
L
Linus Torvalds 已提交
536
	}
537 538 539
	rpcauth_destroy_credlist(&free);
out:
	return cred;
L
Linus Torvalds 已提交
540
}
541
EXPORT_SYMBOL_GPL(rpcauth_lookup_credcache);
L
Linus Torvalds 已提交
542 543

struct rpc_cred *
544
rpcauth_lookupcred(struct rpc_auth *auth, int flags)
L
Linus Torvalds 已提交
545
{
546
	struct auth_cred acred;
L
Linus Torvalds 已提交
547
	struct rpc_cred *ret;
548
	const struct cred *cred = current_cred();
L
Linus Torvalds 已提交
549

550
	dprintk("RPC:       looking up %s cred\n",
L
Linus Torvalds 已提交
551
		auth->au_ops->au_name);
552 553 554 555 556 557

	memset(&acred, 0, sizeof(acred));
	acred.uid = cred->fsuid;
	acred.gid = cred->fsgid;
	acred.group_info = get_group_info(((struct cred *)cred)->group_info);

558
	ret = auth->au_ops->lookup_cred(auth, &acred, flags);
L
Linus Torvalds 已提交
559 560 561 562
	put_group_info(acred.group_info);
	return ret;
}

563 564 565 566 567
void
rpcauth_init_cred(struct rpc_cred *cred, const struct auth_cred *acred,
		  struct rpc_auth *auth, const struct rpc_credops *ops)
{
	INIT_HLIST_NODE(&cred->cr_hash);
568
	INIT_LIST_HEAD(&cred->cr_lru);
569 570 571 572 573 574 575 576 577
	atomic_set(&cred->cr_count, 1);
	cred->cr_auth = auth;
	cred->cr_ops = ops;
	cred->cr_expire = jiffies;
#ifdef RPC_DEBUG
	cred->cr_magic = RPCAUTH_CRED_MAGIC;
#endif
	cred->cr_uid = acred->uid;
}
578
EXPORT_SYMBOL_GPL(rpcauth_init_cred);
579

580
struct rpc_cred *
581
rpcauth_generic_bind_cred(struct rpc_task *task, struct rpc_cred *cred, int lookupflags)
582 583 584
{
	dprintk("RPC: %5u holding %s cred %p\n", task->tk_pid,
			cred->cr_auth->au_ops->au_name, cred);
585
	return get_rpccred(cred);
586
}
587
EXPORT_SYMBOL_GPL(rpcauth_generic_bind_cred);
588

589
static struct rpc_cred *
590
rpcauth_bind_root_cred(struct rpc_task *task, int lookupflags)
L
Linus Torvalds 已提交
591
{
592
	struct rpc_auth *auth = task->tk_client->cl_auth;
L
Linus Torvalds 已提交
593
	struct auth_cred acred = {
594 595
		.uid = GLOBAL_ROOT_UID,
		.gid = GLOBAL_ROOT_GID,
L
Linus Torvalds 已提交
596 597
	};

598
	dprintk("RPC: %5u looking up %s cred\n",
599
		task->tk_pid, task->tk_client->cl_auth->au_ops->au_name);
600
	return auth->au_ops->lookup_cred(auth, &acred, lookupflags);
601 602
}

603
static struct rpc_cred *
604
rpcauth_bind_new_cred(struct rpc_task *task, int lookupflags)
605 606 607 608 609
{
	struct rpc_auth *auth = task->tk_client->cl_auth;

	dprintk("RPC: %5u looking up %s cred\n",
		task->tk_pid, auth->au_ops->au_name);
610
	return rpcauth_lookupcred(auth, lookupflags);
L
Linus Torvalds 已提交
611 612
}

613
static int
614
rpcauth_bindcred(struct rpc_task *task, struct rpc_cred *cred, int flags)
L
Linus Torvalds 已提交
615
{
616
	struct rpc_rqst *req = task->tk_rqstp;
617
	struct rpc_cred *new;
618 619 620 621
	int lookupflags = 0;

	if (flags & RPC_TASK_ASYNC)
		lookupflags |= RPCAUTH_LOOKUP_NEW;
622
	if (cred != NULL)
623
		new = cred->cr_ops->crbind(task, cred, lookupflags);
624
	else if (flags & RPC_TASK_ROOTCREDS)
625
		new = rpcauth_bind_root_cred(task, lookupflags);
626
	else
627 628 629
		new = rpcauth_bind_new_cred(task, lookupflags);
	if (IS_ERR(new))
		return PTR_ERR(new);
630 631 632
	if (req->rq_cred != NULL)
		put_rpccred(req->rq_cred);
	req->rq_cred = new;
633
	return 0;
L
Linus Torvalds 已提交
634 635 636 637 638
}

void
put_rpccred(struct rpc_cred *cred)
{
639
	/* Fast path for unhashed credentials */
640 641 642
	if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) == 0) {
		if (atomic_dec_and_test(&cred->cr_count))
			cred->cr_ops->crdestroy(cred);
L
Linus Torvalds 已提交
643
		return;
644 645
	}

646 647
	if (!atomic_dec_and_lock(&cred->cr_count, &rpc_credcache_lock))
		return;
648 649
	if (!list_empty(&cred->cr_lru)) {
		number_cred_unused--;
650
		list_del_init(&cred->cr_lru);
651
	}
652
	if (test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0) {
653 654 655 656 657 658 659 660 661 662
		if (test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0) {
			cred->cr_expire = jiffies;
			list_add_tail(&cred->cr_lru, &cred_unused);
			number_cred_unused++;
			goto out_nodestroy;
		}
		if (!rpcauth_unhash_cred(cred)) {
			/* We were hashed and someone looked us up... */
			goto out_nodestroy;
		}
663 664
	}
	spin_unlock(&rpc_credcache_lock);
L
Linus Torvalds 已提交
665
	cred->cr_ops->crdestroy(cred);
666 667 668
	return;
out_nodestroy:
	spin_unlock(&rpc_credcache_lock);
L
Linus Torvalds 已提交
669
}
670
EXPORT_SYMBOL_GPL(put_rpccred);
L
Linus Torvalds 已提交
671

672 673
__be32 *
rpcauth_marshcred(struct rpc_task *task, __be32 *p)
L
Linus Torvalds 已提交
674
{
675
	struct rpc_cred	*cred = task->tk_rqstp->rq_cred;
L
Linus Torvalds 已提交
676

677
	dprintk("RPC: %5u marshaling %s cred %p\n",
678
		task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
679

L
Linus Torvalds 已提交
680 681 682
	return cred->cr_ops->crmarshal(task, p);
}

683 684
__be32 *
rpcauth_checkverf(struct rpc_task *task, __be32 *p)
L
Linus Torvalds 已提交
685
{
686
	struct rpc_cred	*cred = task->tk_rqstp->rq_cred;
L
Linus Torvalds 已提交
687

688
	dprintk("RPC: %5u validating %s cred %p\n",
689
		task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
690

L
Linus Torvalds 已提交
691 692 693
	return cred->cr_ops->crvalidate(task, p);
}

694 695 696 697 698 699 700 701 702
static void rpcauth_wrap_req_encode(kxdreproc_t encode, struct rpc_rqst *rqstp,
				   __be32 *data, void *obj)
{
	struct xdr_stream xdr;

	xdr_init_encode(&xdr, &rqstp->rq_snd_buf, data);
	encode(rqstp, &xdr, obj);
}

L
Linus Torvalds 已提交
703
int
704
rpcauth_wrap_req(struct rpc_task *task, kxdreproc_t encode, void *rqstp,
705
		__be32 *data, void *obj)
L
Linus Torvalds 已提交
706
{
707
	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
L
Linus Torvalds 已提交
708

709
	dprintk("RPC: %5u using %s cred %p to wrap rpc data\n",
L
Linus Torvalds 已提交
710 711 712 713
			task->tk_pid, cred->cr_ops->cr_name, cred);
	if (cred->cr_ops->crwrap_req)
		return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
	/* By default, we encode the arguments normally. */
714 715
	rpcauth_wrap_req_encode(encode, rqstp, data, obj);
	return 0;
L
Linus Torvalds 已提交
716 717
}

718 719 720 721 722 723 724 725 726 727
static int
rpcauth_unwrap_req_decode(kxdrdproc_t decode, struct rpc_rqst *rqstp,
			  __be32 *data, void *obj)
{
	struct xdr_stream xdr;

	xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, data);
	return decode(rqstp, &xdr, obj);
}

L
Linus Torvalds 已提交
728
int
729
rpcauth_unwrap_resp(struct rpc_task *task, kxdrdproc_t decode, void *rqstp,
730
		__be32 *data, void *obj)
L
Linus Torvalds 已提交
731
{
732
	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
L
Linus Torvalds 已提交
733

734
	dprintk("RPC: %5u using %s cred %p to unwrap rpc data\n",
L
Linus Torvalds 已提交
735 736 737 738 739
			task->tk_pid, cred->cr_ops->cr_name, cred);
	if (cred->cr_ops->crunwrap_resp)
		return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
						   data, obj);
	/* By default, we decode the arguments normally. */
740
	return rpcauth_unwrap_req_decode(decode, rqstp, data, obj);
L
Linus Torvalds 已提交
741 742 743 744 745
}

int
rpcauth_refreshcred(struct rpc_task *task)
{
746
	struct rpc_cred	*cred;
L
Linus Torvalds 已提交
747 748
	int err;

749 750 751 752 753 754
	cred = task->tk_rqstp->rq_cred;
	if (cred == NULL) {
		err = rpcauth_bindcred(task, task->tk_msg.rpc_cred, task->tk_flags);
		if (err < 0)
			goto out;
		cred = task->tk_rqstp->rq_cred;
J
Joe Perches 已提交
755
	}
756
	dprintk("RPC: %5u refreshing %s cred %p\n",
757
		task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
758

L
Linus Torvalds 已提交
759
	err = cred->cr_ops->crrefresh(task);
760
out:
L
Linus Torvalds 已提交
761 762 763 764 765 766 767 768
	if (err < 0)
		task->tk_status = err;
	return err;
}

void
rpcauth_invalcred(struct rpc_task *task)
{
769
	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
770

771
	dprintk("RPC: %5u invalidating %s cred %p\n",
772
		task->tk_pid, cred->cr_auth->au_ops->au_name, cred);
773 774
	if (cred)
		clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags);
L
Linus Torvalds 已提交
775 776 777 778 779
}

int
rpcauth_uptodatecred(struct rpc_task *task)
{
780
	struct rpc_cred *cred = task->tk_rqstp->rq_cred;
781 782 783

	return cred == NULL ||
		test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) != 0;
L
Linus Torvalds 已提交
784
}
785

786 787 788 789
static struct shrinker rpc_cred_shrinker = {
	.shrink = rpcauth_cache_shrinker,
	.seeks = DEFAULT_SEEKS,
};
790

791
int __init rpcauth_init_module(void)
792
{
793 794 795 796 797 798 799 800
	int err;

	err = rpc_init_authunix();
	if (err < 0)
		goto out1;
	err = rpc_init_generic_auth();
	if (err < 0)
		goto out2;
801
	register_shrinker(&rpc_cred_shrinker);
802 803 804 805 806
	return 0;
out2:
	rpc_destroy_authunix();
out1:
	return err;
807 808
}

809
void rpcauth_remove_module(void)
810
{
811 812
	rpc_destroy_authunix();
	rpc_destroy_generic_auth();
813
	unregister_shrinker(&rpc_cred_shrinker);
814
}