From: Anvik Loitongbam <permissiontogallop@gmail.com>
To: keyrings@vger.kernel.org
Cc: David Howells <dhowells@redhat.com>,
Jarkko Sakkinen <jarkko@kernel.org>,
linux-security-module@vger.kernel.org,
linux-kernel@vger.kernel.org,
Anvik Loitongbam <permissiontogallop@gmail.com>,
stable@vger.kernel.org
Subject: [PATCH] KEYS: request_key: enforce keyring restrictions on constructed keys
Date: Fri, 30 Oct 2026 23:09:30 +0530 [thread overview]
Message-ID: <20261030173930.40948-1-permissiontogallop@gmail.com> (raw)
construct_alloc_key() pre-links a newly allocated key into the destination
keyring with __key_link(), bypassing __key_link_check_restriction(). If
the request-key helper instantiates the key with ringid 0 (or any keyring
other than the restricted destination), the key becomes positive while the
provisional link in a restricted keyring remains, so the restriction never
sees the payload, potentially resulting in a policy violation, where a
user with write access can inject keys which are restricted on the restricted
keyring.
Restricted destinations lose the provisional-link deduplication while the
key is being constructed. Callers that pass
KEY_ALLOC_BYPASS_RESTRICTION keep the existing provisional-link behavior.
Fixes: 5ac7eace2d00 ("KEYS: Add a check to restrict new links into a keyring")
Cc: stable@vger.kernel.org
Signed-off-by: Anvik Loitongbam <permissiontogallop@gmail.com>
---
security/keys/request_key.c | 97 ++++++++++++++++++++++++++++++++-----
1 file changed, 85 insertions(+), 12 deletions(-)
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
reply other threads:[~2026-10-30 17:48 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
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=20261030173930.40948-1-permissiontogallop@gmail.com \
--to=permissiontogallop@gmail.com \
--cc=dhowells@redhat.com \
--cc=jarkko@kernel.org \
--cc=keyrings@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-security-module@vger.kernel.org \
--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