diff options
Diffstat (limited to 'security/keys/process_keys.c')
| -rw-r--r-- | security/keys/process_keys.c | 400 | 
1 files changed, 215 insertions, 185 deletions
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 504bdd2452b..0cf8a130a26 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -1,4 +1,4 @@ -/* Management of a process's keyrings +/* Manage a process's keyrings   *   * Copyright (C) 2004-2005, 2008 Red Hat, Inc. All Rights Reserved.   * Written by David Howells (dhowells@redhat.com) @@ -21,41 +21,43 @@  #include <asm/uaccess.h>  #include "internal.h" -/* session keyring create vs join semaphore */ +/* Session keyring create vs join semaphore */  static DEFINE_MUTEX(key_session_mutex); -/* user keyring creation semaphore */ +/* User keyring creation semaphore */  static DEFINE_MUTEX(key_user_keyring_mutex); -/* the root user's tracking struct */ +/* The root user's tracking struct */  struct key_user root_key_user = {  	.usage		= ATOMIC_INIT(3),  	.cons_lock	= __MUTEX_INITIALIZER(root_key_user.cons_lock),  	.lock		= __SPIN_LOCK_UNLOCKED(root_key_user.lock),  	.nkeys		= ATOMIC_INIT(2),  	.nikeys		= ATOMIC_INIT(2), -	.uid		= 0, -	.user_ns	= &init_user_ns, +	.uid		= GLOBAL_ROOT_UID,  }; -/*****************************************************************************/  /* - * install user and user session keyrings for a particular UID + * Install the user and user session keyrings for the current process's UID.   */  int install_user_keyrings(void)  {  	struct user_struct *user;  	const struct cred *cred;  	struct key *uid_keyring, *session_keyring; +	key_perm_t user_keyring_perm;  	char buf[20];  	int ret; +	uid_t uid; +	user_keyring_perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_ALL;  	cred = current_cred();  	user = cred->user; +	uid = from_kuid(cred->user_ns, user->uid); -	kenter("%p{%u}", user, user->uid); +	kenter("%p{%u}", user, uid); -	if (user->uid_keyring) { +	if (user->uid_keyring && user->session_keyring) {  		kleave(" = 0 [exist]");  		return 0;  	} @@ -68,13 +70,13 @@ int install_user_keyrings(void)  		 * - 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); +		sprintf(buf, "_uid.%u", uid);  		uid_keyring = find_keyring_by_name(buf, true);  		if (IS_ERR(uid_keyring)) { -			uid_keyring = keyring_alloc(buf, user->uid, (gid_t) -1, -						    cred, KEY_ALLOC_IN_QUOTA, -						    NULL); +			uid_keyring = keyring_alloc(buf, user->uid, INVALID_GID, +						    cred, user_keyring_perm, +						    KEY_ALLOC_IN_QUOTA, NULL);  			if (IS_ERR(uid_keyring)) {  				ret = PTR_ERR(uid_keyring);  				goto error; @@ -83,13 +85,14 @@ int install_user_keyrings(void)  		/* get a default session keyring (which might also exist  		 * already) */ -		sprintf(buf, "_uid_ses.%u", user->uid); +		sprintf(buf, "_uid_ses.%u", uid);  		session_keyring = find_keyring_by_name(buf, true);  		if (IS_ERR(session_keyring)) {  			session_keyring = -				keyring_alloc(buf, user->uid, (gid_t) -1, -					      cred, KEY_ALLOC_IN_QUOTA, NULL); +				keyring_alloc(buf, user->uid, INVALID_GID, +					      cred, user_keyring_perm, +					      KEY_ALLOC_IN_QUOTA, NULL);  			if (IS_ERR(session_keyring)) {  				ret = PTR_ERR(session_keyring);  				goto error_release; @@ -122,13 +125,15 @@ error:  }  /* - * install a fresh thread keyring directly to new credentials + * Install a fresh thread keyring directly to new credentials.  This keyring is + * allowed to overrun the quota.   */  int install_thread_keyring_to_cred(struct cred *new)  {  	struct key *keyring;  	keyring = keyring_alloc("_tid", new->uid, new->gid, new, +				KEY_POS_ALL | KEY_USR_VIEW,  				KEY_ALLOC_QUOTA_OVERRUN, NULL);  	if (IS_ERR(keyring))  		return PTR_ERR(keyring); @@ -138,7 +143,7 @@ int install_thread_keyring_to_cred(struct cred *new)  }  /* - * install a fresh thread keyring, discarding the old one + * Install a fresh thread keyring, discarding the old one.   */  static int install_thread_keyring(void)  { @@ -161,39 +166,34 @@ static int install_thread_keyring(void)  }  /* - * 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 + * Install a process keyring directly to a credentials struct. + * + * Returns -EEXIST if there was already a process keyring, 0 if one installed, + * and other value on any other error   */  int install_process_keyring_to_cred(struct cred *new)  {  	struct key *keyring; -	int ret; -	if (new->tgcred->process_keyring) +	if (new->process_keyring)  		return -EEXIST; -	keyring = keyring_alloc("_pid", new->uid, new->gid, -				new, KEY_ALLOC_QUOTA_OVERRUN, NULL); +	keyring = keyring_alloc("_pid", new->uid, new->gid, new, +				KEY_POS_ALL | KEY_USR_VIEW, +				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); -	return ret; +	new->process_keyring = keyring; +	return 0;  }  /* - * make sure a process keyring is installed - * - we + * Make sure a process keyring is installed for the current process.  The + * existing process keyring is not replaced. + * + * Returns 0 if there is a process keyring by the end of this function, some + * error otherwise.   */  static int install_process_keyring(void)  { @@ -214,7 +214,7 @@ static int install_process_keyring(void)  }  /* - * install a session keyring directly to a credentials struct + * Install a session keyring directly to a credentials struct.   */  int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)  { @@ -226,36 +226,31 @@ int install_session_keyring_to_cred(struct cred *cred, struct key *keyring)  	/* create an empty session keyring */  	if (!keyring) {  		flags = KEY_ALLOC_QUOTA_OVERRUN; -		if (cred->tgcred->session_keyring) +		if (cred->session_keyring)  			flags = KEY_ALLOC_IN_QUOTA; -		keyring = keyring_alloc("_ses", cred->uid, cred->gid, -					cred, flags, NULL); +		keyring = keyring_alloc("_ses", cred->uid, cred->gid, cred, +					KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ, +					flags, NULL);  		if (IS_ERR(keyring))  			return PTR_ERR(keyring);  	} else { -		atomic_inc(&keyring->usage); +		__key_get(keyring);  	}  	/* install the keyring */ -	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); - -	/* 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(); +	old = cred->session_keyring; +	rcu_assign_pointer(cred->session_keyring, keyring); + +	if (old)  		key_put(old); -	}  	return 0;  }  /* - * install a session keyring, discarding the old one - * - if a keyring is not supplied, an empty one is invented + * 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 key *keyring)  { @@ -266,7 +261,7 @@ static int install_session_keyring(struct key *keyring)  	if (!new)  		return -ENOMEM; -	ret = install_session_keyring_to_cred(new, NULL); +	ret = install_session_keyring_to_cred(new, keyring);  	if (ret < 0) {  		abort_creds(new);  		return ret; @@ -275,9 +270,8 @@ static int install_session_keyring(struct key *keyring)  	return commit_creds(new);  } -/*****************************************************************************/  /* - * the filesystem user ID changed + * Handle the fsuid changing.   */  void key_fsuid_changed(struct task_struct *tsk)  { @@ -288,12 +282,10 @@ void key_fsuid_changed(struct task_struct *tsk)  		tsk->cred->thread_keyring->uid = tsk->cred->fsuid;  		up_write(&tsk->cred->thread_keyring->sem);  	} +} -} /* end key_fsuid_changed() */ - -/*****************************************************************************/  /* - * the filesystem group ID changed + * Handle the fsgid changing.   */  void key_fsgid_changed(struct task_struct *tsk)  { @@ -304,21 +296,30 @@ void key_fsgid_changed(struct task_struct *tsk)  		tsk->cred->thread_keyring->gid = tsk->cred->fsgid;  		up_write(&tsk->cred->thread_keyring->sem);  	} +} -} /* end key_fsgid_changed() */ - -/*****************************************************************************/  /* - * search only my 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 + * Search the process keyrings attached to the supplied cred for the first + * matching key. + * + * The search criteria are the type and the match function.  The description is + * given to the match function as a parameter, but doesn't otherwise influence + * the search.  Typically the match function will compare the description + * parameter to the key's description. + * + * This can only search keyrings that grant Search permission to the supplied + * credentials.  Keyrings linked to searched keyrings will also be searched if + * they grant Search permission too.  Keys can only be found if they grant + * Search permission to the credentials. + * + * Returns a pointer to the key with the key usage count incremented if + * successful, -EAGAIN if we didn't find any matching key or -ENOKEY if we only + * matched negative keys. + * + * In the case of a successful return, the possession attribute is set on the + * returned key reference.   */ -key_ref_t search_my_process_keyrings(struct key_type *type, -				     const void *description, -				     key_match_func_t match, -				     const struct cred *cred) +key_ref_t search_my_process_keyrings(struct keyring_search_context *ctx)  {  	key_ref_t key_ref, ret, err; @@ -334,17 +335,14 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  	err = ERR_PTR(-EAGAIN);  	/* search the thread keyring first */ -	if (cred->thread_keyring) { +	if (ctx->cred->thread_keyring) {  		key_ref = keyring_search_aux( -			make_key_ref(cred->thread_keyring, 1), -			cred, type, description, match); +			make_key_ref(ctx->cred->thread_keyring, 1), ctx);  		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;  			break; @@ -355,10 +353,9 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  	}  	/* search the process keyring second */ -	if (cred->tgcred->process_keyring) { +	if (ctx->cred->process_keyring) {  		key_ref = keyring_search_aux( -			make_key_ref(cred->tgcred->process_keyring, 1), -			cred, type, description, match); +			make_key_ref(ctx->cred->process_keyring, 1), ctx);  		if (!IS_ERR(key_ref))  			goto found; @@ -376,13 +373,11 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  	}  	/* search the session keyring */ -	if (cred->tgcred->session_keyring) { +	if (ctx->cred->session_keyring) {  		rcu_read_lock();  		key_ref = keyring_search_aux( -			make_key_ref(rcu_dereference( -					     cred->tgcred->session_keyring), -				     1), -			cred, type, description, match); +			make_key_ref(rcu_dereference(ctx->cred->session_keyring), 1), +			ctx);  		rcu_read_unlock();  		if (!IS_ERR(key_ref)) @@ -401,10 +396,10 @@ key_ref_t search_my_process_keyrings(struct key_type *type,  		}  	}  	/* or search the user-session keyring */ -	else if (cred->user->session_keyring) { +	else if (ctx->cred->user->session_keyring) {  		key_ref = keyring_search_aux( -			make_key_ref(cred->user->session_keyring, 1), -			cred, type, description, match); +			make_key_ref(ctx->cred->user->session_keyring, 1), +			ctx);  		if (!IS_ERR(key_ref))  			goto found; @@ -428,25 +423,22 @@ 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 + * Search the process keyrings attached to the supplied cred for the first + * matching key in the manner of search_my_process_keyrings(), but also search + * the keys attached to the assumed authorisation key using its credentials if + * one is available. + * + * Return same as search_my_process_keyrings().   */ -key_ref_t search_process_keyrings(struct key_type *type, -				  const void *description, -				  key_match_func_t match, -				  const struct cred *cred) +key_ref_t search_process_keyrings(struct keyring_search_context *ctx)  {  	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); +	key_ref = search_my_process_keyrings(ctx);  	if (!IS_ERR(key_ref))  		goto found;  	err = key_ref; @@ -455,18 +447,21 @@ key_ref_t search_process_keyrings(struct key_type *type,  	 * search the keyrings of the process mentioned there  	 * - we don't permit access to request_key auth keys via this method  	 */ -	if (cred->request_key_auth && -	    cred == current_cred() && -	    type != &key_type_request_key_auth +	if (ctx->cred->request_key_auth && +	    ctx->cred == current_cred() && +	    ctx->index_key.type != &key_type_request_key_auth  	    ) { +		const struct cred *cred = ctx->cred; +  		/* defend against the auth key being revoked */  		down_read(&cred->request_key_auth->sem); -		if (key_validate(cred->request_key_auth) == 0) { -			rka = cred->request_key_auth->payload.data; +		if (key_validate(ctx->cred->request_key_auth) == 0) { +			rka = ctx->cred->request_key_auth->payload.data; -			key_ref = search_process_keyrings(type, description, -							  match, rka->cred); +			ctx->cred = rka->cred; +			key_ref = search_process_keyrings(ctx); +			ctx->cred = cred;  			up_read(&cred->request_key_auth->sem); @@ -489,41 +484,54 @@ key_ref_t search_process_keyrings(struct key_type *type,  found:  	return key_ref; +} -} /* end search_process_keyrings() */ - -/*****************************************************************************/  /* - * see if the key we're looking at is the target key + * See if the key we're looking at is the target key.   */  int lookup_user_key_possessed(const struct key *key, const void *target)  {  	return key == target; +} -} /* end lookup_user_key_possessed() */ - -/*****************************************************************************/  /* - * 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 + * Look up a key ID given us by userspace with a given permissions mask to get + * the key it refers to. + * + * Flags can be passed to request that special keyrings be created if referred + * to directly, to permit partially constructed keys to be found and to skip + * validity and permission checks on the found key. + * + * Returns a pointer to the key with an incremented usage count if successful; + * -EINVAL if the key ID is invalid; -ENOKEY if the key ID does not correspond + * to a key or the best found key was a negative key; -EKEYREVOKED or + * -EKEYEXPIRED if the best found key was revoked or expired; -EACCES if the + * found key doesn't grant the requested permit or the LSM denied access to it; + * or -ENOMEM if a special keyring couldn't be created. + * + * In the case of a successful return, the possession attribute is set on the + * returned key reference.   */  key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,  			  key_perm_t perm)  { +	struct keyring_search_context ctx = { +		.match	= lookup_user_key_possessed, +		.flags	= (KEYRING_SEARCH_NO_STATE_CHECK | +			   KEYRING_SEARCH_LOOKUP_DIRECT), +	};  	struct request_key_auth *rka; -	const struct cred *cred;  	struct key *key;  	key_ref_t key_ref, skey_ref;  	int ret;  try_again: -	cred = get_current_cred(); +	ctx.cred = get_current_cred();  	key_ref = ERR_PTR(-ENOKEY);  	switch (id) {  	case KEY_SPEC_THREAD_KEYRING: -		if (!cred->thread_keyring) { +		if (!ctx.cred->thread_keyring) {  			if (!(lflags & KEY_LOOKUP_CREATE))  				goto error; @@ -535,13 +543,13 @@ try_again:  			goto reget_creds;  		} -		key = cred->thread_keyring; -		atomic_inc(&key->usage); +		key = ctx.cred->thread_keyring; +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_PROCESS_KEYRING: -		if (!cred->tgcred->process_keyring) { +		if (!ctx.cred->process_keyring) {  			if (!(lflags & KEY_LOOKUP_CREATE))  				goto error; @@ -553,54 +561,64 @@ try_again:  			goto reget_creds;  		} -		key = cred->tgcred->process_keyring; -		atomic_inc(&key->usage); +		key = ctx.cred->process_keyring; +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_SESSION_KEYRING: -		if (!cred->tgcred->session_keyring) { +		if (!ctx.cred->session_keyring) {  			/* always install a session keyring upon access if one  			 * doesn't exist yet */  			ret = install_user_keyrings();  			if (ret < 0)  				goto error; -			ret = install_session_keyring( -				cred->user->session_keyring); +			if (lflags & KEY_LOOKUP_CREATE) +				ret = join_session_keyring(NULL); +			else +				ret = install_session_keyring( +					ctx.cred->user->session_keyring);  			if (ret < 0)  				goto error;  			goto reget_creds; +		} else if (ctx.cred->session_keyring == +			   ctx.cred->user->session_keyring && +			   lflags & KEY_LOOKUP_CREATE) { +			ret = join_session_keyring(NULL); +			if (ret < 0) +				goto error; +			goto reget_creds;  		}  		rcu_read_lock(); -		key = rcu_dereference(cred->tgcred->session_keyring); -		atomic_inc(&key->usage); +		key = rcu_dereference(ctx.cred->session_keyring); +		__key_get(key);  		rcu_read_unlock();  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_USER_KEYRING: -		if (!cred->user->uid_keyring) { +		if (!ctx.cred->user->uid_keyring) {  			ret = install_user_keyrings();  			if (ret < 0)  				goto error;  		} -		key = cred->user->uid_keyring; -		atomic_inc(&key->usage); +		key = ctx.cred->user->uid_keyring; +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_USER_SESSION_KEYRING: -		if (!cred->user->session_keyring) { +		if (!ctx.cred->user->session_keyring) {  			ret = install_user_keyrings();  			if (ret < 0)  				goto error;  		} -		key = cred->user->session_keyring; -		atomic_inc(&key->usage); +		key = ctx.cred->user->session_keyring; +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break; @@ -610,28 +628,29 @@ try_again:  		goto error;  	case KEY_SPEC_REQKEY_AUTH_KEY: -		key = cred->request_key_auth; +		key = ctx.cred->request_key_auth;  		if (!key)  			goto error; -		atomic_inc(&key->usage); +		__key_get(key);  		key_ref = make_key_ref(key, 1);  		break;  	case KEY_SPEC_REQUESTOR_KEYRING: -		if (!cred->request_key_auth) +		if (!ctx.cred->request_key_auth)  			goto error; -		down_read(&cred->request_key_auth->sem); -		if (cred->request_key_auth->flags & KEY_FLAG_REVOKED) { +		down_read(&ctx.cred->request_key_auth->sem); +		if (test_bit(KEY_FLAG_REVOKED, +			     &ctx.cred->request_key_auth->flags)) {  			key_ref = ERR_PTR(-EKEYREVOKED);  			key = NULL;  		} else { -			rka = cred->request_key_auth->payload.data; +			rka = ctx.cred->request_key_auth->payload.data;  			key = rka->dest_keyring; -			atomic_inc(&key->usage); +			__key_get(key);  		} -		up_read(&cred->request_key_auth->sem); +		up_read(&ctx.cred->request_key_auth->sem);  		if (!key)  			goto error;  		key_ref = make_key_ref(key, 1); @@ -651,9 +670,13 @@ try_again:  		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, -						   cred); +		ctx.index_key.type		= key->type; +		ctx.index_key.description	= key->description; +		ctx.index_key.desc_len		= strlen(key->description); +		ctx.match_data			= key; +		kdebug("check possessed"); +		skey_ref = search_process_keyrings(&ctx); +		kdebug("possessed=%p", skey_ref);  		if (!IS_ERR(skey_ref)) {  			key_put(key); @@ -693,12 +716,14 @@ try_again:  		goto invalid_key;  	/* check the permissions */ -	ret = key_task_permission(key_ref, cred, perm); +	ret = key_task_permission(key_ref, ctx.cred, perm);  	if (ret < 0)  		goto invalid_key; +	key->last_used_at = current_kernel_time().tv_sec; +  error: -	put_cred(cred); +	put_cred(ctx.cred);  	return key_ref;  invalid_key: @@ -709,17 +734,20 @@ invalid_key:  	/* if we attempted to install a keyring, then it may have caused new  	 * creds to be installed */  reget_creds: -	put_cred(cred); +	put_cred(ctx.cred);  	goto try_again; +} -} /* 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 + * Join the named keyring as the session keyring if possible else attempt to + * create a new one of that name and join that. + * + * If the name is NULL, an empty anonymous keyring will be installed as the + * session keyring. + * + * Named session keyrings are joined with a semaphore held to prevent the + * keyrings from going away whilst the attempt is made to going them and also + * to prevent a race in creating compatible session keyrings.   */  long join_session_keyring(const char *name)  { @@ -728,12 +756,6 @@ long join_session_keyring(const char *name)  	struct key *keyring;  	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 */ -	if (!current_is_single_threaded()) -		return -EMLINK; -  	new = prepare_creds();  	if (!new)  		return -ENOMEM; @@ -745,7 +767,7 @@ long join_session_keyring(const char *name)  		if (ret < 0)  			goto error; -		serial = new->tgcred->session_keyring->serial; +		serial = new->session_keyring->serial;  		ret = commit_creds(new);  		if (ret == 0)  			ret = serial; @@ -759,8 +781,10 @@ long join_session_keyring(const char *name)  	keyring = find_keyring_by_name(name, false);  	if (PTR_ERR(keyring) == -ENOKEY) {  		/* not found - try and create a new one */ -		keyring = keyring_alloc(name, old->uid, old->gid, old, -					KEY_ALLOC_IN_QUOTA, NULL); +		keyring = keyring_alloc( +			name, old->uid, old->gid, old, +			KEY_POS_ALL | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_LINK, +			KEY_ALLOC_IN_QUOTA, NULL);  		if (IS_ERR(keyring)) {  			ret = PTR_ERR(keyring);  			goto error2; @@ -768,6 +792,9 @@ long join_session_keyring(const char *name)  	} else if (IS_ERR(keyring)) {  		ret = PTR_ERR(keyring);  		goto error2; +	} else if (keyring == new->session_keyring) { +		ret = 0; +		goto error2;  	}  	/* we've got a keyring - now to install it */ @@ -791,26 +818,19 @@ error:  }  /* - * Replace a process's session keyring when that process resumes userspace on - * behalf of one of its children + * Replace a process's session keyring on behalf of one of its children when + * the target  process is about to resume userspace execution.   */ -void key_replace_session_keyring(void) +void key_change_session_keyring(struct callback_head *twork)  { -	const struct cred *old; -	struct cred *new; +	const struct cred *old = current_cred(); +	struct cred *new = container_of(twork, struct cred, rcu); -	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) +	if (unlikely(current->flags & PF_EXITING)) { +		put_cred(new);  		return; +	} -	old = current_cred();  	new->  uid	= old->  uid;  	new-> euid	= old-> euid;  	new-> suid	= old-> suid; @@ -820,6 +840,7 @@ void key_replace_session_keyring(void)  	new-> sgid	= old-> sgid;  	new->fsgid	= old->fsgid;  	new->user	= get_uid(old->user); +	new->user_ns	= get_user_ns(old->user_ns);  	new->group_info	= get_group_info(old->group_info);  	new->securebits	= old->securebits; @@ -830,10 +851,19 @@ void key_replace_session_keyring(void)  	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); +	new->process_keyring	= key_get(old->process_keyring);  	security_transfer_creds(new, old);  	commit_creds(new);  } + +/* + * Make sure that root's user and user-session keyrings exist. + */ +static int __init init_root_keyring(void) +{ +	return install_user_keyrings(); +} + +late_initcall(init_root_keyring);  | 
