Linux Security Modules development
 help / color / mirror / Atom feed
* [PATCH RFC 0/4] IPE: add a dedicated policy keyring
@ 2026-10-19 21:40 Manuel Huber via B4 Relay
  2026-10-19 21:40 ` [PATCH RFC 1/4] ipe: add the .ipe " Manuel Huber via B4 Relay
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Manuel Huber via B4 Relay @ 2026-10-19 21:40 UTC (permalink / raw)
  To: Fan Wu, David Howells, Jarkko Sakkinen
  Cc: Christian Brauner, Andrew Halaney, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Shuah Khan, Shuah Khan,
	Randy Dunlap, Alexei Starovoitov, Daniel Borkmann,
	linux-security-module, keyrings, linux-kselftest, linux-doc, bpf,
	linux-kernel, Manuel Huber

Add a dedicated ".ipe" keyring for IPE policy-signature verification,
modeled after the dm-verity keyring, allowing userspace to provision
IPE-specific policy-signing certificates. Systems that do not provision
the keyring retain their existing behavior.

This RFC is based on commit 48f936e302a9 ("ipe: update the project and
test suite links") from ipe/next so review can begin before the
prerequisite keyring work reaches that branch. The series will be rebased
onto a refreshed ipe/next once that work is available there [1].

The keyring readiness check currently follows the dm-verity and BPF
implementations. A parallel keys RFC introduces keyring_is_empty() for
this purpose [2]. A later revision will use the helper if it is available
in the submission base.

Testing:
- Built an x86_64 kernel with GCC 15.2.0 and Clang/LLD 21.1.8.
- Passed all 33 IPE parser KUnit assertions.
- Passed all 4 sealed-default keyring selftests under QEMU TCG.
- Passed all 16 provisioning-mode keyring selftests under QEMU TCG with
  ipe.keyring_unsealed=1.

[1] https://lore.kernel.org/r/20261008-ajhalaney-dmverity-key-identifier-v2-0-008456d189cb@amutable.com
[2] https://lore.kernel.org/r/20261016215326.4110064-5-jarkko@kernel.org

Signed-off-by: Manuel Huber <manuelh@nvidia.com>
---
Manuel Huber (4):
      ipe: add the .ipe policy keyring
      keys: add KEY_SPEC_IPE_KEYRING
      Documentation: ipe: document the IPE keyring
      selftests/ipe: test the IPE policy keyring

 Documentation/admin-guide/LSM/ipe.rst           |  41 ++-
 Documentation/admin-guide/kernel-parameters.txt |   9 +
 Documentation/security/ipe.rst                  |  16 +-
 Documentation/security/keys/core.rst            |   9 +
 MAINTAINERS                                     |   1 +
 include/linux/key.h                             |   2 +
 include/uapi/linux/keyctl.h                     |   1 +
 security/ipe/Makefile                           |   1 +
 security/ipe/ipe.c                              |   2 +
 security/ipe/keyring.c                          |  80 ++++++
 security/ipe/keyring.h                          |  16 ++
 security/ipe/policy.c                           |  10 +
 security/keys/process_keys.c                    |  23 ++
 tools/testing/selftests/Makefile                |   1 +
 tools/testing/selftests/ipe/Makefile            |   5 +
 tools/testing/selftests/ipe/config              |   7 +
 tools/testing/selftests/ipe/test-ipe-keyring.sh | 357 ++++++++++++++++++++++++
 17 files changed, 575 insertions(+), 6 deletions(-)
---
base-commit: 48f936e302a9c48cfc032eb5944c3a6246bc9043
change-id: 20261019-b4-ipe-keyring-rfc-b93b9ae129a2

Best regards,
-- 
Manuel Huber <manuelh@nvidia.com>



^ permalink raw reply	[flat|nested] 14+ messages in thread

* [PATCH RFC 1/4] ipe: add the .ipe policy keyring
  2026-10-19 21:40 [PATCH RFC 0/4] IPE: add a dedicated policy keyring Manuel Huber via B4 Relay
@ 2026-10-19 21:40 ` Manuel Huber via B4 Relay
  2026-10-19 21:47   ` sashiko-bot
                     ` (2 more replies)
  2026-10-19 21:40 ` [PATCH RFC 2/4] keys: add KEY_SPEC_IPE_KEYRING Manuel Huber via B4 Relay
                   ` (3 subsequent siblings)
  4 siblings, 3 replies; 14+ messages in thread
From: Manuel Huber via B4 Relay @ 2026-10-19 21:40 UTC (permalink / raw)
  To: Fan Wu, David Howells, Jarkko Sakkinen
  Cc: Christian Brauner, Andrew Halaney, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Shuah Khan, Shuah Khan,
	Randy Dunlap, Alexei Starovoitov, Daniel Borkmann,
	linux-security-module, keyrings, linux-kselftest, linux-doc, bpf,
	linux-kernel, Manuel Huber

From: Manuel Huber <manuelh@nvidia.com>

Add a dedicated .ipe keyring for IPE policy signature verification,
modeled after the .dm-verity and .bpf keyring implementations.

With this, userspace can provision its own keys into the keyring,
then restrict it and activate it for IPE policy signature verification.

This enables scenarios in which a trusted userspace component generates
an IPE policy, and creates an ephemeral policy-signing key pair,
provisions its public certificate, and restricts the IPE keyring during
the early lifetime of a system. The trusted component can then sign the
IPE policy, destroy the private key, and load and activate the policy.
During that phase, the system is considered in a trusted state whereas
the system transitions into a possibly untrusted state afterwards,
running arbitrary user-provided applications. In such a scenario, the
trusted userspace component could reside on a dm-verity protected
rootfs disk, and applications would reside on additional dm-verity
protected disks. The latter disks would be dynamically attached and
their dm-verity root hashes and/or signatures not known a priori to
launching the system.

A concrete usage scenario could be Kata Containers, which enables
running Kubernetes pods in ephemeral, headless utility VMs, possibly
without systemd. The rootfs disk is static and known beforehand, but
containerd's image layers might be acquired and transported into the
VM using the erofs snapshotter and dynamic block device attachment
using dm-verity.

Signed-off-by: Manuel Huber <manuelh@nvidia.com>
---
 Documentation/admin-guide/kernel-parameters.txt |  9 +++
 security/ipe/Makefile                           |  1 +
 security/ipe/ipe.c                              |  2 +
 security/ipe/keyring.c                          | 79 +++++++++++++++++++++++++
 security/ipe/keyring.h                          | 16 +++++
 security/ipe/policy.c                           | 10 ++++
 6 files changed, 117 insertions(+)

diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index e75344f4e0cd..fd508dd779ed 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2733,6 +2733,15 @@ Kernel parameters
 			Determine whether IPE starts in permissive (0) or
 			enforce (1) mode. The default is enforce.
 
+	ipe.keyring_unsealed=
+			[IPE]
+			Format: <bool>
+			When set to 1, leave the IPE policy keyring
+			unsealed after initialization so userspace can provision
+			keys. Once the keyring is nonempty and restricted, it is
+			searched during policy-signature verification. The default
+			is 0, which creates the keyring restricted and empty.
+
 	ipe.success_audit=
 			[IPE]
 			Format: <bool>
diff --git a/security/ipe/Makefile b/security/ipe/Makefile
index 2ffabfa63fe9..109e736696a5 100644
--- a/security/ipe/Makefile
+++ b/security/ipe/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_SECURITY_IPE) += \
 	hooks.o \
 	fs.o \
 	ipe.o \
+	keyring.o \
 	policy.o \
 	policy_fs.o \
 	policy_parser.o \
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
index 495bb765de1b..f5232f850dc2 100644
--- a/security/ipe/ipe.c
+++ b/security/ipe/ipe.c
@@ -7,6 +7,7 @@
 #include "ipe.h"
 #include "eval.h"
 #include "hooks.h"
+#include "keyring.h"
 
 extern const char *const ipe_boot_policy;
 bool ipe_enabled;
@@ -97,4 +98,5 @@ DEFINE_LSM(ipe) = {
 	.init = ipe_init,
 	.blobs = &ipe_blobs,
 	.initcall_fs = ipe_init_securityfs,
+	.initcall_late = ipe_keyring_init,
 };
diff --git a/security/ipe/keyring.c b/security/ipe/keyring.c
new file mode 100644
index 000000000000..ea8d164b62a6
--- /dev/null
+++ b/security/ipe/keyring.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ */
+
+#include <linux/cred.h>
+#include <linux/err.h>
+#include <linux/key.h>
+#include <linux/moduleparam.h>
+
+#include "ipe.h"
+#include "keyring.h"
+
+static struct key *ipe_keyring __ro_after_init;
+
+static bool ipe_keyring_unsealed __ro_after_init;
+
+/* Set the right module name for built-in parameters. */
+#ifdef KBUILD_MODNAME
+#undef KBUILD_MODNAME
+#define KBUILD_MODNAME "ipe"
+#endif
+
+module_param_named(keyring_unsealed, ipe_keyring_unsealed, bool, 0444);
+MODULE_PARM_DESC(keyring_unsealed, "Leave the IPE keyring unsealed");
+
+/**
+ * ipe_keyring_lookup() - Get the IPE keyring if it is ready for use.
+ *
+ * Return: A borrowed pointer to the IPE keyring if it is nonempty and
+ * restricted, or %NULL otherwise. The keyring has system lifetime.
+ */
+struct key *ipe_keyring_lookup(void)
+{
+	if (!ipe_keyring)
+		return NULL;
+
+	if (!READ_ONCE(ipe_keyring->keys.nr_leaves_on_tree) ||
+	    !READ_ONCE(ipe_keyring->restrict_link))
+		return NULL;
+
+	return ipe_keyring;
+}
+
+/**
+ * ipe_keyring_init() - Allocate and configure the IPE keyring.
+ *
+ * Return: Always %0. A failure leaves IPE without its dedicated keyring.
+ */
+int __init ipe_keyring_init(void)
+{
+	struct key *keyring;
+	int rc;
+
+	keyring = keyring_alloc(".ipe", GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
+				current_cred(),
+				KEY_POS_SEARCH |
+				KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE |
+				KEY_USR_SEARCH | KEY_USR_SETATTR,
+				KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
+	if (IS_ERR(keyring)) {
+		pr_err("failed to allocate keyring: %ld\n", PTR_ERR(keyring));
+		return 0;
+	}
+
+	if (!ipe_keyring_unsealed) {
+		rc = keyring_restrict(make_key_ref(keyring, true), NULL, NULL);
+		if (rc) {
+			pr_err("failed to restrict keyring: %d\n", rc);
+			key_revoke(keyring);
+			key_put(keyring);
+			return 0;
+		}
+	}
+
+	ipe_keyring = keyring;
+
+	return 0;
+}
diff --git a/security/ipe/keyring.h b/security/ipe/keyring.h
new file mode 100644
index 000000000000..3d450a465511
--- /dev/null
+++ b/security/ipe/keyring.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+ */
+
+#ifndef _IPE_KEYRING_H
+#define _IPE_KEYRING_H
+
+#include <linux/init.h>
+
+struct key;
+
+int __init ipe_keyring_init(void);
+struct key *ipe_keyring_lookup(void);
+
+#endif /* _IPE_KEYRING_H */
diff --git a/security/ipe/policy.c b/security/ipe/policy.c
index e542329726bc..abc1ff9ac230 100644
--- a/security/ipe/policy.c
+++ b/security/ipe/policy.c
@@ -9,6 +9,7 @@
 #include "ipe.h"
 #include "eval.h"
 #include "fs.h"
+#include "keyring.h"
 #include "policy.h"
 #include "policy_parser.h"
 #include "audit.h"
@@ -160,6 +161,7 @@ struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
 				  const char *pkcs7, size_t pkcs7len)
 {
 	struct ipe_policy *new = NULL;
+	struct key *keyring;
 	int rc = 0;
 
 	new = kzalloc_obj(*new);
@@ -189,6 +191,14 @@ struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
 						    VERIFYING_UNSPECIFIED_SIGNATURE,
 						    set_pkcs7_data, new);
 #endif
+		if (rc == -ENOKEY || rc == -EKEYREJECTED) {
+			keyring = ipe_keyring_lookup();
+			if (keyring)
+				rc = verify_pkcs7_signature(NULL, 0, new->pkcs7,
+							    pkcs7len, keyring,
+							    VERIFYING_UNSPECIFIED_SIGNATURE,
+							    set_pkcs7_data, new);
+		}
 		if (rc)
 			goto err;
 	} else {

-- 
2.53.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH RFC 2/4] keys: add KEY_SPEC_IPE_KEYRING
  2026-10-19 21:40 [PATCH RFC 0/4] IPE: add a dedicated policy keyring Manuel Huber via B4 Relay
  2026-10-19 21:40 ` [PATCH RFC 1/4] ipe: add the .ipe " Manuel Huber via B4 Relay
@ 2026-10-19 21:40 ` Manuel Huber via B4 Relay
  2026-10-19 21:50   ` sashiko-bot
  2026-10-19 23:24   ` Fan Wu
  2026-10-19 21:40 ` [PATCH RFC 3/4] Documentation: ipe: document the IPE keyring Manuel Huber via B4 Relay
                   ` (2 subsequent siblings)
  4 siblings, 2 replies; 14+ messages in thread
From: Manuel Huber via B4 Relay @ 2026-10-19 21:40 UTC (permalink / raw)
  To: Fan Wu, David Howells, Jarkko Sakkinen
  Cc: Christian Brauner, Andrew Halaney, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Shuah Khan, Shuah Khan,
	Randy Dunlap, Alexei Starovoitov, Daniel Borkmann,
	linux-security-module, keyrings, linux-kselftest, linux-doc, bpf,
	linux-kernel, Manuel Huber

From: Manuel Huber <manuelh@nvidia.com>

The IPE policy keyring is allocated unlinked, so userspace would
otherwise have to scrape /proc/keys to name it.

Signed-off-by: Manuel Huber <manuelh@nvidia.com>
---
 Documentation/security/keys/core.rst |  9 +++++++++
 include/linux/key.h                  |  2 ++
 include/uapi/linux/keyctl.h          |  1 +
 security/ipe/keyring.c               |  1 +
 security/keys/process_keys.c         | 23 +++++++++++++++++++++++
 5 files changed, 36 insertions(+)

diff --git a/Documentation/security/keys/core.rst b/Documentation/security/keys/core.rst
index 326b8a973828..e4ecda5c2525 100644
--- a/Documentation/security/keys/core.rst
+++ b/Documentation/security/keys/core.rst
@@ -373,6 +373,15 @@ process making the call::
 	KEY_SPEC_REQKEY_AUTH_KEY	-7	assumed request_key()
 						  authorisation key
 
+There are also values that refer to a specific kernel keyring rather than to
+anything belonging to the caller.  These keyrings are not linked into any
+keyring, so a search cannot reach them and the ID is the only way to name
+one::
+
+	CONSTANT			VALUE	KEY REFERENCED
+	==============================	======	===========================
+	KEY_SPEC_IPE_KEYRING		-12	the ".ipe" keyring
+
 
 The main syscalls are:
 
diff --git a/include/linux/key.h b/include/linux/key.h
index 81b8f05c6898..9c2ac0c70d56 100644
--- a/include/linux/key.h
+++ b/include/linux/key.h
@@ -440,6 +440,8 @@ extern key_ref_t keyring_search(key_ref_t keyring,
 extern int keyring_restrict(key_ref_t keyring, const char *type,
 			    const char *restriction);
 
+extern void key_register_ipe_keyring(struct key *keyring);
+
 extern struct key *key_lookup(key_serial_t id);
 
 static inline key_serial_t key_serial(const struct key *key)
diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h
index 4c8884eea808..e42fca286b15 100644
--- a/include/uapi/linux/keyctl.h
+++ b/include/uapi/linux/keyctl.h
@@ -24,6 +24,7 @@
 #define KEY_SPEC_GROUP_KEYRING		-6	/* - key ID for GID-specific keyring */
 #define KEY_SPEC_REQKEY_AUTH_KEY	-7	/* - key ID for assumed request_key auth key */
 #define KEY_SPEC_REQUESTOR_KEYRING	-8	/* - key ID for request_key() dest keyring */
+#define KEY_SPEC_IPE_KEYRING		-12	/* - key ID for the .ipe keyring */
 
 /* request-key default keyrings */
 #define KEY_REQKEY_DEFL_NO_CHANGE		-1
diff --git a/security/ipe/keyring.c b/security/ipe/keyring.c
index ea8d164b62a6..5158dc7310a7 100644
--- a/security/ipe/keyring.c
+++ b/security/ipe/keyring.c
@@ -74,6 +74,7 @@ int __init ipe_keyring_init(void)
 	}
 
 	ipe_keyring = keyring;
+	key_register_ipe_keyring(ipe_keyring);
 
 	return 0;
 }
diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c
index a63c46bb2d14..20d6faefb75e 100644
--- a/security/keys/process_keys.c
+++ b/security/keys/process_keys.c
@@ -22,6 +22,9 @@
 /* Session keyring create vs join semaphore */
 static DEFINE_MUTEX(key_session_mutex);
 
+/* IPE keyring reachable through KEY_SPEC_IPE_KEYRING */
+static struct key *ipe_keyring __ro_after_init;
+
 /* The root user's tracking struct */
 struct key_user root_key_user = {
 	.usage		= REFCOUNT_INIT(3),
@@ -590,6 +593,18 @@ bool lookup_user_key_possessed(const struct key *key,
 	return key == match_data->raw_data;
 }
 
+/**
+ * key_register_ipe_keyring - Publish the IPE keyring
+ * @keyring: The keyring to publish
+ *
+ * Make @keyring reachable by userspace through the KEY_SPEC_IPE_KEYRING
+ * special key ID. Called once, from an initcall, and never undone.
+ */
+void key_register_ipe_keyring(struct key *keyring)
+{
+	ipe_keyring = keyring;
+}
+
 /*
  * Look up a key ID given us by userspace with a given permissions mask to get
  * the key it refers to.
@@ -741,6 +756,14 @@ key_ref_t lookup_user_key(key_serial_t id, unsigned long lflags,
 		key_ref = make_key_ref(key, 1);
 		break;
 
+	case KEY_SPEC_IPE_KEYRING:
+		key = ipe_keyring;
+		if (!key)
+			goto error;
+		__key_get(key);
+		key_ref = make_key_ref(key, 0);
+		break;
+
 	default:
 		key_ref = ERR_PTR(-EINVAL);
 		if (id < 1)

-- 
2.53.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH RFC 3/4] Documentation: ipe: document the IPE keyring
  2026-10-19 21:40 [PATCH RFC 0/4] IPE: add a dedicated policy keyring Manuel Huber via B4 Relay
  2026-10-19 21:40 ` [PATCH RFC 1/4] ipe: add the .ipe " Manuel Huber via B4 Relay
  2026-10-19 21:40 ` [PATCH RFC 2/4] keys: add KEY_SPEC_IPE_KEYRING Manuel Huber via B4 Relay
@ 2026-10-19 21:40 ` Manuel Huber via B4 Relay
  2026-10-19 21:45   ` sashiko-bot
  2026-10-19 21:40 ` [PATCH RFC 4/4] selftests/ipe: test the IPE policy keyring Manuel Huber via B4 Relay
  2026-10-19 21:54 ` [PATCH RFC 0/4] IPE: add a dedicated " Jarkko Sakkinen
  4 siblings, 1 reply; 14+ messages in thread
From: Manuel Huber via B4 Relay @ 2026-10-19 21:40 UTC (permalink / raw)
  To: Fan Wu, David Howells, Jarkko Sakkinen
  Cc: Christian Brauner, Andrew Halaney, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Shuah Khan, Shuah Khan,
	Randy Dunlap, Alexei Starovoitov, Daniel Borkmann,
	linux-security-module, keyrings, linux-kselftest, linux-doc, bpf,
	linux-kernel, Manuel Huber

From: Manuel Huber <manuelh@nvidia.com>

Document the .ipe policy-verification keyring and its additive use
after the existing trust sources.

Signed-off-by: Manuel Huber <manuelh@nvidia.com>
---
 Documentation/admin-guide/LSM/ipe.rst | 41 ++++++++++++++++++++++++++++++++++-
 Documentation/security/ipe.rst        | 16 +++++++++-----
 2 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/Documentation/admin-guide/LSM/ipe.rst b/Documentation/admin-guide/LSM/ipe.rst
index 9e82a2082b94..48ccad203301 100644
--- a/Documentation/admin-guide/LSM/ipe.rst
+++ b/Documentation/admin-guide/LSM/ipe.rst
@@ -238,7 +238,11 @@ unconstrained root, and deploying an "allow all" policy). These
 policies must be signed by a certificate that chains to the
 ``SYSTEM_TRUSTED_KEYRING``, or to the secondary and/or platform keyrings if
 ``CONFIG_IPE_POLICY_SIG_SECONDARY_KEYRING`` and/or
-``CONFIG_IPE_POLICY_SIG_PLATFORM_KEYRING`` are enabled, respectively.
+``CONFIG_IPE_POLICY_SIG_PLATFORM_KEYRING`` are enabled, respectively. IPE also
+provides a dedicated ``.ipe`` keyring for policy-signing certificates that are
+provisioned by userspace, as described below. The ``.ipe`` keyring is an
+additive final fallback and does not replace the configured system trust
+sources.
 With openssl, the policy can be signed by::
 
    openssl smime -sign \
@@ -251,6 +255,41 @@ With openssl, the policy can be signed by::
       -outform der \
       -out "$MY_POLICY.p7b"
 
+Provisioning the IPE policy keyring
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The ``.ipe`` keyring allows a system to provision an IPE-specific policy
+signing certificate without embedding it in the kernel or firmware trust
+stores. It is not linked into any process keyring. Userspace addresses it
+directly with the ``KEY_SPEC_IPE_KEYRING`` special key ID (``-12``), which does
+not grant possession of the keyring.
+
+The keyring is restricted while empty by default and cannot then be
+provisioned. To provision it, boot with ``ipe.keyring_unsealed=1``. Userspace
+can add a DER-encoded X.509 certificate and then apply a reject-all
+restriction::
+
+   keyctl padd asymmetric "" -12 < policy-signing-key.der
+   keyctl restrict_keyring -12
+
+Both steps are required. IPE ignores the keyring until it is nonempty and
+restricted, so a partially provisioned, mutable keyring is not a trust source.
+The restriction cannot be removed. More than one certificate can be enrolled
+by repeating the ``keyctl padd`` command before restricting the keyring.
+
+Restriction prevents adding keys, but it does not prevent userspace from
+removing keys or otherwise disabling the keyring. To lock it against further
+userspace modification, drop its write and setattr permissions::
+
+   keyctl setperm -12 0x08030000
+
+This retains the permissions needed for verification and inspection.
+Provisioning, restriction, and permission hardening must be completed before
+relying on ``.ipe`` as a trust source.
+
+Once nonempty and restricted, ``.ipe`` is searched after the configured system
+trust sources. An absent, empty, or unrestricted ``.ipe`` keyring is ignored.
+
 Deploying the policies is done through securityfs, through the
 ``new_policy`` node. To deploy a policy, simply cat the file into the
 securityfs node::
diff --git a/Documentation/security/ipe.rst b/Documentation/security/ipe.rst
index 4490aed5918a..80f9893dddb5 100644
--- a/Documentation/security/ipe.rst
+++ b/Documentation/security/ipe.rst
@@ -248,11 +248,17 @@ policy updates have to be done rebootlessly.
 
 To allow an update from an external source, it could be potentially malicious,
 so this policy needs to have a way to be identified as trusted. This is
-done via a signature chained to a trust source in the kernel. Arbitrarily,
-this is  the ``SYSTEM_TRUSTED_KEYRING``, a keyring that is initially
-populated at kernel compile-time, as this matches the expectation that the
-author of the compiled-in policy described above is the same entity that can
-deploy policy updates.
+done via a signature chained to a trust source in the kernel. IPE can use the
+builtin trusted keyring and, when configured, the secondary and platform
+keyrings. This matches systems where the entity that establishes the kernel or
+firmware trust store is also authorized to deploy policy updates.
+
+IPE also has a dedicated ``.ipe`` keyring for deployments that establish an
+IPE-specific trust anchor from userspace. The keyring can be provisioned and
+irreversibly restricted. IPE uses it only when it is nonempty and restricted,
+and only as a fallback after the configured system keyrings. This keeps the
+existing trust model intact while allowing a policy-signing key to be rotated
+across boots without changing the kernel image or a broader system trust store.
 
 Anti-Rollback / Anti-Replay
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~

-- 
2.53.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* [PATCH RFC 4/4] selftests/ipe: test the IPE policy keyring
  2026-10-19 21:40 [PATCH RFC 0/4] IPE: add a dedicated policy keyring Manuel Huber via B4 Relay
                   ` (2 preceding siblings ...)
  2026-10-19 21:40 ` [PATCH RFC 3/4] Documentation: ipe: document the IPE keyring Manuel Huber via B4 Relay
@ 2026-10-19 21:40 ` Manuel Huber via B4 Relay
  2026-10-19 21:57   ` sashiko-bot
  2026-10-19 23:28   ` Fan Wu
  2026-10-19 21:54 ` [PATCH RFC 0/4] IPE: add a dedicated " Jarkko Sakkinen
  4 siblings, 2 replies; 14+ messages in thread
From: Manuel Huber via B4 Relay @ 2026-10-19 21:40 UTC (permalink / raw)
  To: Fan Wu, David Howells, Jarkko Sakkinen
  Cc: Christian Brauner, Andrew Halaney, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Shuah Khan, Shuah Khan,
	Randy Dunlap, Alexei Starovoitov, Daniel Borkmann,
	linux-security-module, keyrings, linux-kselftest, linux-doc, bpf,
	linux-kernel, Manuel Huber

From: Manuel Huber <manuelh@nvidia.com>

Add an IPE kselftest that validates policy keyring operations for
both ipe.keyring_unsealed values 0 and 1.

For the default ipe.keyring_unsealed=0, verify that a) the special key
ID resolves, b) certificate enrollment is rejected, and c) the keyring
is not used for policy verification.
For ipe.keyring_unsealed=1, verify that we can a) provision multiple
certificates, b) restrict the keyring, then reject unenrolled and
corrupted signatures, c) authenticate policy loads and updates with
different enrolled certificates.

Since IPE is built in, keyring restriction is irreversible, which is
why we can only exercise the initially sealed and unsealed scenarios
via separate boots. To get full coverage, CI thus needs to spin up
separate environments with respective parameter values.

Signed-off-by: Manuel Huber <manuelh@nvidia.com>
---
 MAINTAINERS                                     |   1 +
 tools/testing/selftests/Makefile                |   1 +
 tools/testing/selftests/ipe/Makefile            |   5 +
 tools/testing/selftests/ipe/config              |   7 +
 tools/testing/selftests/ipe/test-ipe-keyring.sh | 357 ++++++++++++++++++++++++
 5 files changed, 371 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 65e8a4b5c90b..818f9968b315 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12967,6 +12967,7 @@ F:	Documentation/admin-guide/LSM/ipe.rst
 F:	Documentation/security/ipe.rst
 F:	scripts/ipe/
 F:	security/ipe/
+F:	tools/testing/selftests/ipe/
 
 INTEL 810/815 FRAMEBUFFER DRIVER
 M:	Antonino Daplas <adaplas@gmail.com>
diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index 273853937c25..16ba45a12c5b 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -59,6 +59,7 @@ TARGETS += hid
 TARGETS += intel_pstate
 TARGETS += iommu
 TARGETS += ipc
+TARGETS += ipe
 TARGETS += ir
 TARGETS += kcmp
 TARGETS += kexec
diff --git a/tools/testing/selftests/ipe/Makefile b/tools/testing/selftests/ipe/Makefile
new file mode 100644
index 000000000000..f281e68329c1
--- /dev/null
+++ b/tools/testing/selftests/ipe/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+
+TEST_PROGS := test-ipe-keyring.sh
+
+include ../lib.mk
diff --git a/tools/testing/selftests/ipe/config b/tools/testing/selftests/ipe/config
new file mode 100644
index 000000000000..498c224d2c18
--- /dev/null
+++ b/tools/testing/selftests/ipe/config
@@ -0,0 +1,7 @@
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
+CONFIG_KEYS=y
+CONFIG_PROC_FS=y
+CONFIG_SECURITY=y
+CONFIG_SECURITYFS=y
+CONFIG_SECURITY_IPE=y
diff --git a/tools/testing/selftests/ipe/test-ipe-keyring.sh b/tools/testing/selftests/ipe/test-ipe-keyring.sh
new file mode 100755
index 000000000000..557ccc825fe2
--- /dev/null
+++ b/tools/testing/selftests/ipe/test-ipe-keyring.sh
@@ -0,0 +1,357 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Run from the kernel source tree with:
+#   make -C tools/testing/selftests TARGETS=ipe run_tests
+#
+# Full coverage requires one boot with the default command line and another
+# with ipe.keyring_unsealed=1. The test selects the matching mode automatically.
+# The provisioning mode irreversibly restricts and locks the .ipe keyring, so
+# run it once per boot on a disposable test system.
+#
+# Requirements: root with CAP_MAC_ADMIN, keyctl, and openssl.
+
+DIR="$(dirname "$(readlink -f "$0")")"
+# shellcheck source=../kselftest/ktap_helpers.sh
+source "${DIR}/../kselftest/ktap_helpers.sh"
+
+KEY_SPEC_IPE_KEYRING=-12
+IPE_KEYRING_PERM_LOCKED=0x08030000
+IPE_KEYRING_PERM_INITIAL=0x082f0000
+SECURITYFS=/sys/kernel/security
+IPEFS="$SECURITYFS/ipe"
+WORK_DIR=
+MOUNTED_SECURITYFS=0
+POLICY_NAME="ipe_keyring_selftest_$$"
+WRONG_POLICY_NAME="${POLICY_NAME}_wrong"
+POLICY_LOADED=0
+WRONG_POLICY_LOADED=0
+
+skip()
+{
+	ktap_skip_all "$*"
+	exit "$KSFT_SKIP"
+}
+
+fail()
+{
+	ktap_test_fail "$*"
+	ktap_finished
+}
+
+# shellcheck disable=SC2329 # invoked indirectly via trap
+cleanup()
+{
+	if [ "$WRONG_POLICY_LOADED" -eq 1 ] &&
+	   [ -w "$IPEFS/policies/$WRONG_POLICY_NAME/delete" ]; then
+		echo 1 > "$IPEFS/policies/$WRONG_POLICY_NAME/delete" 2>/dev/null || true
+	fi
+
+	if [ "$POLICY_LOADED" -eq 1 ] &&
+	   [ -w "$IPEFS/policies/$POLICY_NAME/delete" ]; then
+		echo 1 > "$IPEFS/policies/$POLICY_NAME/delete" 2>/dev/null || true
+	fi
+
+	if [ "$MOUNTED_SECURITYFS" -eq 1 ]; then
+		umount "$SECURITYFS" 2>/dev/null || true
+	fi
+
+	if [ -n "$WORK_DIR" ] && [ -d "$WORK_DIR" ]; then
+		rm -rf -- "$WORK_DIR"
+	fi
+}
+
+trap cleanup EXIT
+
+require_command()
+{
+	command -v "$1" >/dev/null 2>&1 || skip "missing command: $1"
+}
+
+generate_keypair()
+{
+	local name=$1
+
+	if ! openssl req -new -x509 -newkey rsa:2048 -sha256 -nodes -days 1 \
+		-subj "/CN=IPE keyring selftest $name/" \
+		-addext "basicConstraints=critical,CA:FALSE" \
+		-addext "keyUsage=critical,digitalSignature" \
+		-keyout "$WORK_DIR/$name-key.pem" \
+		-out "$WORK_DIR/$name-cert.pem" >/dev/null \
+		2> "$WORK_DIR/$name-openssl.log"; then
+		cat "$WORK_DIR/$name-openssl.log" >&2
+		fail "generate the $name signing key"
+	fi
+
+	if ! openssl x509 -in "$WORK_DIR/$name-cert.pem" -outform DER \
+		-out "$WORK_DIR/$name-cert.der" >/dev/null \
+		2> "$WORK_DIR/$name-openssl.log"; then
+		cat "$WORK_DIR/$name-openssl.log" >&2
+		fail "convert the $name certificate to DER"
+	fi
+}
+
+make_policy()
+{
+	local name=$1
+	local version=$2
+	local output=$3
+
+	printf 'policy_name=%s policy_version=%s\nDEFAULT action=ALLOW\n' \
+		"$name" "$version" > "$output"
+}
+
+sign_policy()
+{
+	local policy=$1
+	local signer=$2
+	local output=$3
+
+	if ! openssl smime -sign -in "$policy" \
+		-signer "$WORK_DIR/$signer-cert.pem" \
+		-inkey "$WORK_DIR/$signer-key.pem" \
+		-noattr -nodetach -nosmimecap -outform DER \
+		-out "$output" >/dev/null \
+		2> "$WORK_DIR/$signer-openssl.log"; then
+		cat "$WORK_DIR/$signer-openssl.log" >&2
+		fail "sign policy with the $signer key"
+	fi
+}
+
+corrupt_signed_policy()
+{
+	local input=$1
+	local output=$2
+	local signed_size
+	local corrupt_offset
+
+	cp -- "$input" "$output" || fail "copy the signed policy"
+	signed_size=$(wc -c < "$output") || fail "measure the signed policy"
+	[ "$signed_size" -gt 16 ] || fail "signed policy is unexpectedly short"
+	corrupt_offset=$((signed_size - 16))
+	printf '\377\377\377\377' | dd of="$output" bs=1 \
+		seek="$corrupt_offset" conv=notrunc >/dev/null 2>&1 ||
+		fail "corrupt the signed policy"
+}
+
+keyring_key_count()
+{
+	local output
+
+	output=$(keyctl list "$KEY_SPEC_IPE_KEYRING" 2>/dev/null) || return 1
+	awk 'NR == 1 {
+		if ($0 ~ /empty/)
+			print 0
+		else if ($1 ~ /^[0-9]+$/)
+			print $1
+		else
+			exit 1
+	}' <<< "$output"
+}
+
+check_locked_mutations()
+{
+	local key_serial=$1
+	local keyring_serial=$2
+
+	if keyctl unlink "$key_serial" "$keyring_serial" >/dev/null 2>&1; then
+		fail "locked keyring allowed its certificate to be unlinked"
+	fi
+	if keyctl clear "$keyring_serial" >/dev/null 2>&1; then
+		fail "locked keyring allowed its contents to be cleared"
+	fi
+	if keyctl revoke "$keyring_serial" >/dev/null 2>&1; then
+		fail "locked keyring allowed itself to be revoked"
+	fi
+	if keyctl invalidate "$keyring_serial" >/dev/null 2>&1; then
+		fail "locked keyring allowed itself to be invalidated"
+	fi
+	if keyctl timeout "$keyring_serial" 1 >/dev/null 2>&1; then
+		fail "locked keyring allowed a timeout to be set"
+	fi
+	if keyctl setperm "$keyring_serial" \
+		"$IPE_KEYRING_PERM_INITIAL" >/dev/null 2>&1; then
+		fail "locked keyring allowed its permissions to be restored"
+	fi
+}
+
+test_sealed_keyring()
+{
+	local key_count
+
+	ktap_set_plan 4
+	ktap_test_pass "KEY_SPEC_IPE_KEYRING resolves to the .ipe keyring"
+
+	key_count=$(keyring_key_count) || fail "read the sealed keyring"
+	[ "$key_count" = 0 ] || fail "sealed-default keyring is not empty"
+	ktap_test_pass "the default keyring is empty"
+
+	generate_keypair trusted
+	if keyctl padd asymmetric "ipe-selftest-$$" \
+		"$KEY_SPEC_IPE_KEYRING" < "$WORK_DIR/trusted-cert.der" \
+		>/dev/null 2>&1; then
+		fail "sealed-default keyring accepted a certificate"
+	fi
+	ktap_test_pass "the default keyring rejects new certificates"
+
+	make_policy "$POLICY_NAME" 0.0.1 "$WORK_DIR/policy-v1"
+	sign_policy "$WORK_DIR/policy-v1" trusted "$WORK_DIR/policy-v1.p7b"
+	if cat "$WORK_DIR/policy-v1.p7b" > "$IPEFS/new_policy" 2>/dev/null; then
+		POLICY_LOADED=1
+		fail "empty .ipe keyring authenticated a policy"
+	fi
+	ktap_test_pass "the empty keyring is not used for policy verification"
+}
+
+test_unsealed_keyring()
+{
+	local key_count
+	local key_serial
+
+	ktap_set_plan 16
+	ktap_test_pass "KEY_SPEC_IPE_KEYRING resolves to the .ipe keyring"
+
+	key_count=$(keyring_key_count) || fail "read the unsealed keyring"
+	[ "$key_count" = 0 ] || fail "unsealed keyring is not empty"
+	ktap_test_pass "the unsealed keyring starts empty"
+
+	generate_keypair trusted
+	generate_keypair trusted-alt
+	generate_keypair untrusted
+	key_serial=$(keyctl padd asymmetric "ipe-selftest-$$" \
+		"$KEY_SPEC_IPE_KEYRING" < "$WORK_DIR/trusted-cert.der" \
+		2>/dev/null) || fail "add a certificate to the unsealed keyring"
+	keyctl padd asymmetric "ipe-selftest-alt-$$" \
+		"$KEY_SPEC_IPE_KEYRING" < "$WORK_DIR/trusted-alt-cert.der" \
+		>/dev/null 2>&1 || fail "add a second certificate to the keyring"
+	key_count=$(keyring_key_count) || fail "count the provisioned certificates"
+	[ "$key_count" = 2 ] || fail "keyring does not contain two certificates"
+	ktap_test_pass "multiple certificates can be provisioned"
+
+	make_policy "$POLICY_NAME" 0.0.1 "$WORK_DIR/policy-v1"
+	sign_policy "$WORK_DIR/policy-v1" trusted "$WORK_DIR/policy-v1.p7b"
+	if cat "$WORK_DIR/policy-v1.p7b" > "$IPEFS/new_policy" 2>/dev/null; then
+		POLICY_LOADED=1
+		fail "unrestricted .ipe keyring authenticated a policy"
+	fi
+	ktap_test_pass "an unrestricted keyring is not used for verification"
+
+	keyctl restrict_keyring "$KEY_SPEC_IPE_KEYRING" >/dev/null 2>&1 ||
+		fail "restrict the provisioned keyring"
+	ktap_test_pass "the provisioned keyring can be restricted"
+
+	if printf x | keyctl padd user "ipe-selftest-probe-$$" \
+		"$KEY_SPEC_IPE_KEYRING" >/dev/null 2>&1; then
+		fail "restricted keyring accepted another key"
+	fi
+	ktap_test_pass "the restricted keyring rejects additional keys"
+
+	make_policy "$WRONG_POLICY_NAME" 0.0.1 "$WORK_DIR/policy-wrong"
+	sign_policy "$WORK_DIR/policy-wrong" untrusted \
+		"$WORK_DIR/policy-wrong.p7b"
+	if cat "$WORK_DIR/policy-wrong.p7b" > "$IPEFS/new_policy" 2>/dev/null; then
+		WRONG_POLICY_LOADED=1
+		fail "an unenrolled certificate authenticated a policy"
+	fi
+	ktap_test_pass "an unenrolled certificate is rejected"
+
+	corrupt_signed_policy "$WORK_DIR/policy-v1.p7b" \
+		"$WORK_DIR/policy-v1-corrupt.p7b"
+	if cat "$WORK_DIR/policy-v1-corrupt.p7b" \
+		> "$IPEFS/new_policy" 2>/dev/null; then
+		POLICY_LOADED=1
+		fail "a corrupted signed policy was authenticated"
+	fi
+	ktap_test_pass "a corrupted signed policy is rejected"
+
+	cat "$WORK_DIR/policy-v1.p7b" > "$IPEFS/new_policy" 2>/dev/null ||
+		fail "load a policy signed by the enrolled certificate"
+	POLICY_LOADED=1
+	ktap_test_pass "the restricted keyring authenticates a policy"
+
+	[ "$(cat "$IPEFS/policies/$POLICY_NAME/name")" = "$POLICY_NAME" ] &&
+		[ "$(cat "$IPEFS/policies/$POLICY_NAME/version")" = 0.0.1 ] ||
+		fail "loaded policy metadata does not match"
+	ktap_test_pass "the authenticated policy metadata matches"
+
+	keyctl setperm "$KEY_SPEC_IPE_KEYRING" \
+		"$IPE_KEYRING_PERM_LOCKED" >/dev/null 2>&1 ||
+		fail "drop mutation permissions from the keyring"
+	ktap_test_pass "mutation permissions can be dropped"
+
+	if keyctl id "$KEY_SPEC_IPE_KEYRING" >/dev/null 2>&1; then
+		fail "the special key ID granted possession rights"
+	fi
+	ktap_test_pass "the special key ID grants no possession rights"
+
+	check_locked_mutations "$key_serial" "$keyring_serial"
+	ktap_test_pass "locked-keyring mutation attempts are rejected"
+
+	make_policy "$POLICY_NAME" 0.0.2 "$WORK_DIR/policy-v2"
+	sign_policy "$WORK_DIR/policy-v2" trusted-alt "$WORK_DIR/policy-v2.p7b"
+	cat "$WORK_DIR/policy-v2.p7b" \
+		> "$IPEFS/policies/$POLICY_NAME/update" 2>/dev/null ||
+		fail "update a policy signed by the second enrolled certificate"
+	ktap_test_pass "the second enrolled certificate authenticates policy updates"
+
+	[ "$(cat "$IPEFS/policies/$POLICY_NAME/version")" = 0.0.2 ] ||
+		fail "policy version was not updated"
+	ktap_test_pass "the authenticated policy update was installed"
+
+	echo 1 > "$IPEFS/policies/$POLICY_NAME/delete" 2>/dev/null ||
+		fail "delete the inactive test policy"
+	POLICY_LOADED=0
+	ktap_test_pass "the inactive test policy can be removed"
+}
+
+ktap_print_header
+
+for command in awk cat cp dd grep id keyctl mktemp mount openssl rm umount wc; do
+	require_command "$command"
+done
+
+[ "$(id -u)" -eq 0 ] || skip "must be run as root"
+
+cap_eff=$(awk '/^CapEff:/ { print $2 }' /proc/self/status)
+[ -n "$cap_eff" ] || skip "cannot read effective capabilities"
+(( (16#$cap_eff & (1 << 33)) != 0 )) || skip "CAP_MAC_ADMIN is required"
+
+WORK_DIR=$(mktemp -d -t ipe-keyring-test.XXXXXX) ||
+	skip "cannot create a temporary directory"
+
+if ! grep -qs " $SECURITYFS securityfs " /proc/mounts; then
+	mount -t securityfs none "$SECURITYFS" >/dev/null 2>&1 ||
+		skip "cannot mount securityfs"
+	MOUNTED_SECURITYFS=1
+fi
+
+[ -w "$IPEFS/new_policy" ] || skip "IPE is not enabled"
+[ -r /sys/module/ipe/parameters/keyring_unsealed ] ||
+	skip "IPE policy keyring is not available"
+
+keyring_serial=$(keyctl id "$KEY_SPEC_IPE_KEYRING" 2>/dev/null) ||
+	skip "KEY_SPEC_IPE_KEYRING does not resolve"
+keyring_description=$(keyctl describe "$keyring_serial" 2>/dev/null) ||
+	skip "cannot describe the IPE keyring"
+case "$keyring_description" in
+	*"keyring: .ipe") ;;
+	*) skip "KEY_SPEC_IPE_KEYRING resolved to an unexpected key" ;;
+esac
+
+keyring_unsealed=$(cat /sys/module/ipe/parameters/keyring_unsealed)
+case "$keyring_unsealed" in
+	Y)
+		[ "$(keyring_key_count)" = 0 ] ||
+			skip "the IPE keyring has already been provisioned"
+		test_unsealed_keyring
+		;;
+	N)
+		test_sealed_keyring
+		;;
+	*)
+		ktap_exit_fail_msg \
+			"unexpected ipe.keyring_unsealed value: $keyring_unsealed"
+		;;
+esac
+
+ktap_finished

-- 
2.53.0



^ permalink raw reply related	[flat|nested] 14+ messages in thread

* Re: [PATCH RFC 3/4] Documentation: ipe: document the IPE keyring
  2026-10-19 21:40 ` [PATCH RFC 3/4] Documentation: ipe: document the IPE keyring Manuel Huber via B4 Relay
@ 2026-10-19 21:45   ` sashiko-bot
  0 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-19 21:45 UTC (permalink / raw)
  Cc: linux-security-module

> From: Manuel Huber <manuelh@nvidia.com>
> 
> Document the .ipe policy-verification keyring and its additive use
> after the existing trust sources.
> 
> Signed-off-by: Manuel Huber <manuelh@nvidia.com>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261019-b4-ipe-keyring-rfc-v1-0-156f0b93e843@nvidia.com?part=3


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH RFC 1/4] ipe: add the .ipe policy keyring
  2026-10-19 21:40 ` [PATCH RFC 1/4] ipe: add the .ipe " Manuel Huber via B4 Relay
@ 2026-10-19 21:47   ` sashiko-bot
  2026-10-19 21:52   ` Randy Dunlap
  2026-10-19 23:15   ` Fan Wu
  2 siblings, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-19 21:47 UTC (permalink / raw)
  Cc: linux-security-module

> From: Manuel Huber <manuelh@nvidia.com>
> 
> Add a dedicated .ipe keyring for IPE policy signature verification,
> modeled after the .dm-verity and .bpf keyring implementations.
> 
> With this, userspace can provision its own keys into the keyring,
> then restrict it and activate it for IPE policy signature verification.
> 
> This enables scenarios in which a trusted userspace component generates
> an IPE policy, and creates an ephemeral policy-signing key pair,
> provisions its public certificate, and restricts the IPE keyring during
> the early lifetime of a system. The trusted component can then sign the
> IPE policy, destroy the private key, and load and activate the policy.
> During that phase, the system is considered in a trusted state whereas
> the system transitions into a possibly untrusted state afterwards,
> running arbitrary user-provided applications. In such a scenario, the
> trusted userspace component could reside on a dm-verity protected
> rootfs disk, and applications would reside on additional dm-verity
> protected disks. The latter disks would be dynamically attached and
> their dm-verity root hashes and/or signatures not known a priori to
> launching the system.
> 
> A concrete usage scenario could be Kata Containers, which enables
> running Kubernetes pods in ephemeral, headless utility VMs, possibly
> without systemd. The rootfs disk is static and known beforehand, but
> containerd's image layers might be acquired and transported into the
> VM using the erofs snapshotter and dynamic block device attachment
> using dm-verity.
> 
> Signed-off-by: Manuel Huber <manuelh@nvidia.com>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261019-b4-ipe-keyring-rfc-v1-0-156f0b93e843@nvidia.com?part=1


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH RFC 2/4] keys: add KEY_SPEC_IPE_KEYRING
  2026-10-19 21:40 ` [PATCH RFC 2/4] keys: add KEY_SPEC_IPE_KEYRING Manuel Huber via B4 Relay
@ 2026-10-19 21:50   ` sashiko-bot
  2026-10-19 23:24   ` Fan Wu
  1 sibling, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-19 21:50 UTC (permalink / raw)
  Cc: linux-security-module

> From: Manuel Huber <manuelh@nvidia.com>
> 
> The IPE policy keyring is allocated unlinked, so userspace would
> otherwise have to scrape /proc/keys to name it.
> 
> Signed-off-by: Manuel Huber <manuelh@nvidia.com>

Sashiko has reviewed this patch and found no issues.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261019-b4-ipe-keyring-rfc-v1-0-156f0b93e843@nvidia.com?part=2


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH RFC 1/4] ipe: add the .ipe policy keyring
  2026-10-19 21:40 ` [PATCH RFC 1/4] ipe: add the .ipe " Manuel Huber via B4 Relay
  2026-10-19 21:47   ` sashiko-bot
@ 2026-10-19 21:52   ` Randy Dunlap
  2026-10-19 23:15   ` Fan Wu
  2 siblings, 0 replies; 14+ messages in thread
From: Randy Dunlap @ 2026-10-19 21:52 UTC (permalink / raw)
  To: manuelh, Fan Wu, David Howells, Jarkko Sakkinen
  Cc: Christian Brauner, Andrew Halaney, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Shuah Khan, Shuah Khan,
	Alexei Starovoitov, Daniel Borkmann, linux-security-module,
	keyrings, linux-kselftest, linux-doc, bpf, linux-kernel



On 10/19/26 2:40 PM, Manuel Huber via B4 Relay wrote:
> From: Manuel Huber <manuelh@nvidia.com>
> 
> Add a dedicated .ipe keyring for IPE policy signature verification,
> modeled after the .dm-verity and .bpf keyring implementations.
> 
> With this, userspace can provision its own keys into the keyring,
> then restrict it and activate it for IPE policy signature verification.
> 
> This enables scenarios in which a trusted userspace component generates
> an IPE policy, and creates an ephemeral policy-signing key pair,
> provisions its public certificate, and restricts the IPE keyring during
> the early lifetime of a system. The trusted component can then sign the
> IPE policy, destroy the private key, and load and activate the policy.
> During that phase, the system is considered in a trusted state whereas
> the system transitions into a possibly untrusted state afterwards,
> running arbitrary user-provided applications. In such a scenario, the
> trusted userspace component could reside on a dm-verity protected
> rootfs disk, and applications would reside on additional dm-verity
> protected disks. The latter disks would be dynamically attached and
> their dm-verity root hashes and/or signatures not known a priori to
> launching the system.
> 
> A concrete usage scenario could be Kata Containers, which enables
> running Kubernetes pods in ephemeral, headless utility VMs, possibly
> without systemd. The rootfs disk is static and known beforehand, but
> containerd's image layers might be acquired and transported into the
> VM using the erofs snapshotter and dynamic block device attachment
> using dm-verity.
> 
> Signed-off-by: Manuel Huber <manuelh@nvidia.com>
> ---
>  Documentation/admin-guide/kernel-parameters.txt |  9 +++
>  security/ipe/Makefile                           |  1 +
>  security/ipe/ipe.c                              |  2 +
>  security/ipe/keyring.c                          | 79 +++++++++++++++++++++++++
>  security/ipe/keyring.h                          | 16 +++++
>  security/ipe/policy.c                           | 10 ++++
>  6 files changed, 117 insertions(+)
> 
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index e75344f4e0cd..fd508dd779ed 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -2733,6 +2733,15 @@ Kernel parameters
>  			Determine whether IPE starts in permissive (0) or
>  			enforce (1) mode. The default is enforce.
>  
> +	ipe.keyring_unsealed=
> +			[IPE]
> +			Format: <bool>
> +			When set to 1, leave the IPE policy keyring
> +			unsealed after initialization so userspace can provision
> +			keys. Once the keyring is nonempty and restricted, it is
> +			searched during policy-signature verification. The default
> +			is 0, which creates the keyring restricted and empty.
> +
>  	ipe.success_audit=
>  			[IPE]
>  			Format: <bool>

Please also add an entry in kernel-parameters.txt (near the beginning,
in alphabetical order) for what "IPE" means.

-- 
~Randy


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH RFC 0/4] IPE: add a dedicated policy keyring
  2026-10-19 21:40 [PATCH RFC 0/4] IPE: add a dedicated policy keyring Manuel Huber via B4 Relay
                   ` (3 preceding siblings ...)
  2026-10-19 21:40 ` [PATCH RFC 4/4] selftests/ipe: test the IPE policy keyring Manuel Huber via B4 Relay
@ 2026-10-19 21:54 ` Jarkko Sakkinen
  4 siblings, 0 replies; 14+ messages in thread
From: Jarkko Sakkinen @ 2026-10-19 21:54 UTC (permalink / raw)
  To: manuelh
  Cc: Fan Wu, David Howells, Christian Brauner, Andrew Halaney,
	Paul Moore, James Morris, Serge E. Hallyn, Jonathan Corbet,
	Shuah Khan, Shuah Khan, Randy Dunlap, Alexei Starovoitov,
	Daniel Borkmann, linux-security-module, keyrings, linux-kselftest,
	linux-doc, bpf, linux-kernel

On Mon, Oct 19, 2026 at 09:40:24PM +0000, Manuel Huber via B4 Relay wrote:
> Add a dedicated ".ipe" keyring for IPE policy-signature verification,
> modeled after the dm-verity keyring, allowing userspace to provision
> IPE-specific policy-signing certificates. Systems that do not provision
> the keyring retain their existing behavior.
> 
> This RFC is based on commit 48f936e302a9 ("ipe: update the project and
> test suite links") from ipe/next so review can begin before the
> prerequisite keyring work reaches that branch. The series will be rebased
> onto a refreshed ipe/next once that work is available there [1].
> 
> The keyring readiness check currently follows the dm-verity and BPF
> implementations. A parallel keys RFC introduces keyring_is_empty() for
> this purpose [2]. A later revision will use the helper if it is available
> in the submission base.
> 
> Testing:
> - Built an x86_64 kernel with GCC 15.2.0 and Clang/LLD 21.1.8.
> - Passed all 33 IPE parser KUnit assertions.
> - Passed all 4 sealed-default keyring selftests under QEMU TCG.
> - Passed all 16 provisioning-mode keyring selftests under QEMU TCG with
>   ipe.keyring_unsealed=1.
> 
> [1] https://lore.kernel.org/r/20261008-ajhalaney-dmverity-key-identifier-v2-0-008456d189cb@amutable.com
> [2] https://lore.kernel.org/r/20261016215326.4110064-5-jarkko@kernel.org
> 
> Signed-off-by: Manuel Huber <manuelh@nvidia.com>
> ---
> Manuel Huber (4):
>       ipe: add the .ipe policy keyring
>       keys: add KEY_SPEC_IPE_KEYRING
>       Documentation: ipe: document the IPE keyring
>       selftests/ipe: test the IPE policy keyring
> 
>  Documentation/admin-guide/LSM/ipe.rst           |  41 ++-
>  Documentation/admin-guide/kernel-parameters.txt |   9 +
>  Documentation/security/ipe.rst                  |  16 +-
>  Documentation/security/keys/core.rst            |   9 +
>  MAINTAINERS                                     |   1 +
>  include/linux/key.h                             |   2 +
>  include/uapi/linux/keyctl.h                     |   1 +
>  security/ipe/Makefile                           |   1 +
>  security/ipe/ipe.c                              |   2 +
>  security/ipe/keyring.c                          |  80 ++++++
>  security/ipe/keyring.h                          |  16 ++
>  security/ipe/policy.c                           |  10 +
>  security/keys/process_keys.c                    |  23 ++
>  tools/testing/selftests/Makefile                |   1 +
>  tools/testing/selftests/ipe/Makefile            |   5 +
>  tools/testing/selftests/ipe/config              |   7 +
>  tools/testing/selftests/ipe/test-ipe-keyring.sh | 357 ++++++++++++++++++++++++
>  17 files changed, 575 insertions(+), 6 deletions(-)
> ---
> base-commit: 48f936e302a9c48cfc032eb5944c3a6246bc9043
> change-id: 20261019-b4-ipe-keyring-rfc-b93b9ae129a2
> 
> Best regards,
> -- 
> Manuel Huber <manuelh@nvidia.com>
> 
> 

We are in the phase of release cycle that it would be hard to find any
time to review this properly so might take a while, or I'll review a
follow-up iteration if other feedback arrives.

Br, Jarkko

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH RFC 4/4] selftests/ipe: test the IPE policy keyring
  2026-10-19 21:40 ` [PATCH RFC 4/4] selftests/ipe: test the IPE policy keyring Manuel Huber via B4 Relay
@ 2026-10-19 21:57   ` sashiko-bot
  2026-10-19 23:28   ` Fan Wu
  1 sibling, 0 replies; 14+ messages in thread
From: sashiko-bot @ 2026-10-19 21:57 UTC (permalink / raw)
  To: Manuel Huber; +Cc: linux-security-module, bpf

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] selftests/ipe: missing CONFIG_CRYPTO_SHA256 in config fragment
- [Low] selftests/ipe: keyctl id check precedes already-provisioned keyring check
--

commit 0116188a11e090ec7a12ebe17082312fda668a18
Author: Manuel Huber <manuelh@nvidia.com>

selftests/ipe: test the IPE policy keyring

This commit adds a kselftest script and configuration fragment to validate
IPE policy keyring operations for both sealed and unsealed boot parameter
states.

> diff --git a/tools/testing/selftests/ipe/config b/tools/testing/selftests/ipe/config
> new file mode 100644
> index 0000000000000..498c224d2c182
> --- /dev/null
> +++ b/tools/testing/selftests/ipe/config
> @@ -0,0 +1,7 @@
> +CONFIG_AUDIT=y
> +CONFIG_AUDITSYSCALL=y
> +CONFIG_KEYS=y
> +CONFIG_PROC_FS=y
> +CONFIG_SECURITY=y
> +CONFIG_SECURITYFS=y
> +CONFIG_SECURITY_IPE=y

[Severity: Medium]
Should this config fragment also include CONFIG_CRYPTO_SHA256=y (along with
CONFIG_NET=y and CONFIG_SYSFS=y, which CONFIG_AUDIT and CONFIG_SECURITY
depend on)?

In test-ipe-keyring.sh, generate_keypair() and sign_policy() generate
RSA-2048 X.509 certificates and PKCS#7 signatures using SHA-256. When a
signed policy is written to /sys/kernel/security/ipe/new_policy,
new_policy() -> ipe_new_policy() -> verify_pkcs7_signature() ->
pkcs7_verify() -> pkcs7_digest() allocates the shash algorithm:

crypto/asymmetric_keys/pkcs7_verify.c:pkcs7_digest() {
	...
	tfm = crypto_alloc_shash(sinfo->sig->hash_algo, 0, 0);
	if (IS_ERR(tfm))
		return (PTR_ERR(tfm) == -ENOENT) ? -ENOPKG : PTR_ERR(tfm);
	...
}

Although CONFIG_SECURITY_IPE selects CONFIG_CRYPTO_LIB_SHA256,
CONFIG_PKCS7_MESSAGE_PARSER, and CONFIG_SYSTEM_DATA_VERIFICATION, none of
those select CONFIG_CRYPTO_SHA256 to register the "sha256" crypto_shash
algorithm.

Without CONFIG_CRYPTO_SHA256=y, pkcs7_digest() returns -ENOPKG. That causes
positive policy loads in test_unsealed_keyring() to fail, and causes the
negative checks in test_sealed_keyring() and test_unsealed_keyring() to
falsely pass before ipe_new_policy() ever reaches ipe_keyring_lookup():

security/ipe/policy.c:ipe_new_policy() {
	...
		if (rc == -ENOKEY || rc == -EKEYREJECTED) {
			keyring = ipe_keyring_lookup();
			if (keyring)
				rc = verify_pkcs7_signature(NULL, 0, new->pkcs7,
							    pkcs7len, keyring,
							    VERIFYING_UNSPECIFIED_SIGNATURE,
							    set_pkcs7_data, new);
		}
	...
}

> diff --git a/tools/testing/selftests/ipe/test-ipe-keyring.sh b/tools/testing/selftests/ipe/test-ipe-keyring.sh
> new file mode 100755
> index 0000000000000..557ccc825fe24
> --- /dev/null
> +++ b/tools/testing/selftests/ipe/test-ipe-keyring.sh

[ ... ]

> +	keyctl setperm "$KEY_SPEC_IPE_KEYRING" \
> +		"$IPE_KEYRING_PERM_LOCKED" >/dev/null 2>&1 ||
> +		fail "drop mutation permissions from the keyring"
> +	ktap_test_pass "mutation permissions can be dropped"
> +
> +	if keyctl id "$KEY_SPEC_IPE_KEYRING" >/dev/null 2>&1; then
> +		fail "the special key ID granted possession rights"
> +	fi
> +	ktap_test_pass "the special key ID grants no possession rights"

[ ... ]

> +keyring_serial=$(keyctl id "$KEY_SPEC_IPE_KEYRING" 2>/dev/null) ||
> +	skip "KEY_SPEC_IPE_KEYRING does not resolve"
> +keyring_description=$(keyctl describe "$keyring_serial" 2>/dev/null) ||
> +	skip "cannot describe the IPE keyring"
> +case "$keyring_description" in
> +	*"keyring: .ipe") ;;
> +	*) skip "KEY_SPEC_IPE_KEYRING resolved to an unexpected key" ;;
> +esac
> +
> +keyring_unsealed=$(cat /sys/module/ipe/parameters/keyring_unsealed)
> +case "$keyring_unsealed" in
> +	Y)
> +		[ "$(keyring_key_count)" = 0 ] ||
> +			skip "the IPE keyring has already been provisioned"
> +		test_unsealed_keyring
> +		;;

[Severity: Low]
Can the "the IPE keyring has already been provisioned" skip check be
reached if the keyring was already provisioned and locked?

When the keyring is locked with IPE_KEYRING_PERM_LOCKED (0x08030000),
KEY_USR_SEARCH is dropped. Because lookup_user_key() returns an unpossessed
key reference for KEY_SPEC_IPE_KEYRING, running keyctl id on
KEY_SPEC_IPE_KEYRING fails with -EACCES once locked (as verified in
test_unsealed_keyring() above).

When test-ipe-keyring.sh is run a second time or on a system where the
keyring was already provisioned and locked, the initial keyctl id check at
the top of the script will fail and skip with "KEY_SPEC_IPE_KEYRING does
not resolve" before reaching the keyring_key_count() check (which uses
keyctl list and only needs read/view permissions). Should the
keyring_key_count() check happen before calling keyctl id when
keyring_unsealed is Y?

[ ... ]

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261019-b4-ipe-keyring-rfc-v1-0-156f0b93e843@nvidia.com?part=4

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH RFC 1/4] ipe: add the .ipe policy keyring
  2026-10-19 21:40 ` [PATCH RFC 1/4] ipe: add the .ipe " Manuel Huber via B4 Relay
  2026-10-19 21:47   ` sashiko-bot
  2026-10-19 21:52   ` Randy Dunlap
@ 2026-10-19 23:15   ` Fan Wu
  2 siblings, 0 replies; 14+ messages in thread
From: Fan Wu @ 2026-10-19 23:15 UTC (permalink / raw)
  To: manuelh, David Howells, Jarkko Sakkinen
  Cc: Christian Brauner, Andrew Halaney, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Shuah Khan, Shuah Khan,
	Randy Dunlap, Alexei Starovoitov, Daniel Borkmann,
	linux-security-module, keyrings, linux-kselftest, linux-doc, bpf,
	linux-kernel



On 10/19/2026 2:40 PM, Manuel Huber via B4 Relay wrote:
> From: Manuel Huber <manuelh@nvidia.com>
> 
> Add a dedicated .ipe keyring for IPE policy signature verification,
> modeled after the .dm-verity and .bpf keyring implementations.
> 
> With this, userspace can provision its own keys into the keyring,
> then restrict it and activate it for IPE policy signature verification.
> 
> This enables scenarios in which a trusted userspace component generates
> an IPE policy, and creates an ephemeral policy-signing key pair,
> provisions its public certificate, and restricts the IPE keyring during
> the early lifetime of a system. The trusted component can then sign the
> IPE policy, destroy the private key, and load and activate the policy.
> During that phase, the system is considered in a trusted state whereas
> the system transitions into a possibly untrusted state afterwards,
> running arbitrary user-provided applications. In such a scenario, the
> trusted userspace component could reside on a dm-verity protected
> rootfs disk, and applications would reside on additional dm-verity
> protected disks. The latter disks would be dynamically attached and
> their dm-verity root hashes and/or signatures not known a priori to
> launching the system.
> 

You might need to explain why the existing keyrings, i.e. builtin/secondary/platform cannot be used in your scenario. Like the firmware is not trusted?

> A concrete usage scenario could be Kata Containers, which enables
> running Kubernetes pods in ephemeral, headless utility VMs, possibly
> without systemd. The rootfs disk is static and known beforehand, but
> containerd's image layers might be acquired and transported into the
> VM using the erofs snapshotter and dynamic block device attachment
> using dm-verity.
> 
> Signed-off-by: Manuel Huber <manuelh@nvidia.com>
> ---
>  Documentation/admin-guide/kernel-parameters.txt |  9 +++
>  security/ipe/Makefile                           |  1 +
>  security/ipe/ipe.c                              |  2 +
>  security/ipe/keyring.c                          | 79 +++++++++++++++++++++++++
>  security/ipe/keyring.h                          | 16 +++++
>  security/ipe/policy.c                           | 10 ++++
>  6 files changed, 117 insertions(+)
> 
> diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
> index e75344f4e0cd..fd508dd779ed 100644
> --- a/Documentation/admin-guide/kernel-parameters.txt
> +++ b/Documentation/admin-guide/kernel-parameters.txt
> @@ -2733,6 +2733,15 @@ Kernel parameters
>  			Determine whether IPE starts in permissive (0) or
>  			enforce (1) mode. The default is enforce.
>  
> +	ipe.keyring_unsealed=
> +			[IPE]
> +			Format: <bool>
> +			When set to 1, leave the IPE policy keyring
> +			unsealed after initialization so userspace can provision
> +			keys. Once the keyring is nonempty and restricted, it is
> +			searched during policy-signature verification. The default
> +			is 0, which creates the keyring restricted and empty.
> +

I would prefer a Kconfig CONFIG_SECURITY_IPE_KEYRING default to N instead, like the fsverity keyring. In the kconfig you can also describe in which cases should this option be turned on. 

>  	ipe.success_audit=
>  			[IPE]
>  			Format: <bool>
> diff --git a/security/ipe/Makefile b/security/ipe/Makefile
> index 2ffabfa63fe9..109e736696a5 100644
> --- a/security/ipe/Makefile
> +++ b/security/ipe/Makefile
> @@ -20,6 +20,7 @@ obj-$(CONFIG_SECURITY_IPE) += \
>  	hooks.o \
>  	fs.o \
>  	ipe.o \
> +	keyring.o \

with Kconfig this can be obj-$(CONFIG_IPE_KEYRING) += keyring.o

>  	policy.o \
>  	policy_fs.o \
>  	policy_parser.o \
> diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
> index 495bb765de1b..f5232f850dc2 100644
> --- a/security/ipe/ipe.c
> +++ b/security/ipe/ipe.c
> @@ -7,6 +7,7 @@
>  #include "ipe.h"
>  #include "eval.h"
>  #include "hooks.h"
> +#include "keyring.h"
>  
>  extern const char *const ipe_boot_policy;
>  bool ipe_enabled;
> @@ -97,4 +98,5 @@ DEFINE_LSM(ipe) = {
>  	.init = ipe_init,
>  	.blobs = &ipe_blobs,
>  	.initcall_fs = ipe_init_securityfs,
> +	.initcall_late = ipe_keyring_init,
>  };
> diff --git a/security/ipe/keyring.c b/security/ipe/keyring.c
> new file mode 100644
> index 000000000000..ea8d164b62a6
> --- /dev/null
> +++ b/security/ipe/keyring.c
> @@ -0,0 +1,79 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
> + */
> +
> +#include <linux/cred.h>
> +#include <linux/err.h>
> +#include <linux/key.h>
> +#include <linux/moduleparam.h>
> +
> +#include "ipe.h"
> +#include "keyring.h"
> +
> +static struct key *ipe_keyring __ro_after_init;

You can remove static here then the other ipe code can directly use the variable instead of a function call. 

> +
> +static bool ipe_keyring_unsealed __ro_after_init;
> +
> +/* Set the right module name for built-in parameters. */
> +#ifdef KBUILD_MODNAME
> +#undef KBUILD_MODNAME
> +#define KBUILD_MODNAME "ipe"
> +#endif
> +
> +module_param_named(keyring_unsealed, ipe_keyring_unsealed, bool, 0444);
> +MODULE_PARM_DESC(keyring_unsealed, "Leave the IPE keyring unsealed");
> +

...

> +
> +/**
> + * ipe_keyring_init() - Allocate and configure the IPE keyring.
> + *
> + * Return: Always %0. A failure leaves IPE without its dedicated keyring.
> + */
> +int __init ipe_keyring_init(void)
> +{
> +	struct key *keyring;
> +	int rc;
> +
> +	keyring = keyring_alloc(".ipe", GLOBAL_ROOT_UID, GLOBAL_ROOT_GID,
> +				current_cred(),
> +				KEY_POS_SEARCH |
> +				KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE |
> +				KEY_USR_SEARCH | KEY_USR_SETATTR,
> +				KEY_ALLOC_NOT_IN_QUOTA, NULL, NULL);
> +	if (IS_ERR(keyring)) {
> +		pr_err("failed to allocate keyring: %ld\n", PTR_ERR(keyring));
> +		return 0;

You might need to panic here like the fsverity kerying.

> +	}
> +
> +	if (!ipe_keyring_unsealed) {
> +		rc = keyring_restrict(make_key_ref(keyring, true), NULL, NULL);
> +		if (rc) {
> +			pr_err("failed to restrict keyring: %d\n", rc);
> +			key_revoke(keyring);
> +			key_put(keyring);
> +			return 0;
> +		}
> +	}

This looks kind of awkward, like if unseal is not set, it can create an empty keyring then immediately seal it. I would switch to the fsverity's approach to let userspace seal it.

> +
> +	ipe_keyring = keyring;
> +
> +	return 0;
> +}
> diff --git a/security/ipe/keyring.h b/security/ipe/keyring.h
> new file mode 100644
> index 000000000000..3d450a465511
> --- /dev/null
> +++ b/security/ipe/keyring.h
> @@ -0,0 +1,16 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
> + */
> +
> +#ifndef _IPE_KEYRING_H
> +#define _IPE_KEYRING_H
> +
> +#include <linux/init.h>
> +
> +struct key;
> +
> +int __init ipe_keyring_init(void);
> +struct key *ipe_keyring_lookup(void);
> +
> +#endif /* _IPE_KEYRING_H */
> diff --git a/security/ipe/policy.c b/security/ipe/policy.c
> index e542329726bc..abc1ff9ac230 100644
> --- a/security/ipe/policy.c
> +++ b/security/ipe/policy.c
> @@ -9,6 +9,7 @@
>  #include "ipe.h"
>  #include "eval.h"
>  #include "fs.h"
> +#include "keyring.h"
>  #include "policy.h"
>  #include "policy_parser.h"
>  #include "audit.h"
> @@ -160,6 +161,7 @@ struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
>  				  const char *pkcs7, size_t pkcs7len)
>  {
>  	struct ipe_policy *new = NULL;
> +	struct key *keyring;
>  	int rc = 0;
>  
>  	new = kzalloc_obj(*new);
> @@ -189,6 +191,14 @@ struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
>  						    VERIFYING_UNSPECIFIED_SIGNATURE,
>  						    set_pkcs7_data, new);
>  #endif
> +		if (rc == -ENOKEY || rc == -EKEYREJECTED) {
> +			keyring = ipe_keyring_lookup();
> +			if (keyring)
> +				rc = verify_pkcs7_signature(NULL, 0, new->pkcs7,
> +							    pkcs7len, keyring,
> +							    VERIFYING_UNSPECIFIED_SIGNATURE,
> +							    set_pkcs7_data, new);
> +		}

With Kconfig this part can be just like the above cases. 

Fan

>  		if (rc)
>  			goto err;
>  	} else {
> 


^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH RFC 2/4] keys: add KEY_SPEC_IPE_KEYRING
  2026-10-19 21:40 ` [PATCH RFC 2/4] keys: add KEY_SPEC_IPE_KEYRING Manuel Huber via B4 Relay
  2026-10-19 21:50   ` sashiko-bot
@ 2026-10-19 23:24   ` Fan Wu
  1 sibling, 0 replies; 14+ messages in thread
From: Fan Wu @ 2026-10-19 23:24 UTC (permalink / raw)
  To: manuelh, David Howells, Jarkko Sakkinen
  Cc: Christian Brauner, Andrew Halaney, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Shuah Khan, Shuah Khan,
	Randy Dunlap, Alexei Starovoitov, Daniel Borkmann,
	linux-security-module, keyrings, linux-kselftest, linux-doc, bpf,
	linux-kernel



On 10/19/2026 2:40 PM, Manuel Huber via B4 Relay wrote:
> From: Manuel Huber <manuelh@nvidia.com>
> 
> The IPE policy keyring is allocated unlinked, so userspace would
> otherwise have to scrape /proc/keys to name it.
> 
> Signed-off-by: Manuel Huber <manuelh@nvidia.com>
> ---

I'm ok with this id addition, but why can't Kata use keyctl to find the keyring by name? e.g. keyctl padd asymmetric "" %:.ipe < cert.der.

If the concern is that /proc/keys can be spoofed, that would mean other processes are already running while the keyring is still unsealed, which seems even less appropriate.

Fan

^ permalink raw reply	[flat|nested] 14+ messages in thread

* Re: [PATCH RFC 4/4] selftests/ipe: test the IPE policy keyring
  2026-10-19 21:40 ` [PATCH RFC 4/4] selftests/ipe: test the IPE policy keyring Manuel Huber via B4 Relay
  2026-10-19 21:57   ` sashiko-bot
@ 2026-10-19 23:28   ` Fan Wu
  1 sibling, 0 replies; 14+ messages in thread
From: Fan Wu @ 2026-10-19 23:28 UTC (permalink / raw)
  To: manuelh, David Howells, Jarkko Sakkinen
  Cc: Christian Brauner, Andrew Halaney, Paul Moore, James Morris,
	Serge E. Hallyn, Jonathan Corbet, Shuah Khan, Shuah Khan,
	Randy Dunlap, Alexei Starovoitov, Daniel Borkmann,
	linux-security-module, keyrings, linux-kselftest, linux-doc, bpf,
	linux-kernel



On 10/19/2026 2:40 PM, Manuel Huber via B4 Relay wrote:
> From: Manuel Huber <manuelh@nvidia.com>
> 
> Add an IPE kselftest that validates policy keyring operations for
> both ipe.keyring_unsealed values 0 and 1.
> 
> For the default ipe.keyring_unsealed=0, verify that a) the special key
> ID resolves, b) certificate enrollment is rejected, and c) the keyring
> is not used for policy verification.
> For ipe.keyring_unsealed=1, verify that we can a) provision multiple
> certificates, b) restrict the keyring, then reject unenrolled and
> corrupted signatures, c) authenticate policy loads and updates with
> different enrolled certificates.
> 
> Since IPE is built in, keyring restriction is irreversible, which is
> why we can only exercise the initially sealed and unsealed scenarios
> via separate boots. To get full coverage, CI thus needs to spin up
> separate environments with respective parameter values.
> 
> Signed-off-by: Manuel Huber <manuelh@nvidia.com>
> ---

IPE doesn't use kselftest. Our tests live in the IPE CI [1], which already has keyring tests for policy signatures [2] and an initrd unit that provisions and seals the .fs-verity keyring [3]. Please add the .ipe cases there instead.
[1] https://github.com/Integrity-Policy-Enforcement/ci
[2] https://github.com/Integrity-Policy-Enforcement/ci/blob/main/suite/cases/policy_signature.py
[3] https://github.com/Integrity-Policy-Enforcement/ci/blob/main/image/initrd/anchor-fsverity.service

Fan


^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2026-10-19 23:28 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-19 21:40 [PATCH RFC 0/4] IPE: add a dedicated policy keyring Manuel Huber via B4 Relay
2026-10-19 21:40 ` [PATCH RFC 1/4] ipe: add the .ipe " Manuel Huber via B4 Relay
2026-10-19 21:47   ` sashiko-bot
2026-10-19 21:52   ` Randy Dunlap
2026-10-19 23:15   ` Fan Wu
2026-10-19 21:40 ` [PATCH RFC 2/4] keys: add KEY_SPEC_IPE_KEYRING Manuel Huber via B4 Relay
2026-10-19 21:50   ` sashiko-bot
2026-10-19 23:24   ` Fan Wu
2026-10-19 21:40 ` [PATCH RFC 3/4] Documentation: ipe: document the IPE keyring Manuel Huber via B4 Relay
2026-10-19 21:45   ` sashiko-bot
2026-10-19 21:40 ` [PATCH RFC 4/4] selftests/ipe: test the IPE policy keyring Manuel Huber via B4 Relay
2026-10-19 21:57   ` sashiko-bot
2026-10-19 23:28   ` Fan Wu
2026-10-19 21:54 ` [PATCH RFC 0/4] IPE: add a dedicated " Jarkko Sakkinen

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox