diff options
Diffstat (limited to 'security/keys/request_key.c')
| -rw-r--r-- | security/keys/request_key.c | 35 | 
1 files changed, 24 insertions, 11 deletions
diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 07a0ef2baacd..a7673ad86d18 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -401,17 +401,21 @@ static int construct_alloc_key(struct keyring_search_context *ctx,  	set_bit(KEY_FLAG_USER_CONSTRUCT, &key->flags);  	if (dest_keyring) { -		ret = __key_link_lock(dest_keyring, &ctx->index_key); +		ret = __key_link_lock(dest_keyring, &key->index_key);  		if (ret < 0)  			goto link_lock_failed; -		ret = __key_link_begin(dest_keyring, &ctx->index_key, &edit); -		if (ret < 0) -			goto link_prealloc_failed;  	} -	/* attach the key to the destination keyring under lock, but we do need +	/* +	 * Attach the key to the destination keyring under lock, but we do need  	 * to do another check just in case someone beat us to it whilst we -	 * waited for locks */ +	 * waited for locks. +	 * +	 * The caller might specify a comparison function which looks for keys +	 * that do not exactly match but are still equivalent from the caller's +	 * perspective. The __key_link_begin() operation must be done only after +	 * an actual key is determined. +	 */  	mutex_lock(&key_construction_mutex);  	rcu_read_lock(); @@ -420,12 +424,16 @@ static int construct_alloc_key(struct keyring_search_context *ctx,  	if (!IS_ERR(key_ref))  		goto key_already_present; -	if (dest_keyring) +	if (dest_keyring) { +		ret = __key_link_begin(dest_keyring, &key->index_key, &edit); +		if (ret < 0) +			goto link_alloc_failed;  		__key_link(dest_keyring, key, &edit); +	}  	mutex_unlock(&key_construction_mutex);  	if (dest_keyring) -		__key_link_end(dest_keyring, &ctx->index_key, edit); +		__key_link_end(dest_keyring, &key->index_key, edit);  	mutex_unlock(&user->cons_lock);  	*_key = key;  	kleave(" = 0 [%d]", key_serial(key)); @@ -438,10 +446,13 @@ key_already_present:  	mutex_unlock(&key_construction_mutex);  	key = key_ref_to_ptr(key_ref);  	if (dest_keyring) { +		ret = __key_link_begin(dest_keyring, &key->index_key, &edit); +		if (ret < 0) +			goto link_alloc_failed_unlocked;  		ret = __key_link_check_live_key(dest_keyring, key);  		if (ret == 0)  			__key_link(dest_keyring, key, &edit); -		__key_link_end(dest_keyring, &ctx->index_key, edit); +		__key_link_end(dest_keyring, &key->index_key, edit);  		if (ret < 0)  			goto link_check_failed;  	} @@ -456,8 +467,10 @@ link_check_failed:  	kleave(" = %d [linkcheck]", ret);  	return ret; -link_prealloc_failed: -	__key_link_end(dest_keyring, &ctx->index_key, edit); +link_alloc_failed: +	mutex_unlock(&key_construction_mutex); +link_alloc_failed_unlocked: +	__key_link_end(dest_keyring, &key->index_key, edit);  link_lock_failed:  	mutex_unlock(&user->cons_lock);  	key_put(key);  |