diff options
Diffstat (limited to 'security/keys/request_key.c')
| -rw-r--r-- | security/keys/request_key.c | 48 | 
1 files changed, 37 insertions, 11 deletions
diff --git a/security/keys/request_key.c b/security/keys/request_key.c index e8036cd0ad54..114f7408feee 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -251,11 +251,12 @@ static int construct_key(struct key *key, const void *callout_info,   * The keyring selected is returned with an extra reference upon it which the   * caller must release.   */ -static void construct_get_dest_keyring(struct key **_dest_keyring) +static int construct_get_dest_keyring(struct key **_dest_keyring)  {  	struct request_key_auth *rka;  	const struct cred *cred = current_cred();  	struct key *dest_keyring = *_dest_keyring, *authkey; +	int ret;  	kenter("%p", dest_keyring); @@ -264,6 +265,8 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)  		/* the caller supplied one */  		key_get(dest_keyring);  	} else { +		bool do_perm_check = true; +  		/* use a default keyring; falling through the cases until we  		 * find one that we actually have */  		switch (cred->jit_keyring) { @@ -278,8 +281,10 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)  					dest_keyring =  						key_get(rka->dest_keyring);  				up_read(&authkey->sem); -				if (dest_keyring) +				if (dest_keyring) { +					do_perm_check = false;  					break; +				}  			}  		case KEY_REQKEY_DEFL_THREAD_KEYRING: @@ -314,11 +319,29 @@ static void construct_get_dest_keyring(struct key **_dest_keyring)  		default:  			BUG();  		} + +		/* +		 * Require Write permission on the keyring.  This is essential +		 * because the default keyring may be the session keyring, and +		 * joining a keyring only requires Search permission. +		 * +		 * However, this check is skipped for the "requestor keyring" so +		 * that /sbin/request-key can itself use request_key() to add +		 * keys to the original requestor's destination keyring. +		 */ +		if (dest_keyring && do_perm_check) { +			ret = key_permission(make_key_ref(dest_keyring, 1), +					     KEY_NEED_WRITE); +			if (ret) { +				key_put(dest_keyring); +				return ret; +			} +		}  	}  	*_dest_keyring = dest_keyring;  	kleave(" [dk %d]", key_serial(dest_keyring)); -	return; +	return 0;  }  /* @@ -444,11 +467,15 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx,  	if (ctx->index_key.type == &key_type_keyring)  		return ERR_PTR(-EPERM); -	user = key_user_lookup(current_fsuid()); -	if (!user) -		return ERR_PTR(-ENOMEM); +	ret = construct_get_dest_keyring(&dest_keyring); +	if (ret) +		goto error; -	construct_get_dest_keyring(&dest_keyring); +	user = key_user_lookup(current_fsuid()); +	if (!user) { +		ret = -ENOMEM; +		goto error_put_dest_keyring; +	}  	ret = construct_alloc_key(ctx, dest_keyring, flags, user, &key);  	key_user_put(user); @@ -463,7 +490,7 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx,  	} else if (ret == -EINPROGRESS) {  		ret = 0;  	} else { -		goto couldnt_alloc_key; +		goto error_put_dest_keyring;  	}  	key_put(dest_keyring); @@ -473,8 +500,9 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx,  construction_failed:  	key_negate_and_link(key, key_negative_timeout, NULL, NULL);  	key_put(key); -couldnt_alloc_key: +error_put_dest_keyring:  	key_put(dest_keyring); +error:  	kleave(" = %d", ret);  	return ERR_PTR(ret);  } @@ -546,9 +574,7 @@ struct key *request_key_and_link(struct key_type *type,  	if (!IS_ERR(key_ref)) {  		key = key_ref_to_ptr(key_ref);  		if (dest_keyring) { -			construct_get_dest_keyring(&dest_keyring);  			ret = key_link(dest_keyring, key); -			key_put(dest_keyring);  			if (ret < 0) {  				key_put(key);  				key = ERR_PTR(ret);  |