process_keys.c 17.3 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>
20
#include <linux/user_namespace.h>
L
Linus Torvalds 已提交
21 22 23 24
#include <asm/uaccess.h>
#include "internal.h"

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

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

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

/*****************************************************************************/
/*
43
 * install user and user session keyrings for a particular UID
L
Linus Torvalds 已提交
44
 */
45
int install_user_keyrings(void)
L
Linus Torvalds 已提交
46
{
D
David Howells 已提交
47 48
	struct user_struct *user;
	const struct cred *cred;
L
Linus Torvalds 已提交
49 50 51 52
	struct key *uid_keyring, *session_keyring;
	char buf[20];
	int ret;

D
David Howells 已提交
53 54 55
	cred = current_cred();
	user = cred->user;

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

58 59 60
	if (user->uid_keyring) {
		kleave(" = 0 [exist]");
		return 0;
L
Linus Torvalds 已提交
61 62
	}

63 64
	mutex_lock(&key_user_keyring_mutex);
	ret = 0;
L
Linus Torvalds 已提交
65

66 67 68 69 70 71 72 73 74 75
	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,
D
David Howells 已提交
76
						    cred, KEY_ALLOC_IN_QUOTA,
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
						    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,
D
David Howells 已提交
92
					      cred, KEY_ALLOC_IN_QUOTA, NULL);
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
			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 已提交
108 109
	}

110 111 112
	mutex_unlock(&key_user_keyring_mutex);
	kleave(" = 0");
	return 0;
L
Linus Torvalds 已提交
113

114 115 116 117
error_release_both:
	key_put(session_keyring);
error_release:
	key_put(uid_keyring);
118
error:
119 120
	mutex_unlock(&key_user_keyring_mutex);
	kleave(" = %d", ret);
L
Linus Torvalds 已提交
121
	return ret;
122
}
L
Linus Torvalds 已提交
123 124

/*
D
David Howells 已提交
125
 * install a fresh thread keyring directly to new credentials
L
Linus Torvalds 已提交
126
 */
D
David Howells 已提交
127
int install_thread_keyring_to_cred(struct cred *new)
L
Linus Torvalds 已提交
128
{
D
David Howells 已提交
129
	struct key *keyring;
L
Linus Torvalds 已提交
130

D
David Howells 已提交
131 132 133 134
	keyring = keyring_alloc("_tid", new->uid, new->gid, new,
				KEY_ALLOC_QUOTA_OVERRUN, NULL);
	if (IS_ERR(keyring))
		return PTR_ERR(keyring);
L
Linus Torvalds 已提交
135

D
David Howells 已提交
136 137 138
	new->thread_keyring = keyring;
	return 0;
}
L
Linus Torvalds 已提交
139 140 141 142

/*
 * install a fresh thread keyring, discarding the old one
 */
D
David Howells 已提交
143
static int install_thread_keyring(void)
L
Linus Torvalds 已提交
144
{
D
David Howells 已提交
145
	struct cred *new;
L
Linus Torvalds 已提交
146 147
	int ret;

D
David Howells 已提交
148 149 150
	new = prepare_creds();
	if (!new)
		return -ENOMEM;
L
Linus Torvalds 已提交
151

D
David Howells 已提交
152 153 154 155 156 157
	BUG_ON(new->thread_keyring);

	ret = install_thread_keyring_to_cred(new);
	if (ret < 0) {
		abort_creds(new);
		return ret;
L
Linus Torvalds 已提交
158 159
	}

D
David Howells 已提交
160 161
	return commit_creds(new);
}
L
Linus Torvalds 已提交
162

D
David Howells 已提交
163 164 165 166 167 168 169 170 171
/*
 * install a process keyring directly to a credentials struct
 * - returns -EEXIST if there was already a process keyring, 0 if one installed,
 *   and other -ve on any other error
 */
int install_process_keyring_to_cred(struct cred *new)
{
	struct key *keyring;
	int ret;
L
Linus Torvalds 已提交
172

D
David Howells 已提交
173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
	if (new->tgcred->process_keyring)
		return -EEXIST;

	keyring = keyring_alloc("_pid", new->uid, new->gid,
				new, KEY_ALLOC_QUOTA_OVERRUN, NULL);
	if (IS_ERR(keyring))
		return PTR_ERR(keyring);

	spin_lock_irq(&new->tgcred->lock);
	if (!new->tgcred->process_keyring) {
		new->tgcred->process_keyring = keyring;
		keyring = NULL;
		ret = 0;
	} else {
		ret = -EEXIST;
	}
	spin_unlock_irq(&new->tgcred->lock);
	key_put(keyring);
L
Linus Torvalds 已提交
191
	return ret;
D
David Howells 已提交
192
}
L
Linus Torvalds 已提交
193 194 195

/*
 * make sure a process keyring is installed
D
David Howells 已提交
196
 * - we
L
Linus Torvalds 已提交
197
 */
D
David Howells 已提交
198
static int install_process_keyring(void)
L
Linus Torvalds 已提交
199
{
D
David Howells 已提交
200
	struct cred *new;
L
Linus Torvalds 已提交
201 202
	int ret;

D
David Howells 已提交
203 204 205
	new = prepare_creds();
	if (!new)
		return -ENOMEM;
L
Linus Torvalds 已提交
206

D
David Howells 已提交
207 208 209 210
	ret = install_process_keyring_to_cred(new);
	if (ret < 0) {
		abort_creds(new);
		return ret != -EEXIST ?: 0;
L
Linus Torvalds 已提交
211 212
	}

D
David Howells 已提交
213 214
	return commit_creds(new);
}
L
Linus Torvalds 已提交
215 216

/*
D
David Howells 已提交
217
 * install a session keyring directly to a credentials struct
L
Linus Torvalds 已提交
218
 */
D
David Howells 已提交
219 220
static int install_session_keyring_to_cred(struct cred *cred,
					   struct key *keyring)
L
Linus Torvalds 已提交
221
{
222
	unsigned long flags;
L
Linus Torvalds 已提交
223
	struct key *old;
224 225

	might_sleep();
L
Linus Torvalds 已提交
226 227 228

	/* create an empty session keyring */
	if (!keyring) {
229
		flags = KEY_ALLOC_QUOTA_OVERRUN;
D
David Howells 已提交
230
		if (cred->tgcred->session_keyring)
231 232
			flags = KEY_ALLOC_IN_QUOTA;

D
David Howells 已提交
233 234
		keyring = keyring_alloc("_ses", cred->uid, cred->gid,
					cred, flags, NULL);
235 236
		if (IS_ERR(keyring))
			return PTR_ERR(keyring);
D
David Howells 已提交
237
	} else {
L
Linus Torvalds 已提交
238 239 240 241
		atomic_inc(&keyring->usage);
	}

	/* install the keyring */
D
David Howells 已提交
242 243 244 245
	spin_lock_irq(&cred->tgcred->lock);
	old = cred->tgcred->session_keyring;
	rcu_assign_pointer(cred->tgcred->session_keyring, keyring);
	spin_unlock_irq(&cred->tgcred->lock);
L
Linus Torvalds 已提交
246

247 248 249 250 251 252
	/* 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 已提交
253

254
	return 0;
D
David Howells 已提交
255
}
L
Linus Torvalds 已提交
256 257

/*
D
David Howells 已提交
258 259
 * install a session keyring, discarding the old one
 * - if a keyring is not supplied, an empty one is invented
L
Linus Torvalds 已提交
260
 */
D
David Howells 已提交
261
static int install_session_keyring(struct key *keyring)
L
Linus Torvalds 已提交
262
{
D
David Howells 已提交
263 264
	struct cred *new;
	int ret;
L
Linus Torvalds 已提交
265

D
David Howells 已提交
266 267 268
	new = prepare_creds();
	if (!new)
		return -ENOMEM;
L
Linus Torvalds 已提交
269

D
David Howells 已提交
270 271 272 273 274
	ret = install_session_keyring_to_cred(new, NULL);
	if (ret < 0) {
		abort_creds(new);
		return ret;
	}
L
Linus Torvalds 已提交
275

D
David Howells 已提交
276 277
	return commit_creds(new);
}
L
Linus Torvalds 已提交
278 279 280 281 282 283 284 285

/*****************************************************************************/
/*
 * the filesystem user ID changed
 */
void key_fsuid_changed(struct task_struct *tsk)
{
	/* update the ownership of the thread keyring */
286 287 288 289 290
	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 已提交
291 292 293 294 295 296 297 298 299 300 301
	}

} /* end key_fsuid_changed() */

/*****************************************************************************/
/*
 * the filesystem group ID changed
 */
void key_fsgid_changed(struct task_struct *tsk)
{
	/* update the ownership of the thread keyring */
302 303 304 305 306
	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 已提交
307 308 309 310 311 312 313 314 315 316 317 318
	}

} /* 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
 */
319 320 321
key_ref_t search_process_keyrings(struct key_type *type,
				  const void *description,
				  key_match_func_t match,
D
David Howells 已提交
322
				  const struct cred *cred)
L
Linus Torvalds 已提交
323
{
324
	struct request_key_auth *rka;
325
	key_ref_t key_ref, ret, err;
L
Linus Torvalds 已提交
326

327 328
	might_sleep();

L
Linus Torvalds 已提交
329 330 331 332 333 334 335
	/* 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
	 */
336
	key_ref = NULL;
L
Linus Torvalds 已提交
337 338 339 340
	ret = NULL;
	err = ERR_PTR(-EAGAIN);

	/* search the thread keyring first */
341
	if (cred->thread_keyring) {
342
		key_ref = keyring_search_aux(
343
			make_key_ref(cred->thread_keyring, 1),
D
David Howells 已提交
344
			cred, type, description, match);
345
		if (!IS_ERR(key_ref))
L
Linus Torvalds 已提交
346 347
			goto found;

348
		switch (PTR_ERR(key_ref)) {
L
Linus Torvalds 已提交
349 350 351 352
		case -EAGAIN: /* no key */
			if (ret)
				break;
		case -ENOKEY: /* negative key */
353
			ret = key_ref;
L
Linus Torvalds 已提交
354 355
			break;
		default:
356
			err = key_ref;
L
Linus Torvalds 已提交
357 358 359 360 361
			break;
		}
	}

	/* search the process keyring second */
362
	if (cred->tgcred->process_keyring) {
363
		key_ref = keyring_search_aux(
364
			make_key_ref(cred->tgcred->process_keyring, 1),
D
David Howells 已提交
365
			cred, type, description, match);
366
		if (!IS_ERR(key_ref))
L
Linus Torvalds 已提交
367 368
			goto found;

369
		switch (PTR_ERR(key_ref)) {
L
Linus Torvalds 已提交
370 371 372 373
		case -EAGAIN: /* no key */
			if (ret)
				break;
		case -ENOKEY: /* negative key */
374
			ret = key_ref;
L
Linus Torvalds 已提交
375 376
			break;
		default:
377
			err = key_ref;
L
Linus Torvalds 已提交
378 379 380 381
			break;
		}
	}

382
	/* search the session keyring */
383
	if (cred->tgcred->session_keyring) {
384
		rcu_read_lock();
385 386
		key_ref = keyring_search_aux(
			make_key_ref(rcu_dereference(
387
					     cred->tgcred->session_keyring),
388
				     1),
D
David Howells 已提交
389
			cred, type, description, match);
390
		rcu_read_unlock();
391

392
		if (!IS_ERR(key_ref))
393 394
			goto found;

395
		switch (PTR_ERR(key_ref)) {
396 397 398 399
		case -EAGAIN: /* no key */
			if (ret)
				break;
		case -ENOKEY: /* negative key */
400
			ret = key_ref;
401 402
			break;
		default:
403
			err = key_ref;
404 405
			break;
		}
406 407
	}
	/* or search the user-session keyring */
408
	else if (cred->user->session_keyring) {
409
		key_ref = keyring_search_aux(
410
			make_key_ref(cred->user->session_keyring, 1),
D
David Howells 已提交
411
			cred, type, description, match);
412
		if (!IS_ERR(key_ref))
413 414
			goto found;

415
		switch (PTR_ERR(key_ref)) {
416 417 418 419
		case -EAGAIN: /* no key */
			if (ret)
				break;
		case -ENOKEY: /* negative key */
420
			ret = key_ref;
421 422
			break;
		default:
423
			err = key_ref;
424 425
			break;
		}
426
	}
427 428 429 430 431

	/* 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
	 */
432
	if (cred->request_key_auth &&
D
David Howells 已提交
433
	    cred == current_cred() &&
434
	    type != &key_type_request_key_auth
435
	    ) {
436
		/* defend against the auth key being revoked */
437
		down_read(&cred->request_key_auth->sem);
438

439 440
		if (key_validate(cred->request_key_auth) == 0) {
			rka = cred->request_key_auth->payload.data;
441

442
			key_ref = search_process_keyrings(type, description,
D
David Howells 已提交
443
							  match, rka->cred);
L
Linus Torvalds 已提交
444

445
			up_read(&cred->request_key_auth->sem);
446 447 448 449 450 451 452 453 454 455

			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;
456
				break;
457 458 459 460 461
			default:
				err = key_ref;
				break;
			}
		} else {
462
			up_read(&cred->request_key_auth->sem);
463
		}
L
Linus Torvalds 已提交
464 465 466
	}

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

469
found:
470
	return key_ref;
L
Linus Torvalds 已提交
471 472 473

} /* end search_process_keyrings() */

474 475 476 477 478 479 480 481 482 483
/*****************************************************************************/
/*
 * 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 已提交
484 485 486 487 488 489
/*****************************************************************************/
/*
 * 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
 */
490
key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
491
			  key_perm_t perm)
L
Linus Torvalds 已提交
492
{
493
	struct request_key_auth *rka;
D
David Howells 已提交
494
	const struct cred *cred;
L
Linus Torvalds 已提交
495
	struct key *key;
496
	key_ref_t key_ref, skey_ref;
L
Linus Torvalds 已提交
497 498
	int ret;

499 500
try_again:
	cred = get_current_cred();
501
	key_ref = ERR_PTR(-ENOKEY);
L
Linus Torvalds 已提交
502 503 504

	switch (id) {
	case KEY_SPEC_THREAD_KEYRING:
505
		if (!cred->thread_keyring) {
506
			if (!(lflags & KEY_LOOKUP_CREATE))
L
Linus Torvalds 已提交
507 508
				goto error;

509
			ret = install_thread_keyring();
L
Linus Torvalds 已提交
510 511 512 513
			if (ret < 0) {
				key = ERR_PTR(ret);
				goto error;
			}
514
			goto reget_creds;
L
Linus Torvalds 已提交
515 516
		}

517
		key = cred->thread_keyring;
L
Linus Torvalds 已提交
518
		atomic_inc(&key->usage);
519
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
520 521 522
		break;

	case KEY_SPEC_PROCESS_KEYRING:
523
		if (!cred->tgcred->process_keyring) {
524
			if (!(lflags & KEY_LOOKUP_CREATE))
L
Linus Torvalds 已提交
525 526
				goto error;

527
			ret = install_process_keyring();
L
Linus Torvalds 已提交
528 529 530 531
			if (ret < 0) {
				key = ERR_PTR(ret);
				goto error;
			}
532
			goto reget_creds;
L
Linus Torvalds 已提交
533 534
		}

535
		key = cred->tgcred->process_keyring;
L
Linus Torvalds 已提交
536
		atomic_inc(&key->usage);
537
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
538 539 540
		break;

	case KEY_SPEC_SESSION_KEYRING:
541
		if (!cred->tgcred->session_keyring) {
L
Linus Torvalds 已提交
542 543
			/* always install a session keyring upon access if one
			 * doesn't exist yet */
544
			ret = install_user_keyrings();
545 546
			if (ret < 0)
				goto error;
547 548
			ret = install_session_keyring(
				cred->user->session_keyring);
D
David Howells 已提交
549

L
Linus Torvalds 已提交
550 551
			if (ret < 0)
				goto error;
552
			goto reget_creds;
L
Linus Torvalds 已提交
553 554
		}

555
		rcu_read_lock();
556
		key = rcu_dereference(cred->tgcred->session_keyring);
L
Linus Torvalds 已提交
557
		atomic_inc(&key->usage);
558
		rcu_read_unlock();
559
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
560 561 562
		break;

	case KEY_SPEC_USER_KEYRING:
563
		if (!cred->user->uid_keyring) {
564
			ret = install_user_keyrings();
565 566 567 568
			if (ret < 0)
				goto error;
		}

569
		key = cred->user->uid_keyring;
L
Linus Torvalds 已提交
570
		atomic_inc(&key->usage);
571
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
572 573 574
		break;

	case KEY_SPEC_USER_SESSION_KEYRING:
575
		if (!cred->user->session_keyring) {
576
			ret = install_user_keyrings();
577 578 579 580
			if (ret < 0)
				goto error;
		}

581
		key = cred->user->session_keyring;
L
Linus Torvalds 已提交
582
		atomic_inc(&key->usage);
583
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
584 585 586 587 588 589 590
		break;

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

591
	case KEY_SPEC_REQKEY_AUTH_KEY:
592
		key = cred->request_key_auth;
593 594 595 596 597 598 599
		if (!key)
			goto error;

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

600
	case KEY_SPEC_REQUESTOR_KEYRING:
601
		if (!cred->request_key_auth)
602 603
			goto error;

604 605
		down_read(&cred->request_key_auth->sem);
		if (cred->request_key_auth->flags & KEY_FLAG_REVOKED) {
606 607 608
			key_ref = ERR_PTR(-EKEYREVOKED);
			key = NULL;
		} else {
609
			rka = cred->request_key_auth->payload.data;
610 611 612
			key = rka->dest_keyring;
			atomic_inc(&key->usage);
		}
613
		up_read(&cred->request_key_auth->sem);
614 615 616 617 618
		if (!key)
			goto error;
		key_ref = make_key_ref(key, 1);
		break;

L
Linus Torvalds 已提交
619
	default:
620
		key_ref = ERR_PTR(-EINVAL);
L
Linus Torvalds 已提交
621 622 623 624
		if (id < 1)
			goto error;

		key = key_lookup(id);
625
		if (IS_ERR(key)) {
626
			key_ref = ERR_CAST(key);
L
Linus Torvalds 已提交
627
			goto error;
628 629 630 631 632 633 634
		}

		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,
D
David Howells 已提交
635
						   cred);
636 637 638 639 640 641

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

L
Linus Torvalds 已提交
642 643 644
		break;
	}

645 646 647 648 649 650 651 652
	/* unlink does not use the nominated key in any way, so can skip all
	 * the permission checks as it is only concerned with the keyring */
	if (lflags & KEY_LOOKUP_FOR_UNLINK) {
		ret = 0;
		goto error;
	}

	if (!(lflags & KEY_LOOKUP_PARTIAL)) {
653 654 655 656 657 658 659 660 661 662 663
		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 已提交
664 665 666 667 668 669
		ret = key_validate(key);
		if (ret < 0)
			goto invalid_key;
	}

	ret = -EIO;
670 671
	if (!(lflags & KEY_LOOKUP_PARTIAL) &&
	    !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
L
Linus Torvalds 已提交
672 673
		goto invalid_key;

674
	/* check the permissions */
D
David Howells 已提交
675
	ret = key_task_permission(key_ref, cred, perm);
676
	if (ret < 0)
L
Linus Torvalds 已提交
677 678
		goto invalid_key;

679
error:
680
	put_cred(cred);
681
	return key_ref;
L
Linus Torvalds 已提交
682

683 684 685
invalid_key:
	key_ref_put(key_ref);
	key_ref = ERR_PTR(ret);
L
Linus Torvalds 已提交
686 687
	goto error;

688 689 690 691 692 693
	/* if we attempted to install a keyring, then it may have caused new
	 * creds to be installed */
reget_creds:
	put_cred(cred);
	goto try_again;

L
Linus Torvalds 已提交
694 695 696 697 698 699 700 701 702 703 704
} /* 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)
{
D
David Howells 已提交
705 706
	const struct cred *old;
	struct cred *new;
L
Linus Torvalds 已提交
707
	struct key *keyring;
D
David Howells 已提交
708 709 710 711 712
	long ret, serial;

	/* only permit this if there's a single thread in the thread group -
	 * this avoids us having to adjust the creds on all threads and risking
	 * ENOMEM */
713
	if (!current_is_single_threaded())
D
David Howells 已提交
714 715 716 717 718 719
		return -EMLINK;

	new = prepare_creds();
	if (!new)
		return -ENOMEM;
	old = current_cred();
L
Linus Torvalds 已提交
720 721 722

	/* if no name is provided, install an anonymous keyring */
	if (!name) {
D
David Howells 已提交
723
		ret = install_session_keyring_to_cred(new, NULL);
L
Linus Torvalds 已提交
724 725 726
		if (ret < 0)
			goto error;

D
David Howells 已提交
727 728 729 730 731
		serial = new->tgcred->session_keyring->serial;
		ret = commit_creds(new);
		if (ret == 0)
			ret = serial;
		goto okay;
L
Linus Torvalds 已提交
732 733 734
	}

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

	/* look for an existing keyring of this name */
738
	keyring = find_keyring_by_name(name, false);
L
Linus Torvalds 已提交
739 740
	if (PTR_ERR(keyring) == -ENOKEY) {
		/* not found - try and create a new one */
D
David Howells 已提交
741
		keyring = keyring_alloc(name, old->uid, old->gid, old,
742
					KEY_ALLOC_IN_QUOTA, NULL);
L
Linus Torvalds 已提交
743 744
		if (IS_ERR(keyring)) {
			ret = PTR_ERR(keyring);
745
			goto error2;
L
Linus Torvalds 已提交
746
		}
D
David Howells 已提交
747
	} else if (IS_ERR(keyring)) {
L
Linus Torvalds 已提交
748 749 750 751 752
		ret = PTR_ERR(keyring);
		goto error2;
	}

	/* we've got a keyring - now to install it */
D
David Howells 已提交
753
	ret = install_session_keyring_to_cred(new, keyring);
L
Linus Torvalds 已提交
754 755 756
	if (ret < 0)
		goto error2;

D
David Howells 已提交
757 758 759
	commit_creds(new);
	mutex_unlock(&key_session_mutex);

L
Linus Torvalds 已提交
760 761
	ret = keyring->serial;
	key_put(keyring);
D
David Howells 已提交
762 763
okay:
	return ret;
L
Linus Torvalds 已提交
764

765
error2:
I
Ingo Molnar 已提交
766
	mutex_unlock(&key_session_mutex);
767
error:
D
David Howells 已提交
768
	abort_creds(new);
L
Linus Torvalds 已提交
769
	return ret;
D
David Howells 已提交
770
}