process_keys.c 19.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
 * 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/keyctl.h>
#include <linux/fs.h>
#include <linux/err.h>
I
Ingo Molnar 已提交
18
#include <linux/mutex.h>
19
#include <linux/security.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
	ret = install_process_keyring_to_cred(new);
	if (ret < 0) {
		abort_creds(new);
210
		return ret != -EEXIST ? ret : 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
 */
219
int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)
L
Linus Torvalds 已提交
220
{
221
	unsigned long flags;
L
Linus Torvalds 已提交
222
	struct key *old;
223 224

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

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

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

	/* install the keyring */
D
David Howells 已提交
241 242 243 244
	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 已提交
245

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

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

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

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

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

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

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

} /* end key_fsuid_changed() */

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

} /* end key_fsgid_changed() */

/*****************************************************************************/
/*
312
 * search only my process keyrings for the first matching key
L
Linus Torvalds 已提交
313 314 315 316 317
 * - 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
 */
318 319 320 321
key_ref_t search_my_process_keyrings(struct key_type *type,
				     const void *description,
				     key_match_func_t match,
				     const struct cred *cred)
L
Linus Torvalds 已提交
322
{
323
	key_ref_t key_ref, ret, err;
L
Linus Torvalds 已提交
324 325 326 327 328 329 330 331

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

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

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

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

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

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

388
		if (!IS_ERR(key_ref))
389 390
			goto found;

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

411
		switch (PTR_ERR(key_ref)) {
412 413 414 415
		case -EAGAIN: /* no key */
			if (ret)
				break;
		case -ENOKEY: /* negative key */
416
			ret = key_ref;
417 418
			break;
		default:
419
			err = key_ref;
420 421
			break;
		}
422
	}
423

424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453
	/* no key - decide on the error we're going to go for */
	key_ref = ret ? ret : err;

found:
	return key_ref;
}

/*****************************************************************************/
/*
 * 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
 */
key_ref_t search_process_keyrings(struct key_type *type,
				  const void *description,
				  key_match_func_t match,
				  const struct cred *cred)
{
	struct request_key_auth *rka;
	key_ref_t key_ref, ret = ERR_PTR(-EACCES), err;

	might_sleep();

	key_ref = search_my_process_keyrings(type, description, match, cred);
	if (!IS_ERR(key_ref))
		goto found;
	err = key_ref;

454 455 456 457
	/* 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
	 */
458
	if (cred->request_key_auth &&
D
David Howells 已提交
459
	    cred == current_cred() &&
460
	    type != &key_type_request_key_auth
461
	    ) {
462
		/* defend against the auth key being revoked */
463
		down_read(&cred->request_key_auth->sem);
464

465 466
		if (key_validate(cred->request_key_auth) == 0) {
			rka = cred->request_key_auth->payload.data;
467

468
			key_ref = search_process_keyrings(type, description,
D
David Howells 已提交
469
							  match, rka->cred);
L
Linus Torvalds 已提交
470

471
			up_read(&cred->request_key_auth->sem);
472 473 474 475

			if (!IS_ERR(key_ref))
				goto found;

476
			ret = key_ref;
477
		} else {
478
			up_read(&cred->request_key_auth->sem);
479
		}
L
Linus Torvalds 已提交
480 481 482
	}

	/* no key - decide on the error we're going to go for */
483 484 485 486 487 488
	if (err == ERR_PTR(-ENOKEY) || ret == ERR_PTR(-ENOKEY))
		key_ref = ERR_PTR(-ENOKEY);
	else if (err == ERR_PTR(-EACCES))
		key_ref = ret;
	else
		key_ref = err;
L
Linus Torvalds 已提交
489

490
found:
491
	return key_ref;
L
Linus Torvalds 已提交
492 493 494

} /* end search_process_keyrings() */

495 496 497 498
/*****************************************************************************/
/*
 * see if the key we're looking at is the target key
 */
499
int lookup_user_key_possessed(const struct key *key, const void *target)
500 501 502 503 504
{
	return key == target;

} /* end lookup_user_key_possessed() */

L
Linus Torvalds 已提交
505 506 507 508 509 510
/*****************************************************************************/
/*
 * 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
 */
511
key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
512
			  key_perm_t perm)
L
Linus Torvalds 已提交
513
{
514
	struct request_key_auth *rka;
D
David Howells 已提交
515
	const struct cred *cred;
L
Linus Torvalds 已提交
516
	struct key *key;
517
	key_ref_t key_ref, skey_ref;
L
Linus Torvalds 已提交
518 519
	int ret;

520 521
try_again:
	cred = get_current_cred();
522
	key_ref = ERR_PTR(-ENOKEY);
L
Linus Torvalds 已提交
523 524 525

	switch (id) {
	case KEY_SPEC_THREAD_KEYRING:
526
		if (!cred->thread_keyring) {
527
			if (!(lflags & KEY_LOOKUP_CREATE))
L
Linus Torvalds 已提交
528 529
				goto error;

530
			ret = install_thread_keyring();
L
Linus Torvalds 已提交
531
			if (ret < 0) {
532
				key_ref = ERR_PTR(ret);
L
Linus Torvalds 已提交
533 534
				goto error;
			}
535
			goto reget_creds;
L
Linus Torvalds 已提交
536 537
		}

538
		key = cred->thread_keyring;
L
Linus Torvalds 已提交
539
		atomic_inc(&key->usage);
540
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
541 542 543
		break;

	case KEY_SPEC_PROCESS_KEYRING:
544
		if (!cred->tgcred->process_keyring) {
545
			if (!(lflags & KEY_LOOKUP_CREATE))
L
Linus Torvalds 已提交
546 547
				goto error;

548
			ret = install_process_keyring();
L
Linus Torvalds 已提交
549
			if (ret < 0) {
550
				key_ref = ERR_PTR(ret);
L
Linus Torvalds 已提交
551 552
				goto error;
			}
553
			goto reget_creds;
L
Linus Torvalds 已提交
554 555
		}

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

	case KEY_SPEC_SESSION_KEYRING:
562
		if (!cred->tgcred->session_keyring) {
L
Linus Torvalds 已提交
563 564
			/* always install a session keyring upon access if one
			 * doesn't exist yet */
565
			ret = install_user_keyrings();
566 567
			if (ret < 0)
				goto error;
568 569
			ret = install_session_keyring(
				cred->user->session_keyring);
D
David Howells 已提交
570

L
Linus Torvalds 已提交
571 572
			if (ret < 0)
				goto error;
573
			goto reget_creds;
L
Linus Torvalds 已提交
574 575
		}

576
		rcu_read_lock();
577
		key = rcu_dereference(cred->tgcred->session_keyring);
L
Linus Torvalds 已提交
578
		atomic_inc(&key->usage);
579
		rcu_read_unlock();
580
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
581 582 583
		break;

	case KEY_SPEC_USER_KEYRING:
584
		if (!cred->user->uid_keyring) {
585
			ret = install_user_keyrings();
586 587 588 589
			if (ret < 0)
				goto error;
		}

590
		key = cred->user->uid_keyring;
L
Linus Torvalds 已提交
591
		atomic_inc(&key->usage);
592
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
593 594 595
		break;

	case KEY_SPEC_USER_SESSION_KEYRING:
596
		if (!cred->user->session_keyring) {
597
			ret = install_user_keyrings();
598 599 600 601
			if (ret < 0)
				goto error;
		}

602
		key = cred->user->session_keyring;
L
Linus Torvalds 已提交
603
		atomic_inc(&key->usage);
604
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
605 606 607 608
		break;

	case KEY_SPEC_GROUP_KEYRING:
		/* group keyrings are not yet supported */
609
		key_ref = ERR_PTR(-EINVAL);
L
Linus Torvalds 已提交
610 611
		goto error;

612
	case KEY_SPEC_REQKEY_AUTH_KEY:
613
		key = cred->request_key_auth;
614 615 616 617 618 619 620
		if (!key)
			goto error;

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

621
	case KEY_SPEC_REQUESTOR_KEYRING:
622
		if (!cred->request_key_auth)
623 624
			goto error;

625 626
		down_read(&cred->request_key_auth->sem);
		if (cred->request_key_auth->flags & KEY_FLAG_REVOKED) {
627 628 629
			key_ref = ERR_PTR(-EKEYREVOKED);
			key = NULL;
		} else {
630
			rka = cred->request_key_auth->payload.data;
631 632 633
			key = rka->dest_keyring;
			atomic_inc(&key->usage);
		}
634
		up_read(&cred->request_key_auth->sem);
635 636 637 638 639
		if (!key)
			goto error;
		key_ref = make_key_ref(key, 1);
		break;

L
Linus Torvalds 已提交
640
	default:
641
		key_ref = ERR_PTR(-EINVAL);
L
Linus Torvalds 已提交
642 643 644 645
		if (id < 1)
			goto error;

		key = key_lookup(id);
646
		if (IS_ERR(key)) {
647
			key_ref = ERR_CAST(key);
L
Linus Torvalds 已提交
648
			goto error;
649 650 651 652 653 654 655
		}

		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 已提交
656
						   cred);
657 658 659 660 661 662

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

L
Linus Torvalds 已提交
663 664 665
		break;
	}

666 667 668 669 670 671 672 673
	/* 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)) {
674 675 676 677 678 679 680 681 682 683 684
		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 已提交
685 686 687 688 689 690
		ret = key_validate(key);
		if (ret < 0)
			goto invalid_key;
	}

	ret = -EIO;
691 692
	if (!(lflags & KEY_LOOKUP_PARTIAL) &&
	    !test_bit(KEY_FLAG_INSTANTIATED, &key->flags))
L
Linus Torvalds 已提交
693 694
		goto invalid_key;

695
	/* check the permissions */
D
David Howells 已提交
696
	ret = key_task_permission(key_ref, cred, perm);
697
	if (ret < 0)
L
Linus Torvalds 已提交
698 699
		goto invalid_key;

700
error:
701
	put_cred(cred);
702
	return key_ref;
L
Linus Torvalds 已提交
703

704 705 706
invalid_key:
	key_ref_put(key_ref);
	key_ref = ERR_PTR(ret);
L
Linus Torvalds 已提交
707 708
	goto error;

709 710 711 712 713 714
	/* 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 已提交
715 716 717 718 719 720 721 722 723 724 725
} /* 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 已提交
726 727
	const struct cred *old;
	struct cred *new;
L
Linus Torvalds 已提交
728
	struct key *keyring;
D
David Howells 已提交
729 730 731 732 733
	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 */
734
	if (!current_is_single_threaded())
D
David Howells 已提交
735 736 737 738 739 740
		return -EMLINK;

	new = prepare_creds();
	if (!new)
		return -ENOMEM;
	old = current_cred();
L
Linus Torvalds 已提交
741 742 743

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

D
David Howells 已提交
748 749 750 751 752
		serial = new->tgcred->session_keyring->serial;
		ret = commit_creds(new);
		if (ret == 0)
			ret = serial;
		goto okay;
L
Linus Torvalds 已提交
753 754 755
	}

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

	/* look for an existing keyring of this name */
759
	keyring = find_keyring_by_name(name, false);
L
Linus Torvalds 已提交
760 761
	if (PTR_ERR(keyring) == -ENOKEY) {
		/* not found - try and create a new one */
D
David Howells 已提交
762
		keyring = keyring_alloc(name, old->uid, old->gid, old,
763
					KEY_ALLOC_IN_QUOTA, NULL);
L
Linus Torvalds 已提交
764 765
		if (IS_ERR(keyring)) {
			ret = PTR_ERR(keyring);
766
			goto error2;
L
Linus Torvalds 已提交
767
		}
D
David Howells 已提交
768
	} else if (IS_ERR(keyring)) {
L
Linus Torvalds 已提交
769 770 771 772 773
		ret = PTR_ERR(keyring);
		goto error2;
	}

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

D
David Howells 已提交
778 779 780
	commit_creds(new);
	mutex_unlock(&key_session_mutex);

L
Linus Torvalds 已提交
781 782
	ret = keyring->serial;
	key_put(keyring);
D
David Howells 已提交
783 784
okay:
	return ret;
L
Linus Torvalds 已提交
785

786
error2:
I
Ingo Molnar 已提交
787
	mutex_unlock(&key_session_mutex);
788
error:
D
David Howells 已提交
789
	abort_creds(new);
L
Linus Torvalds 已提交
790
	return ret;
D
David Howells 已提交
791
}
792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839

/*
 * Replace a process's session keyring when that process resumes userspace on
 * behalf of one of its children
 */
void key_replace_session_keyring(void)
{
	const struct cred *old;
	struct cred *new;

	if (!current->replacement_session_keyring)
		return;

	write_lock_irq(&tasklist_lock);
	new = current->replacement_session_keyring;
	current->replacement_session_keyring = NULL;
	write_unlock_irq(&tasklist_lock);

	if (!new)
		return;

	old = current_cred();
	new->  uid	= old->  uid;
	new-> euid	= old-> euid;
	new-> suid	= old-> suid;
	new->fsuid	= old->fsuid;
	new->  gid	= old->  gid;
	new-> egid	= old-> egid;
	new-> sgid	= old-> sgid;
	new->fsgid	= old->fsgid;
	new->user	= get_uid(old->user);
	new->group_info	= get_group_info(old->group_info);

	new->securebits	= old->securebits;
	new->cap_inheritable	= old->cap_inheritable;
	new->cap_permitted	= old->cap_permitted;
	new->cap_effective	= old->cap_effective;
	new->cap_bset		= old->cap_bset;

	new->jit_keyring	= old->jit_keyring;
	new->thread_keyring	= key_get(old->thread_keyring);
	new->tgcred->tgid	= old->tgcred->tgid;
	new->tgcred->process_keyring = key_get(old->tgcred->process_keyring);

	security_transfer_creds(new, old);

	commit_creds(new);
}