process_keys.c 17.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2
/* process_keys.c: management of a process's keyrings
 *
3
 * Copyright (C) 2004-5 Red Hat, Inc. All Rights Reserved.
L
Linus Torvalds 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
 * 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>
#include <asm/uaccess.h>
#include "internal.h"

/* session keyring create vs join semaphore */
static DECLARE_MUTEX(key_session_sem);

/* the root user's tracking struct */
struct key_user root_key_user = {
	.usage		= ATOMIC_INIT(3),
	.consq		= LIST_HEAD_INIT(root_key_user.consq),
	.lock		= SPIN_LOCK_UNLOCKED,
	.nkeys		= ATOMIC_INIT(2),
	.nikeys		= ATOMIC_INIT(2),
	.uid		= 0,
};

/* the root user's UID keyring */
struct key root_user_keyring = {
	.usage		= ATOMIC_INIT(1),
	.serial		= 2,
	.type		= &key_type_keyring,
	.user		= &root_key_user,
	.sem		= __RWSEM_INITIALIZER(root_user_keyring.sem),
42
	.perm		= KEY_POS_ALL | KEY_USR_ALL,
43
	.flags		= 1 << KEY_FLAG_INSTANTIATED,
L
Linus Torvalds 已提交
44 45 46 47 48 49 50 51 52 53 54 55 56
	.description	= "_uid.0",
#ifdef KEY_DEBUGGING
	.magic		= KEY_DEBUG_MAGIC,
#endif
};

/* the root user's default session keyring */
struct key root_session_keyring = {
	.usage		= ATOMIC_INIT(1),
	.serial		= 1,
	.type		= &key_type_keyring,
	.user		= &root_key_user,
	.sem		= __RWSEM_INITIALIZER(root_session_keyring.sem),
57
	.perm		= KEY_POS_ALL | KEY_USR_ALL,
58
	.flags		= 1 << KEY_FLAG_INSTANTIATED,
L
Linus Torvalds 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
	.description	= "_uid_ses.0",
#ifdef KEY_DEBUGGING
	.magic		= KEY_DEBUG_MAGIC,
#endif
};

/*****************************************************************************/
/*
 * allocate the keyrings to be associated with a UID
 */
int alloc_uid_keyring(struct user_struct *user)
{
	struct key *uid_keyring, *session_keyring;
	char buf[20];
	int ret;

	/* concoct a default session keyring */
	sprintf(buf, "_uid_ses.%u", user->uid);

	session_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0, NULL);
	if (IS_ERR(session_keyring)) {
		ret = PTR_ERR(session_keyring);
		goto error;
	}

	/* and a UID specific keyring, pointed to by the default session
	 * keyring */
	sprintf(buf, "_uid.%u", user->uid);

	uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, 0,
				    session_keyring);
	if (IS_ERR(uid_keyring)) {
		key_put(session_keyring);
		ret = PTR_ERR(uid_keyring);
		goto error;
	}

	/* install the keyrings */
	user->uid_keyring = uid_keyring;
	user->session_keyring = session_keyring;
	ret = 0;

101
error:
L
Linus Torvalds 已提交
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 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 150 151 152 153 154 155 156 157 158
	return ret;

} /* end alloc_uid_keyring() */

/*****************************************************************************/
/*
 * 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
 */
int install_thread_keyring(struct task_struct *tsk)
{
	struct key *keyring, *old;
	char buf[20];
	int ret;

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

	keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL);
	if (IS_ERR(keyring)) {
		ret = PTR_ERR(keyring);
		goto error;
	}

	task_lock(tsk);
	old = tsk->thread_keyring;
	tsk->thread_keyring = keyring;
	task_unlock(tsk);

	ret = 0;

	key_put(old);
159
error:
L
Linus Torvalds 已提交
160 161 162 163 164 165 166 167
	return ret;

} /* end install_thread_keyring() */

/*****************************************************************************/
/*
 * make sure a process keyring is installed
 */
168
int install_process_keyring(struct task_struct *tsk)
L
Linus Torvalds 已提交
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
{
	unsigned long flags;
	struct key *keyring;
	char buf[20];
	int ret;

	if (!tsk->signal->process_keyring) {
		sprintf(buf, "_pid.%u", tsk->tgid);

		keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL);
		if (IS_ERR(keyring)) {
			ret = PTR_ERR(keyring);
			goto error;
		}

184
		/* attach keyring */
L
Linus Torvalds 已提交
185 186 187 188 189 190 191 192 193 194 195
		spin_lock_irqsave(&tsk->sighand->siglock, flags);
		if (!tsk->signal->process_keyring) {
			tsk->signal->process_keyring = keyring;
			keyring = NULL;
		}
		spin_unlock_irqrestore(&tsk->sighand->siglock, flags);

		key_put(keyring);
	}

	ret = 0;
196
error:
L
Linus Torvalds 已提交
197 198 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
	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
 */
static int install_session_keyring(struct task_struct *tsk,
				   struct key *keyring)
{
	unsigned long flags;
	struct key *old;
	char buf[20];
	int ret;

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

		keyring = keyring_alloc(buf, tsk->uid, tsk->gid, 1, NULL);
		if (IS_ERR(keyring)) {
			ret = PTR_ERR(keyring);
			goto error;
		}
	}
	else {
		atomic_inc(&keyring->usage);
	}

	/* install the keyring */
	spin_lock_irqsave(&tsk->sighand->siglock, flags);
230 231
	old = rcu_dereference(tsk->signal->session_keyring);
	rcu_assign_pointer(tsk->signal->session_keyring, keyring);
L
Linus Torvalds 已提交
232 233 234 235
	spin_unlock_irqrestore(&tsk->sighand->siglock, flags);

	ret = 0;

236
	/* we're using RCU on the pointer */
237
	synchronize_rcu();
L
Linus Torvalds 已提交
238
	key_put(old);
239
error:
L
Linus Torvalds 已提交
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256
	return ret;

} /* 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 */
257
	rcu_read_lock();
L
Linus Torvalds 已提交
258
	tsk->signal->session_keyring =
259 260
		key_get(rcu_dereference(current->signal->session_keyring));
	rcu_read_unlock();
L
Linus Torvalds 已提交
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 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 376 377 378

	return 0;

} /* end copy_thread_group_keys() */

/*****************************************************************************/
/*
 * copy the keys for fork
 */
int copy_keys(unsigned long clone_flags, struct task_struct *tsk)
{
	key_check(tsk->thread_keyring);

	/* no thread keyring yet */
	tsk->thread_keyring = NULL;
	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() */

/*****************************************************************************/
/*
 * dispose of keys upon thread exit
 */
void exit_keys(struct task_struct *tsk)
{
	key_put(tsk->thread_keyring);

} /* end exit_keys() */

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

	/* newly exec'd tasks don't get a thread keyring */
	task_lock(tsk);
	old = tsk->thread_keyring;
	tsk->thread_keyring = NULL;
	task_unlock(tsk);

	key_put(old);

	/* discard the process keyring from a newly exec'd task */
	spin_lock_irqsave(&tsk->sighand->siglock, flags);
	old = tsk->signal->process_keyring;
	tsk->signal->process_keyring = NULL;
	spin_unlock_irqrestore(&tsk->sighand->siglock, flags);

	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 */
	if (tsk->thread_keyring) {
		down_write(&tsk->thread_keyring->sem);
		tsk->thread_keyring->uid = tsk->fsuid;
		up_write(&tsk->thread_keyring->sem);
	}

} /* end key_fsuid_changed() */

/*****************************************************************************/
/*
 * the filesystem group ID changed
 */
void key_fsgid_changed(struct task_struct *tsk)
{
	/* update the ownership of the thread keyring */
	if (tsk->thread_keyring) {
		down_write(&tsk->thread_keyring->sem);
		tsk->thread_keyring->gid = tsk->fsgid;
		up_write(&tsk->thread_keyring->sem);
	}

} /* 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
 */
379 380 381 382
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 已提交
383
{
384
	struct request_key_auth *rka;
385
	key_ref_t key_ref, ret, err, instkey_ref;
L
Linus Torvalds 已提交
386 387 388 389 390 391 392 393

	/* 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
	 */
394
	key_ref = NULL;
L
Linus Torvalds 已提交
395 396 397 398
	ret = NULL;
	err = ERR_PTR(-EAGAIN);

	/* search the thread keyring first */
399
	if (context->thread_keyring) {
400 401 402 403
		key_ref = keyring_search_aux(
			make_key_ref(context->thread_keyring, 1),
			context, type, description, match);
		if (!IS_ERR(key_ref))
L
Linus Torvalds 已提交
404 405
			goto found;

406
		switch (PTR_ERR(key_ref)) {
L
Linus Torvalds 已提交
407 408 409 410
		case -EAGAIN: /* no key */
			if (ret)
				break;
		case -ENOKEY: /* negative key */
411
			ret = key_ref;
L
Linus Torvalds 已提交
412 413
			break;
		default:
414
			err = key_ref;
L
Linus Torvalds 已提交
415 416 417 418 419
			break;
		}
	}

	/* search the process keyring second */
420
	if (context->signal->process_keyring) {
421 422 423 424
		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 已提交
425 426
			goto found;

427
		switch (PTR_ERR(key_ref)) {
L
Linus Torvalds 已提交
428 429 430 431
		case -EAGAIN: /* no key */
			if (ret)
				break;
		case -ENOKEY: /* negative key */
432
			ret = key_ref;
L
Linus Torvalds 已提交
433 434
			break;
		default:
435
			err = key_ref;
L
Linus Torvalds 已提交
436 437 438 439
			break;
		}
	}

440 441
	/* search the session keyring */
	if (context->signal->session_keyring) {
442
		rcu_read_lock();
443 444 445 446
		key_ref = keyring_search_aux(
			make_key_ref(rcu_dereference(
					     context->signal->session_keyring),
				     1),
447
			context, type, description, match);
448
		rcu_read_unlock();
449

450
		if (!IS_ERR(key_ref))
451 452
			goto found;

453
		switch (PTR_ERR(key_ref)) {
454 455 456 457
		case -EAGAIN: /* no key */
			if (ret)
				break;
		case -ENOKEY: /* negative key */
458
			ret = key_ref;
459 460
			break;
		default:
461
			err = key_ref;
462 463 464 465 466 467 468 469 470 471
			break;
		}

		/* if this process has a session keyring and that has an
		 * instantiation authorisation key in the bottom level, then we
		 * also search the keyrings of the process mentioned there */
		if (context != current)
			goto no_key;

		rcu_read_lock();
472 473 474 475
		instkey_ref = __keyring_search_one(
			make_key_ref(rcu_dereference(
					     context->signal->session_keyring),
				     1),
476 477 478
			&key_type_request_key_auth, NULL, 0);
		rcu_read_unlock();

479
		if (IS_ERR(instkey_ref))
480 481
			goto no_key;

482
		rka = key_ref_to_ptr(instkey_ref)->payload.data;
483

484 485 486
		key_ref = search_process_keyrings(type, description, match,
						  rka->context);
		key_ref_put(instkey_ref);
487

488
		if (!IS_ERR(key_ref))
489 490
			goto found;

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

511
		switch (PTR_ERR(key_ref)) {
512 513 514 515
		case -EAGAIN: /* no key */
			if (ret)
				break;
		case -ENOKEY: /* negative key */
516
			ret = key_ref;
L
Linus Torvalds 已提交
517
			break;
518
		default:
519
			err = key_ref;
520 521
			break;
		}
L
Linus Torvalds 已提交
522 523
	}

524 525

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

529
found:
530
	return key_ref;
L
Linus Torvalds 已提交
531 532 533

} /* end search_process_keyrings() */

534 535 536 537 538 539 540 541 542 543
/*****************************************************************************/
/*
 * 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 已提交
544 545 546 547 548 549
/*****************************************************************************/
/*
 * 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
 */
550 551
key_ref_t lookup_user_key(struct task_struct *context, key_serial_t id,
			  int create, int partial, key_perm_t perm)
L
Linus Torvalds 已提交
552
{
553
	key_ref_t key_ref, skey_ref;
L
Linus Torvalds 已提交
554 555 556
	struct key *key;
	int ret;

557 558 559
	if (!context)
		context = current;

560
	key_ref = ERR_PTR(-ENOKEY);
L
Linus Torvalds 已提交
561 562 563

	switch (id) {
	case KEY_SPEC_THREAD_KEYRING:
564
		if (!context->thread_keyring) {
L
Linus Torvalds 已提交
565 566 567
			if (!create)
				goto error;

568
			ret = install_thread_keyring(context);
L
Linus Torvalds 已提交
569 570 571 572 573 574
			if (ret < 0) {
				key = ERR_PTR(ret);
				goto error;
			}
		}

575
		key = context->thread_keyring;
L
Linus Torvalds 已提交
576
		atomic_inc(&key->usage);
577
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
578 579 580
		break;

	case KEY_SPEC_PROCESS_KEYRING:
581
		if (!context->signal->process_keyring) {
L
Linus Torvalds 已提交
582 583 584
			if (!create)
				goto error;

585
			ret = install_process_keyring(context);
L
Linus Torvalds 已提交
586 587 588 589 590 591
			if (ret < 0) {
				key = ERR_PTR(ret);
				goto error;
			}
		}

592
		key = context->signal->process_keyring;
L
Linus Torvalds 已提交
593
		atomic_inc(&key->usage);
594
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
595 596 597
		break;

	case KEY_SPEC_SESSION_KEYRING:
598
		if (!context->signal->session_keyring) {
L
Linus Torvalds 已提交
599 600 601
			/* always install a session keyring upon access if one
			 * doesn't exist yet */
			ret = install_session_keyring(
602
				context, context->user->session_keyring);
L
Linus Torvalds 已提交
603 604 605 606
			if (ret < 0)
				goto error;
		}

607 608
		rcu_read_lock();
		key = rcu_dereference(context->signal->session_keyring);
L
Linus Torvalds 已提交
609
		atomic_inc(&key->usage);
610
		rcu_read_unlock();
611
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
612 613 614
		break;

	case KEY_SPEC_USER_KEYRING:
615
		key = context->user->uid_keyring;
L
Linus Torvalds 已提交
616
		atomic_inc(&key->usage);
617
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
618 619 620
		break;

	case KEY_SPEC_USER_SESSION_KEYRING:
621
		key = context->user->session_keyring;
L
Linus Torvalds 已提交
622
		atomic_inc(&key->usage);
623
		key_ref = make_key_ref(key, 1);
L
Linus Torvalds 已提交
624 625 626 627 628 629 630 631
		break;

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

	default:
632
		key_ref = ERR_PTR(-EINVAL);
L
Linus Torvalds 已提交
633 634 635 636
		if (id < 1)
			goto error;

		key = key_lookup(id);
637 638
		if (IS_ERR(key)) {
			key_ref = ERR_PTR(PTR_ERR(key));
L
Linus Torvalds 已提交
639
			goto error;
640 641 642 643 644 645 646 647 648 649 650 651 652 653
		}

		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 已提交
654 655 656
		break;
	}

657
	/* check the status */
L
Linus Torvalds 已提交
658 659 660 661 662 663 664
	if (perm) {
		ret = key_validate(key);
		if (ret < 0)
			goto invalid_key;
	}

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

668
	/* check the permissions */
L
Linus Torvalds 已提交
669
	ret = -EACCES;
670

671
	if (!key_task_permission(key_ref, context, perm))
L
Linus Torvalds 已提交
672 673
		goto invalid_key;

674 675
error:
	return key_ref;
L
Linus Torvalds 已提交
676

677 678 679
invalid_key:
	key_ref_put(key_ref);
	key_ref = ERR_PTR(ret);
L
Linus Torvalds 已提交
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702
	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) {
		ret = install_session_keyring(tsk, NULL);
		if (ret < 0)
			goto error;

703 704 705
		rcu_read_lock();
		ret = rcu_dereference(tsk->signal->session_keyring)->serial;
		rcu_read_unlock();
L
Linus Torvalds 已提交
706 707 708 709 710 711 712 713 714 715 716 717 718
		goto error;
	}

	/* allow the user to join or create a named keyring */
	down(&key_session_sem);

	/* look for an existing keyring of this name */
	keyring = find_keyring_by_name(name, 0);
	if (PTR_ERR(keyring) == -ENOKEY) {
		/* not found - try and create a new one */
		keyring = keyring_alloc(name, tsk->uid, tsk->gid, 0, NULL);
		if (IS_ERR(keyring)) {
			ret = PTR_ERR(keyring);
719
			goto error2;
L
Linus Torvalds 已提交
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
		}
	}
	else if (IS_ERR(keyring)) {
		ret = PTR_ERR(keyring);
		goto error2;
	}

	/* we've got a keyring - now to install it */
	ret = install_session_keyring(tsk, keyring);
	if (ret < 0)
		goto error2;

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

735
error2:
L
Linus Torvalds 已提交
736
	up(&key_session_sem);
737
error:
L
Linus Torvalds 已提交
738 739 740
	return ret;

} /* end join_session_keyring() */