process_keys.c 19.1 KB
Newer Older
1
/* Management of a process's keyrings
L
Linus Torvalds 已提交
2
 *
3
 * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.
L
Linus Torvalds 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 * Written by David Howells (dhowells@redhat.com)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version
 * 2 of the License, or (at your option) any later version.
 */

#include <linux/module.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/keyctl.h>
#include <linux/fs.h>
#include <linux/err.h>
I
Ingo Molnar 已提交
19
#include <linux/mutex.h>
L
Linus Torvalds 已提交
20 21 22 23
#include <asm/uaccess.h>
#include "internal.h"

/* session keyring create vs join semaphore */
I
Ingo Molnar 已提交
24
static DEFINE_MUTEX(key_session_mutex);
L
Linus Torvalds 已提交
25

26 27 28
/* user keyring creation semaphore */
static DEFINE_MUTEX(key_user_keyring_mutex);

L
Linus Torvalds 已提交
29 30 31
/* the root user's tracking struct */
struct key_user root_key_user = {
	.usage		= ATOMIC_INIT(3),
32
	.cons_lock	= __MUTEX_INITIALIZER(root_key_user.cons_lock),
33
	.lock		= __SPIN_LOCK_UNLOCKED(root_key_user.lock),
L
Linus Torvalds 已提交
34 35 36 37 38 39 40
	.nkeys		= ATOMIC_INIT(2),
	.nikeys		= ATOMIC_INIT(2),
	.uid		= 0,
};

/*****************************************************************************/
/*
41
 * install user and user session keyrings for a particular UID
L
Linus Torvalds 已提交
42
 */
43
int install_user_keyrings(void)
L
Linus Torvalds 已提交
44
{
45
	struct user_struct *user = current->cred->user;
L
Linus Torvalds 已提交
46 47 48 49
	struct key *uid_keyring, *session_keyring;
	char buf[20];
	int ret;

50
	kenter("%p{%u}", user, user->uid);
L
Linus Torvalds 已提交
51

52 53 54
	if (user->uid_keyring) {
		kleave(" = 0 [exist]");
		return 0;
L
Linus Torvalds 已提交
55 56
	}

57 58
	mutex_lock(&key_user_keyring_mutex);
	ret = 0;
L
Linus Torvalds 已提交
59

60 61 62 63 64 65 66 67 68 69
	if (!user->uid_keyring) {
		/* get the UID-specific keyring
		 * - there may be one in existence already as it may have been
		 *   pinned by a session, but the user_struct pointing to it
		 *   may have been destroyed by setuid */
		sprintf(buf, "_uid.%u", user->uid);

		uid_keyring = find_keyring_by_name(buf, true);
		if (IS_ERR(uid_keyring)) {
			uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1,
70
						    current, KEY_ALLOC_IN_QUOTA,
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
						    NULL);
			if (IS_ERR(uid_keyring)) {
				ret = PTR_ERR(uid_keyring);
				goto error;
			}
		}

		/* get a default session keyring (which might also exist
		 * already) */
		sprintf(buf, "_uid_ses.%u", user->uid);

		session_keyring = find_keyring_by_name(buf, true);
		if (IS_ERR(session_keyring)) {
			session_keyring =
				keyring_alloc(buf, user->uid, (gid_t) -1,
86 87
					      current, KEY_ALLOC_IN_QUOTA,
					      NULL);
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
			if (IS_ERR(session_keyring)) {
				ret = PTR_ERR(session_keyring);
				goto error_release;
			}

			/* we install a link from the user session keyring to
			 * the user keyring */
			ret = key_link(session_keyring, uid_keyring);
			if (ret < 0)
				goto error_release_both;
		}

		/* install the keyrings */
		user->uid_keyring = uid_keyring;
		user->session_keyring = session_keyring;
L
Linus Torvalds 已提交
103 104
	}

105 106 107
	mutex_unlock(&key_user_keyring_mutex);
	kleave(" = 0");
	return 0;
L
Linus Torvalds 已提交
108

109 110 111 112
error_release_both:
	key_put(session_keyring);
error_release:
	key_put(uid_keyring);
113
error:
114 115
	mutex_unlock(&key_user_keyring_mutex);
	kleave(" = %d", ret);
L
Linus Torvalds 已提交
116
	return ret;
117
}
L
Linus Torvalds 已提交
118 119 120 121 122 123 124 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

/*****************************************************************************/
/*
 * deal with the UID changing
 */
void switch_uid_keyring(struct user_struct *new_user)
{
#if 0 /* do nothing for now */
	struct key *old;

	/* switch to the new user's session keyring if we were running under
	 * root's default session keyring */
	if (new_user->uid != 0 &&
	    current->session_keyring == &root_session_keyring
	    ) {
		atomic_inc(&new_user->session_keyring->usage);

		task_lock(current);
		old = current->session_keyring;
		current->session_keyring = new_user->session_keyring;
		task_unlock(current);

		key_put(old);
	}
#endif

} /* end switch_uid_keyring() */

/*****************************************************************************/
/*
 * install a fresh thread keyring, discarding the old one
 */
150
int install_thread_keyring(void)
L
Linus Torvalds 已提交
151
{
152
	struct task_struct *tsk = current;
L
Linus Torvalds 已提交
153 154 155 156 157 158
	struct key *keyring, *old;
	char buf[20];
	int ret;

	sprintf(buf, "_tid.%u", tsk->pid);

159
	keyring = keyring_alloc(buf, tsk->cred->uid, tsk->cred->gid, tsk,
160
				KEY_ALLOC_QUOTA_OVERRUN, NULL);
L
Linus Torvalds 已提交
161 162 163 164 165 166
	if (IS_ERR(keyring)) {
		ret = PTR_ERR(keyring);
		goto error;
	}

	task_lock(tsk);
167 168
	old = tsk->cred->thread_keyring;
	tsk->cred->thread_keyring = keyring;
L
Linus Torvalds 已提交
169 170 171 172 173
	task_unlock(tsk);

	ret = 0;

	key_put(old);
174
error:
L
Linus Torvalds 已提交
175 176 177 178 179 180 181 182
	return ret;

} /* end install_thread_keyring() */

/*****************************************************************************/
/*
 * make sure a process keyring is installed
 */
183
int install_process_keyring(void)
L
Linus Torvalds 已提交
184
{
185
	struct task_struct *tsk = current;
L
Linus Torvalds 已提交
186 187 188 189
	struct key *keyring;
	char buf[20];
	int ret;

190 191
	might_sleep();

L
Linus Torvalds 已提交
192 193 194
	if (!tsk->signal->process_keyring) {
		sprintf(buf, "_pid.%u", tsk->tgid);

195
		keyring = keyring_alloc(buf, tsk->cred->uid, tsk->cred->gid, tsk,
196
					KEY_ALLOC_QUOTA_OVERRUN, NULL);
L
Linus Torvalds 已提交
197 198 199 200 201
		if (IS_ERR(keyring)) {
			ret = PTR_ERR(keyring);
			goto error;
		}

202
		/* attach keyring */
203
		spin_lock_irq(&tsk->sighand->siglock);
L
Linus Torvalds 已提交
204 205 206 207
		if (!tsk->signal->process_keyring) {
			tsk->signal->process_keyring = keyring;
			keyring = NULL;
		}
208
		spin_unlock_irq(&tsk->sighand->siglock);
L
Linus Torvalds 已提交
209 210 211 212 213

		key_put(keyring);
	}

	ret = 0;
214
error:
L
Linus Torvalds 已提交
215 216 217 218 219 220 221 222 223
	return ret;

} /* end install_process_keyring() */

/*****************************************************************************/
/*
 * install a session keyring, discarding the old one
 * - if a keyring is not supplied, an empty one is invented
 */
224
static int install_session_keyring(struct key *keyring)
L
Linus Torvalds 已提交
225
{
226
	struct task_struct *tsk = current;
227
	unsigned long flags;
L
Linus Torvalds 已提交
228 229
	struct key *old;
	char buf[20];
230 231

	might_sleep();
L
Linus Torvalds 已提交
232 233 234 235 236

	/* create an empty session keyring */
	if (!keyring) {
		sprintf(buf, "_ses.%u", tsk->tgid);

237 238 239 240
		flags = KEY_ALLOC_QUOTA_OVERRUN;
		if (tsk->signal->session_keyring)
			flags = KEY_ALLOC_IN_QUOTA;

241
		keyring = keyring_alloc(buf, tsk->cred->uid, tsk->cred->gid, tsk,
242
					flags, NULL);
243 244
		if (IS_ERR(keyring))
			return PTR_ERR(keyring);
L
Linus Torvalds 已提交
245 246 247 248 249 250
	}
	else {
		atomic_inc(&keyring->usage);
	}

	/* install the keyring */
251 252
	spin_lock_irq(&tsk->sighand->siglock);
	old = tsk->signal->session_keyring;
253
	rcu_assign_pointer(tsk->signal->session_keyring, keyring);
254
	spin_unlock_irq(&tsk->sighand->siglock);
L
Linus Torvalds 已提交
255

256 257 258 259 260 261
	/* we're using RCU on the pointer, but there's no point synchronising
	 * on it if it didn't previously point to anything */
	if (old) {
		synchronize_rcu();
		key_put(old);
	}
L
Linus Torvalds 已提交
262

263
	return 0;
L
Linus Torvalds 已提交
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279

} /* end install_session_keyring() */

/*****************************************************************************/
/*
 * copy the keys in a thread group for fork without CLONE_THREAD
 */
int copy_thread_group_keys(struct task_struct *tsk)
{
	key_check(current->thread_group->session_keyring);
	key_check(current->thread_group->process_keyring);

	/* no process keyring yet */
	tsk->signal->process_keyring = NULL;

	/* same session keyring */
280
	rcu_read_lock();
L
Linus Torvalds 已提交
281
	tsk->signal->session_keyring =
282 283
		key_get(rcu_dereference(current->signal->session_keyring));
	rcu_read_unlock();
L
Linus Torvalds 已提交
284 285 286 287 288 289 290 291 292 293 294

	return 0;

} /* end copy_thread_group_keys() */

/*****************************************************************************/
/*
 * copy the keys for fork
 */
int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
{
295 296
	key_check(tsk->cred->thread_keyring);
	key_check(tsk->cred->request_key_auth);
L
Linus Torvalds 已提交
297 298

	/* no thread keyring yet */
299
	tsk->cred->thread_keyring = NULL;
300 301

	/* copy the request_key() authorisation for this thread */
302
	key_get(tsk->cred->request_key_auth);
303

L
Linus Torvalds 已提交
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
	return 0;

} /* end copy_keys() */

/*****************************************************************************/
/*
 * dispose of thread group keys upon thread group destruction
 */
void exit_thread_group_keys(struct signal_struct *tg)
{
	key_put(tg->session_keyring);
	key_put(tg->process_keyring);

} /* end exit_thread_group_keys() */

/*****************************************************************************/
/*
321
 * dispose of per-thread keys upon thread exit
L
Linus Torvalds 已提交
322 323 324
 */
void exit_keys(struct task_struct *tsk)
{
325 326
	key_put(tsk->cred->thread_keyring);
	key_put(tsk->cred->request_key_auth);
L
Linus Torvalds 已提交
327 328 329 330 331 332 333 334 335 336 337 338 339

} /* end exit_keys() */

/*****************************************************************************/
/*
 * deal with execve()
 */
int exec_keys(struct task_struct *tsk)
{
	struct key *old;

	/* newly exec'd tasks don't get a thread keyring */
	task_lock(tsk);
340 341
	old = tsk->cred->thread_keyring;
	tsk->cred->thread_keyring = NULL;
L
Linus Torvalds 已提交
342 343 344 345 346
	task_unlock(tsk);

	key_put(old);

	/* discard the process keyring from a newly exec'd task */
347
	spin_lock_irq(&tsk->sighand->siglock);
L
Linus Torvalds 已提交
348 349
	old = tsk->signal->process_keyring;
	tsk->signal->process_keyring = NULL;
350
	spin_unlock_irq(&tsk->sighand->siglock);
L
Linus Torvalds 已提交
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375

	key_put(old);

	return 0;

} /* end exec_keys() */

/*****************************************************************************/
/*
 * deal with SUID programs
 * - we might want to make this invent a new session keyring
 */
int suid_keys(struct task_struct *tsk)
{
	return 0;

} /* end suid_keys() */

/*****************************************************************************/
/*
 * the filesystem user ID changed
 */
void key_fsuid_changed(struct task_struct *tsk)
{
	/* update the ownership of the thread keyring */
376 377 378 379 380
	BUG_ON(!tsk->cred);
	if (tsk->cred->thread_keyring) {
		down_write(&tsk->cred->thread_keyring->sem);
		tsk->cred->thread_keyring->uid = tsk->cred->fsuid;
		up_write(&tsk->cred->thread_keyring->sem);
L
Linus Torvalds 已提交
381 382 383 384 385 386 387 388 389 390 391
	}

} /* end key_fsuid_changed() */

/*****************************************************************************/
/*
 * the filesystem group ID changed
 */
void key_fsgid_changed(struct task_struct *tsk)
{
	/* update the ownership of the thread keyring */
392 393 394 395 396
	BUG_ON(!tsk->cred);
	if (tsk->cred->thread_keyring) {
		down_write(&tsk->cred->thread_keyring->sem);
		tsk->cred->thread_keyring->gid = tsk->cred->fsgid;
		up_write(&tsk->cred->thread_keyring->sem);
L
Linus Torvalds 已提交
397 398 399 400 401 402 403 404 405 406 407 408
	}

} /* end key_fsgid_changed() */

/*****************************************************************************/
/*
 * search the process keyrings for the first matching key
 * - we use the supplied match function to see if the description (or other
 *   feature of interest) matches
 * - we return -EAGAIN if we didn't find any matching key
 * - we return -ENOKEY if we found only negative matching keys
 */
409 410 411 412
key_ref_t search_process_keyrings(struct key_type *type,
				  const void *description,
				  key_match_func_t match,
				  struct task_struct *context)
L
Linus Torvalds 已提交
413
{
414
	struct request_key_auth *rka;
415
	struct cred *cred;
416
	key_ref_t key_ref, ret, err;
L
Linus Torvalds 已提交
417

418 419
	might_sleep();

420 421
	cred = get_task_cred(context);

L
Linus Torvalds 已提交
422 423 424 425 426 427 428
	/* we want to return -EAGAIN or -ENOKEY if any of the keyrings were
	 * searchable, but we failed to find a key or we found a negative key;
	 * otherwise we want to return a sample error (probably -EACCES) if
	 * none of the keyrings were searchable
	 *
	 * in terms of priority: success > -ENOKEY > -EAGAIN > other error
	 */
429
	key_ref = NULL;
L
Linus Torvalds 已提交
430 431 432 433
	ret = NULL;
	err = ERR_PTR(-EAGAIN);

	/* search the thread keyring first */
434
	if (cred->thread_keyring) {
435
		key_ref = keyring_search_aux(
436
			make_key_ref(cred->thread_keyring, 1),
437 438
			context, type, description, match);
		if (!IS_ERR(key_ref))
L
Linus Torvalds 已提交
439 440
			goto found;

441
		switch (PTR_ERR(key_ref)) {
L
Linus Torvalds 已提交
442 443 444 445
		case -EAGAIN: /* no key */
			if (ret)
				break;
		case -ENOKEY: /* negative key */
446
			ret = key_ref;
L
Linus Torvalds 已提交
447 448
			break;
		default:
449
			err = key_ref;
L
Linus Torvalds 已提交
450 451 452 453 454
			break;
		}
	}

	/* search the process keyring second */
455
	if (context->signal->process_keyring) {
456 457 458 459
		key_ref = keyring_search_aux(
			make_key_ref(context->signal->process_keyring, 1),
			context, type, description, match);
		if (!IS_ERR(key_ref))
L
Linus Torvalds 已提交
460 461
			goto found;

462
		switch (PTR_ERR(key_ref)) {
L
Linus Torvalds 已提交
463 464 465 466
		case -EAGAIN: /* no key */
			if (ret)
				break;
		case -ENOKEY: /* negative key */
467
			ret = key_ref;
L
Linus Torvalds 已提交
468 469
			break;
		default:
470
			err = key_ref;
L
Linus Torvalds 已提交
471 472 473 474
			break;
		}
	}

475 476
	/* search the session keyring */
	if (context->signal->session_keyring) {
477
		rcu_read_lock();
478 479 480 481
		key_ref = keyring_search_aux(
			make_key_ref(rcu_dereference(
					     context->signal->session_keyring),
				     1),
482
			context, type, description, match);
483
		rcu_read_unlock();
484

485
		if (!IS_ERR(key_ref))
486 487
			goto found;

488
		switch (PTR_ERR(key_ref)) {
489 490 491 492
		case -EAGAIN: /* no key */
			if (ret)
				break;
		case -ENOKEY: /* negative key */
493
			ret = key_ref;
494 495
			break;
		default:
496
			err = key_ref;
497 498
			break;
		}
499 500
	}
	/* or search the user-session keyring */
501
	else if (cred->user->session_keyring) {
502
		key_ref = keyring_search_aux(
503
			make_key_ref(cred->user->session_keyring, 1),
504
			context, type, description, match);
505
		if (!IS_ERR(key_ref))
506 507
			goto found;

508
		switch (PTR_ERR(key_ref)) {
509 510 511 512
		case -EAGAIN: /* no key */
			if (ret)
				break;
		case -ENOKEY: /* negative key */
513
			ret = key_ref;
514 515
			break;
		default:
516
			err = key_ref;
517 518
			break;
		}
519
	}
520 521 522 523 524

	/* if this process has an instantiation authorisation key, then we also
	 * search the keyrings of the process mentioned there
	 * - we don't permit access to request_key auth keys via this method
	 */
525
	if (cred->request_key_auth &&
526
	    context == current &&
527
	    type != &key_type_request_key_auth
528
	    ) {
529
		/* defend against the auth key being revoked */
530
		down_read(&cred->request_key_auth->sem);
531

532 533
		if (key_validate(cred->request_key_auth) == 0) {
			rka = cred->request_key_auth->payload.data;
534

535 536
			key_ref = search_process_keyrings(type, description,
							  match, rka->context);
L
Linus Torvalds 已提交
537

538
			up_read(&cred->request_key_auth->sem);
539 540 541 542 543 544 545 546 547 548

			if (!IS_ERR(key_ref))
				goto found;

			switch (PTR_ERR(key_ref)) {
			case -EAGAIN: /* no key */
				if (ret)
					break;
			case -ENOKEY: /* negative key */
				ret = key_ref;
549
				break;
550 551 552 553 554
			default:
				err = key_ref;
				break;
			}
		} else {
555
			up_read(&cred->request_key_auth->sem);
556
		}
L
Linus Torvalds 已提交
557 558 559
	}

	/* no key - decide on the error we're going to go for */
560
	key_ref = ret ? ret : err;
L
Linus Torvalds 已提交
561

562
found:
563
	put_cred(cred);
564
	return key_ref;
L
Linus Torvalds 已提交
565 566 567

} /* end search_process_keyrings() */

568 569 570 571 572 573 574 575 576 577
/*****************************************************************************/
/*
 * see if the key we're looking at is the target key
 */
static int lookup_user_key_possessed(const struct key *key, const void *target)
{
	return key == target;

} /* end lookup_user_key_possessed() */

L
Linus Torvalds 已提交
578 579 580 581 582 583
/*****************************************************************************/
/*
 * lookup a key given a key ID from userspace with a given permissions mask
 * - don't create special keyrings unless so requested
 * - partially constructed keys aren't found unless requested
 */
584 585
key_ref_t lookup_user_key(key_serial_t id, int create, int partial,
			  key_perm_t perm)
L
Linus Torvalds 已提交
586
{
587 588
	struct request_key_auth *rka;
	struct task_struct *t = current;
589
	struct cred *cred = current_cred();
L
Linus Torvalds 已提交
590
	struct key *key;
591
	key_ref_t key_ref, skey_ref;
L
Linus Torvalds 已提交
592 593
	int ret;

594
	key_ref = ERR_PTR(-ENOKEY);
L
Linus Torvalds 已提交
595 596 597

	switch (id) {
	case KEY_SPEC_THREAD_KEYRING:
598
		if (!cred->thread_keyring) {
L
Linus Torvalds 已提交
599 600 601
			if (!create)
				goto error;

602
			ret = install_thread_keyring();
L
Linus Torvalds 已提交
603 604 605 606 607 608
			if (ret < 0) {
				key = ERR_PTR(ret);
				goto error;
			}
		}

609
		key = cred->thread_keyring;
L
Linus Torvalds 已提交
610
		atomic_inc(&key->usage);
611
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
612 613 614
		break;

	case KEY_SPEC_PROCESS_KEYRING:
615
		if (!t->signal->process_keyring) {
L
Linus Torvalds 已提交
616 617 618
			if (!create)
				goto error;

619
			ret = install_process_keyring();
L
Linus Torvalds 已提交
620 621 622 623 624 625
			if (ret < 0) {
				key = ERR_PTR(ret);
				goto error;
			}
		}

626
		key = t->signal->process_keyring;
L
Linus Torvalds 已提交
627
		atomic_inc(&key->usage);
628
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
629 630 631
		break;

	case KEY_SPEC_SESSION_KEYRING:
632
		if (!t->signal->session_keyring) {
L
Linus Torvalds 已提交
633 634
			/* always install a session keyring upon access if one
			 * doesn't exist yet */
635
			ret = install_user_keyrings();
636 637
			if (ret < 0)
				goto error;
638 639
			ret = install_session_keyring(
				cred->user->session_keyring);
L
Linus Torvalds 已提交
640 641 642 643
			if (ret < 0)
				goto error;
		}

644
		rcu_read_lock();
645
		key = rcu_dereference(t->signal->session_keyring);
L
Linus Torvalds 已提交
646
		atomic_inc(&key->usage);
647
		rcu_read_unlock();
648
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
649 650 651
		break;

	case KEY_SPEC_USER_KEYRING:
652
		if (!cred->user->uid_keyring) {
653
			ret = install_user_keyrings();
654 655 656 657
			if (ret < 0)
				goto error;
		}

658
		key = cred->user->uid_keyring;
L
Linus Torvalds 已提交
659
		atomic_inc(&key->usage);
660
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
661 662 663
		break;

	case KEY_SPEC_USER_SESSION_KEYRING:
664
		if (!cred->user->session_keyring) {
665
			ret = install_user_keyrings();
666 667 668 669
			if (ret < 0)
				goto error;
		}

670
		key = cred->user->session_keyring;
L
Linus Torvalds 已提交
671
		atomic_inc(&key->usage);
672
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
673 674 675 676 677 678 679
		break;

	case KEY_SPEC_GROUP_KEYRING:
		/* group keyrings are not yet supported */
		key = ERR_PTR(-EINVAL);
		goto error;

680
	case KEY_SPEC_REQKEY_AUTH_KEY:
681
		key = cred->request_key_auth;
682 683 684 685 686 687 688
		if (!key)
			goto error;

		atomic_inc(&key->usage);
		key_ref = make_key_ref(key, 1);
		break;

689
	case KEY_SPEC_REQUESTOR_KEYRING:
690
		if (!cred->request_key_auth)
691 692
			goto error;

693 694
		down_read(&cred->request_key_auth->sem);
		if (cred->request_key_auth->flags & KEY_FLAG_REVOKED) {
695 696 697
			key_ref = ERR_PTR(-EKEYREVOKED);
			key = NULL;
		} else {
698
			rka = cred->request_key_auth->payload.data;
699 700 701
			key = rka->dest_keyring;
			atomic_inc(&key->usage);
		}
702
		up_read(&cred->request_key_auth->sem);
703 704 705 706 707
		if (!key)
			goto error;
		key_ref = make_key_ref(key, 1);
		break;

L
Linus Torvalds 已提交
708
	default:
709
		key_ref = ERR_PTR(-EINVAL);
L
Linus Torvalds 已提交
710 711 712 713
		if (id < 1)
			goto error;

		key = key_lookup(id);
714
		if (IS_ERR(key)) {
715
			key_ref = ERR_CAST(key);
L
Linus Torvalds 已提交
716
			goto error;
717 718 719 720 721 722 723 724 725 726 727 728 729 730
		}

		key_ref = make_key_ref(key, 0);

		/* check to see if we possess the key */
		skey_ref = search_process_keyrings(key->type, key,
						   lookup_user_key_possessed,
						   current);

		if (!IS_ERR(skey_ref)) {
			key_put(key);
			key_ref = skey_ref;
		}

L
Linus Torvalds 已提交
731 732 733
		break;
	}

734 735 736 737 738 739 740 741 742 743 744 745
	if (!partial) {
		ret = wait_for_key_construction(key, true);
		switch (ret) {
		case -ERESTARTSYS:
			goto invalid_key;
		default:
			if (perm)
				goto invalid_key;
		case 0:
			break;
		}
	} else if (perm) {
L
Linus Torvalds 已提交
746 747 748 749 750 751
		ret = key_validate(key);
		if (ret < 0)
			goto invalid_key;
	}

	ret = -EIO;
752
	if (!partial && !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
L
Linus Torvalds 已提交
753 754
		goto invalid_key;

755
	/* check the permissions */
756
	ret = key_task_permission(key_ref, t, perm);
757
	if (ret < 0)
L
Linus Torvalds 已提交
758 759
		goto invalid_key;

760 761
error:
	return key_ref;
L
Linus Torvalds 已提交
762

763 764 765
invalid_key:
	key_ref_put(key_ref);
	key_ref = ERR_PTR(ret);
L
Linus Torvalds 已提交
766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784
	goto error;

} /* end lookup_user_key() */

/*****************************************************************************/
/*
 * join the named keyring as the session keyring if possible, or attempt to
 * create a new one of that name if not
 * - if the name is NULL, an empty anonymous keyring is installed instead
 * - named session keyring joining is done with a semaphore held
 */
long join_session_keyring(const char *name)
{
	struct task_struct *tsk = current;
	struct key *keyring;
	long ret;

	/* if no name is provided, install an anonymous keyring */
	if (!name) {
785
		ret = install_session_keyring(NULL);
L
Linus Torvalds 已提交
786 787 788
		if (ret < 0)
			goto error;

789 790 791
		rcu_read_lock();
		ret = rcu_dereference(tsk->signal->session_keyring)->serial;
		rcu_read_unlock();
L
Linus Torvalds 已提交
792 793 794 795
		goto error;
	}

	/* allow the user to join or create a named keyring */
I
Ingo Molnar 已提交
796
	mutex_lock(&key_session_mutex);
L
Linus Torvalds 已提交
797 798

	/* look for an existing keyring of this name */
799
	keyring = find_keyring_by_name(name, false);
L
Linus Torvalds 已提交
800 801
	if (PTR_ERR(keyring) == -ENOKEY) {
		/* not found - try and create a new one */
802
		keyring = keyring_alloc(name, tsk->cred->uid, tsk->cred->gid, tsk,
803
					KEY_ALLOC_IN_QUOTA, NULL);
L
Linus Torvalds 已提交
804 805
		if (IS_ERR(keyring)) {
			ret = PTR_ERR(keyring);
806
			goto error2;
L
Linus Torvalds 已提交
807 808 809 810 811 812 813 814
		}
	}
	else if (IS_ERR(keyring)) {
		ret = PTR_ERR(keyring);
		goto error2;
	}

	/* we've got a keyring - now to install it */
815
	ret = install_session_keyring(keyring);
L
Linus Torvalds 已提交
816 817 818 819 820 821
	if (ret < 0)
		goto error2;

	ret = keyring->serial;
	key_put(keyring);

822
error2:
I
Ingo Molnar 已提交
823
	mutex_unlock(&key_session_mutex);
824
error:
L
Linus Torvalds 已提交
825 826 827
	return ret;

} /* end join_session_keyring() */