Linux Security Modules development
 help / color / mirror / Atom feed
From: Jarkko Sakkinen <jarkko@kernel.org>
To: Anvik Loitongbam <permissiontogallop@gmail.com>
Cc: keyrings@vger.kernel.org, David Howells <dhowells@redhat.com>,
	linux-security-module@vger.kernel.org,
	linux-kernel@vger.kernel.org, stable@vger.kernel.org
Subject: Re: [PATCH] KEYS: request_key: enforce keyring restrictions on constructed keys
Date: Sat, 31 Oct 2026 10:20:40 +0200	[thread overview]
Message-ID: <auWk2PAYDgQGXLqr@kernel.org> (raw)
In-Reply-To: <20261030173930.40948-1-permissiontogallop@gmail.com>

On Fri, Oct 30, 2026 at 11:09:30PM +0530, Anvik Loitongbam wrote:
> diff --git a/security/keys/request_key.c b/security/keys/request_key.c
> index e6ba2d054399..3de6c9160651 100644
> --- a/security/keys/request_key.c
> +++ b/security/keys/request_key.c
> @@ -370,18 +370,21 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
>  			       struct key *dest_keyring,
>  			       unsigned long flags,
>  			       struct key_user *user,
> -			       struct key **_key)
> +			       struct key **_key,
> +			       bool *defer_link)
>  {
>  	struct assoc_array_edit *edit = NULL;
>  	struct key *key;
>  	key_perm_t perm;
>  	key_ref_t key_ref;
> +	bool link_locked = false;
>  	int ret;
>  
>  	kenter("%s,%s,,,",
>  	       ctx->index_key.type->name, ctx->index_key.description);
>  
>  	*_key = NULL;
> +	*defer_link = false;
>  	mutex_lock(&user->cons_lock);
>  
>  	perm = KEY_POS_VIEW | KEY_POS_SEARCH | KEY_POS_LINK | KEY_POS_SETATTR;
> @@ -404,6 +407,21 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
>  		ret = __key_link_lock(dest_keyring, &key->index_key);
>  		if (ret < 0)
>  			goto link_lock_failed;
> +
> +		/*
> +		 * A restricted keyring should not take an uninstantiated key,
> +		 * the restriction has to be evaluated with the payload,
> +		 * which is only available after construction. Skip the
> +		 * provisional link and add the key with key_link() later,
> +		 * unless the caller explicitly asked to bypass restrictions.
> +		 */
> +		if (dest_keyring->restrict_link &&
> +		    !(flags & KEY_ALLOC_BYPASS_RESTRICTION)) {
> +			__key_link_end(dest_keyring, &key->index_key, NULL);
> +			*defer_link = true;
> +		} else {
> +			link_locked = true;
> +		}
>  	}
>  
>  	/*
> @@ -424,7 +442,7 @@ 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 && link_locked) {
>  		ret = __key_link_begin(dest_keyring, &key->index_key, &edit);
>  		if (ret < 0)
>  			goto link_alloc_failed;
> @@ -432,7 +450,7 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
>  	}
>  
>  	mutex_unlock(&key_construction_mutex);
> -	if (dest_keyring)
> +	if (link_locked)
>  		__key_link_end(dest_keyring, &key->index_key, edit);
>  	mutex_unlock(&user->cons_lock);
>  	*_key = key;
> @@ -446,15 +464,44 @@ static int construct_alloc_key(struct keyring_search_context *ctx,
>  	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, &key->index_key, edit);
> -		if (ret < 0)
> +		/*
> +		 * The outer search checks Link permission on a key it finds.
> +		 * The second search must do the same; otherwise a key that
> +		 * appeared in the race window could be linked without the
> +		 * caller holding Link permission on it.
> +		 */
> +		ret = key_task_permission(key_ref, ctx->cred, KEY_NEED_LINK);
> +		if (ret < 0) {
> +			if (link_locked)
> +				__key_link_end(dest_keyring, &key->index_key,
> +					       NULL);
>  			goto link_check_failed;
> +		}
> +		if (*defer_link) {
> +			/*
> +			 * The key may still be under construction, in which
> +			 * case its payload cannot be checked yet.  Do not add
> +			 * a provisional link to a restricted keyring; only an
> +			 * already instantiated key can be checked here.
> +			 */
> +			if (!key_is_positive(key)) {
> +				ret = -EPERM;
> +				goto link_check_failed;
> +			}
> +			ret = key_link(dest_keyring, key);
> +			if (ret < 0)
> +				goto link_check_failed;
> +		} else {
> +			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, &key->index_key, edit);
> +			if (ret < 0)
> +				goto link_check_failed;
> +		}
>  	}
>  	mutex_unlock(&user->cons_lock);
>  	*_key = key;
> @@ -495,6 +542,7 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx,
>  {
>  	struct key_user *user;
>  	struct key *key;
> +	bool defer_link;
>  	int ret;
>  
>  	kenter("");
> @@ -512,7 +560,8 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx,
>  		goto error_put_dest_keyring;
>  	}
>  
> -	ret = construct_alloc_key(ctx, dest_keyring, flags, user, &key);
> +	ret = construct_alloc_key(ctx, dest_keyring, flags, user, &key,
> +				  &defer_link);
>  	key_user_put(user);
>  
>  	if (ret == 0) {
> @@ -522,6 +571,30 @@ static struct key *construct_key_and_link(struct keyring_search_context *ctx,
>  			kdebug("cons failed");
>  			goto construction_failed;
>  		}
> +
> +		/*
> +		 * The key was not provisionally linked because the
> +		 * destination keyring is restricted. Link it now that the
> +		 * payload is available so the restriction can be checked.
> +		 */
> +		if (defer_link) {
> +			/*
> +			 * A helper may have negatively instantiated the key
> +			 * (e.g. KEYCTL_REJECT) and returned success.  A
> +			 * restricted keyring must not cache a negative key;
> +			 * key_reject_and_link() rejects those too.
> +			 */
> +			if (!key_is_positive(key)) {
> +				ret = -EPERM;
> +				key_put(key);
> +				goto error_put_dest_keyring;
> +			}
> +			ret = key_link(dest_keyring, key);
> +			if (ret < 0) {
> +				key_put(key);
> +				goto error_put_dest_keyring;
> +			}
> +		}
>  	} else if (ret == -EINPROGRESS) {
>  		ret = 0;
>  	} else {
> -- 
> 2.56.0
> 
> 

I tried to go through the code change now as it is huge and there was no
explanation why you need to do all of this.

It is full out-of-scope changes. Please redo this just as a scoped bug
fix, and don't add any new flags.

Br, Jarkk

  parent reply	other threads:[~2026-10-31  8:20 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-30 17:39 [PATCH] KEYS: request_key: enforce keyring restrictions on constructed keys Anvik Loitongbam
2026-10-30 18:07 ` sashiko-bot
2026-10-30 18:18 ` Jarkko Sakkinen
2026-10-31  4:40   ` Gallop Permission
2026-10-31  7:53     ` Jarkko Sakkinen
2026-10-31  7:56       ` Jarkko Sakkinen
2026-10-31  7:58         ` Jarkko Sakkinen
2026-10-31  8:20 ` Jarkko Sakkinen [this message]
2026-10-31  9:19   ` Jarkko Sakkinen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=auWk2PAYDgQGXLqr@kernel.org \
    --to=jarkko@kernel.org \
    --cc=dhowells@redhat.com \
    --cc=keyrings@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=permissiontogallop@gmail.com \
    --cc=stable@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox