linux-doc.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/4] Introducing Hornet LSM
@ 2025-05-02 18:44 Blaise Boscaccy
  2025-05-02 18:44 ` [PATCH v3 1/4] security: " Blaise Boscaccy
                   ` (4 more replies)
  0 siblings, 5 replies; 41+ messages in thread
From: Blaise Boscaccy @ 2025-05-02 18:44 UTC (permalink / raw)
  To: Jonathan Corbet, David Howells, Herbert Xu, David S. Miller,
	Paul Moore, James Morris, Serge E. Hallyn, Masahiro Yamada,
	Nathan Chancellor, Nicolas Schier, Shuah Khan,
	Mickaël Salaün, Günther Noack, Nick Desaulniers,
	Bill Wendling, Justin Stitt, Blaise Boscaccy, Jarkko Sakkinen,
	Jan Stancek, Neal Gompa, linux-doc, linux-kernel, keyrings,
	linux-crypto, linux-security-module, linux-kbuild,
	linux-kselftest, bpf, llvm, nkapron, teknoraver, roberto.sassu,
	xiyou.wangcong, Tyler Hicks, James Bottomley

This patch series introduces the Hornet LSM. The goal of Hornet is to
provide a signature verification mechanism for eBPF programs.

eBPF has similar requirements to that of modules when it comes to
loading: find symbol addresses, fix up ELF relocations, some struct
field offset handling stuff called CO-RE (compile-once run-anywhere),
and some other miscellaneous bookkeeping. During eBPF program
compilation, pseudo-values get written to the immediate operands of
instructions. During loading, those pseudo-values get rewritten with
concrete addresses or data applicable to the currently running system,
e.g., a kallsyms address or an fd for a map. This needs to happen
before the instructions for a bpf program are loaded into the kernel
via the bpf() syscall. Unlike modules, an in-kernel loader
unfortunately doesn't exist. Typically, the instruction rewriting is
done dynamically in userspace via libbpf. Since the relocations and
instruction modifications are happening in userspace, and their values
may change depending upon the running system, this breaks known
signature verification mechanisms.

Light skeleton programs were introduced in order to support early
loading of eBPF programs along with user-mode drivers. They utilize a
separate eBPF program that can load a target eBPF program and perform
all necessary relocations in-kernel without needing a working
userspace. Light skeletons were mentioned as a possible path forward
for signature verification.

Hornet takes a simple approach to light-skeleton-based eBPF signature
verification. A PKCS#7 signature of a data buffer containing the raw
instructions of an eBPF program, followed by the initial values of any
maps used by the program is used. A utility script is provided to
parse and extract the contents of autogenerated header files created
via bpftool. That payload can then be signed and appended to the light
skeleton executable.

Maps are checked that they are frozen to prevent TOCTOU bugs where a
sufficiently privileged user could rewrite map data between the calls
to BPF_PROG_LOAD and BPF_PROG_RUN. Additionally, both
sparse-array-based and fd_array_cnt-based map fd arrays are supported
for signature verification.

References:
  [1] https://lore.kernel.org/bpf/20220209054315.73833-1-alexei.starovoitov@gmail.com/
  [2] https://lore.kernel.org/bpf/CAADnVQ+wPK1KKZhCgb-Nnf0Xfjk8M1UpX5fnXC=cBzdEYbv_kg@mail.gmail.com/

Change list:
- v2 -> v3
  - Remove any and all usage of proprietary bpf APIs
  - Add optional systemd/pid1 whitelisting
  - Minor Makefile cleanup
  - Fixed buffer leak
  - Handled null current task
  - Made magic number required
  - Defensive checks against invalid buffer signature reads

- v1 -> v2
  - Jargon clarification, maintainer entry and a few cosmetic fixes

Revisions:
- v1
  https://lore.kernel.org/bpf/20250321164537.16719-1-bboscaccy@linux.microsoft.com
- v2
  https://lore.kernel.org/linux-security-module/20250404215527.1563146-1-bboscaccy@linux.microsoft.com

Blaise Boscaccy (4):
  security: Hornet LSM
  hornet: Introduce sign-ebpf
  hornet: Add a light skeleton data extractor script
  selftests/hornet: Add a selftest for the Hornet LSM

 Documentation/admin-guide/LSM/Hornet.rst      |  65 +++
 Documentation/admin-guide/LSM/index.rst       |   1 +
 MAINTAINERS                                   |   9 +
 crypto/asymmetric_keys/pkcs7_verify.c         |  10 +
 include/linux/kernel_read_file.h              |   1 +
 include/linux/verification.h                  |   1 +
 include/uapi/linux/lsm.h                      |   1 +
 scripts/Makefile                              |   1 +
 scripts/hornet/Makefile                       |   5 +
 scripts/hornet/extract-skel.sh                |  29 ++
 scripts/hornet/sign-ebpf.c                    | 411 ++++++++++++++++++
 security/Kconfig                              |   3 +-
 security/Makefile                             |   1 +
 security/hornet/Kconfig                       |  24 +
 security/hornet/Makefile                      |   4 +
 security/hornet/hornet_lsm.c                  | 250 +++++++++++
 security/selinux/hooks.c                      |  12 +-
 security/selinux/include/classmap.h           |   2 +-
 tools/testing/selftests/Makefile              |   1 +
 tools/testing/selftests/hornet/Makefile       |  58 +++
 tools/testing/selftests/hornet/fail_loader.sh |   3 +
 tools/testing/selftests/hornet/frozen_skel.h  | 393 +++++++++++++++++
 tools/testing/selftests/hornet/loader.c       |  22 +
 tools/testing/selftests/hornet/trivial.bpf.c  |  33 ++
 24 files changed, 1336 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/admin-guide/LSM/Hornet.rst
 create mode 100644 scripts/hornet/Makefile
 create mode 100755 scripts/hornet/extract-skel.sh
 create mode 100644 scripts/hornet/sign-ebpf.c
 create mode 100644 security/hornet/Kconfig
 create mode 100644 security/hornet/Makefile
 create mode 100644 security/hornet/hornet_lsm.c
 create mode 100644 tools/testing/selftests/hornet/Makefile
 create mode 100755 tools/testing/selftests/hornet/fail_loader.sh
 create mode 100644 tools/testing/selftests/hornet/frozen_skel.h
 create mode 100644 tools/testing/selftests/hornet/loader.c
 create mode 100644 tools/testing/selftests/hornet/trivial.bpf.c

-- 
2.48.1


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

* [PATCH v3 1/4] security: Hornet LSM
  2025-05-02 18:44 [PATCH v3 0/4] Introducing Hornet LSM Blaise Boscaccy
@ 2025-05-02 18:44 ` Blaise Boscaccy
  2025-05-04 15:02   ` Paul Moore
  2025-05-02 18:44 ` [PATCH v3 2/4] hornet: Introduce sign-ebpf Blaise Boscaccy
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 41+ messages in thread
From: Blaise Boscaccy @ 2025-05-02 18:44 UTC (permalink / raw)
  To: Jonathan Corbet, David Howells, Herbert Xu, David S. Miller,
	Paul Moore, James Morris, Serge E. Hallyn, Masahiro Yamada,
	Nathan Chancellor, Nicolas Schier, Shuah Khan,
	Mickaël Salaün, Günther Noack, Nick Desaulniers,
	Bill Wendling, Justin Stitt, Blaise Boscaccy, Jarkko Sakkinen,
	Jan Stancek, Neal Gompa, linux-doc, linux-kernel, keyrings,
	linux-crypto, linux-security-module, linux-kbuild,
	linux-kselftest, bpf, llvm, nkapron, teknoraver, roberto.sassu,
	xiyou.wangcong, Tyler Hicks, James Bottomley

This adds the Hornet Linux Security Module which provides signature
verification of eBPF programs. This allows users to continue to
maintain an invariant that all code running inside of the kernel has
been signed.

The primary target for signature verification is light-skeleton based
eBPF programs which was introduced here:
https://lore.kernel.org/bpf/20220209054315.73833-1-alexei.starovoitov@gmail.com/

eBPF programs, before loading, undergo a complex set of operations
which transform pseudo-values within the immediate operands of
instructions into concrete values based on the running
system. Typically, this is done by libbpf in
userspace. Light-skeletons were introduced in order to support
preloading of bpf programs and user-mode-drivers by removing the
dependency on libbpf and userspace-based operations.

Userpace modifications, which may change every time a program gets
loaded or runs on a slightly different kernel, break known signature
verification algorithms. A method is needed for passing unadulterated
binary buffers into the kernel in-order to use existing signature
verification algorithms. Light-skeleton loaders with their support of
only in-kernel relocations fit that constraint.

Hornet employs a signature verification scheme similar to that of
kernel modules. A signature is appended to the end of an
executable file. During an invocation of the BPF_PROG_LOAD subcommand,
a signature is extracted from the current task's executable file. That
signature is used to verify the integrity of the bpf instructions and
maps which were passed into the kernel. Additionally, Hornet
implicitly trusts any programs which were loaded from inside kernel
rather than userspace, which allows BPF_PRELOAD programs along with
outputs for BPF_SYSCALL programs to run.

The validation check consists of checking a PKCS#7 formatted signature
against a data buffer containing the raw instructions of an eBPF
program, followed by the initial values of any maps used by the
program. Maps are verified to be frozen before signature verification
checking to stop TOCTOU attacks.

Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
 Documentation/admin-guide/LSM/Hornet.rst |  65 ++++++
 Documentation/admin-guide/LSM/index.rst  |   1 +
 MAINTAINERS                              |   9 +
 crypto/asymmetric_keys/pkcs7_verify.c    |  10 +
 include/linux/kernel_read_file.h         |   1 +
 include/linux/verification.h             |   1 +
 include/uapi/linux/lsm.h                 |   1 +
 security/Kconfig                         |   3 +-
 security/Makefile                        |   1 +
 security/hornet/Kconfig                  |  24 +++
 security/hornet/Makefile                 |   4 +
 security/hornet/hornet_lsm.c             | 250 +++++++++++++++++++++++
 security/selinux/hooks.c                 |  12 +-
 security/selinux/include/classmap.h      |   2 +-
 14 files changed, 380 insertions(+), 4 deletions(-)
 create mode 100644 Documentation/admin-guide/LSM/Hornet.rst
 create mode 100644 security/hornet/Kconfig
 create mode 100644 security/hornet/Makefile
 create mode 100644 security/hornet/hornet_lsm.c

diff --git a/Documentation/admin-guide/LSM/Hornet.rst b/Documentation/admin-guide/LSM/Hornet.rst
new file mode 100644
index 000000000000..a8109c2a5fdf
--- /dev/null
+++ b/Documentation/admin-guide/LSM/Hornet.rst
@@ -0,0 +1,65 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======
+Hornet
+======
+
+Hornet is a Linux Security Module that provides signature verification
+for eBPF programs. This is selectable at build-time with
+``CONFIG_SECURITY_HORNET``.
+
+Overview
+========
+
+Hornet provides signature verification for eBPF programs by utilizing
+the existing PKCS#7 infrastructure that's used for module signature
+verification. Hornet works by creating a buffer containing the eBPF
+program instructions along with its associated maps and checking a
+signature against that buffer. The signature is appended to the end of
+the lskel executable file and is extracted at runtime via
+get_task_exe_file. Hornet works by hooking into the
+security_bpf_prog_load hook. Load invocations that originate from the
+kernel (bpf preload, results of bpf_syscall programs, etc.) are
+allowed to run unconditionally. Calls that originate from userspace
+require signature verification. If signature verification fails, the
+program will fail to load.  Maps are verified to be frozen before the
+signature check to prevent TOCTOU exploits where a sufficiently
+privileged user could rewrite map data between the calls to
+BPF_PROG_LOAD and BPF_PROG_RUN.
+
+Instruction/Map Ordering
+========================
+
+Hornet supports both sparse-array based maps via map discovery along
+with the newly added fd_array_cnt API for continuous map arrays. The
+buffer used for signature verification is assumed to be the
+instructions followed by all maps used, ordered by their index in
+fd_array.
+
+Configuration Options
+=====================
+
+Hornet provides a kconfig knob
+CONFIG_SECURITY_HORNET_WHITELIST_PID_ONE.  Enabling this will allow
+bpf programs to be loaded from pid 1 without undergoing a signature
+verification check. This option is not recommened for production
+systems.
+
+Tooling
+=======
+
+Some tooling is provided to aid with the development of signed eBPF
+light-skeletons.
+
+extract-skel.sh
+---------------
+
+This shell script extracts the instructions and map data used by the
+light skeleton from the autogenerated header file created by bpftool.
+
+sign-ebpf
+---------
+
+sign-ebpf works similarly to the sign-file script with one key
+difference: it takes a separate input binary used for signature
+verification and will append the signature to a different output file.
diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
index b44ef68f6e4d..57f6e9fbe5fd 100644
--- a/Documentation/admin-guide/LSM/index.rst
+++ b/Documentation/admin-guide/LSM/index.rst
@@ -49,3 +49,4 @@ subdirectories.
    SafeSetID
    ipe
    landlock
+   Hornet
diff --git a/MAINTAINERS b/MAINTAINERS
index 3cbf9ac0d83f..f4123ed2fb1c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10855,6 +10855,15 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/iio/pressure/honeywell,mprls0025pa.yaml
 F:	drivers/iio/pressure/mprls0025pa*
 
+HORNET SECURITY MODULE
+M:	Blaise Boscaccy <bboscaccy@linux.microsoft.com>
+L:	linux-security-module@vger.kernel.org
+S:	Supported
+T:	git https://github.com/blaiseboscaccy/hornet.git
+F:	Documentation/admin-guide/LSM/Hornet.rst
+F:	scripts/hornet/
+F:	security/hornet/
+
 HP BIOSCFG DRIVER
 M:	Jorge Lopez <jorge.lopez2@hp.com>
 L:	platform-driver-x86@vger.kernel.org
diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c
index f0d4ff3c20a8..1a5fbb361218 100644
--- a/crypto/asymmetric_keys/pkcs7_verify.c
+++ b/crypto/asymmetric_keys/pkcs7_verify.c
@@ -428,6 +428,16 @@ int pkcs7_verify(struct pkcs7_message *pkcs7,
 		}
 		/* Authattr presence checked in parser */
 		break;
+	case VERIFYING_EBPF_SIGNATURE:
+		if (pkcs7->data_type != OID_data) {
+			pr_warn("Invalid ebpf sig (not pkcs7-data)\n");
+			return -EKEYREJECTED;
+		}
+		if (pkcs7->have_authattrs) {
+			pr_warn("Invalid ebpf sig (has authattrs)\n");
+			return -EKEYREJECTED;
+		}
+		break;
 	case VERIFYING_UNSPECIFIED_SIGNATURE:
 		if (pkcs7->data_type != OID_data) {
 			pr_warn("Invalid unspecified sig (not pkcs7-data)\n");
diff --git a/include/linux/kernel_read_file.h b/include/linux/kernel_read_file.h
index 90451e2e12bd..7ed9337be542 100644
--- a/include/linux/kernel_read_file.h
+++ b/include/linux/kernel_read_file.h
@@ -14,6 +14,7 @@
 	id(KEXEC_INITRAMFS, kexec-initramfs)	\
 	id(POLICY, security-policy)		\
 	id(X509_CERTIFICATE, x509-certificate)	\
+	id(EBPF, ebpf)				\
 	id(MAX_ID, )
 
 #define __fid_enumify(ENUM, dummy) READING_ ## ENUM,
diff --git a/include/linux/verification.h b/include/linux/verification.h
index 4f3022d081c3..812be8ad5f74 100644
--- a/include/linux/verification.h
+++ b/include/linux/verification.h
@@ -35,6 +35,7 @@ enum key_being_used_for {
 	VERIFYING_KEXEC_PE_SIGNATURE,
 	VERIFYING_KEY_SIGNATURE,
 	VERIFYING_KEY_SELF_SIGNATURE,
+	VERIFYING_EBPF_SIGNATURE,
 	VERIFYING_UNSPECIFIED_SIGNATURE,
 	NR__KEY_BEING_USED_FOR
 };
diff --git a/include/uapi/linux/lsm.h b/include/uapi/linux/lsm.h
index 938593dfd5da..2ff9bcdd551e 100644
--- a/include/uapi/linux/lsm.h
+++ b/include/uapi/linux/lsm.h
@@ -65,6 +65,7 @@ struct lsm_ctx {
 #define LSM_ID_IMA		111
 #define LSM_ID_EVM		112
 #define LSM_ID_IPE		113
+#define LSM_ID_HORNET		114
 
 /*
  * LSM_ATTR_XXX definitions identify different LSM attributes
diff --git a/security/Kconfig b/security/Kconfig
index 4816fc74f81e..5cd47bbff765 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -230,6 +230,7 @@ source "security/safesetid/Kconfig"
 source "security/lockdown/Kconfig"
 source "security/landlock/Kconfig"
 source "security/ipe/Kconfig"
+source "security/hornet/Kconfig"
 
 source "security/integrity/Kconfig"
 
@@ -273,7 +274,7 @@ config LSM
 	default "landlock,lockdown,yama,loadpin,safesetid,apparmor,selinux,smack,tomoyo,ipe,bpf" if DEFAULT_SECURITY_APPARMOR
 	default "landlock,lockdown,yama,loadpin,safesetid,tomoyo,ipe,bpf" if DEFAULT_SECURITY_TOMOYO
 	default "landlock,lockdown,yama,loadpin,safesetid,ipe,bpf" if DEFAULT_SECURITY_DAC
-	default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,bpf"
+	default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,ipe,hornet,bpf"
 	help
 	  A comma-separated list of LSMs, in initialization order.
 	  Any LSMs left off this list, except for those with order
diff --git a/security/Makefile b/security/Makefile
index 22ff4c8bd8ce..e24bccd951f8 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_CGROUPS)			+= device_cgroup.o
 obj-$(CONFIG_BPF_LSM)			+= bpf/
 obj-$(CONFIG_SECURITY_LANDLOCK)		+= landlock/
 obj-$(CONFIG_SECURITY_IPE)		+= ipe/
+obj-$(CONFIG_SECURITY_HORNET)		+= hornet/
 
 # Object integrity file lists
 obj-$(CONFIG_INTEGRITY)			+= integrity/
diff --git a/security/hornet/Kconfig b/security/hornet/Kconfig
new file mode 100644
index 000000000000..539503dafe94
--- /dev/null
+++ b/security/hornet/Kconfig
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config SECURITY_HORNET
+	bool "Hornet support"
+	depends on SECURITY
+	default n
+	help
+	  This selects Hornet.
+	  Further information can be found in
+	  Documentation/admin-guide/LSM/Hornet.rst.
+
+	  If you are unsure how to answer this question, answer N.
+
+
+config SECURITY_HORNET_WHITELIST_PID_ONE
+	bool "Whiltelist unsigned eBPF programs from PID 1"
+	depends on SECURITY_HORNET
+	default n
+	help
+	  Selecting this will configure Hornet to allow eBPF loaded from pid 1
+	  to load without a verification check.
+	  Further information can be found in
+	  Documentation/admin-guide/LSM/Hornet.rst.
+
+	  If you are unsure how to answer this question, answer N.
diff --git a/security/hornet/Makefile b/security/hornet/Makefile
new file mode 100644
index 000000000000..79f4657b215f
--- /dev/null
+++ b/security/hornet/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_SECURITY_HORNET) := hornet.o
+
+hornet-y := hornet_lsm.o
diff --git a/security/hornet/hornet_lsm.c b/security/hornet/hornet_lsm.c
new file mode 100644
index 000000000000..012bb2dc45b2
--- /dev/null
+++ b/security/hornet/hornet_lsm.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Hornet Linux Security Module
+ *
+ * Author: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
+ *
+ * Copyright (C) 2025 Microsoft Corporation
+ */
+
+#include <linux/lsm_hooks.h>
+#include <uapi/linux/lsm.h>
+#include <linux/bpf.h>
+#include <linux/verification.h>
+#include <crypto/public_key.h>
+#include <linux/module_signature.h>
+#include <crypto/pkcs7.h>
+#include <linux/sort.h>
+
+#define EBPF_SIG_STRING "~eBPF signature appended~\n"
+#define MAX_USED_MAPS 64
+
+struct hornet_maps {
+	u32 used_idx[MAX_USED_MAPS];
+	u32 used_map_cnt;
+	bpfptr_t fd_array;
+};
+
+static int cmp_idx(const void *a, const void *b)
+{
+	return *(const u32 *)a - *(const u32 *)b;
+}
+
+static int add_used_map(struct hornet_maps *maps, int idx)
+{
+	int i;
+
+	for (i = 0; i < maps->used_map_cnt; i++)
+		if (maps->used_idx[i] == idx)
+			return i;
+
+	if (maps->used_map_cnt >= MAX_USED_MAPS)
+		return -E2BIG;
+
+	maps->used_idx[maps->used_map_cnt] = idx;
+	return maps->used_map_cnt++;
+}
+
+static int hornet_find_maps(struct bpf_prog *prog, struct hornet_maps *maps)
+{
+	struct bpf_insn *insn = prog->insnsi;
+	int insn_cnt = prog->len;
+	int i;
+	int err;
+
+	for (i = 0; i < insn_cnt; i++, insn++) {
+		if (insn[0].code == (BPF_LD | BPF_IMM | BPF_DW)) {
+			switch (insn[0].src_reg) {
+			case BPF_PSEUDO_MAP_IDX_VALUE:
+			case BPF_PSEUDO_MAP_IDX:
+				err = add_used_map(maps, insn[0].imm);
+				if (err < 0)
+					return err;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+	/* Sort the spare-array indices. This should match the map ordering used during
+	 * signature generation
+	 */
+	sort(maps->used_idx, maps->used_map_cnt, sizeof(*maps->used_idx),
+	     cmp_idx, NULL);
+
+	return 0;
+}
+
+static int hornet_populate_fd_array(struct hornet_maps *maps, u32 fd_array_cnt)
+{
+	int i;
+
+	if (fd_array_cnt > MAX_USED_MAPS)
+		return -E2BIG;
+
+	for (i = 0; i < fd_array_cnt; i++)
+		maps->used_idx[i] = i;
+
+	maps->used_map_cnt = fd_array_cnt;
+	return 0;
+}
+
+static int hornet_verify_lskel(struct bpf_prog *prog, struct hornet_maps *maps,
+			       void *sig, size_t sig_len)
+{
+	int map_fd;
+	u32 i;
+	void *buf;
+	void *new;
+	size_t buf_sz;
+	struct bpf_map *map;
+	int err = 0;
+	int key = 0;
+
+	buf = kmalloc_array(prog->len, sizeof(struct bpf_insn), GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+	buf_sz = prog->len * sizeof(struct bpf_insn);
+	memcpy(buf, prog->insnsi, buf_sz);
+
+	for (i = 0; i < maps->used_map_cnt; i++) {
+		err = copy_from_bpfptr_offset(&map_fd, maps->fd_array,
+					      maps->used_idx[i] * sizeof(map_fd),
+					      sizeof(map_fd));
+		if (err < 0)
+			continue;
+
+		CLASS(fd, f)(map_fd);
+		if (fd_empty(f))
+			continue;
+		if (unlikely(fd_file(f)->f_op != &bpf_map_fops))
+			continue;
+		map = fd_file(f)->private_data;
+
+		if (!map->frozen) {
+			err = -EINVAL;
+			goto out;
+		}
+
+		new = krealloc(buf, buf_sz + map->value_size, GFP_KERNEL);
+		if (!new) {
+			err = -ENOMEM;
+			goto out;
+		}
+		buf = new;
+		new = map->ops->map_lookup_elem(map, &key);
+		if (!new) {
+			err = -ENOENT;
+			goto out;
+		}
+		memcpy(buf + buf_sz, new, map->value_size);
+		buf_sz += map->value_size;
+	}
+
+	err = verify_pkcs7_signature(buf, buf_sz, sig, sig_len,
+				     VERIFY_USE_SECONDARY_KEYRING,
+				     VERIFYING_EBPF_SIGNATURE,
+				     NULL, NULL);
+out:
+	kfree(buf);
+	return err;
+}
+
+static int hornet_check_binary(struct bpf_prog *prog, union bpf_attr *attr,
+			       struct hornet_maps *maps)
+{
+	struct file *file;
+	const unsigned long markerlen = sizeof(EBPF_SIG_STRING) - 1;
+	void *buf = NULL;
+	size_t sz = 0, sig_len, prog_len, buf_sz;
+	int err = 0;
+	struct module_signature sig;
+
+	file = get_task_exe_file(current);
+	if (!file)
+		return -1;
+
+	buf_sz = kernel_read_file(file, 0, &buf, INT_MAX, &sz, READING_EBPF);
+	fput(file);
+	if (!buf_sz)
+		return -1;
+
+	prog_len = buf_sz;
+
+	if (prog_len > markerlen &&
+	    memcmp(buf + prog_len - markerlen, EBPF_SIG_STRING, markerlen) == 0) {
+		/* We truncate the program to discard the signature */
+		prog_len -= markerlen;
+		if (prog_len < sizeof(sig)) {
+			err = -EINVAL;
+			goto out;
+		}
+
+		memcpy(&sig, buf + (prog_len - sizeof(sig)), sizeof(sig));
+		sig_len = be32_to_cpu(sig.sig_len);
+		prog_len -= sig_len + sizeof(sig);
+
+		err = mod_check_sig(&sig, prog->len * sizeof(struct bpf_insn), "ebpf");
+		if (err)
+			goto out;
+
+		err = hornet_verify_lskel(prog, maps, buf + prog_len, sig_len);
+	} else {
+		err = -EINVAL;
+	}
+out:
+	kvfree(buf);
+	return err;
+}
+
+static int hornet_check_signature(struct bpf_prog *prog, union bpf_attr *attr,
+				  struct bpf_token *token)
+{
+	struct hornet_maps maps = {0};
+	int err;
+
+	/* support both sparse arrays and explicit continuous arrays of map fds */
+	if (attr->fd_array_cnt)
+		err = hornet_populate_fd_array(&maps, attr->fd_array_cnt);
+	else
+		err = hornet_find_maps(prog, &maps);
+
+	if (err < 0)
+		return err;
+
+	maps.fd_array = make_bpfptr(attr->fd_array, false);
+	return hornet_check_binary(prog, attr, &maps);
+}
+
+static int hornet_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
+				struct bpf_token *token, bool is_kernel)
+{
+	if (is_kernel)
+		return 0;
+#ifdef CONFIG_SECURITY_HORNET_WHITELIST_PID_ONE
+	if (current->pid == 1)
+		return 0;
+#endif
+	return hornet_check_signature(prog, attr, token);
+}
+
+static struct security_hook_list hornet_hooks[] __ro_after_init = {
+	LSM_HOOK_INIT(bpf_prog_load, hornet_bpf_prog_load),
+};
+
+static const struct lsm_id hornet_lsmid = {
+	.name = "hornet",
+	.id = LSM_ID_HORNET,
+};
+
+static int __init hornet_init(void)
+{
+	pr_info("Hornet: eBPF signature verification enabled\n");
+	security_add_hooks(hornet_hooks, ARRAY_SIZE(hornet_hooks), &hornet_lsmid);
+	return 0;
+}
+
+DEFINE_LSM(hornet) = {
+	.name = "hornet",
+	.init = hornet_init,
+};
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index e7a7dcab81db..901fa6574083 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -4133,7 +4133,7 @@ static int selinux_kernel_read_file(struct file *file,
 {
 	int rc = 0;
 
-	BUILD_BUG_ON_MSG(READING_MAX_ID > 7,
+	BUILD_BUG_ON_MSG(READING_MAX_ID > 8,
 			 "New kernel_read_file_id introduced; update SELinux!");
 
 	switch (id) {
@@ -4158,6 +4158,10 @@ static int selinux_kernel_read_file(struct file *file,
 		rc = selinux_kernel_load_from_file(file,
 						SYSTEM__X509_CERTIFICATE_LOAD);
 		break;
+	case READING_EBPF:
+		rc = selinux_kernel_load_from_file(file,
+						   SYSTEM__EBPF_LOAD);
+		break;
 	default:
 		break;
 	}
@@ -4169,7 +4173,7 @@ static int selinux_kernel_load_data(enum kernel_load_data_id id, bool contents)
 {
 	int rc = 0;
 
-	BUILD_BUG_ON_MSG(LOADING_MAX_ID > 7,
+	BUILD_BUG_ON_MSG(LOADING_MAX_ID > 8,
 			 "New kernel_load_data_id introduced; update SELinux!");
 
 	switch (id) {
@@ -4195,6 +4199,10 @@ static int selinux_kernel_load_data(enum kernel_load_data_id id, bool contents)
 		rc = selinux_kernel_load_from_file(NULL,
 						SYSTEM__X509_CERTIFICATE_LOAD);
 		break;
+	case LOADING_EBPF:
+		rc = selinux_kernel_load_from_file(NULL,
+						   SYSTEM__EBPF_LOAD);
+		break;
 	default:
 		break;
 	}
diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h
index 04a9b480885e..671db23451df 100644
--- a/security/selinux/include/classmap.h
+++ b/security/selinux/include/classmap.h
@@ -65,7 +65,7 @@ const struct security_class_mapping secclass_map[] = {
 	  { "ipc_info", "syslog_read", "syslog_mod", "syslog_console",
 	    "module_request", "module_load", "firmware_load",
 	    "kexec_image_load", "kexec_initramfs_load", "policy_load",
-	    "x509_certificate_load", NULL } },
+	    "x509_certificate_load", "ebpf_load", NULL } },
 	{ "capability", { COMMON_CAP_PERMS, NULL } },
 	{ "filesystem",
 	  { "mount", "remount", "unmount", "getattr", "relabelfrom",
-- 
2.48.1


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

* [PATCH v3 2/4] hornet: Introduce sign-ebpf
  2025-05-02 18:44 [PATCH v3 0/4] Introducing Hornet LSM Blaise Boscaccy
  2025-05-02 18:44 ` [PATCH v3 1/4] security: " Blaise Boscaccy
@ 2025-05-02 18:44 ` Blaise Boscaccy
  2025-05-02 18:44 ` [PATCH v3 3/4] hornet: Add a light skeleton data extractor script Blaise Boscaccy
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 41+ messages in thread
From: Blaise Boscaccy @ 2025-05-02 18:44 UTC (permalink / raw)
  To: Jonathan Corbet, David Howells, Herbert Xu, David S. Miller,
	Paul Moore, James Morris, Serge E. Hallyn, Masahiro Yamada,
	Nathan Chancellor, Nicolas Schier, Shuah Khan,
	Mickaël Salaün, Günther Noack, Nick Desaulniers,
	Bill Wendling, Justin Stitt, Blaise Boscaccy, Jarkko Sakkinen,
	Jan Stancek, Neal Gompa, linux-doc, linux-kernel, keyrings,
	linux-crypto, linux-security-module, linux-kbuild,
	linux-kselftest, bpf, llvm, nkapron, teknoraver, roberto.sassu,
	xiyou.wangcong, Tyler Hicks, James Bottomley

This introduces the sign-ebpf tool. It is very similar to the existing
sign-file script, with one key difference, it will sign a file with
with a signature computed off of arbitrary input data. This can used
to sign an ebpf light skeleton loader program for verification via
Hornet.

Typical usage is to provide a payload containing the light skeleton
ebpf syscall program binary and it's associated maps, which can be
extracted from the auto-generated skeleton header.

Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
 scripts/Makefile           |   1 +
 scripts/hornet/Makefile    |   5 +
 scripts/hornet/sign-ebpf.c | 411 +++++++++++++++++++++++++++++++++++++
 3 files changed, 417 insertions(+)
 create mode 100644 scripts/hornet/Makefile
 create mode 100644 scripts/hornet/sign-ebpf.c

diff --git a/scripts/Makefile b/scripts/Makefile
index 46f860529df5..a2cace05d734 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -57,6 +57,7 @@ subdir-$(CONFIG_GENKSYMS) += genksyms
 subdir-$(CONFIG_GENDWARFKSYMS) += gendwarfksyms
 subdir-$(CONFIG_SECURITY_SELINUX) += selinux
 subdir-$(CONFIG_SECURITY_IPE) += ipe
+subdir-$(CONFIG_SECURITY_HORNET) += hornet
 
 # Let clean descend into subdirs
 subdir-	+= basic dtc gdb kconfig mod
diff --git a/scripts/hornet/Makefile b/scripts/hornet/Makefile
new file mode 100644
index 000000000000..ab71dbb8688e
--- /dev/null
+++ b/scripts/hornet/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0
+hostprogs-always-y	:= sign-ebpf
+
+HOSTCFLAGS_sign-ebpf.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null)
+HOSTLDLIBS_sign-ebpf = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
diff --git a/scripts/hornet/sign-ebpf.c b/scripts/hornet/sign-ebpf.c
new file mode 100644
index 000000000000..e9d2cb6d5cfb
--- /dev/null
+++ b/scripts/hornet/sign-ebpf.c
@@ -0,0 +1,411 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Sign ebpf programs and skeletons using the given key.
+ *
+ * This program is heavily based on the kernel's sign-file tool
+ * with some minor additions to support the signing of eBPF lskels.
+ *
+ * Copyright © 2014-2016 Red Hat, Inc. All Rights Reserved.
+ * Copyright © 2015      Intel Corporation.
+ * Copyright © 2016      Hewlett Packard Enterprise Development LP
+ * Copyright © 2025      Microsoft Corporation.
+ */
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <string.h>
+#include <getopt.h>
+#include <err.h>
+#include <arpa/inet.h>
+#include <openssl/opensslv.h>
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#if OPENSSL_VERSION_MAJOR >= 3
+# define USE_PKCS11_PROVIDER
+# include <openssl/provider.h>
+# include <openssl/store.h>
+#else
+# if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+#  define USE_PKCS11_ENGINE
+#  include <openssl/engine.h>
+# endif
+#endif
+#include "../ssl-common.h"
+
+/*
+ * Use CMS if we have openssl-1.0.0 or newer available - otherwise we have to
+ * assume that it's not available and its header file is missing and that we
+ * should use PKCS#7 instead.  Switching to the older PKCS#7 format restricts
+ * the options we have on specifying the X.509 certificate we want.
+ *
+ * Further, older versions of OpenSSL don't support manually adding signers to
+ * the PKCS#7 message so have to accept that we get a certificate included in
+ * the signature message.  Nor do such older versions of OpenSSL support
+ * signing with anything other than SHA1 - so we're stuck with that if such is
+ * the case.
+ */
+#if defined(LIBRESSL_VERSION_NUMBER) || \
+	OPENSSL_VERSION_NUMBER < 0x10000000L || \
+	defined(OPENSSL_NO_CMS)
+#define USE_PKCS7
+#endif
+#ifndef USE_PKCS7
+#include <openssl/cms.h>
+#else
+#include <openssl/pkcs7.h>
+#endif
+
+struct module_signature {
+	uint8_t		algo;		/* Public-key crypto algorithm [0] */
+	uint8_t		hash;		/* Digest algorithm [0] */
+	uint8_t		id_type;	/* Key identifier type [PKEY_ID_PKCS7] */
+	uint8_t		signer_len;	/* Length of signer's name [0] */
+	uint8_t		key_id_len;	/* Length of key identifier [0] */
+	uint8_t		__pad[3];
+	uint32_t	sig_len;	/* Length of signature data */
+};
+
+#define PKEY_ID_PKCS7 2
+
+static char magic_number[] = "~eBPF signature appended~\n";
+
+static __attribute__((noreturn))
+void format(void)
+{
+	fprintf(stderr,
+		"Usage: scripts/sign-ebpf [-dp] <hash algo> <key> <x509> <bin> <loader> [<dest>]\n");
+	exit(2);
+}
+
+static const char *key_pass;
+
+static int pem_pw_cb(char *buf, int len, int w, void *v)
+{
+	int pwlen;
+
+	if (!key_pass)
+		return -1;
+
+	pwlen = strlen(key_pass);
+	if (pwlen >= len)
+		return -1;
+
+	strcpy(buf, key_pass);
+
+	/* If it's wrong, don't keep trying it. */
+	key_pass = NULL;
+
+	return pwlen;
+}
+
+static EVP_PKEY *read_private_key_pkcs11(const char *private_key_name)
+{
+	EVP_PKEY *private_key = NULL;
+#ifdef USE_PKCS11_PROVIDER
+	OSSL_STORE_CTX *store;
+
+	if (!OSSL_PROVIDER_try_load(NULL, "pkcs11", true))
+		ERR(1, "OSSL_PROVIDER_try_load(pkcs11)");
+	if (!OSSL_PROVIDER_try_load(NULL, "default", true))
+		ERR(1, "OSSL_PROVIDER_try_load(default)");
+
+	store = OSSL_STORE_open(private_key_name, NULL, NULL, NULL, NULL);
+	ERR(!store, "OSSL_STORE_open");
+
+	while (!OSSL_STORE_eof(store)) {
+		OSSL_STORE_INFO *info = OSSL_STORE_load(store);
+
+		if (!info) {
+			drain_openssl_errors(__LINE__, 0);
+			continue;
+		}
+		if (OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PKEY) {
+			private_key = OSSL_STORE_INFO_get1_PKEY(info);
+			ERR(!private_key, "OSSL_STORE_INFO_get1_PKEY");
+		}
+		OSSL_STORE_INFO_free(info);
+		if (private_key)
+			break;
+	}
+	OSSL_STORE_close(store);
+#elif defined(USE_PKCS11_ENGINE)
+	ENGINE *e;
+
+	ENGINE_load_builtin_engines();
+	drain_openssl_errors(__LINE__, 1);
+	e = ENGINE_by_id("pkcs11");
+	ERR(!e, "Load PKCS#11 ENGINE");
+	if (ENGINE_init(e))
+		drain_openssl_errors(__LINE__, 1);
+	else
+		ERR(1, "ENGINE_init");
+	if (key_pass)
+		ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0), "Set PKCS#11 PIN");
+	private_key = ENGINE_load_private_key(e, private_key_name, NULL, NULL);
+	ERR(!private_key, "%s", private_key_name);
+#else
+	fprintf(stderr, "no pkcs11 engine/provider available\n");
+	exit(1);
+#endif
+	return private_key;
+}
+
+static EVP_PKEY *read_private_key(const char *private_key_name)
+{
+	if (!strncmp(private_key_name, "pkcs11:", 7)) {
+		return read_private_key_pkcs11(private_key_name);
+	} else {
+		EVP_PKEY *private_key;
+		BIO *b;
+
+		b = BIO_new_file(private_key_name, "rb");
+		ERR(!b, "%s", private_key_name);
+		private_key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb,
+						      NULL);
+		ERR(!private_key, "%s", private_key_name);
+		BIO_free(b);
+
+		return private_key;
+	}
+}
+
+static X509 *read_x509(const char *x509_name)
+{
+	unsigned char buf[2];
+	X509 *x509;
+	BIO *b;
+	int n;
+
+	b = BIO_new_file(x509_name, "rb");
+	ERR(!b, "%s", x509_name);
+
+	/* Look at the first two bytes of the file to determine the encoding */
+	n = BIO_read(b, buf, 2);
+	if (n != 2) {
+		if (BIO_should_retry(b)) {
+			fprintf(stderr, "%s: Read wanted retry\n", x509_name);
+			exit(1);
+		}
+		if (n >= 0) {
+			fprintf(stderr, "%s: Short read\n", x509_name);
+			exit(1);
+		}
+		ERR(1, "%s", x509_name);
+	}
+
+	ERR(BIO_reset(b) != 0, "%s", x509_name);
+
+	if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
+		/* Assume raw DER encoded X.509 */
+		x509 = d2i_X509_bio(b, NULL);
+	else
+		/* Assume PEM encoded X.509 */
+		x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
+
+	BIO_free(b);
+	ERR(!x509, "%s", x509_name);
+
+	return x509;
+}
+
+int main(int argc, char **argv)
+{
+	struct module_signature sig_info = { .id_type = PKEY_ID_PKCS7 };
+	char *hash_algo = NULL;
+	char *private_key_name = NULL, *raw_sig_name = NULL;
+	char *x509_name, *bin_name, *loader_name, *dest_name;
+	bool save_sig = false, replace_orig;
+	bool sign_only = false;
+	bool raw_sig = false;
+	unsigned char buf[4096];
+	unsigned long loader_size, sig_size;
+	unsigned int use_signed_attrs;
+	const EVP_MD *digest_algo;
+	EVP_PKEY *private_key;
+#ifndef USE_PKCS7
+	CMS_ContentInfo *cms = NULL;
+	unsigned int use_keyid = 0;
+#else
+	PKCS7 *pkcs7 = NULL;
+#endif
+	X509 *x509;
+	BIO *bd, *bm, *bl;
+	int opt, n;
+	OpenSSL_add_all_algorithms();
+	ERR_load_crypto_strings();
+	ERR_clear_error();
+
+	key_pass = getenv("KBUILD_SIGN_PIN");
+
+#ifndef USE_PKCS7
+	use_signed_attrs = CMS_NOATTR;
+#else
+	use_signed_attrs = PKCS7_NOATTR;
+#endif
+
+	do {
+		opt = getopt(argc, argv, "sdpk");
+		switch (opt) {
+		case 's': raw_sig = true; break;
+		case 'p': save_sig = true; break;
+		case 'd': sign_only = true; save_sig = true; break;
+#ifndef USE_PKCS7
+		case 'k': use_keyid = CMS_USE_KEYID; break;
+#endif
+		case -1: break;
+		default: format();
+		}
+	} while (opt != -1);
+
+	argc -= optind;
+	argv += optind;
+	if (argc < 5 || argc > 6)
+		format();
+
+	if (raw_sig) {
+		raw_sig_name = argv[0];
+		hash_algo = argv[1];
+	} else {
+		hash_algo = argv[0];
+		private_key_name = argv[1];
+	}
+	x509_name = argv[2];
+	bin_name = argv[3];
+	loader_name = argv[4];
+	if (argc == 6 && strcmp(argv[4], argv[5]) != 0) {
+		dest_name = argv[5];
+		replace_orig = false;
+	} else {
+		ERR(asprintf(&dest_name, "%s.~signed~", loader_name) < 0,
+		    "asprintf");
+		replace_orig = true;
+	}
+
+#ifdef USE_PKCS7
+	if (strcmp(hash_algo, "sha1") != 0) {
+		fprintf(stderr, "sign-file: %s only supports SHA1 signing\n",
+			OPENSSL_VERSION_TEXT);
+		exit(3);
+	}
+#endif
+
+	/* Open the bin file */
+	bm = BIO_new_file(bin_name, "rb");
+	ERR(!bm, "%s", bin_name);
+
+	if (!raw_sig) {
+		/* Read the private key and the X.509 cert the PKCS#7 message
+		 * will point to.
+		 */
+		private_key = read_private_key(private_key_name);
+		x509 = read_x509(x509_name);
+
+		/* Digest the module data. */
+		OpenSSL_add_all_digests();
+		drain_openssl_errors(__LINE__, 0);
+		digest_algo = EVP_get_digestbyname(hash_algo);
+		ERR(!digest_algo, "EVP_get_digestbyname");
+
+#ifndef USE_PKCS7
+		/* Load the signature message from the digest buffer. */
+		cms = CMS_sign(NULL, NULL, NULL, NULL,
+			       CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY |
+			       CMS_DETACHED | CMS_STREAM);
+		ERR(!cms, "CMS_sign");
+
+		ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo,
+				     CMS_NOCERTS | CMS_BINARY |
+				     CMS_NOSMIMECAP | use_keyid |
+				     use_signed_attrs),
+		    "CMS_add1_signer");
+		ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) != 1,
+		    "CMS_final");
+
+#else
+		pkcs7 = PKCS7_sign(x509, private_key, NULL, bm,
+				   PKCS7_NOCERTS | PKCS7_BINARY |
+				   PKCS7_DETACHED | use_signed_attrs);
+		ERR(!pkcs7, "PKCS7_sign");
+#endif
+
+		if (save_sig) {
+			char *sig_file_name;
+			BIO *b;
+
+			ERR(asprintf(&sig_file_name, "%s.p7s", bin_name) < 0,
+			    "asprintf");
+			b = BIO_new_file(sig_file_name, "wb");
+			ERR(!b, "%s", sig_file_name);
+#ifndef USE_PKCS7
+			ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) != 1,
+			    "%s", sig_file_name);
+#else
+			ERR(i2d_PKCS7_bio(b, pkcs7) != 1,
+			    "%s", sig_file_name);
+#endif
+			BIO_free(b);
+		}
+
+		if (sign_only) {
+			BIO_free(bm);
+			return 0;
+		}
+	}
+
+	/* Open the destination file now so that we can shovel the loader data
+	 * across as we read it.
+	 */
+	bd = BIO_new_file(dest_name, "wb");
+	ERR(!bd, "%s", dest_name);
+
+	bl = BIO_new_file(loader_name, "rb");
+	ERR(!bl, "%s", loader_name);
+
+
+	/* Append the marker and the PKCS#7 message to the destination file */
+	ERR(BIO_reset(bm) < 0, "%s", bin_name);
+	ERR(BIO_reset(bl) < 0, "%s", loader_name);
+	while ((n = BIO_read(bl, buf, sizeof(buf))),
+	       n > 0) {
+		ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name);
+	}
+	BIO_free(bl);
+	BIO_free(bm);
+	ERR(n < 0, "%s", loader_name);
+	loader_size = BIO_number_written(bd);
+
+	if (!raw_sig) {
+#ifndef USE_PKCS7
+		ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) != 1, "%s", dest_name);
+#else
+		ERR(i2d_PKCS7_bio(bd, pkcs7) != 1, "%s", dest_name);
+#endif
+	} else {
+		BIO *b;
+
+		/* Read the raw signature file and write the data to the
+		 * destination file
+		 */
+		b = BIO_new_file(raw_sig_name, "rb");
+		ERR(!b, "%s", raw_sig_name);
+		while ((n = BIO_read(b, buf, sizeof(buf))), n > 0)
+			ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name);
+		BIO_free(b);
+	}
+
+	sig_size = BIO_number_written(bd) - loader_size;
+	sig_info.sig_len = htonl(sig_size);
+	ERR(BIO_write(bd, &sig_info, sizeof(sig_info)) < 0, "%s", dest_name);
+	ERR(BIO_write(bd, magic_number, sizeof(magic_number) - 1) < 0, "%s", dest_name);
+
+	ERR(BIO_free(bd) != 1, "%s", dest_name);
+
+	/* Finally, if we're signing in place, replace the original. */
+	if (replace_orig)
+		ERR(rename(dest_name, loader_name) < 0, "%s", dest_name);
+
+	return 0;
+}
-- 
2.48.1


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

* [PATCH v3 3/4] hornet: Add a light skeleton data extractor script
  2025-05-02 18:44 [PATCH v3 0/4] Introducing Hornet LSM Blaise Boscaccy
  2025-05-02 18:44 ` [PATCH v3 1/4] security: " Blaise Boscaccy
  2025-05-02 18:44 ` [PATCH v3 2/4] hornet: Introduce sign-ebpf Blaise Boscaccy
@ 2025-05-02 18:44 ` Blaise Boscaccy
  2025-05-02 18:44 ` [PATCH v3 4/4] selftests/hornet: Add a selftest for the Hornet LSM Blaise Boscaccy
  2025-05-02 21:00 ` [PATCH v3 0/4] Introducing " KP Singh
  4 siblings, 0 replies; 41+ messages in thread
From: Blaise Boscaccy @ 2025-05-02 18:44 UTC (permalink / raw)
  To: Jonathan Corbet, David Howells, Herbert Xu, David S. Miller,
	Paul Moore, James Morris, Serge E. Hallyn, Masahiro Yamada,
	Nathan Chancellor, Nicolas Schier, Shuah Khan,
	Mickaël Salaün, Günther Noack, Nick Desaulniers,
	Bill Wendling, Justin Stitt, Blaise Boscaccy, Jarkko Sakkinen,
	Jan Stancek, Neal Gompa, linux-doc, linux-kernel, keyrings,
	linux-crypto, linux-security-module, linux-kbuild,
	linux-kselftest, bpf, llvm, nkapron, teknoraver, roberto.sassu,
	xiyou.wangcong, Tyler Hicks, James Bottomley

This script eases light skeleton development against Hornet by
generating a data payload which can be used for signing a light
skeleton binary using sign-ebpf. The binary payload it generates
contains the skeleton's ebpf instructions followed by the skeleton
loader's map.

Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
 scripts/hornet/extract-skel.sh | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)
 create mode 100755 scripts/hornet/extract-skel.sh

diff --git a/scripts/hornet/extract-skel.sh b/scripts/hornet/extract-skel.sh
new file mode 100755
index 000000000000..9ace78794b85
--- /dev/null
+++ b/scripts/hornet/extract-skel.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Microsoft Corporation
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of version 2 of the GNU General Public
+# License as published by the Free Software Foundation.
+
+function usage() {
+    echo "Sample script for extracting instructions and map data out of"
+    echo "autogenerated eBPF lskel headers"
+    echo ""
+    echo "USAGE: header_file output_file"
+    exit
+}
+
+ARGC=$#
+
+EXPECTED_ARGS=2
+
+if [ $ARGC -ne $EXPECTED_ARGS ] ; then
+    usage
+else
+    printf $(gcc -E $1 | grep "static const char opts_insn" | \
+		 awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g') > $2
+    printf $(gcc -E $1 | grep "static const char opts_data" | \
+		 awk -F"=" '{print $2}' | sed 's/;\+$//' | sed 's/\"//g') >> $2
+fi
-- 
2.48.1


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

* [PATCH v3 4/4] selftests/hornet: Add a selftest for the Hornet LSM
  2025-05-02 18:44 [PATCH v3 0/4] Introducing Hornet LSM Blaise Boscaccy
                   ` (2 preceding siblings ...)
  2025-05-02 18:44 ` [PATCH v3 3/4] hornet: Add a light skeleton data extractor script Blaise Boscaccy
@ 2025-05-02 18:44 ` Blaise Boscaccy
  2025-05-02 21:00 ` [PATCH v3 0/4] Introducing " KP Singh
  4 siblings, 0 replies; 41+ messages in thread
From: Blaise Boscaccy @ 2025-05-02 18:44 UTC (permalink / raw)
  To: Jonathan Corbet, David Howells, Herbert Xu, David S. Miller,
	Paul Moore, James Morris, Serge E. Hallyn, Masahiro Yamada,
	Nathan Chancellor, Nicolas Schier, Shuah Khan,
	Mickaël Salaün, Günther Noack, Nick Desaulniers,
	Bill Wendling, Justin Stitt, Blaise Boscaccy, Jarkko Sakkinen,
	Jan Stancek, Neal Gompa, linux-doc, linux-kernel, keyrings,
	linux-crypto, linux-security-module, linux-kbuild,
	linux-kselftest, bpf, llvm, nkapron, teknoraver, roberto.sassu,
	xiyou.wangcong, Tyler Hicks, James Bottomley

This selftest contains a testcase that utilizes light skeleton eBPF
loaders. One version of the light skeleton is signed with the
autogenerated module signing key, another is not. A test driver
attempts to load the programs. With Hornet enabled, the signed version
should successfully be loaded, and the unsigned version should fail.

Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
---
 tools/testing/selftests/Makefile              |   1 +
 tools/testing/selftests/hornet/Makefile       |  58 +++
 tools/testing/selftests/hornet/fail_loader.sh |   3 +
 tools/testing/selftests/hornet/frozen_skel.h  | 393 ++++++++++++++++++
 tools/testing/selftests/hornet/loader.c       |  22 +
 tools/testing/selftests/hornet/trivial.bpf.c  |  33 ++
 6 files changed, 510 insertions(+)
 create mode 100644 tools/testing/selftests/hornet/Makefile
 create mode 100755 tools/testing/selftests/hornet/fail_loader.sh
 create mode 100644 tools/testing/selftests/hornet/frozen_skel.h
 create mode 100644 tools/testing/selftests/hornet/loader.c
 create mode 100644 tools/testing/selftests/hornet/trivial.bpf.c

diff --git a/tools/testing/selftests/Makefile b/tools/testing/selftests/Makefile
index c77c8c8e3d9b..14f5d8ede199 100644
--- a/tools/testing/selftests/Makefile
+++ b/tools/testing/selftests/Makefile
@@ -42,6 +42,7 @@ TARGETS += ftrace
 TARGETS += futex
 TARGETS += gpio
 TARGETS += hid
+TARGETS += hornet
 TARGETS += intel_pstate
 TARGETS += iommu
 TARGETS += ipc
diff --git a/tools/testing/selftests/hornet/Makefile b/tools/testing/selftests/hornet/Makefile
new file mode 100644
index 000000000000..cd6902918564
--- /dev/null
+++ b/tools/testing/selftests/hornet/Makefile
@@ -0,0 +1,58 @@
+# SPDX-License-Identifier: GPL-2.0
+include ../../../build/Build.include
+include ../../../scripts/Makefile.arch
+include ../../../scripts/Makefile.include
+
+CLANG ?= clang
+CFLAGS := -g -O2 -Wall
+BPFTOOL ?= bpftool
+SCRIPTSDIR := $(abspath ../../../../scripts/hornet)
+TOOLSDIR := $(abspath ../../..)
+LIBDIR := $(TOOLSDIR)/lib
+BPFDIR := $(LIBDIR)/bpf
+TOOLSINCDIR := $(TOOLSDIR)/include
+APIDIR := $(TOOLSINCDIR)/uapi
+CERTDIR := $(abspath ../../../../certs)
+PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config
+
+TEST_GEN_PROGS_EXTENDED := loader
+TEST_GEN_PROGS := signed_loader
+TEST_PROGS := fail_loader.sh
+TEST_GEN_FILES := vmlinux.h loader.h trivial.bin trivial.bpf.o
+$(TEST_GEN_PROGS): LDLIBS += -lbpf
+$(TEST_GEN_PROGS): $(TEST_GEN_FILES)
+
+include ../lib.mk
+
+BPF_CFLAGS := -target bpf \
+	-D__TARGET_ARCH_$(ARCH) \
+	-I/usr/include/$(shell uname -m)-linux-gnu \
+	$(KHDR_INCLUDES)
+
+vmlinux.h:
+	$(BPFTOOL) btf dump file /sys/kernel/btf/vmlinux format c > vmlinux.h
+
+trivial.bpf.o: trivial.bpf.c vmlinux.h
+	$(CLANG) $(CFLAGS) $(BPF_CFLAGS) -c $< -o $@
+
+loader.h: trivial.bpf.o
+	$(BPFTOOL) gen skeleton -L $< name trivial > $@
+
+trivial.bin: loader.h
+	$(SCRIPTSDIR)/extract-skel.sh $< $@
+
+loader: loader.c loader.h
+	$(CC) $(CFLAGS) -I$(LIBDIR) -I$(APIDIR) $< -o $@ -lbpf
+
+$(OUTPUT)/sign-ebpf: ../../../../scripts/hornet/sign-ebpf.c
+	$(call msg,SIGN-EBPF,,$@)
+	$(Q)$(CC) $(shell $(PKG_CONFIG) --cflags libcrypto 2> /dev/null) \
+		  $< -o $@ \
+		  $(shell $(PKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
+
+signed_loader: trivial.bin loader $(OUTPUT)/sign-ebpf
+	$(OUTPUT)/sign-ebpf sha256 $(CERTDIR)/signing_key.pem  $(CERTDIR)/signing_key.x509 \
+		trivial.bin loader signed_loader
+	chmod u+x $@
+
+EXTRA_CLEAN = $(OUTPUT)/sign-ebpf
diff --git a/tools/testing/selftests/hornet/fail_loader.sh b/tools/testing/selftests/hornet/fail_loader.sh
new file mode 100755
index 000000000000..99314369f5de
--- /dev/null
+++ b/tools/testing/selftests/hornet/fail_loader.sh
@@ -0,0 +1,3 @@
+#!/bin/bash
+
+./loader && exit 1; exit 0
diff --git a/tools/testing/selftests/hornet/frozen_skel.h b/tools/testing/selftests/hornet/frozen_skel.h
new file mode 100644
index 000000000000..2e31a52f41c2
--- /dev/null
+++ b/tools/testing/selftests/hornet/frozen_skel.h
@@ -0,0 +1,393 @@
+/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
+/* Copyright (c) 2021 Facebook */
+#ifndef __SKEL_INTERNAL_H
+#define __SKEL_INTERNAL_H
+
+#ifdef __KERNEL__
+#include <linux/fdtable.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/slab.h>
+#include <linux/bpf.h>
+#else
+#include <unistd.h>
+#include <sys/syscall.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <bpf/bpf.h>
+#endif
+
+#ifndef __NR_bpf
+# if defined(__mips__) && defined(_ABIO32)
+#  define __NR_bpf 4355
+# elif defined(__mips__) && defined(_ABIN32)
+#  define __NR_bpf 6319
+# elif defined(__mips__) && defined(_ABI64)
+#  define __NR_bpf 5315
+# endif
+#endif
+
+/* This file is a base header for auto-generated *.lskel.h files.
+ * Its contents will change and may become part of auto-generation in the future.
+ *
+ * The layout of bpf_[map|prog]_desc and bpf_loader_ctx is feature dependent
+ * and will change from one version of libbpf to another and features
+ * requested during loader program generation.
+ */
+struct bpf_map_desc {
+	/* output of the loader prog */
+	int map_fd;
+	/* input for the loader prog */
+	__u32 max_entries;
+	__aligned_u64 initial_value;
+};
+struct bpf_prog_desc {
+	int prog_fd;
+};
+
+enum {
+	BPF_SKEL_KERNEL = (1ULL << 0),
+};
+
+struct bpf_loader_ctx {
+	__u32 sz;
+	__u32 flags;
+	__u32 log_level;
+	__u32 log_size;
+	__u64 log_buf;
+};
+
+struct bpf_load_and_run_opts {
+	struct bpf_loader_ctx *ctx;
+	const void *data;
+	const void *insns;
+	__u32 data_sz;
+	__u32 insns_sz;
+	const char *errstr;
+};
+
+long kern_sys_bpf(__u32 cmd, void *attr, __u32 attr_size);
+
+static inline int skel_sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
+			  unsigned int size)
+{
+#ifdef __KERNEL__
+	return kern_sys_bpf(cmd, attr, size);
+#else
+	return syscall(__NR_bpf, cmd, attr, size);
+#endif
+}
+
+#ifdef __KERNEL__
+static inline int close(int fd)
+{
+	return close_fd(fd);
+}
+
+static inline void *skel_alloc(size_t size)
+{
+	struct bpf_loader_ctx *ctx = kzalloc(size, GFP_KERNEL);
+
+	if (!ctx)
+		return NULL;
+	ctx->flags |= BPF_SKEL_KERNEL;
+	return ctx;
+}
+
+static inline void skel_free(const void *p)
+{
+	kfree(p);
+}
+
+/* skel->bss/rodata maps are populated the following way:
+ *
+ * For kernel use:
+ * skel_prep_map_data() allocates kernel memory that kernel module can directly access.
+ * Generated lskel stores the pointer in skel->rodata and in skel->maps.rodata.initial_value.
+ * The loader program will perform probe_read_kernel() from maps.rodata.initial_value.
+ * skel_finalize_map_data() sets skel->rodata to point to actual value in a bpf map and
+ * does maps.rodata.initial_value = ~0ULL to signal skel_free_map_data() that kvfree
+ * is not necessary.
+ *
+ * For user space:
+ * skel_prep_map_data() mmaps anon memory into skel->rodata that can be accessed directly.
+ * Generated lskel stores the pointer in skel->rodata and in skel->maps.rodata.initial_value.
+ * The loader program will perform copy_from_user() from maps.rodata.initial_value.
+ * skel_finalize_map_data() remaps bpf array map value from the kernel memory into
+ * skel->rodata address.
+ *
+ * The "bpftool gen skeleton -L" command generates lskel.h that is suitable for
+ * both kernel and user space. The generated loader program does
+ * either bpf_probe_read_kernel() or bpf_copy_from_user() from initial_value
+ * depending on bpf_loader_ctx->flags.
+ */
+static inline void skel_free_map_data(void *p, __u64 addr, size_t sz)
+{
+	if (addr != ~0ULL)
+		kvfree(p);
+	/* When addr == ~0ULL the 'p' points to
+	 * ((struct bpf_array *)map)->value. See skel_finalize_map_data.
+	 */
+}
+
+static inline void *skel_prep_map_data(const void *val, size_t mmap_sz, size_t val_sz)
+{
+	void *addr;
+
+	addr = kvmalloc(val_sz, GFP_KERNEL);
+	if (!addr)
+		return NULL;
+	memcpy(addr, val, val_sz);
+	return addr;
+}
+
+static inline void *skel_finalize_map_data(__u64 *init_val, size_t mmap_sz, int flags, int fd)
+{
+	struct bpf_map *map;
+	void *addr = NULL;
+
+	kvfree((void *) (long) *init_val);
+	*init_val = ~0ULL;
+
+	/* At this point bpf_load_and_run() finished without error and
+	 * 'fd' is a valid bpf map FD. All sanity checks below should succeed.
+	 */
+	map = bpf_map_get(fd);
+	if (IS_ERR(map))
+		return NULL;
+	if (map->map_type != BPF_MAP_TYPE_ARRAY)
+		goto out;
+	addr = ((struct bpf_array *)map)->value;
+	/* the addr stays valid, since FD is not closed */
+out:
+	bpf_map_put(map);
+	return addr;
+}
+
+#else
+
+static inline void *skel_alloc(size_t size)
+{
+	return calloc(1, size);
+}
+
+static inline void skel_free(void *p)
+{
+	free(p);
+}
+
+static inline void skel_free_map_data(void *p, __u64 addr, size_t sz)
+{
+	munmap(p, sz);
+}
+
+static inline void *skel_prep_map_data(const void *val, size_t mmap_sz, size_t val_sz)
+{
+	void *addr;
+
+	addr = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE,
+		    MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+	if (addr == (void *) -1)
+		return NULL;
+	memcpy(addr, val, val_sz);
+	return addr;
+}
+
+static inline void *skel_finalize_map_data(__u64 *init_val, size_t mmap_sz, int flags, int fd)
+{
+	void *addr;
+
+	addr = mmap((void *) (long) *init_val, mmap_sz, flags, MAP_SHARED | MAP_FIXED, fd, 0);
+	if (addr == (void *) -1)
+		return NULL;
+	return addr;
+}
+#endif
+
+static inline int skel_closenz(int fd)
+{
+	if (fd > 0)
+		return close(fd);
+	return -EINVAL;
+}
+
+#ifndef offsetofend
+#define offsetofend(TYPE, MEMBER) \
+	(offsetof(TYPE, MEMBER)	+ sizeof((((TYPE *)0)->MEMBER)))
+#endif
+
+static inline int skel_map_create(enum bpf_map_type map_type,
+				  const char *map_name,
+				  __u32 key_size,
+				  __u32 value_size,
+				  __u32 max_entries)
+{
+	const size_t attr_sz = offsetofend(union bpf_attr, map_extra);
+	union bpf_attr attr;
+
+	memset(&attr, 0, attr_sz);
+
+	attr.map_type = map_type;
+	strncpy(attr.map_name, map_name, sizeof(attr.map_name));
+	attr.key_size = key_size;
+	attr.value_size = value_size;
+	attr.max_entries = max_entries;
+
+	return skel_sys_bpf(BPF_MAP_CREATE, &attr, attr_sz);
+}
+
+static inline int skel_map_update_elem(int fd, const void *key,
+				       const void *value, __u64 flags)
+{
+	const size_t attr_sz = offsetofend(union bpf_attr, flags);
+	union bpf_attr attr;
+
+	memset(&attr, 0, attr_sz);
+	attr.map_fd = fd;
+	attr.key = (long) key;
+	attr.value = (long) value;
+	attr.flags = flags;
+
+	return skel_sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, attr_sz);
+}
+
+static inline int skel_map_delete_elem(int fd, const void *key)
+{
+	const size_t attr_sz = offsetofend(union bpf_attr, flags);
+	union bpf_attr attr;
+
+	memset(&attr, 0, attr_sz);
+	attr.map_fd = fd;
+	attr.key = (long)key;
+
+	return skel_sys_bpf(BPF_MAP_DELETE_ELEM, &attr, attr_sz);
+}
+
+static inline int skel_map_freeze(int fd)
+{
+	const size_t attr_sz = offsetofend(union bpf_attr, map_fd);
+	union bpf_attr attr;
+
+	memset(&attr, 0, attr_sz);
+	attr.map_fd = fd;
+
+	return skel_sys_bpf(BPF_MAP_FREEZE, &attr, attr_sz);
+}
+
+static inline int skel_map_get_fd_by_id(__u32 id)
+{
+	const size_t attr_sz = offsetofend(union bpf_attr, flags);
+	union bpf_attr attr;
+
+	memset(&attr, 0, attr_sz);
+	attr.map_id = id;
+
+	return skel_sys_bpf(BPF_MAP_GET_FD_BY_ID, &attr, attr_sz);
+}
+
+static inline int skel_raw_tracepoint_open(const char *name, int prog_fd)
+{
+	const size_t attr_sz = offsetofend(union bpf_attr, raw_tracepoint.prog_fd);
+	union bpf_attr attr;
+
+	memset(&attr, 0, attr_sz);
+	attr.raw_tracepoint.name = (long) name;
+	attr.raw_tracepoint.prog_fd = prog_fd;
+
+	return skel_sys_bpf(BPF_RAW_TRACEPOINT_OPEN, &attr, attr_sz);
+}
+
+static inline int skel_link_create(int prog_fd, int target_fd,
+				   enum bpf_attach_type attach_type)
+{
+	const size_t attr_sz = offsetofend(union bpf_attr, link_create.iter_info_len);
+	union bpf_attr attr;
+
+	memset(&attr, 0, attr_sz);
+	attr.link_create.prog_fd = prog_fd;
+	attr.link_create.target_fd = target_fd;
+	attr.link_create.attach_type = attach_type;
+
+	return skel_sys_bpf(BPF_LINK_CREATE, &attr, attr_sz);
+}
+
+#ifdef __KERNEL__
+#define set_err
+#else
+#define set_err err = -errno
+#endif
+
+static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
+{
+	const size_t prog_load_attr_sz = offsetofend(union bpf_attr, fd_array);
+	const size_t test_run_attr_sz = offsetofend(union bpf_attr, test);
+	int map_fd = -1, prog_fd = -1, key = 0, err;
+	union bpf_attr attr;
+
+	err = map_fd = skel_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4, opts->data_sz, 1);
+	if (map_fd < 0) {
+		opts->errstr = "failed to create loader map";
+		set_err;
+		goto out;
+	}
+
+	err = skel_map_update_elem(map_fd, &key, opts->data, 0);
+	if (err < 0) {
+		opts->errstr = "failed to update loader map";
+		set_err;
+		goto out;
+	}
+
+	err = skel_map_freeze(map_fd);
+	if (err < 0) {
+		opts->errstr = "failed to freeze map";
+		set_err;
+		goto out;
+	}
+
+	memset(&attr, 0, prog_load_attr_sz);
+	attr.prog_type = BPF_PROG_TYPE_SYSCALL;
+	attr.insns = (long) opts->insns;
+	attr.insn_cnt = opts->insns_sz / sizeof(struct bpf_insn);
+	attr.license = (long) "Dual BSD/GPL";
+	memcpy(attr.prog_name, "__loader.prog", sizeof("__loader.prog"));
+	attr.fd_array = (long) &map_fd;
+	attr.log_level = opts->ctx->log_level;
+	attr.log_size = opts->ctx->log_size;
+	attr.log_buf = opts->ctx->log_buf;
+	attr.prog_flags = BPF_F_SLEEPABLE;
+	err = prog_fd = skel_sys_bpf(BPF_PROG_LOAD, &attr, prog_load_attr_sz);
+	if (prog_fd < 0) {
+		opts->errstr = "failed to load loader prog";
+		set_err;
+		goto out;
+	}
+
+	memset(&attr, 0, test_run_attr_sz);
+	attr.test.prog_fd = prog_fd;
+	attr.test.ctx_in = (long) opts->ctx;
+	attr.test.ctx_size_in = opts->ctx->sz;
+	err = skel_sys_bpf(BPF_PROG_RUN, &attr, test_run_attr_sz);
+	if (err < 0 || (int)attr.test.retval < 0) {
+		if (err < 0) {
+			opts->errstr = "failed to execute loader prog";
+			set_err;
+		} else {
+			opts->errstr = "error returned by loader prog";
+			err = (int)attr.test.retval;
+#ifndef __KERNEL__
+			errno = -err;
+#endif
+		}
+		goto out;
+	}
+	err = 0;
+out:
+	if (map_fd >= 0)
+		close(map_fd);
+	if (prog_fd >= 0)
+		close(prog_fd);
+	return err;
+}
+
+#endif
diff --git a/tools/testing/selftests/hornet/loader.c b/tools/testing/selftests/hornet/loader.c
new file mode 100644
index 000000000000..548b50a0c1fe
--- /dev/null
+++ b/tools/testing/selftests/hornet/loader.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stddef.h>
+#include <sys/resource.h>
+#include <bpf/libbpf.h>
+#include <errno.h>
+#include "frozen_skel.h"
+#include  "loader.h"
+
+int main(int argc, char **argv)
+{
+	struct trivial *skel;
+
+	skel = trivial__open_and_load();
+	if (!skel)
+		return -1;
+
+	trivial__destroy(skel);
+	return 0;
+}
diff --git a/tools/testing/selftests/hornet/trivial.bpf.c b/tools/testing/selftests/hornet/trivial.bpf.c
new file mode 100644
index 000000000000..d38c5b53ff93
--- /dev/null
+++ b/tools/testing/selftests/hornet/trivial.bpf.c
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
+#include "vmlinux.h"
+
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_core_read.h>
+
+char LICENSE[] SEC("license") = "Dual BSD/GPL";
+
+int monitored_pid = 0;
+
+SEC("tracepoint/syscalls/sys_enter_unlinkat")
+int handle_enter_unlink(struct trace_event_raw_sys_enter *ctx)
+{
+	char filename[128] = { 0 };
+	struct task_struct *task;
+	unsigned long start_time = 0;
+	int pid = bpf_get_current_pid_tgid() >> 32;
+	char *pathname_ptr = (char *) BPF_CORE_READ(ctx, args[1]);
+
+	bpf_probe_read_str(filename, sizeof(filename), pathname_ptr);
+	task = (struct task_struct *)bpf_get_current_task();
+	start_time = BPF_CORE_READ(task, start_time);
+
+	bpf_printk("BPF triggered unlinkat by PID: %d, start_time %ld. pathname = %s",
+		   pid, start_time, filename);
+
+	if (monitored_pid == pid)
+		bpf_printk("target pid found");
+
+	return 0;
+}
-- 
2.48.1


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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-02 18:44 [PATCH v3 0/4] Introducing Hornet LSM Blaise Boscaccy
                   ` (3 preceding siblings ...)
  2025-05-02 18:44 ` [PATCH v3 4/4] selftests/hornet: Add a selftest for the Hornet LSM Blaise Boscaccy
@ 2025-05-02 21:00 ` KP Singh
  2025-05-04 17:36   ` Paul Moore
  2025-05-05 17:30   ` Blaise Boscaccy
  4 siblings, 2 replies; 41+ messages in thread
From: KP Singh @ 2025-05-02 21:00 UTC (permalink / raw)
  To: bboscaccy
  Cc: James.Bottomley, bpf, code, corbet, davem, dhowells, gnoack,
	herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	paul, roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong,
	KP Singh

> This patch series introduces the Hornet LSM. The goal of Hornet is to provide
> a signature verification mechanism for eBPF programs.
>

[...]

>
> References: [1]
> https://lore.kernel.org/bpf/20220209054315.73833-1-alexei.starovoitov@gmail.com/
> [2]
> https://lore.kernel.org/bpf/CAADnVQ+wPK1KKZhCgb-Nnf0Xfjk8M1UpX5fnXC=cBzdEYbv_kg@mail.gmail.com/
>
> Change list: - v2 -> v3 - Remove any and all usage of proprietary bpf APIs

BPF APIs are not proprietary, but you cannot implement BPF program signing
for BPF users without aligning with the BPF maintainers and the community.
Signed programs are a UAPI and a key part of how developers experience BPF
and this is not how we would like signing to be experienced by BPF users.

Some more feedback (which should be pretty obvious) but explicitly:

* Hacks like if (current->pid == 1) return 0; also break your threat model
  about root being untrusted. This is all the more reason I think signing
  should be integrated into other LSMs, only a proper LSM policy can breathe
  life into the root / kernel boundary. 

* You also did not take the feedback into account:

   new = map->ops->map_lookup_elem(map, &key);

  This is not okay without having the BPF maintainers aligned, the same way as 

  https://patchwork.kernel.org/project/netdevbpf/patch/20240629084331.3807368-4-kpsingh@kernel.org/#25928981

  was not okay. Let's not have double standards.

* And you copy pasted tools/testing/selftests/hornet/frozen_skel.h which
  is what you expect users to do? Not acceptable either.

So for this approach, it's a:

Nacked-by: KP Singh <kpsingh@kernel.org>

Now if you really care about the use-case and want to work with the maintainers
and implement signing for the community, here's how we think it should be done:

* The core signing logic and the tooling stays in BPF, something that the users
  are already using. No new tooling.
* The policy decision on the effect of signing can be built into various
  existing LSMs. I don't think we need a new LSM for it.
* A simple UAPI (emphasis on UAPI!) change to union bpf_attr in uapi/bpf.h in
  the BPF_PROG_LOAD command:

__aligned_u64 signature; 
__u32 signature_size;


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

* Re: [PATCH v3 1/4] security: Hornet LSM
  2025-05-02 18:44 ` [PATCH v3 1/4] security: " Blaise Boscaccy
@ 2025-05-04 15:02   ` Paul Moore
  0 siblings, 0 replies; 41+ messages in thread
From: Paul Moore @ 2025-05-04 15:02 UTC (permalink / raw)
  To: Blaise Boscaccy
  Cc: Jonathan Corbet, David Howells, Herbert Xu, David S. Miller,
	James Morris, Serge E. Hallyn, Masahiro Yamada, Nathan Chancellor,
	Nicolas Schier, Shuah Khan, Mickaël Salaün,
	Günther Noack, Nick Desaulniers, Bill Wendling, Justin Stitt,
	Jarkko Sakkinen, Jan Stancek, Neal Gompa, linux-doc, linux-kernel,
	keyrings, linux-crypto, linux-security-module, linux-kbuild,
	linux-kselftest, bpf, llvm, nkapron, teknoraver, roberto.sassu,
	xiyou.wangcong, Tyler Hicks, James Bottomley

On Fri, May 2, 2025 at 2:44 PM Blaise Boscaccy
<bboscaccy@linux.microsoft.com> wrote:
>
> This adds the Hornet Linux Security Module which provides signature
> verification of eBPF programs. This allows users to continue to
> maintain an invariant that all code running inside of the kernel has
> been signed.
>
> The primary target for signature verification is light-skeleton based
> eBPF programs which was introduced here:
> https://lore.kernel.org/bpf/20220209054315.73833-1-alexei.starovoitov@gmail.com/
>
> eBPF programs, before loading, undergo a complex set of operations
> which transform pseudo-values within the immediate operands of
> instructions into concrete values based on the running
> system. Typically, this is done by libbpf in
> userspace. Light-skeletons were introduced in order to support
> preloading of bpf programs and user-mode-drivers by removing the
> dependency on libbpf and userspace-based operations.
>
> Userpace modifications, which may change every time a program gets
> loaded or runs on a slightly different kernel, break known signature
> verification algorithms. A method is needed for passing unadulterated
> binary buffers into the kernel in-order to use existing signature
> verification algorithms. Light-skeleton loaders with their support of
> only in-kernel relocations fit that constraint.
>
> Hornet employs a signature verification scheme similar to that of
> kernel modules. A signature is appended to the end of an
> executable file. During an invocation of the BPF_PROG_LOAD subcommand,
> a signature is extracted from the current task's executable file. That
> signature is used to verify the integrity of the bpf instructions and
> maps which were passed into the kernel. Additionally, Hornet
> implicitly trusts any programs which were loaded from inside kernel
> rather than userspace, which allows BPF_PRELOAD programs along with
> outputs for BPF_SYSCALL programs to run.
>
> The validation check consists of checking a PKCS#7 formatted signature
> against a data buffer containing the raw instructions of an eBPF
> program, followed by the initial values of any maps used by the
> program. Maps are verified to be frozen before signature verification
> checking to stop TOCTOU attacks.
>
> Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
> ---
>  Documentation/admin-guide/LSM/Hornet.rst |  65 ++++++
>  Documentation/admin-guide/LSM/index.rst  |   1 +
>  MAINTAINERS                              |   9 +
>  crypto/asymmetric_keys/pkcs7_verify.c    |  10 +
>  include/linux/kernel_read_file.h         |   1 +
>  include/linux/verification.h             |   1 +
>  include/uapi/linux/lsm.h                 |   1 +
>  security/Kconfig                         |   3 +-
>  security/Makefile                        |   1 +
>  security/hornet/Kconfig                  |  24 +++
>  security/hornet/Makefile                 |   4 +
>  security/hornet/hornet_lsm.c             | 250 +++++++++++++++++++++++
>  security/selinux/hooks.c                 |  12 +-
>  security/selinux/include/classmap.h      |   2 +-
>  14 files changed, 380 insertions(+), 4 deletions(-)
>  create mode 100644 Documentation/admin-guide/LSM/Hornet.rst
>  create mode 100644 security/hornet/Kconfig
>  create mode 100644 security/hornet/Makefile
>  create mode 100644 security/hornet/hornet_lsm.c

...

> +Configuration Options
> +=====================
> +
> +Hornet provides a kconfig knob
> +CONFIG_SECURITY_HORNET_WHITELIST_PID_ONE.  Enabling this will allow
> +bpf programs to be loaded from pid 1 without undergoing a signature
> +verification check. This option is not recommened for production
> +systems.

...

> +config SECURITY_HORNET_WHITELIST_PID_ONE
> +       bool "Whiltelist unsigned eBPF programs from PID 1"
> +       depends on SECURITY_HORNET
> +       default n
> +       help
> +         Selecting this will configure Hornet to allow eBPF loaded from pid 1
> +         to load without a verification check.
> +         Further information can be found in
> +         Documentation/admin-guide/LSM/Hornet.rst.
> +
> +         If you are unsure how to answer this question, answer N.

...

> +static int hornet_bpf_prog_load(struct bpf_prog *prog, union bpf_attr *attr,
> +                               struct bpf_token *token, bool is_kernel)
> +{
> +       if (is_kernel)
> +               return 0;
> +#ifdef CONFIG_SECURITY_HORNET_WHITELIST_PID_ONE
> +       if (current->pid == 1)
> +               return 0;
> +#endif

Two quick comments on the build-time conditional above.  First, unless
there is some subtle reason why you only want the exception above to
apply to a single thread in the init process, I would suggest using
task_tgid_nr() instead of current->pid as I believe you want the init
exception to apply to all threads running within the init process.
Second, I think it would be helpful to rename the Kconfig knob to
CONFIG_SECURITY_HORNET_PIDONE_TRANSITION, or similar, to help indicate
that this is a transitional configuration option designed to make it
easier for developers to move to a system with signed BPF programs
without excessive warnings/errors from systemd in the beginning.  I
would highlight the transitory intent of this Kconfig knob both in the
Kconfig description as well as the Hornet.rst doc, a brief explanation
of the drawback for enabling this long term or on "production" systems
in the Hornet.rst section would also be a good idea.

-- 
paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-02 21:00 ` [PATCH v3 0/4] Introducing " KP Singh
@ 2025-05-04 17:36   ` Paul Moore
  2025-05-04 23:25     ` KP Singh
  2025-05-05  9:22     ` Daniel Borkmann
  2025-05-05 17:30   ` Blaise Boscaccy
  1 sibling, 2 replies; 41+ messages in thread
From: Paul Moore @ 2025-05-04 17:36 UTC (permalink / raw)
  To: KP Singh
  Cc: bboscaccy, James.Bottomley, bpf, code, corbet, davem, dhowells,
	gnoack, herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong

On Fri, May 2, 2025 at 5:00 PM KP Singh <kpsingh@kernel.org> wrote:
>
> > This patch series introduces the Hornet LSM. The goal of Hornet is to provide
> > a signature verification mechanism for eBPF programs.
> >
>
> [...]
>
> >
> > References: [1]
> > https://lore.kernel.org/bpf/20220209054315.73833-1-alexei.starovoitov@gmail.com/
> > [2]
> > https://lore.kernel.org/bpf/CAADnVQ+wPK1KKZhCgb-Nnf0Xfjk8M1UpX5fnXC=cBzdEYbv_kg@mail.gmail.com/
> >
> > Change list: - v2 -> v3 - Remove any and all usage of proprietary bpf APIs
>
> BPF APIs are not proprietary, but you cannot implement BPF program signing
> for BPF users without aligning with the BPF maintainers and the community.
> Signed programs are a UAPI and a key part of how developers experience BPF
> and this is not how we would like signing to be experienced by BPF users.
>
> Some more feedback (which should be pretty obvious) but explicitly:
>
> * Hacks like if (current->pid == 1) return 0; also break your threat model
>   about root being untrusted.

Speaking with Blaise off-list when that change was discussed, I
believe the intent behind that Kconfig option was simply for
development/transition purposes, and not for any long term usage.  My
understanding is that this is why it was a separate build time
configuration and not something that could be toggled at runtime, e.g.
sysctl or similar.

> * You also did not take the feedback into account:
>
>    new = map->ops->map_lookup_elem(map, &key);
>
>   This is not okay without having the BPF maintainers aligned, the same way as
>
>   https://patchwork.kernel.org/project/netdevbpf/patch/20240629084331.3807368-4-kpsingh@kernel.org/#25928981
>
>   was not okay. Let's not have double standards.

From my perspective these two patches are not the same.  Even on a
quick inspection we notice two significant differences.  First, Hornet
reads data (BPF maps) passed from userspace to determine if loading
the associated BPF program should be allowed to load; whereas the
patch you reference above had the BPF LSM directly modifying the very
core of the LSM framework state, the LSM hook data.  Secondly, we can
see multiple cases under net/ where map_lookup_elem() is either called
or takes things a step further and provides an alternate
implementation outside of the BPF subsystem, Hornet's use of
map_lookup_elem() doesn't appear to be a significant shift in how the
API is used; whereas the patch you reference attempted to do something
no other LSM has ever been allowed to do as it could jeopardize other
LSMs.  However, let us not forget perhaps the biggest difference
between Hornet and patchset you mentioned; the LSM community worked
with you and ultimately merged your static call patchset, I even had
to argue *on*your*behalf* against Tetsuo Handa to get your patchset
into Linus' tree.

I'm sorry you are upset that a portion of your original design for the
static call patchset didn't make it into Linus' tree, but ultimately
the vast majority of your original design *did* make it into Linus
tree, and the process to get there involved the LSM community working
with you in good faith, including arguing along side of you to support
your patchset against a dissenting LSM.

This might also be a good time to remind others who don't follow LSM
development of a couple other things that we've done recently in LSM
land to make things easier, or better, for BPF and/or the BPF LSM.
Perhaps the most important was the work to resolve a number of issues
with the LSM hook default values, solving some immediate issues and
preventing similar problems from occurring in the future; this was a
significant improvement and helped pave the way for greater
flexibility around where the BPF LSM could be inserted into the LSM
ordering.  There was also the work around inspecting and normalizing a
number of LSM hooks to make it easier for the BPF verifier to verify
BPF LSM callbacks; granted we were not able to normalize every single
LSM hook, but we did improve on a number of them and the BPF verifier
was able to take advantage of those improvements.

From what I've seen in Blaise's efforts to implement BPF signature
validation in the upstream kernel he has been working in good faith
and has been trying to work with the greater BPF community at each
step along the way.  He attempted to learn from previously rejected
attempts with his first patchset, however, that too was rejected, but
with feedback on how he might proceed.  Blaise took that feedback and
implemented Hornet, traveling to LSFMMBPF to present his idea to the
BPF community, as well as the usual mailing list postings.  When there
was feedback that certain APIs would not be permitted, despite being
EXPORT_SYMBOL'd, Blaise made some adjustments and came back to the
lists with an updated version.  You are obviously free to object to
portions of Hornet, but I don't believe you can claim Blaise isn't
trying to work with the BPF community on this effort.

> So for this approach, it's a:
>
> Nacked-by: KP Singh <kpsingh@kernel.org>

Noted.

> Now if you really care about the use-case and want to work with the maintainers
> and implement signing for the community, here's how we think it should be done:
>
> * The core signing logic and the tooling stays in BPF, something that the users
>   are already using. No new tooling.

I think we need a more detailed explanation of this approach on-list.
There has been a lot of vague guidance on BPF signature validation
from the BPF community which I believe has partly led us into the
situation we are in now.  If you are going to require yet another
approach, I think we all need to see a few paragraphs on-list
outlining the basic design.

> * The policy decision on the effect of signing can be built into various
>   existing LSMs. I don't think we need a new LSM for it.
> * A simple UAPI (emphasis on UAPI!) change to union bpf_attr in uapi/bpf.h in
>   the BPF_PROG_LOAD command:
>
> __aligned_u64 signature;
> __u32 signature_size;

-- 
paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-04 17:36   ` Paul Moore
@ 2025-05-04 23:25     ` KP Singh
  2025-05-05 16:22       ` Paul Moore
  2025-05-11  2:01       ` KP Singh
  2025-05-05  9:22     ` Daniel Borkmann
  1 sibling, 2 replies; 41+ messages in thread
From: KP Singh @ 2025-05-04 23:25 UTC (permalink / raw)
  To: Paul Moore
  Cc: bboscaccy, James.Bottomley, bpf, code, corbet, davem, dhowells,
	gnoack, herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong

On Sun, May 4, 2025 at 7:36 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Fri, May 2, 2025 at 5:00 PM KP Singh <kpsingh@kernel.org> wrote:
> >
> > > This patch series introduces the Hornet LSM. The goal of Hornet is to provide
> > > a signature verification mechanism for eBPF programs.
> > >
> >
> > [...]
> >
> > >
> > > References: [1]
> > > https://lore.kernel.org/bpf/20220209054315.73833-1-alexei.starovoitov@gmail.com/
> > > [2]
> > > https://lore.kernel.org/bpf/CAADnVQ+wPK1KKZhCgb-Nnf0Xfjk8M1UpX5fnXC=cBzdEYbv_kg@mail.gmail.com/
> > >
> > > Change list: - v2 -> v3 - Remove any and all usage of proprietary bpf APIs
> >
> > BPF APIs are not proprietary, but you cannot implement BPF program signing
> > for BPF users without aligning with the BPF maintainers and the community.
> > Signed programs are a UAPI and a key part of how developers experience BPF
> > and this is not how we would like signing to be experienced by BPF users.
> >
> > Some more feedback (which should be pretty obvious) but explicitly:
> >
> > * Hacks like if (current->pid == 1) return 0; also break your threat model
> >   about root being untrusted.
>
> Speaking with Blaise off-list when that change was discussed, I
> believe the intent behind that Kconfig option was simply for
> development/transition purposes, and not for any long term usage.  My
> understanding is that this is why it was a separate build time
> configuration and not something that could be toggled at runtime, e.g.
> sysctl or similar.
>
> > * You also did not take the feedback into account:
> >
> >    new = map->ops->map_lookup_elem(map, &key);
> >
> >   This is not okay without having the BPF maintainers aligned, the same way as

[...]

>
> From what I've seen in Blaise's efforts to implement BPF signature
> validation in the upstream kernel he has been working in good faith
> and has been trying to work with the greater BPF community at each
> step along the way.  He attempted to learn from previously rejected
> attempts with his first patchset, however, that too was rejected, but
> with feedback on how he might proceed.  Blaise took that feedback and
> implemented Hornet, traveling to LSFMMBPF to present his idea to the
> BPF community, as well as the usual mailing list postings.  When there
> was feedback that certain APIs would not be permitted, despite being
> EXPORT_SYMBOL'd, Blaise made some adjustments and came back to the
> lists with an updated version.  You are obviously free to object to
> portions of Hornet, but I don't believe you can claim Blaise isn't
> trying to work with the BPF community on this effort.

Calling map->ops->map_lookup_elem wax objected to by Alexei.
This feedback was not incorporated.

>
> > So for this approach, it's a:
> >
> > Nacked-by: KP Singh <kpsingh@kernel.org>
>
> Noted.
>
> > Now if you really care about the use-case and want to work with the maintainers
> > and implement signing for the community, here's how we think it should be done:
> >
> > * The core signing logic and the tooling stays in BPF, something that the users
> >   are already using. No new tooling.
>
> I think we need a more detailed explanation of this approach on-list.
> There has been a lot of vague guidance on BPF signature validation
> from the BPF community which I believe has partly led us into the
> situation we are in now.  If you are going to require yet another
> approach, I think we all need to see a few paragraphs on-list
> outlining the basic design.

Definitely, happy to share design / code.



- KP

>
> > * The policy decision on the effect of signing can be built into various
> >   existing LSMs. I don't think we need a new LSM for it.
> > * A simple UAPI (emphasis on UAPI!) change to union bpf_attr in uapi/bpf.h in
> >   the BPF_PROG_LOAD command:
> >
> > __aligned_u64 signature;
> > __u32 signature_size;
>
> --
> paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-04 17:36   ` Paul Moore
  2025-05-04 23:25     ` KP Singh
@ 2025-05-05  9:22     ` Daniel Borkmann
  1 sibling, 0 replies; 41+ messages in thread
From: Daniel Borkmann @ 2025-05-05  9:22 UTC (permalink / raw)
  To: Paul Moore, KP Singh
  Cc: bboscaccy, James.Bottomley, bpf, code, corbet, davem, dhowells,
	gnoack, herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong

On 5/4/25 7:36 PM, Paul Moore wrote:
> On Fri, May 2, 2025 at 5:00 PM KP Singh <kpsingh@kernel.org> wrote:
[...]
>  From what I've seen in Blaise's efforts to implement BPF signature
> validation in the upstream kernel he has been working in good faith
> and has been trying to work with the greater BPF community at each
> step along the way.  He attempted to learn from previously rejected
> attempts with his first patchset, however, that too was rejected, but
> with feedback on how he might proceed.  Blaise took that feedback and
> implemented Hornet, traveling to LSFMMBPF to present his idea to the
> BPF community, as well as the usual mailing list postings.  When there
> was feedback that certain APIs would not be permitted, despite being
> EXPORT_SYMBOL'd, Blaise made some adjustments and came back to the
> lists with an updated version.  You are obviously free to object to
> portions of Hornet, but I don't believe you can claim Blaise isn't
> trying to work with the BPF community on this effort.

We also discussed at LSFMMBPF that the current approach taken addresses
only a tiny fraction of BPF programs out there, meaning it will not be
applicable to 99% of projects utilizing BPF (e.g. for a OSS listing see
https://ebpf.io/applications/). What guidance would you provide to these
projects once this is set in place? "Please do a full rewrite (iff even
feasible) or accept user space breakage if some distro sets this generally
in place (wrongly assuming this provides a generic solution for all BPF)?"

In the presentation it was mentioned that you need something like Hornet
for your Azure Smart NICs in order to utilize BPF for livesite investigation
which is fine ofc, but given this only addresses a *tiny niche* of use cases,
the guidance given at the LSFMMBPF conference was to go via BPF LSM route
and implement it this way instead which Blaise agreed to look into. Given
this is a niche use case it is exactly the fit for BPF LSM.

>> So for this approach, it's a:
>>
>> Nacked-by: KP Singh <kpsingh@kernel.org>
> 
> Noted.

Nacked-by: Daniel Borkmann <daniel@iogearbox.net>

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-04 23:25     ` KP Singh
@ 2025-05-05 16:22       ` Paul Moore
  2025-05-11  2:01       ` KP Singh
  1 sibling, 0 replies; 41+ messages in thread
From: Paul Moore @ 2025-05-05 16:22 UTC (permalink / raw)
  To: KP Singh
  Cc: bboscaccy, James.Bottomley, bpf, code, corbet, davem, dhowells,
	gnoack, herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong

On Sun, May 4, 2025 at 7:25 PM KP Singh <kpsingh@kernel.org> wrote:
> On Sun, May 4, 2025 at 7:36 PM Paul Moore <paul@paul-moore.com> wrote:
> > On Fri, May 2, 2025 at 5:00 PM KP Singh <kpsingh@kernel.org> wrote:

...

> > > ... here's how we think it should be done:
> > >
> > > * The core signing logic and the tooling stays in BPF, something that the users
> > >   are already using. No new tooling.
> >
> > I think we need a more detailed explanation of this approach on-list.
> > There has been a lot of vague guidance on BPF signature validation
> > from the BPF community which I believe has partly led us into the
> > situation we are in now.  If you are going to require yet another
> > approach, I think we all need to see a few paragraphs on-list
> > outlining the basic design.
>
> Definitely, happy to share design / code.

At this point I think a quick paragraph or two on how you believe the
design should work would be a good start, I don't think code is
necessary unless you happen to already have something written.

-- 
paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-02 21:00 ` [PATCH v3 0/4] Introducing " KP Singh
  2025-05-04 17:36   ` Paul Moore
@ 2025-05-05 17:30   ` Blaise Boscaccy
  2025-05-05 20:41     ` KP Singh
  1 sibling, 1 reply; 41+ messages in thread
From: Blaise Boscaccy @ 2025-05-05 17:30 UTC (permalink / raw)
  To: KP Singh
  Cc: James.Bottomley, bpf, code, corbet, davem, dhowells, gnoack,
	herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	paul, roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong,
	KP Singh

KP Singh <kpsingh@kernel.org> writes:

[...]

> Now if you really care about the use-case and want to work with the maintainers
> and implement signing for the community, here's how we think it should be done:
>
> * The core signing logic and the tooling stays in BPF, something that the users
>   are already using. No new tooling.
> * The policy decision on the effect of signing can be built into various
>   existing LSMs. I don't think we need a new LSM for it.
> * A simple UAPI (emphasis on UAPI!) change to union bpf_attr in uapi/bpf.h in
>   the BPF_PROG_LOAD command:
>
> __aligned_u64 signature; 
> __u32 signature_size;

I think having some actual details on the various parties' requirements
here would be helpful. KP, I do look forward to seeing your design;
however, having code signing proposals where the capabilities are
dictated from above and any dissent is dismissed as "you're doing it
wrong" isn't going to be helpful to anyone that needs to use this in
practice.

Also, I don't think anyone actually cares, at least I don't, who calls
verify_pkcs7_signature or whatnot. Verifying signed binary blobs with a
private key is a solved problem and isn't really interesting.

Our requirements for code signing are just an extension of secure boot
and module signing logic:

* Prove all code running in ring zero has been signed
* Not trivially defeatable by root
* Ultimately, no trusted userspace components
* Secure from and not vulnerable to TOCTOU attacks
* Shouldn't be overly vulnerable to supply-chain attacks
* The signature checking logic and control paths should be human-readable
* Work easily and be backportable to stable kernels
* There should be a simple kconfig option to turn this on or off
* This solution needs to be in the mainline kernel

Hornet was implemented to meet those requirements, living in the LSM
subsystem, written in C. As of today, one cannot accomplish those
requirements via BPF-LSM, which is why C was chosen.

One can easily realize there is absolutely no way to have a single
one-size-fits-all signing solution for everything listed in
https://ebpf.io/applications/.

If you want to go the UAPI route, I would wholeheartedly recommend
making it extensible and having this data be available to the policy
LSMs.

enum bpf_signature_type {
  /* x509 signature check against program instructions only */
  BPF_SIGNATURE_PROG_ONLY = 0,
  /* x509 combined signature check against program instructions and used maps */
  BPF_SIGNATURE_PROG_USED_MAPS = 1,
  /* more of these to be determined via usage */ 
  ...
};

_aligned_u64 signature; 
__u32 signature_size;
__u32 signature_type;

The other option for solving this in the general is in-kernel
loaders. That's gotten pushback as well. 

-blaise






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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-05 17:30   ` Blaise Boscaccy
@ 2025-05-05 20:41     ` KP Singh
  2025-05-05 21:04       ` Paul Moore
  2025-05-07 17:48       ` James Bottomley
  0 siblings, 2 replies; 41+ messages in thread
From: KP Singh @ 2025-05-05 20:41 UTC (permalink / raw)
  To: Blaise Boscaccy
  Cc: James.Bottomley, bpf, code, corbet, davem, dhowells, gnoack,
	herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	paul, roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong

On Mon, May 5, 2025 at 7:30 PM Blaise Boscaccy
<bboscaccy@linux.microsoft.com> wrote:
>
> KP Singh <kpsingh@kernel.org> writes:
>
> [...]
>
> > Now if you really care about the use-case and want to work with the maintainers
> > and implement signing for the community, here's how we think it should be done:
> >
> > * The core signing logic and the tooling stays in BPF, something that the users
> >   are already using. No new tooling.
> > * The policy decision on the effect of signing can be built into various
> >   existing LSMs. I don't think we need a new LSM for it.
> > * A simple UAPI (emphasis on UAPI!) change to union bpf_attr in uapi/bpf.h in
> >   the BPF_PROG_LOAD command:
> >
> > __aligned_u64 signature;
> > __u32 signature_size;
>
> I think having some actual details on the various parties' requirements
> here would be helpful. KP, I do look forward to seeing your design;
> however, having code signing proposals where the capabilities are
> dictated from above and any dissent is dismissed as "you're doing it
> wrong" isn't going to be helpful to anyone that needs to use this in
> practice.

Please don't misrepresent the facts, you got feedback from Alexei in
various threads, but you chose to argue on the points that were
convenient for you (i.e. usage of BPF internal APIs) and yet you claim
to "work with the BPF community and maintainers" by arguing instead of
collaborating and paying attention to the feedback given to you.

1. https://lore.kernel.org/bpf/CAADnVQKF+B_YYwOCFsPBbrTBGKe4b22WVJFb8C0PHGmRAjbusQ@mail.gmail.com/

  Your solution to address the ELF loader specific issue was to just
allow list systemd? You totally ignored the part about loaders in
golang and Rust that do not use ELF. How is this    "directive from
above?"

2. Alexei suggested to you in
https://lore.kernel.org/bpf/87plhhjwqy.fsf@microsoft.com/

  "A signature for the map plus a signature for the prog
  that is tied to a map might be a better option.
  At map creation time the contents can be checked,
  the map is frozen, and then the verifier can proceed
  with prog's signature checking."

You never replied to this.

3. To signing the attachment points, you replied

> That statement is quite questionable. Yes, IIRC you brought that up. And
> again, runtime policy enforcement has nothing to do with proving code
> provenance. They are completely independent concerns.

The place where the BPF program is attached is a key part of the
provenance of the BPF program and its security (and other) effects can
vary greatly based on that. (e.g. imagine a reject all LSM program
that is attached to the wrong LSM hook). This is why it's not the same
as module loading.

4. https://lore.kernel.org/bpf/CAADnVQKF+B_YYwOCFsPBbrTBGKe4b22WVJFb8C0PHGmRAjbusQ@mail.gmail.com/

Programs can still access maps, now if you combine the issue of
ELF-less loaders and that maps are writeable from other programs as
freezing only affects userspace (i.e. when a binary gets an FD to a
map and tries to modify it with syscalls) your implementation fails.

The reply there about trusted user-space still needs to come with
better guarantees from the kernel, and the kernel can indeed give
better guarantees, which we will share. The reason for this is that
your trusted binary is not immune to attacks, and once an attacker
gains code execution as this trusted binary, there is no containing
the compromise.

- KP

>
> Also, I don't think anyone actually cares, at least I don't, who calls
> verify_pkcs7_signature or whatnot. Verifying signed binary blobs with a
> private key is a solved problem and isn't really interesting.
>
> Our requirements for code signing are just an extension of secure boot
> and module signing logic:
>
> * Prove all code running in ring zero has been signed
> * Not trivially defeatable by root
> * Ultimately, no trusted userspace components
> * Secure from and not vulnerable to TOCTOU attacks
> * Shouldn't be overly vulnerable to supply-chain attacks
> * The signature checking logic and control paths should be human-readable
> * Work easily and be backportable to stable kernels
> * There should be a simple kconfig option to turn this on or off
> * This solution needs to be in the mainline kernel
>
> Hornet was implemented to meet those requirements, living in the LSM
> subsystem, written in C. As of today, one cannot accomplish those
> requirements via BPF-LSM, which is why C was chosen.
>
> One can easily realize there is absolutely no way to have a single
> one-size-fits-all signing solution for everything listed in
> https://ebpf.io/applications/.
>
> If you want to go the UAPI route, I would wholeheartedly recommend
> making it extensible and having this data be available to the policy
> LSMs.
>
> enum bpf_signature_type {
>   /* x509 signature check against program instructions only */
>   BPF_SIGNATURE_PROG_ONLY = 0,
>   /* x509 combined signature check against program instructions and used maps */
>   BPF_SIGNATURE_PROG_USED_MAPS = 1,
>   /* more of these to be determined via usage */
>   ...
> };
>
> _aligned_u64 signature;
> __u32 signature_size;
> __u32 signature_type;
>
> The other option for solving this in the general is in-kernel
> loaders. That's gotten pushback as well.
>
> -blaise
>
>
>
>
>

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-05 20:41     ` KP Singh
@ 2025-05-05 21:04       ` Paul Moore
  2025-05-07 17:48       ` James Bottomley
  1 sibling, 0 replies; 41+ messages in thread
From: Paul Moore @ 2025-05-05 21:04 UTC (permalink / raw)
  To: KP Singh
  Cc: Blaise Boscaccy, James.Bottomley, bpf, code, corbet, davem,
	dhowells, gnoack, herbert, jarkko, jmorris, jstancek, justinstitt,
	keyrings, linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong

On Mon, May 5, 2025 at 4:41 PM KP Singh <kpsingh@kernel.org> wrote:
> On Mon, May 5, 2025 at 7:30 PM Blaise Boscaccy
> <bboscaccy@linux.microsoft.com> wrote:
> >
> > KP Singh <kpsingh@kernel.org> writes:
> >
> > [...]
> >
> > > Now if you really care about the use-case and want to work with the maintainers
> > > and implement signing for the community, here's how we think it should be done:
> > >
> > > * The core signing logic and the tooling stays in BPF, something that the users
> > >   are already using. No new tooling.
> > > * The policy decision on the effect of signing can be built into various
> > >   existing LSMs. I don't think we need a new LSM for it.
> > > * A simple UAPI (emphasis on UAPI!) change to union bpf_attr in uapi/bpf.h in
> > >   the BPF_PROG_LOAD command:
> > >
> > > __aligned_u64 signature;
> > > __u32 signature_size;
> >
> > I think having some actual details on the various parties' requirements
> > here would be helpful. KP, I do look forward to seeing your design;
> > however, having code signing proposals where the capabilities are
> > dictated from above and any dissent is dismissed as "you're doing it
> > wrong" isn't going to be helpful to anyone that needs to use this in
> > practice.
>
> Please don't misrepresent the facts ...

KP, I believe the most constructive thing at this point is to share
your design idea with this thread as previously requested so everyone
can review it.  We can continue to argue about how we got to where we
are at if you like, but that isn't likely to help us move towards a
solution.  If you are unable to share your design idea, either in a
couple of paragraphs or in some form of PoC, please let us know.

-- 
paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-05 20:41     ` KP Singh
  2025-05-05 21:04       ` Paul Moore
@ 2025-05-07 17:48       ` James Bottomley
  2025-05-07 23:21         ` Paul Moore
  1 sibling, 1 reply; 41+ messages in thread
From: James Bottomley @ 2025-05-07 17:48 UTC (permalink / raw)
  To: KP Singh, Blaise Boscaccy
  Cc: bpf, code, corbet, davem, dhowells, gnoack, herbert, jarkko,
	jmorris, jstancek, justinstitt, keyrings, linux-crypto, linux-doc,
	linux-kbuild, linux-kernel, linux-kselftest,
	linux-security-module, llvm, masahiroy, mic, morbo, nathan, neal,
	nick.desaulniers+lkml, nicolas, nkapron, paul, roberto.sassu,
	serge, shuah, teknoraver, xiyou.wangcong

On Mon, 2025-05-05 at 22:41 +0200, KP Singh wrote:
> On Mon, May 5, 2025 at 7:30 PM Blaise Boscaccy
> <bboscaccy@linux.microsoft.com> wrote:
> > 
> > KP Singh <kpsingh@kernel.org> writes:
> > 
> > [...]
> > 
> > > Now if you really care about the use-case and want to work with
> > > the maintainers and implement signing for the community, here's
> > > how we think it should be done:
> > > 
> > > * The core signing logic and the tooling stays in BPF, something
> > > that the users are already using. No new tooling.
> > > * The policy decision on the effect of signing can be built into
> > > various existing LSMs. I don't think we need a new LSM for it.
> > > * A simple UAPI (emphasis on UAPI!) change to union bpf_attr in
> > > uapi/bpf.h in the BPF_PROG_LOAD command:
> > > 
> > > __aligned_u64 signature;
> > > __u32 signature_size;
> > 
> > I think having some actual details on the various parties'
> > requirements here would be helpful. KP, I do look forward to seeing
> > your design; however, having code signing proposals where the
> > capabilities are dictated from above and any dissent is dismissed
> > as "you're doing it wrong" isn't going to be helpful to anyone that
> > needs to use this in practice.
> 
> Please don't misrepresent the facts, you got feedback from Alexei in
> various threads, but you chose to argue on the points that were
> convenient for you (i.e. usage of BPF internal APIs) and yet you
> claim to "work with the BPF community and maintainers" by arguing
> instead of collaborating and paying attention to the feedback given
> to you.

I'm with Paul on this: if you could share your design ideas more fully
than you have above that would help make this debate way more
technical.

However, I will try to respond to the other technical points from a
general security point of view if it helps but I've made some guesses
about what will work for you.

[...]
> 2. Alexei suggested to you in
> https://lore.kernel.org/bpf/87plhhjwqy.fsf@microsoft.com/
> 
>   "A signature for the map plus a signature for the prog
>   that is tied to a map might be a better option.
>   At map creation time the contents can be checked,
>   the map is frozen, and then the verifier can proceed
>   with prog's signature checking."
> 
> You never replied to this.

From a general security perspective allowing the creation of signed
objects that must be combined in order to produce a thing (be it a
program, a policy or whatever) is always problematic because after
you've produced a range of these objects signed with the same key, they
can be mixed and matched by an attacker, in ways you didn't anticipate,
to produce unintended results and possibly exploits.  To avoid this, a
single signature must cover the entire combination.

Having a single signature doesn't mean you can't make your scheme work:
the signature could be over an array of two hashes, one for the program
and one for the map, meaning that the signature could be verified and
the hashes extracted, which could then be verified at different points
in the execution and verification flow: say verify the program hash
immediately but do the map hash after you're sure it can't be modified.

> 3. To signing the attachment points, you replied
> 
> > That statement is quite questionable. Yes, IIRC you brought that
> > up. And again, runtime policy enforcement has nothing to do with
> > proving code provenance. They are completely independent concerns.
> 
> The place where the BPF program is attached is a key part of the
> provenance of the BPF program and its security (and other) effects
> can vary greatly based on that. (e.g. imagine a reject all LSM
> program that is attached to the wrong LSM hook). This is why it's not
> the same as module loading.

I don't believe anyone's disputing this.  However, there is a
difference between provenance and policy.  Signatures are just about
provenance. You proposed adding a policy language to signing at LPC in
2022 and I believe this could be done incrementally to the current
patch set.

One possible stepping stone to this is to add signatures now and policy
later but if you want the attach point also covered now, how about
adding an optional third hash over attach_btf_id and the fd; if the
hash is present it binds the attach point and if it isn't the skeleton
can attach arbitrarily? 

> 4.
> https://lore.kernel.org/bpf/CAADnVQKF+B_YYwOCFsPBbrTBGKe4b22WVJFb8C0PHGmRAjbusQ@mail.gmail.com/
> 
> Programs can still access maps, now if you combine the issue of
> ELF-less loaders and that maps are writeable from other programs as
> freezing only affects userspace (i.e. when a binary gets an FD to a
> map and tries to modify it with syscalls) your implementation fails.

In a world of trusted BPF, I think you can get to some confidence that
an attacker can't simply insert an arbitrary BPF program to do this and
nor could they get into ring0 without compromising the kernel, so map
freezing provides a reasonable measure of safety.  It's not perfect,
but it's good enough.

> The reply there about trusted user-space still needs to come with
> better guarantees from the kernel, and the kernel can indeed give
> better guarantees, which we will share. The reason for this is that
> your trusted binary is not immune to attacks, and once an attacker
> gains code execution as this trusted binary, there is no containing
> the compromise.

This is whitelisting systemd again?  I think as a temporary measure
until systemd gets on-board with BPF signing, a config parameter is not
unreasonable.

I also get the impression that there might be a disagreement over
scope: what seems to be coming out of BPF is that every signing problem
and scenario must be solved before signing can be considered
acceptable; however, I think it's not unreasonable to attempt to cover
a portion of the use cases and allow for future additions of things
like policy so we can get some forward motion to allow others to play
with it and produce patches based on their use cases.

Regards,

James


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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-07 17:48       ` James Bottomley
@ 2025-05-07 23:21         ` Paul Moore
  2025-05-08 17:44           ` Alexei Starovoitov
  0 siblings, 1 reply; 41+ messages in thread
From: Paul Moore @ 2025-05-07 23:21 UTC (permalink / raw)
  To: KP Singh, James Bottomley
  Cc: Blaise Boscaccy, bpf, code, corbet, davem, dhowells, gnoack,
	herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong

On Wed, May 7, 2025 at 1:48 PM James Bottomley
<James.Bottomley@hansenpartnership.com> wrote:
>
> I'm with Paul on this: if you could share your design ideas more fully
> than you have above that would help make this debate way more
> technical.

I think it would also help some of us, at the very least me, put your
objections into context.  I believe the more durable solutions that
end up in Linus' tree are combinations of designs created out of
compromise, and right now we are missing the context and detail of
your ideal solution to be able to do that compromise and get to a
design and implementation we can all begrudgingly accept.  In the
absence of a detailed alternate design, and considering that BPF
signature validation efforts have sputtered along for years without
any real success, we'll continue to push forward on-list with
refinements to the current proposal in an effort to drive this to some
form of resolution.

> I also get the impression that there might be a disagreement over
> scope: what seems to be coming out of BPF is that every signing problem
> and scenario must be solved before signing can be considered
> acceptable; however, I think it's not unreasonable to attempt to cover
> a portion of the use cases and allow for future additions of things
> like policy so we can get some forward motion to allow others to play
> with it and produce patches based on their use cases.

Beyond any potential future updates to Hornet, I just wanted to make
it clear that the Hornet LSM approach, like any LSM, can be disabled
both at compile time for those users who build their own kernels, as
well as at kernel boot time using the "lsm=" command line option for
those who are limited to pre-built kernels, e.g. distro kernels.
Users can always disable Hornet and replace it with another LSM,
either a BPF LSM or a native/C LSM, of their choosing; the LSM
framework is intentionally flexible to allow for this substitution and
replacement, with plenty of existing examples already.

-- 
paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-07 23:21         ` Paul Moore
@ 2025-05-08 17:44           ` Alexei Starovoitov
  2025-05-08 19:23             ` Paul Moore
  0 siblings, 1 reply; 41+ messages in thread
From: Alexei Starovoitov @ 2025-05-08 17:44 UTC (permalink / raw)
  To: Paul Moore, Linus Torvalds
  Cc: KP Singh, James Bottomley, Blaise Boscaccy, bpf, code,
	Jonathan Corbet, David S. Miller, David Howells,
	Günther Noack, Herbert Xu, Jarkko Sakkinen, James Morris,
	Jan Stancek, Justin Stitt, keyrings, Linux Crypto Mailing List,
	open list:DOCUMENTATION, Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang

On Wed, May 7, 2025 at 4:24 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Wed, May 7, 2025 at 1:48 PM James Bottomley
> <James.Bottomley@hansenpartnership.com> wrote:
> >
> > I'm with Paul on this: if you could share your design ideas more fully
> > than you have above that would help make this debate way more
> > technical.
>
> I think it would also help some of us, at the very least me, put your
> objections into context.  I believe the more durable solutions that
> end up in Linus' tree are combinations of designs created out of
> compromise, and right now we are missing the context and detail of
> your ideal solution to be able to do that compromise and get to a
> design and implementation we can all begrudgingly accept.  In the
> absence of a detailed alternate design, and considering that BPF
> signature validation efforts have sputtered along for years without
> any real success, we'll continue to push forward on-list with
> refinements to the current proposal in an effort to drive this to some
> form of resolution.

It sounds like you're asking for Linus's opinion.

This 'hornet' LSM attempts to implement signed bpf programs by
hacking into bpf internals:
https://lore.kernel.org/bpf/20250502184421.1424368-2-bboscaccy@linux.microsoft.com/

It got 3 Nacks from bpf maintainers.
Let me recap our objections:

1. Your definition of attack surface says that root is untrusted.
Yet this "hornet" LSM allowlists systemd as trusted.  Allegedly,
it's an intermediate solution.  What it really means that as presented
the security is broken, since an untrusted root can poke into systemd
and make it load whatever bpf programs it wants.

2. you propose to mangle every binary in the system that needs to load
bpf programs with an extra "signature" at the end of the binary that
breaks ELF format.  I already explained earlier that such an approach
was a border line acceptable solution for kernel modules, but
certainly not ok as a general approach for all binaries.  The kernel
modules are consumed by the kernel and user space doesn't touch them.
It's not ok to mangle general purpose binaries.  The user space
tooling relies on well formed ELF files. 'perf record' and any
profiling tool will parse various ELF binaries to symbolize addresses.
If ELF is corrupted such tools may crash. So far you've been lucky
that ld-linux.so was able to interpret such corrupted ELF.
It's not something to rely on.

3. To read this mangled ELF you do:
file = get_task_exe_file(current);
buf_sz = kernel_read_file(file, 0, &buf, INT_MAX, &sz, READING_EBPF);
A malicious user can give you a multi gigabyte file and your LSM will
happily read it into the kernel memory. It's an obvious DoS vector.

4. I said multiple times it's not ok to do
bpf_map->ops->map_lookup_elem() outside of the bpf subsystem.
You did 'git grep' and argued that something in the net/ directory
is doing that.  Well,
./scripts/get_maintainer.pl `git grep -l -- '->map_lookup_elem' net/`
is your answer.  net/core/filter.c is a part of the bpf subsystem.
The bpf originated in networking.
Also, do build 'hornet' LSM with LOCKDEP and see how many bpf map
lifetime rules you violated with that map_lookup_elem() call.

5. I also explained that map->frozen == true doesn't guarantee that
the map is immutable.  It only freezes the map from user space writes.
You argued that when all bpf programs are signed then the non-signed
attacker cannot mangle the map.  That's broken for two reasons:
- you allow systemd to load bpf without signatures
- even when all bpf progs are signed, the program actions are not
controlled after loading, so signed bpf prog that uses map-in-map is
subject to an attack where a malicious root can add loader-map as
inner map-in-map and an unsuspecting program will mangle it.

6. Though bpf instructions have standardized format LSMs shouldn't not
be in the business of parsing them. New instructions are being added.
We don't need a headache that an LSM will start crashing/misbehaving
when a new instruction is added or extended.
bpf instruction parsing belongs in the bpf subsystem.

7. You do: copy_from_bpfptr_offset(&map_fd, ...) then proceed with
content extraction, but this is a user controlled address. It's an
obvious TOCTOU bug. The user can replace that map_fd after your LSM
read it and before bpf verifier reads it. So the signature checking
from LSM is fundamentally broken. I already explained that the
signature check has to be done within a bpf subsystem.

In the last kernel release we added 'bool kernel' parameter to
security_bpf_prog_load() LSM hook assuming that you're going to work
with us on actual solution for signed bpf programs, but so far you
ignored our feedback and accused us of artificially delaying a
solution to signed programs, though we told you that the "light
skeleton" (that you incorrectly attempt to use here) was designed
specifically as a building block towards signed bpf programs:

See the cover letter from 2021:
https://lore.kernel.org/bpf/20210514003623.28033-1-alexei.starovoitov@gmail.com/

"
This is a first step towards signed bpf programs and the third approach
of that kind.
...
Future steps:
- support CO-RE in the kernel
- generate light skeleton for kernel
- finally do the signing of the loader program
"
Later in 2022-23 we implemented "CORE in the kernel" and "light skel
for kernel" steps.  There are a few more steps to do that we didn't
anticipate back in 2021.  Like the exclusive map <-> prog relationship
and error propagation through the loading process.

When Blaise volunteered to work on signed progs we pointed him to
light skeleton assuming that you're going to work with us to finish
this complex task. Instead you went with this broken LSM and now
argue that your insecure approach somehow should be accepted by
upstream and microsoft users.
Sorry, but users deserve better than this.

The only path forward here is to:
- stop pushing broken code
- internalize the feedback
- work with the bpf community

The problem is difficult and a truly secure solution will take time.
You cannot short circuit this path.

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-08 17:44           ` Alexei Starovoitov
@ 2025-05-08 19:23             ` Paul Moore
  2025-05-11  2:14               ` KP Singh
  0 siblings, 1 reply; 41+ messages in thread
From: Paul Moore @ 2025-05-08 19:23 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: Linus Torvalds, KP Singh, James Bottomley, Blaise Boscaccy, bpf,
	code, Jonathan Corbet, David S. Miller, David Howells,
	Günther Noack, Herbert Xu, Jarkko Sakkinen, James Morris,
	Jan Stancek, Justin Stitt, keyrings, Linux Crypto Mailing List,
	open list:DOCUMENTATION, Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang

On Thu, May 8, 2025 at 1:45 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
> On Wed, May 7, 2025 at 4:24 PM Paul Moore <paul@paul-moore.com> wrote:
> > On Wed, May 7, 2025 at 1:48 PM James Bottomley
> > <James.Bottomley@hansenpartnership.com> wrote:
> > >
> > > I'm with Paul on this: if you could share your design ideas more fully
> > > than you have above that would help make this debate way more
> > > technical.
> >
> > I think it would also help some of us, at the very least me, put your
> > objections into context.  I believe the more durable solutions that
> > end up in Linus' tree are combinations of designs created out of
> > compromise, and right now we are missing the context and detail of
> > your ideal solution to be able to do that compromise and get to a
> > design and implementation we can all begrudgingly accept.  In the
> > absence of a detailed alternate design, and considering that BPF
> > signature validation efforts have sputtered along for years without
> > any real success, we'll continue to push forward on-list with
> > refinements to the current proposal in an effort to drive this to some
> > form of resolution.
>
> It sounds like you're asking for Linus's opinion.

At this point no, although of course he is welcome to comment if he
likes.  What we've been asking for is some detail on your preferred
solution; all we've seen on the various threads thus far has been a
small number of short sentences that leave far too much ambiguity
around important parts of the design.  Without any clear direction on
your ideal solution, Blaise has been continuing forward with proposals
that we believe solve at least one use case and can be extended in the
future to support others.

Blaise started this most recent effort by attempting to address the
concerns brought up in previous efforts, you and others rejected this
first attempt and directed Blaise towards a light skeleton and LSM
based approach, which is where he is at with Hornet.  Once again, you
reject this approach with minimal guidance on what would be
acceptable, and our response is to ask for clarification on your
preferred design.  We're not asking for a full working solution,
simply a couple of paragraphs outlining the design with enough detail
to put forward a working solution that isn't immediately NACK'd.
We've made this request multiple times in the past, most recently this
past weekend, where KP replied that he would be "happy" to share
designs/code.  Unfortunately, since then all we've received from
either you or KP since then has been effectively just a list of your
objections on repeat; surely typing out a couple of paragraphs
outlining a design would have been quicker, easier, and more
constructive then your latest reply?

> This 'hornet' LSM attempts to implement signed bpf programs by
> hacking into bpf internals:
> https://lore.kernel.org/bpf/20250502184421.1424368-2-bboscaccy@linux.microsoft.com/

I guess there are always two sides to every story, but "hacking into
bpf internals" is not how I would characterize the Hornet approach.
Hornet reads the light skeleton BPF loader and the associated BPF maps
(the original BPF program) passed in from userspace into a buffer in
order to verify a signature across the two BPF programs (loader +
original).

Hornet does this to verify the provenance and load time integrity of
BPF programs, two very basic security goals that people have wanted
for BPF programs for years, Blaise is simply the most recent person to
try and get something into Linus' tree.

> It got 3 Nacks from bpf maintainers.
> Let me recap our objections:
>
> 1. Your definition of attack surface says that root is untrusted.
> Yet this "hornet" LSM allowlists systemd as trusted.  Allegedly,
> it's an intermediate solution ...

The Hornet proposal is not the first thing to implement a transition
mechanism, but if that is really the blocker you want to go with I'm
sure Blaise would be happy to back out the change.  My understanding
is that it is really about reducing warnings from systemd and nothing
more.

> 2. you propose to mangle every binary in the system that needs to load
> bpf programs with an extra "signature" at the end of the binary that
> breaks ELF format.  I already explained earlier that such an approach
> was a border line acceptable solution for kernel modules, but
> certainly not ok as a general approach for all binaries.

Let's just ignore the "borderline" comment on kernel module signing;
the fact is that kernel module signing has been an accepted part of
the upstream Linux kernel for years now, which is part of why Blaise
used that as a starting point for the Hornet approach: keep things
simple and build on something that works.  This is especially
important as we are still largely guessing about the details of your
ideal solution.

Based on some of the vague feedback from you and KP, this week Blaise
has been experimenting with passing a signature via the bpf_attr
struct, but until you provide more detail on-list it is still a
guessing game as to what you might accept.  The kmod inspired
signature-on-ELF approach has the advantage of being much more
self-contained, which is the reason we saw it as a good starting point
that could be augmented with additional schemes in the future if/when
needed.

> 3. To read this mangled ELF you do:
> file = get_task_exe_file(current);
> buf_sz = kernel_read_file(file, 0, &buf, INT_MAX, &sz, READING_EBPF);
> A malicious user can give you a multi gigabyte file and your LSM will
> happily read it into the kernel memory. It's an obvious DoS vector.

The LSM hook used by Hornet happens well after all of the BPF based
capability and access control checks.  If anything, the ability of a
malicious user to tamper with the BPF program being loaded helps
highlight the importance of validating signatures on BPF programs.  In
the worst case, if the kernel can't allocate a buffer the
kernel_read_file() will fail and an error will be returned to
userspace.

> 4. I said multiple times it's not ok to do
> bpf_map->ops->map_lookup_elem() outside of the bpf subsystem.
> You did 'git grep' and argued that something in the net/ directory
> is doing that.  Well,
> ./scripts/get_maintainer.pl `git grep -l -- '->map_lookup_elem' net/`
> is your answer.  net/core/filter.c is a part of the bpf subsystem.
> The bpf originated in networking.

Yet BPF was extracted out as a standalone mechanism that has grown
well beyond its initial use as a socket data filter.  From my
perspective either BPF is a standalone entity and the
map_lookup_elem() API is necessary for at least one current user, the
socket fitler, or map_lookup_elem() isn't an API in which case BPF
isn't the general purpose tool that everyone claims.

This touches on another issue that I've brought up before in this
thread which is important.  The LSM subsystem doesn't care about how
the LSM is implemented; C, BPF, and Rust (work is obviously still very
early on the Rust side) are all valid implementation languages, and we
make it clear that if you can do something in one language, you should
be able to do it another.  While I don't believe you or KP have
explicitly stated that your objection to the Hornet approach is
largely due to it being written in C, Daniel did make a comment that
Hornet should be written as a BPF LSM.  From the perspective of the
LSM, something is either "legal" to do in a LSM, or it isn't; we don't
qualify that determination based on the source language of the
implementation.

> Also, do build 'hornet' LSM with LOCKDEP and see how many bpf map
> lifetime rules you violated with that map_lookup_elem() call.

I'm sure Blaise will look into that.  I'm sure that if you, KP,
Daniel, or anyone else in BPF land wanted to provide an alternate API
for map access I'm sure Blaise would be happy to rework his code.  As
we've stated multiple times, don't just say "no" say "no" with a good
description about how to move forward.

> 5. I also explained that map->frozen == true doesn't guarantee that
> the map is immutable.  It only freezes the map from user space writes.

For this initial use case, we believe that is sufficient and is inline
with kernel module loading, which is a good analogy.  The most
critical part as far as we are concerned is that userspace can not
tamper with the BPF map once the signature has been verified.  Yes,
another in-kernel component might be able to tamper with the BPF map,
but in-kernel components can do a number of bad things; this is yet
one more reason why validating code that executes in kernel context is
important.

> 6. Though bpf instructions have standardized format LSMs shouldn't not
> be in the business of parsing them. New instructions are being added.
> We don't need a headache that an LSM will start crashing/misbehaving
> when a new instruction is added or extended.
> bpf instruction parsing belongs in the bpf subsystem.

The current Hornet implementation ignores things it doesn't know
about, sticking to parts of the BPF instruction set that is currently
defined.  Who knows what Hornet might look like in another few
revisions, or if it is replaced with another approach, but if it
becomes part of Linus' tree it should be trivial to update it along
with BPF.  There is precedence for this with other LSMs and other
kernel subsystems.

> 7. You do: copy_from_bpfptr_offset(&map_fd, ...) then proceed with
> content extraction, but this is a user controlled address. It's an
> obvious TOCTOU bug. The user can replace that map_fd after your LSM
> read it and before bpf verifier reads it. So the signature checking
> from LSM is fundamentally broken. I already explained that the
> signature check has to be done within a bpf subsystem.

As mentioned *many* times before, please provide more information on
this.  To start, where *exactly* in the BPF subsystem would you accept
a signature check.

> In the last kernel release we added 'bool kernel' parameter to
> security_bpf_prog_load() LSM hook assuming that you're going to work
> with us on actual solution for signed bpf programs, but so far you
> ignored our feedback and accused us of artificially delaying a
> solution to signed programs, though we told you that the "light
> skeleton" (that you incorrectly attempt to use here) was designed
> specifically as a building block towards signed bpf programs:
>
> See the cover letter from 2021:
> https://lore.kernel.org/bpf/20210514003623.28033-1-alexei.starovoitov@gmail.com/

...

> When Blaise volunteered to work on signed progs we pointed him to
> light skeleton assuming that you're going to work with us to finish
> this complex task.

In order to work effectively we need a more detailed explanation of
what you want to see with respect to BPF signing.  Thus far we've only
seen rejections with very hand-wavy explanations on how to proceed.
KP indicated almost a week ago that he is happy to share a couple of
paragraphs on a preferred BPF signing approach, but we're still left
waiting with the only feedback being a repeat of previous rejections
and more vitrol.

-- 
paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-04 23:25     ` KP Singh
  2025-05-05 16:22       ` Paul Moore
@ 2025-05-11  2:01       ` KP Singh
  2025-05-14  3:06         ` Paul Moore
  2025-05-14 15:39         ` James Bottomley
  1 sibling, 2 replies; 41+ messages in thread
From: KP Singh @ 2025-05-11  2:01 UTC (permalink / raw)
  To: Paul Moore
  Cc: bboscaccy, James.Bottomley, bpf, code, corbet, davem, dhowells,
	gnoack, herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong,
	kysrinivasan

> > I think we need a more detailed explanation of this approach on-list.
> > There has been a lot of vague guidance on BPF signature validation
> > from the BPF community which I believe has partly led us into the
> > situation we are in now.  If you are going to require yet another
> > approach, I think we all need to see a few paragraphs on-list
> > outlining the basic design.
>
> Definitely, happy to share design / code.

Here’s the design that Alexei and I have been discussing. It's
extensible, independent of ELF formats, handles all identified
use-cases, paves the way for signed unprivileged eBPF, and meets the
requirements of anyone who wants to run signed eBPF programs.

# Trusted Hash Chain

The key idea of the design is to use a signing algorithm that allows
us to integrity-protect a number of future payloads, including their
order, by creating a chain of trust.

Consider that Alice needs to send messages M_1, M_2, ..., M_n to Bob.
We define blocks of data such that:

    B_n = M_n || H(termination_marker)

(Each block contains its corresponding message and the hash of the
*next* block in the chain.)

    B_{n-1} = M_{n-1} || H(B_n)
    B_{n-2} = M_{n-2} || H(B_{n-1})

  ...

    B_2 = M_2 || H(B_3)
    B_1 = M_1 || H(B_2)

Alice does the following (e.g., on a build system where all payloads
are available):

  * Assembles the blocks B_1, B_2, ..., B_n.
  * Calculates H(B_1) and signs it, yielding Sig(H(B_1)).

Alice sends the following to Bob:

    M_1, H(B_2), Sig(H(B_1))

Bob receives this payload and does the following:

    * Reconstructs B_1 as B_1' using the received M_1 and H(B_2)
(i.e., B_1' = M_1 || H(B_2)).
    * Recomputes H(B_1') and verifies the signature against the
received Sig(H(B_1)).
    * If the signature verifies, it establishes the integrity of M_1
and H(B_2) (and transitively, the integrity of the entire chain). Bob
now stores the verified H(B_2) until it receives the next message.
    * When Bob receives M_2 (and H(B_3) if n > 2), it reconstructs
B_2' (e.g., B_2' = M_2 || H(B_3), or if n=2, B_2' = M_2 ||
H(termination_marker)). Bob then computes H(B_2') and compares it
against the stored H(B_2) that was verified in the previous step.

This process continues until the last block is received and verified.

Now, applying this to the BPF signing use-case, we simplify to two messages:

    M_1 = I_loader (the instructions of the loader program)
    M_2 = M_metadata (the metadata for the loader program, passed in a
map, which includes the programs to be loaded and other context)

For this specific BPF case, we will directly sign a composite of the
first message and the hash of the second. Let H_meta = H(M_metadata).
The block to be signed is effectively:

    B_signed = I_loader || H_meta

The signature generated is Sig(B_signed).

The process then follows a similar pattern to the Alice and Bob model,
where the kernel (Bob) verifies I_loader and H_meta using the
signature. Then, the trusted I_loader is responsible for verifying
M_metadata against the trusted H_meta.

From an implementation standpoint:

# Build

bpftool (or some other tool in the user's build environment) knows
about the metadata (M_metadata) and the loader program (I_loader). It
first calculates H_meta = H(M_metadata). Then it constructs the object
to be signed and computes the signature:

    Sig(I_loader || H_meta)

# Loader

bpftool generates the loader program. The initial instructions of this
loader program are designed to verify the SHA256 hash of the metadata
(M_metadata) that will be passed in a map. These instructions
effectively embed the precomputed H_meta as immediate values.

    ld_imm64 r1, const_ptr_to_map // insn[0].src_reg == BPF_PSEUDO_MAP_IDX
    r2 = *(u64 *)(r1 + 0);
    ld_imm64 r3, sha256_of_map_part1 // constant precomputed by
bpftool (part of H_meta)
    if r2 != r3 goto out;

    r2 = *(u64 *)(r1 + 8);
    ld_imm64 r3, sha256_of_map_part2 // (part of H_meta)
    if r2 != r3 goto out;

    r2 = *(u64 *)(r1 + 16);
    ld_imm64 r3, sha256_of_map_part3 // (part of H_meta)
    if r2 != r3 goto out;

    r2 = *(u64 *)(r1 + 24);
    ld_imm64 r3, sha256_of_map_part4 // (part of H_meta)
    if r2 != r3 goto out;
    ...

This implicitly makes the payload equivalent to the signed block (B_signed)

    I_loader || H_meta

bpftool then generates the signature of this I_loader payload (which
now contains the expected H_meta) using a key (system or user) with
new flags that work in combination with bpftool -L

This signature is stored in bpf_attr, which is extended as follows for
the BPF_PROG_LOAD command:

    __aligned_u64 signature;
    __u32 signature_size;
    __u32 user_keyring_serial;
    __u64 system_keyring_id;

# New BPF Commands

## BPF_MAP_GET_HASH args: (map_fd, &sha256_output, output_size)

This command instructs the kernel to compute the SHA256 hash of the
map's data. If sha256_output is non-NULL, the hash is returned to
userspace. (While not strictly needed for this specific signing
use-case to function, it's a useful utility for userspace debugging or
other applications.)

The kernel also stores this computed hash internally within its struct bpf_map:

    struct bpf_map {
    +   u64 sha[4];
        const struct bpf_map_ops *ops;
        struct bpf_map *inner_map_meta;
    };

## BPF_MAP_MAKE_EXCLUSIVE args: (map_fd, sha256_of_future_prog)

Exclusivity ensures that the map can only be used by a future BPF
program whose SHA256 hash matches sha256_of_future_prog.

First, bpf_prog_calc_tag() is updated to compute the SHA256 instead of
SHA1, and this hash is stored in struct bpf_prog_aux:

    @@ -1588,6 +1588,7 @@ struct bpf_prog_aux {
         int cgroup_atype; /* enum cgroup_bpf_attach_type */
         struct bpf_map *cgroup_storage[MAX_BPF_CGROUP_STORAGE_TYPE];
         char name[BPF_OBJ_NAME_LEN];
    +    u64 sha[4];
         u64 (*bpf_exception_cb)(u64 cookie, u64 sp, u64 bp, u64, u64);
         // ...
    };

Once BPF_MAP_MAKE_EXCLUSIVE is called with map_fd and the target
program's SHA256 hash, the kernel marks the map as exclusive. When a
BPF program is subsequently loaded, if it attempts to use this map,
the kernel will compare the program's own SHA256 hash against the one
registered with the map, if matching, it will be added to
prog->used_maps[]. The program load will fail if the hashes do not
match or if the map is already in use by another (non-matching)
exclusive program.

Any program with a different SHA256 will fail to load if it attempts
to use the exclusive map.

NOTE: Exclusive maps cannot be added as inner maps.

# Light Skeleton Sequence (Userspace Example)

    // Create and populate the metadata map

    map_fd = skel_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4,
opts->data_sz, 1);
    skel_map_update_elem(map_fd, &key, opts->data, 0);

    // Freeze the map to prevent further userspace modifications.
    // This makes its content immutable from userspace.

    skel_map_freeze(map_fd);

    // Make the map exclusive to the intended loader program.
    // sha256_of_loader_prog is the hash of the I_loader binary
    skel_map_make_exclusive(map_fd, sha256_of_loader_prog);

    skel_map_get_hash(map_fd, NULL, 0);

    // Load the loader program (I_loader) with its signature.
    opts.ctx = (struct bpf_loader_ctx *)skel;
    opts.data_sz = sizeof(opts_data) - 1;
    opts.data = (void *)opts_data;
    opts.insns_sz = sizeof(opts_insn) - 1;
    opts.insns = (void *)opts_insn;

    opts.signature = … signature of the opts_insn[] bytes…
    opts.signature_size = sizeof(..);
    opts. system_keyring_id  = ...

    OR

    opts.user_keyring_serial = … depending on what flag was used in bpftool.
    err = bpf_load_and_run(&opts);

The kernel verifier will:

    * Compute the hash of the provided I_loader bytecode.
    * Verify the signature against this computed hash.
    * Check if the metadata map (now exclusive) is intended for this
program's hash.

The signature check in the verifier (during BPF_PROG_LOAD):

    verify_pkcs7_signature(prog->aux->sha, sizeof(prog->aux->sha),
sig_from_bpf_attr, …);

This ensures that the loaded loader program (I_loader), including the
embedded expected hash of the metadata (H_meta), is trusted.
Since the loader program is now trusted, it can be entrusted to verify
the actual metadata (M_metadata) read from the (now exclusive and
frozen) map against the embedded (and trusted) H_meta. There is no
Time-of-Check-Time-of-Use (TOCTOU) vulnerability here because:

    * The signature covers the I_loader and its embedded H_meta.
    * The metadata map M_metadata is frozen before the loader program
is loaded and associated with it.
    * The map is made exclusive to the specific (signed and verified)
loader program.

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-08 19:23             ` Paul Moore
@ 2025-05-11  2:14               ` KP Singh
  0 siblings, 0 replies; 41+ messages in thread
From: KP Singh @ 2025-05-11  2:14 UTC (permalink / raw)
  To: Paul Moore
  Cc: Alexei Starovoitov, Linus Torvalds, James Bottomley,
	Blaise Boscaccy, bpf, code, Jonathan Corbet, David S. Miller,
	David Howells, Günther Noack, Herbert Xu, Jarkko Sakkinen,
	James Morris, Jan Stancek, Justin Stitt, keyrings,
	Linux Crypto Mailing List, open list:DOCUMENTATION,
	Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang, kysrinivasan

[...]

> Blaise started this most recent effort by attempting to address the
> concerns brought up in previous efforts, you and others rejected this
> first attempt and directed Blaise towards a light skeleton and LSM
> based approach, which is where he is at with Hornet.  Once again, you
> reject this approach with minimal guidance on what would be
> acceptable, and our response is to ask for clarification on your
> preferred design.  We're not asking for a full working solution,
> simply a couple of paragraphs outlining the design with enough detail
> to put forward a working solution that isn't immediately NACK'd.
> We've made this request multiple times in the past, most recently this
> past weekend, where KP replied that he would be "happy" to share

Here's the proposed design:

https://lore.kernel.org/bpf/CACYkzJ6VQUExfyt0=-FmXz46GHJh3d=FXh5j4KfexcEFbHV-vg@mail.gmail.com/#t


> designs/code.  Unfortunately, since then all we've received from
> either you or KP since then has been effectively just a list of your
> objections on repeat; surely typing out a couple of paragraphs
> outlining a design would have been quicker, easier, and more
> constructive then your latest reply?

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-11  2:01       ` KP Singh
@ 2025-05-14  3:06         ` Paul Moore
  2025-05-14 18:48           ` KP Singh
  2025-05-14 15:39         ` James Bottomley
  1 sibling, 1 reply; 41+ messages in thread
From: Paul Moore @ 2025-05-14  3:06 UTC (permalink / raw)
  To: KP Singh
  Cc: bboscaccy, James.Bottomley, bpf, code, corbet, davem, dhowells,
	gnoack, herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong,
	kysrinivasan

On Sat, May 10, 2025 at 10:01 PM KP Singh <kpsingh@kernel.org> wrote:
>

...

> The signature check in the verifier (during BPF_PROG_LOAD):
>
>     verify_pkcs7_signature(prog->aux->sha, sizeof(prog->aux->sha),
> sig_from_bpf_attr, …);

I think we still need to clarify the authorization aspect of your
proposed design.

Working under the assumption that the core BPF kernel code doesn't
want to enforce any restrictions, or at least as few as possible, I'm
expecting that the BPF kernel code would want to adopt an "allow all"
policy when it comes to authorizing signed and unsigned BPF programs,
delegating any additional restrictions to the LSM.  With that in mind
I think we need to agree on a way for the BPF verifier to indicate
that it has verified the signature is correct to the LSM, and we need
a new LSM hook which runs *after* the verifier so that it can inspect
the results of the signature verification.  While it might be tempting
to relocate the existing security_bpf_prog_load() hook, I believe it
makes sense to leave that hook before the verifier for those LSMs that
wish control access prior to the verifier's inspection using criteria
other than signatures.

With respect to the LSM hook, since it appears that the signature is
going to be included in the bpf_attr struct, and I'm *guessing* the
best way for the verifier to indicate the result of the signature
verification is via a field inside bpf_prog_aux, this means the hook
could look something like this:

  int security_bpf_prog_verified(bpf_prog, bpf_attr);

... and be called immediately after bpf_check() in bpf_prog_load().
As far as the new field in bpf_prog_aux is concerned, I think we can
probably start off with a simple bool to indicate whether a signature
was verified or not, with an understanding that we can move to a
richer construct in the future if we find it necessary.  Neither of
these are directly visible to userspace so we have the ability to
start simple and modify as needed.

Does this sound reasonable to everyone?  Does anyone have any other
thoughts on the authorization aspect of BPF signature verification?

-- 
paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-11  2:01       ` KP Singh
  2025-05-14  3:06         ` Paul Moore
@ 2025-05-14 15:39         ` James Bottomley
  2025-05-14 17:17           ` KP Singh
  1 sibling, 1 reply; 41+ messages in thread
From: James Bottomley @ 2025-05-14 15:39 UTC (permalink / raw)
  To: KP Singh, Paul Moore
  Cc: bboscaccy, bpf, code, corbet, davem, dhowells, gnoack, herbert,
	jarkko, jmorris, jstancek, justinstitt, keyrings, linux-crypto,
	linux-doc, linux-kbuild, linux-kernel, linux-kselftest,
	linux-security-module, llvm, masahiroy, mic, morbo, nathan, neal,
	nick.desaulniers+lkml, nicolas, nkapron, roberto.sassu, serge,
	shuah, teknoraver, xiyou.wangcong, kysrinivasan

On Sun, 2025-05-11 at 04:01 +0200, KP Singh wrote:
[...]
> > 
> For this specific BPF case, we will directly sign a composite of the
> first message and the hash of the second. Let H_meta = H(M_metadata).
> The block to be signed is effectively:
> 
>     B_signed = I_loader || H_meta
> 
> The signature generated is Sig(B_signed).
> 
> The process then follows a similar pattern to the Alice and Bob
> model,
> where the kernel (Bob) verifies I_loader and H_meta using the
> signature. Then, the trusted I_loader is responsible for verifying
> M_metadata against the trusted H_meta.
> 
> From an implementation standpoint:
> 
> # Build
> 
> bpftool (or some other tool in the user's build environment) knows
> about the metadata (M_metadata) and the loader program (I_loader). It
> first calculates H_meta = H(M_metadata). Then it constructs the
> object
> to be signed and computes the signature:
> 
>     Sig(I_loader || H_meta)
> 
> # Loader
> 
> bpftool generates the loader program. The initial instructions of
> this loader program are designed to verify the SHA256 hash of the
> metadata (M_metadata) that will be passed in a map. These
> instructions effectively embed the precomputed H_meta as immediate
> values.
> 
>     ld_imm64 r1, const_ptr_to_map // insn[0].src_reg ==
> BPF_PSEUDO_MAP_IDX
>     r2 = *(u64 *)(r1 + 0);
>     ld_imm64 r3, sha256_of_map_part1 // constant precomputed by
> bpftool (part of H_meta)
>     if r2 != r3 goto out;
> 
>     r2 = *(u64 *)(r1 + 8);
>     ld_imm64 r3, sha256_of_map_part2 // (part of H_meta)
>     if r2 != r3 goto out;
> 
>     r2 = *(u64 *)(r1 + 16);
>     ld_imm64 r3, sha256_of_map_part3 // (part of H_meta)
>     if r2 != r3 goto out;
> 
>     r2 = *(u64 *)(r1 + 24);
>     ld_imm64 r3, sha256_of_map_part4 // (part of H_meta)
>     if r2 != r3 goto out;
>     ...
> 
> This implicitly makes the payload equivalent to the signed block
> (B_signed)
> 
>     I_loader || H_meta
> 
> bpftool then generates the signature of this I_loader payload (which
> now contains the expected H_meta) using a key (system or user) with
> new flags that work in combination with bpftool -L

Could I just push back a bit on this.  The theory of hash chains (which
I've cut to shorten) is about pure data structures.  The reason for
that is that the entire hash chain is supposed to be easily
independently verifiable in any environment because anything can
compute the hashes of the blocks and links.  This independent
verification of the chain is key to formally proving hash chains to be
correct.  In your proposal we lose the easy verifiability because the
link hash is embedded in the ebpf loader program which has to be
disassembled to do the extraction of the hash and verify the loader is
actually checking it.

I was looking at ways we could use a pure hash chain (i.e. signature
over loader and real map hash) and it does strike me that the above
ebpf hash verification code is pretty invariant and easy to construct,
so it could run as a separate BPF fragment that then jumps to the real
loader.  In that case, it could be constructed on the fly in a trusted
environment, like the kernel, from the link hash in the signature and
the signature could just be Sig(loader || map hash) which can then be
easily verified without having to disassemble ebpf code.  So we get the
formal provability benefits of using a real hash chain while still
keeping your verification in BPF. 

Regards,

James



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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-14 15:39         ` James Bottomley
@ 2025-05-14 17:17           ` KP Singh
  2025-05-14 17:45             ` James Bottomley
  0 siblings, 1 reply; 41+ messages in thread
From: KP Singh @ 2025-05-14 17:17 UTC (permalink / raw)
  To: James Bottomley
  Cc: Paul Moore, bboscaccy, bpf, code, corbet, davem, dhowells, gnoack,
	herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong,
	kysrinivasan, Linus Torvalds

On Wed, May 14, 2025 at 5:39 PM James Bottomley
<James.Bottomley@hansenpartnership.com> wrote:
>
> On Sun, 2025-05-11 at 04:01 +0200, KP Singh wrote:
> [...]
> > >
> > For this specific BPF case, we will directly sign a composite of the
> > first message and the hash of the second. Let H_meta = H(M_metadata).
> > The block to be signed is effectively:
> >
> >     B_signed = I_loader || H_meta
> >
> > The signature generated is Sig(B_signed).
> >
> > The process then follows a similar pattern to the Alice and Bob
> > model,
> > where the kernel (Bob) verifies I_loader and H_meta using the
> > signature. Then, the trusted I_loader is responsible for verifying
> > M_metadata against the trusted H_meta.
> >
> > From an implementation standpoint:
> >
> > # Build
> >
> > bpftool (or some other tool in the user's build environment) knows
> > about the metadata (M_metadata) and the loader program (I_loader). It
> > first calculates H_meta = H(M_metadata). Then it constructs the
> > object
> > to be signed and computes the signature:
> >
> >     Sig(I_loader || H_meta)
> >
> > # Loader
> >
> > bpftool generates the loader program. The initial instructions of
> > this loader program are designed to verify the SHA256 hash of the
> > metadata (M_metadata) that will be passed in a map. These
> > instructions effectively embed the precomputed H_meta as immediate
> > values.
> >
> >     ld_imm64 r1, const_ptr_to_map // insn[0].src_reg ==
> > BPF_PSEUDO_MAP_IDX
> >     r2 = *(u64 *)(r1 + 0);
> >     ld_imm64 r3, sha256_of_map_part1 // constant precomputed by
> > bpftool (part of H_meta)
> >     if r2 != r3 goto out;
> >
> >     r2 = *(u64 *)(r1 + 8);
> >     ld_imm64 r3, sha256_of_map_part2 // (part of H_meta)
> >     if r2 != r3 goto out;
> >
> >     r2 = *(u64 *)(r1 + 16);
> >     ld_imm64 r3, sha256_of_map_part3 // (part of H_meta)
> >     if r2 != r3 goto out;
> >
> >     r2 = *(u64 *)(r1 + 24);
> >     ld_imm64 r3, sha256_of_map_part4 // (part of H_meta)
> >     if r2 != r3 goto out;
> >     ...
> >
> > This implicitly makes the payload equivalent to the signed block
> > (B_signed)
> >
> >     I_loader || H_meta
> >
> > bpftool then generates the signature of this I_loader payload (which
> > now contains the expected H_meta) using a key (system or user) with
> > new flags that work in combination with bpftool -L
>
> Could I just push back a bit on this.  The theory of hash chains (which
> I've cut to shorten) is about pure data structures.  The reason for
> that is that the entire hash chain is supposed to be easily
> independently verifiable in any environment because anything can
> compute the hashes of the blocks and links.  This independent
> verification of the chain is key to formally proving hash chains to be
> correct.  In your proposal we lose the easy verifiability because the
> link hash is embedded in the ebpf loader program which has to be
> disassembled to do the extraction of the hash and verify the loader is
> actually checking it.

I am not sure I understand your concern. This is something that can
easily be built into tooling / annotations.

    bpftool -S -v <verification_key> <loader> <metadata>

Could you explain what's the use-case for "easy verifiability".

>
> I was looking at ways we could use a pure hash chain (i.e. signature
> over loader and real map hash) and it does strike me that the above
> ebpf hash verification code is pretty invariant and easy to construct,
> so it could run as a separate BPF fragment that then jumps to the real
> loader.  In that case, it could be constructed on the fly in a trusted
> environment, like the kernel, from the link hash in the signature and
> the signature could just be Sig(loader || map hash) which can then be

The design I proposed does the same thing:

    Sig(loader || H_metadata)

metadata is actually the data (programs, context etc) that's passed in
the map. The verification just happens in the loader program and the
loader || H_metadata is implemented elegantly to avoid any separate
payloads.

> easily verified without having to disassemble ebpf code.  So we get the
> formal provability benefits of using a real hash chain while still
> keeping your verification in BPF.
>
> Regards,
>
> James
>
>

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-14 17:17           ` KP Singh
@ 2025-05-14 17:45             ` James Bottomley
  2025-05-14 18:35               ` KP Singh
  0 siblings, 1 reply; 41+ messages in thread
From: James Bottomley @ 2025-05-14 17:45 UTC (permalink / raw)
  To: KP Singh
  Cc: Paul Moore, bboscaccy, bpf, code, corbet, davem, dhowells, gnoack,
	herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong,
	kysrinivasan, Linus Torvalds

On Wed, 2025-05-14 at 19:17 +0200, KP Singh wrote:
> On Wed, May 14, 2025 at 5:39 PM James Bottomley
> <James.Bottomley@hansenpartnership.com> wrote:
> > On Sun, 2025-05-11 at 04:01 +0200, KP Singh wrote:
[...]
> > > This implicitly makes the payload equivalent to the signed block
> > > (B_signed)
> > > 
> > >     I_loader || H_meta
> > > 
> > > bpftool then generates the signature of this I_loader payload
> > > (which now contains the expected H_meta) using a key (system or
> > > user) with new flags that work in combination with bpftool -L
> > 
> > Could I just push back a bit on this.  The theory of hash chains
> > (which I've cut to shorten) is about pure data structures.  The
> > reason for that is that the entire hash chain is supposed to be
> > easily independently verifiable in any environment because anything
> > can compute the hashes of the blocks and links.  This independent
> > verification of the chain is key to formally proving hash chains to
> > be correct.  In your proposal we lose the easy verifiability
> > because the link hash is embedded in the ebpf loader program which
> > has to be disassembled to do the extraction of the hash and verify
> > the loader is actually checking it.
> 
> I am not sure I understand your concern. This is something that can
> easily be built into tooling / annotations.
> 
>     bpftool -S -v <verification_key> <loader> <metadata>
> 
> Could you explain what's the use-case for "easy verifiability".

I mean verifiability of the hash chain link.  Given a signed program,
(i.e. a .h file which is generated by bpftool) which is a signature
over the loader only how would one use simple cryptographic operations
to verify it?


> 
> > I was looking at ways we could use a pure hash chain (i.e.
> > signature over loader and real map hash) and it does strike me that
> > the above ebpf hash verification code is pretty invariant and easy
> > to construct, so it could run as a separate BPF fragment that then
> > jumps to the real loader.  In that case, it could be constructed on
> > the fly in a trusted environment, like the kernel, from the link
> > hash in the signature and the signature could just be Sig(loader ||
> > map hash) which can then be
> 
> The design I proposed does the same thing:
> 
>     Sig(loader || H_metadata)
> 
> metadata is actually the data (programs, context etc) that's passed
> in the map. The verification just happens in the loader program and
> the loader || H_metadata is implemented elegantly to avoid any
> separate payloads.

OK, so I think this is the crux of the problem:  In formal methods
proving the validity of a data based hash link is an easy set of
cryptographic operations.  You can assert that's equivalent to a
signature over a program that verifies the hash, but formally proving
it requires a formal analysis of the program to show that 1) it
contains the correct hash and 2) it correctly checks the hash against
the map.  That makes the task of someone receiving the .h file
containing the signed skeleton way harder: it's easy to prove the
signature matches the loader instructions, but they still have to prove
the instructions contain and verify the correct map hash.

Regards,

James



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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-14 17:45             ` James Bottomley
@ 2025-05-14 18:35               ` KP Singh
  2025-05-14 18:35                 ` KP Singh
  2025-05-14 20:31                 ` James Bottomley
  0 siblings, 2 replies; 41+ messages in thread
From: KP Singh @ 2025-05-14 18:35 UTC (permalink / raw)
  To: James Bottomley
  Cc: Paul Moore, bboscaccy, bpf, code, corbet, davem, dhowells, gnoack,
	herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong,
	kysrinivasan, Linus Torvalds

On Wed, May 14, 2025 at 7:45 PM James Bottomley
<James.Bottomley@hansenpartnership.com> wrote:
>
> On Wed, 2025-05-14 at 19:17 +0200, KP Singh wrote:
> > On Wed, May 14, 2025 at 5:39 PM James Bottomley
> > <James.Bottomley@hansenpartnership.com> wrote:
> > > On Sun, 2025-05-11 at 04:01 +0200, KP Singh wrote:
> [...]
> > > > This implicitly makes the payload equivalent to the signed block
> > > > (B_signed)
> > > >
> > > >     I_loader || H_meta
> > > >
> > > > bpftool then generates the signature of this I_loader payload
> > > > (which now contains the expected H_meta) using a key (system or
> > > > user) with new flags that work in combination with bpftool -L
> > >
> > > Could I just push back a bit on this.  The theory of hash chains
> > > (which I've cut to shorten) is about pure data structures.  The
> > > reason for that is that the entire hash chain is supposed to be
> > > easily independently verifiable in any environment because anything
> > > can compute the hashes of the blocks and links.  This independent
> > > verification of the chain is key to formally proving hash chains to
> > > be correct.  In your proposal we lose the easy verifiability
> > > because the link hash is embedded in the ebpf loader program which
> > > has to be disassembled to do the extraction of the hash and verify
> > > the loader is actually checking it.
> >
> > I am not sure I understand your concern. This is something that can
> > easily be built into tooling / annotations.
> >
> >     bpftool -S -v <verification_key> <loader> <metadata>
> >
> > Could you explain what's the use-case for "easy verifiability".
>
> I mean verifiability of the hash chain link.  Given a signed program,
> (i.e. a .h file which is generated by bpftool) which is a signature
> over the loader only how would one use simple cryptographic operations
> to verify it?
>

I literally just said it above the hash can be extracted if you really
want offline verification. Are you saying this code is hard to write?
or is the tooling hard to write? Do you have some definition of
"simple cryptographic operations".  All operations use tooling.

>
> >
> > > I was looking at ways we could use a pure hash chain (i.e.
> > > signature over loader and real map hash) and it does strike me that
> > > the above ebpf hash verification code is pretty invariant and easy
> > > to construct, so it could run as a separate BPF fragment that then
> > > jumps to the real loader.  In that case, it could be constructed on
> > > the fly in a trusted environment, like the kernel, from the link
> > > hash in the signature and the signature could just be Sig(loader ||
> > > map hash) which can then be
> >
> > The design I proposed does the same thing:
> >
> >     Sig(loader || H_metadata)
> >
> > metadata is actually the data (programs, context etc) that's passed
> > in the map. The verification just happens in the loader program and
> > the loader || H_metadata is implemented elegantly to avoid any
> > separate payloads.
>
> OK, so I think this is the crux of the problem:  In formal methods
> proving the validity of a data based hash link is an easy set of
> cryptographic operations.  You can assert that's equivalent to a
> signature over a program that verifies the hash, but formally proving
> it requires a formal analysis of the program to show that 1) it
> contains the correct hash and 2) it correctly checks the hash against
> the map.  That makes the task of someone receiving the .h file
> containing the signed skeleton way harder: it's easy to prove the
> signature matches the loader instructions, but they still have to prove
> the instructions contain and verify the correct map hash.
>

I don't see this as a problem for 2 reasons:

1. It's not hard
2. Your typical user does not want to do formal verification and
extract signatures etc.

[1] alone is enough.

The key user journey is:

* Build the program and the metadata
* Sign the blob once (as explained)
* A simple API to verify the sequence of operations.

The user builds a program and signs the blob, they sign it because it
contains the hash of the metadata. It seems like you are optimizing
for the formal researcher but not for the tooling. The user just needs
good tooling and a simple API which is exactly what was proposed.

- KP

> Regards,
>
> James
>
>

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-14 18:35               ` KP Singh
@ 2025-05-14 18:35                 ` KP Singh
  2025-05-14 20:31                 ` James Bottomley
  1 sibling, 0 replies; 41+ messages in thread
From: KP Singh @ 2025-05-14 18:35 UTC (permalink / raw)
  To: James Bottomley
  Cc: Paul Moore, bboscaccy, bpf, code, corbet, davem, dhowells, gnoack,
	herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong,
	kysrinivasan, Linus Torvalds

On Wed, May 14, 2025 at 8:35 PM KP Singh <kpsingh@kernel.org> wrote:
>
> On Wed, May 14, 2025 at 7:45 PM James Bottomley
> <James.Bottomley@hansenpartnership.com> wrote:
> >
> > On Wed, 2025-05-14 at 19:17 +0200, KP Singh wrote:
> > > On Wed, May 14, 2025 at 5:39 PM James Bottomley
> > > <James.Bottomley@hansenpartnership.com> wrote:
> > > > On Sun, 2025-05-11 at 04:01 +0200, KP Singh wrote:
> > [...]
> > > > > This implicitly makes the payload equivalent to the signed block
> > > > > (B_signed)
> > > > >
> > > > >     I_loader || H_meta
> > > > >
> > > > > bpftool then generates the signature of this I_loader payload
> > > > > (which now contains the expected H_meta) using a key (system or
> > > > > user) with new flags that work in combination with bpftool -L
> > > >
> > > > Could I just push back a bit on this.  The theory of hash chains
> > > > (which I've cut to shorten) is about pure data structures.  The
> > > > reason for that is that the entire hash chain is supposed to be
> > > > easily independently verifiable in any environment because anything
> > > > can compute the hashes of the blocks and links.  This independent
> > > > verification of the chain is key to formally proving hash chains to
> > > > be correct.  In your proposal we lose the easy verifiability
> > > > because the link hash is embedded in the ebpf loader program which
> > > > has to be disassembled to do the extraction of the hash and verify
> > > > the loader is actually checking it.
> > >
> > > I am not sure I understand your concern. This is something that can
> > > easily be built into tooling / annotations.
> > >
> > >     bpftool -S -v <verification_key> <loader> <metadata>
> > >
> > > Could you explain what's the use-case for "easy verifiability".
> >
> > I mean verifiability of the hash chain link.  Given a signed program,
> > (i.e. a .h file which is generated by bpftool) which is a signature
> > over the loader only how would one use simple cryptographic operations
> > to verify it?
> >
>
> I literally just said it above the hash can be extracted if you really
> want offline verification. Are you saying this code is hard to write?
> or is the tooling hard to write? Do you have some definition of
> "simple cryptographic operations".  All operations use tooling.
>
> >
> > >
> > > > I was looking at ways we could use a pure hash chain (i.e.
> > > > signature over loader and real map hash) and it does strike me that
> > > > the above ebpf hash verification code is pretty invariant and easy
> > > > to construct, so it could run as a separate BPF fragment that then
> > > > jumps to the real loader.  In that case, it could be constructed on
> > > > the fly in a trusted environment, like the kernel, from the link
> > > > hash in the signature and the signature could just be Sig(loader ||
> > > > map hash) which can then be
> > >
> > > The design I proposed does the same thing:
> > >
> > >     Sig(loader || H_metadata)
> > >
> > > metadata is actually the data (programs, context etc) that's passed
> > > in the map. The verification just happens in the loader program and
> > > the loader || H_metadata is implemented elegantly to avoid any
> > > separate payloads.
> >
> > OK, so I think this is the crux of the problem:  In formal methods
> > proving the validity of a data based hash link is an easy set of
> > cryptographic operations.  You can assert that's equivalent to a
> > signature over a program that verifies the hash, but formally proving
> > it requires a formal analysis of the program to show that 1) it
> > contains the correct hash and 2) it correctly checks the hash against
> > the map.  That makes the task of someone receiving the .h file
> > containing the signed skeleton way harder: it's easy to prove the
> > signature matches the loader instructions, but they still have to prove
> > the instructions contain and verify the correct map hash.
> >
>
> I don't see this as a problem for 2 reasons:
>
> 1. It's not hard
> 2. Your typical user does not want to do formal verification and
> extract signatures etc.
>
> [1] alone is enough.
>
> The key user journey is:
>
> * Build the program and the metadata
> * Sign the blob once (as explained)
> * A simple API to verify the sequence of operations.
>
> The user builds a program and signs the blob, they sign it because it
> contains the hash of the metadata. It seems like you are optimizing
> for the formal researcher but not for the tooling. The user just needs

I meant not for the user.

> good tooling and a simple API which is exactly what was proposed.
>
> - KP
>
> > Regards,
> >
> > James
> >
> >

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-14  3:06         ` Paul Moore
@ 2025-05-14 18:48           ` KP Singh
  2025-05-16 19:49             ` Paul Moore
  0 siblings, 1 reply; 41+ messages in thread
From: KP Singh @ 2025-05-14 18:48 UTC (permalink / raw)
  To: Paul Moore
  Cc: bboscaccy, James.Bottomley, bpf, code, corbet, davem, dhowells,
	gnoack, herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong,
	kysrinivasan, Linus Torvalds

On Wed, May 14, 2025 at 5:06 AM Paul Moore <paul@paul-moore.com> wrote:
>
> On Sat, May 10, 2025 at 10:01 PM KP Singh <kpsingh@kernel.org> wrote:
> >
>
> ...
>
> > The signature check in the verifier (during BPF_PROG_LOAD):
> >
> >     verify_pkcs7_signature(prog->aux->sha, sizeof(prog->aux->sha),
> > sig_from_bpf_attr, …);
>
> I think we still need to clarify the authorization aspect of your
> proposed design.
>
> Working under the assumption that the core BPF kernel code doesn't
> want to enforce any restrictions, or at least as few as possible, I'm

The assumption is not true, I should have clarified it in the original
design. With the UAPI / bpf_attr the bpf syscall is simply denied if
the signature does not verify, so we don't need any LSM logic for
this. There is really no point in continuing as signature verification
is a part of the API contract when the user passes the sig, keyring in
the bpf syscall.

Also, since we have a solid grasp on the design and its implementation
being contained, it will be much simpler for us to actually implement
the patches. We will keep you posted.

- KP

> expecting that the BPF kernel code would want to adopt an "allow all"
> policy when it comes to authorizing signed and unsigned BPF programs,
> delegating any additional restrictions to the LSM.  With that in mind
> I think we need to agree on a way for the BPF verifier to indicate
> that it has verified the signature is correct to the LSM, and we need
> a new LSM hook which runs *after* the verifier so that it can inspect
> the results of the signature verification.  While it might be tempting
> to relocate the existing security_bpf_prog_load() hook, I believe it
> makes sense to leave that hook before the verifier for those LSMs that
> wish control access prior to the verifier's inspection using criteria
> other than signatures.
>
> With respect to the LSM hook, since it appears that the signature is
> going to be included in the bpf_attr struct, and I'm *guessing* the
> best way for the verifier to indicate the result of the signature
> verification is via a field inside bpf_prog_aux, this means the hook
> could look something like this:
>
>   int security_bpf_prog_verified(bpf_prog, bpf_attr);
>
> ... and be called immediately after bpf_check() in bpf_prog_load().
> As far as the new field in bpf_prog_aux is concerned, I think we can
> probably start off with a simple bool to indicate whether a signature
> was verified or not, with an understanding that we can move to a
> richer construct in the future if we find it necessary.  Neither of
> these are directly visible to userspace so we have the ability to
> start simple and modify as needed.
>
> Does this sound reasonable to everyone?  Does anyone have any other
> thoughts on the authorization aspect of BPF signature verification?
>
> --
> paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-14 18:35               ` KP Singh
  2025-05-14 18:35                 ` KP Singh
@ 2025-05-14 20:31                 ` James Bottomley
  2025-05-14 20:41                   ` KP Singh
  1 sibling, 1 reply; 41+ messages in thread
From: James Bottomley @ 2025-05-14 20:31 UTC (permalink / raw)
  To: KP Singh
  Cc: Paul Moore, bboscaccy, bpf, code, corbet, davem, dhowells, gnoack,
	herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong,
	kysrinivasan, Linus Torvalds

On Wed, 2025-05-14 at 20:35 +0200, KP Singh wrote:
> On Wed, May 14, 2025 at 7:45 PM James Bottomley
> <James.Bottomley@hansenpartnership.com> wrote:
> > 
> > On Wed, 2025-05-14 at 19:17 +0200, KP Singh wrote:
> > > On Wed, May 14, 2025 at 5:39 PM James Bottomley
> > > <James.Bottomley@hansenpartnership.com> wrote:
> > > > On Sun, 2025-05-11 at 04:01 +0200, KP Singh wrote:
> > [...]
> > > > > This implicitly makes the payload equivalent to the signed
> > > > > block
> > > > > (B_signed)
> > > > > 
> > > > >     I_loader || H_meta
> > > > > 
> > > > > bpftool then generates the signature of this I_loader payload
> > > > > (which now contains the expected H_meta) using a key (system
> > > > > or
> > > > > user) with new flags that work in combination with bpftool -L
> > > > 
> > > > Could I just push back a bit on this.  The theory of hash
> > > > chains
> > > > (which I've cut to shorten) is about pure data structures.  The
> > > > reason for that is that the entire hash chain is supposed to be
> > > > easily independently verifiable in any environment because
> > > > anything
> > > > can compute the hashes of the blocks and links.  This
> > > > independent
> > > > verification of the chain is key to formally proving hash
> > > > chains to
> > > > be correct.  In your proposal we lose the easy verifiability
> > > > because the link hash is embedded in the ebpf loader program
> > > > which
> > > > has to be disassembled to do the extraction of the hash and
> > > > verify
> > > > the loader is actually checking it.
> > > 
> > > I am not sure I understand your concern. This is something that
> > > can
> > > easily be built into tooling / annotations.
> > > 
> > >     bpftool -S -v <verification_key> <loader> <metadata>
> > > 
> > > Could you explain what's the use-case for "easy verifiability".
> > 
> > I mean verifiability of the hash chain link.  Given a signed
> > program, (i.e. a .h file which is generated by bpftool) which is a
> > signature over the loader only how would one use simple
> > cryptographic operations to verify it?
> > 
> 
> I literally just said it above the hash can be extracted if you
> really want offline verification. Are you saying this code is hard to
> write? or is the tooling hard to write? Do you have some definition
> of "simple cryptographic operations".  All operations use tooling.

As I said, you have a gap in that you not only have to extract the hash
and verify it against the map (which I agree is fairly simple) but also
verify the loader program actually checks it correctly.  That latter
operation is not a simple cryptographic one and represents a security
gap between this proposal and the hash linked chains you introduced in
your first email in this thread.

> > > > I was looking at ways we could use a pure hash chain (i.e.
> > > > signature over loader and real map hash) and it does strike me
> > > > that the above ebpf hash verification code is pretty invariant
> > > > and easy to construct, so it could run as a separate BPF
> > > > fragment that then jumps to the real loader.  In that case, it
> > > > could be constructed on the fly in a trusted environment, like
> > > > the kernel, from the link hash in the signature and the
> > > > signature could just be Sig(loader || map hash) which can then
> > > > be
> > > 
> > > The design I proposed does the same thing:
> > > 
> > >     Sig(loader || H_metadata)
> > > 
> > > metadata is actually the data (programs, context etc) that's
> > > passed in the map. The verification just happens in the loader
> > > program and the loader || H_metadata is implemented elegantly to
> > > avoid any separate payloads.
> > 
> > OK, so I think this is the crux of the problem:  In formal methods
> > proving the validity of a data based hash link is an easy set of
> > cryptographic operations.  You can assert that's equivalent to a
> > signature over a program that verifies the hash, but formally
> > proving it requires a formal analysis of the program to show that
> > 1) it contains the correct hash and 2) it correctly checks the hash
> > against the map.  That makes the task of someone receiving the .h
> > file containing the signed skeleton way harder: it's easy to prove
> > the signature matches the loader instructions, but they still have
> > to prove the instructions contain and verify the correct map hash.
> > 
> 
> I don't see this as a problem for 2 reasons:
> 
> 1. It's not hard

it requires disassembling the first 20 or so BPF instructions and
verifying their operation, so that's harder than simply calculating
hashes and signatures.

> 2. Your typical user does not want to do formal verification and
> extract signatures etc.

Users don't want to do formal verification, agreed ... but they do want
to know that security experts have verified the algorithms they're
using.

That's why I was thinking, since the loader preamble that verifies the
hash is easy to construct, that the scheme could use a real hash linked
chain, which has already been formally verified and is well understood,
then construct the preamble for the loader you want in a trusted
environment based on the hashes, meaning there's no security gap.

Regards,

James


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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-14 20:31                 ` James Bottomley
@ 2025-05-14 20:41                   ` KP Singh
  0 siblings, 0 replies; 41+ messages in thread
From: KP Singh @ 2025-05-14 20:41 UTC (permalink / raw)
  To: James Bottomley
  Cc: Paul Moore, bboscaccy, bpf, code, corbet, davem, dhowells, gnoack,
	herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong,
	kysrinivasan, Linus Torvalds

On Wed, May 14, 2025 at 10:32 PM James Bottomley
<James.Bottomley@hansenpartnership.com> wrote:
>
> On Wed, 2025-05-14 at 20:35 +0200, KP Singh wrote:
> > On Wed, May 14, 2025 at 7:45 PM James Bottomley
> > <James.Bottomley@hansenpartnership.com> wrote:
> > >
> > > On Wed, 2025-05-14 at 19:17 +0200, KP Singh wrote:
> > > > On Wed, May 14, 2025 at 5:39 PM James Bottomley
> > > > <James.Bottomley@hansenpartnership.com> wrote:
> > > > > On Sun, 2025-05-11 at 04:01 +0200, KP Singh wrote:
> > > [...]
> > > > > > This implicitly makes the payload equivalent to the signed
> > > > > > block
> > > > > > (B_signed)
> > > > > >
> > > > > >     I_loader || H_meta
> > > > > >
> > > > > > bpftool then generates the signature of this I_loader payload
> > > > > > (which now contains the expected H_meta) using a key (system
> > > > > > or
> > > > > > user) with new flags that work in combination with bpftool -L
> > > > >
> > > > > Could I just push back a bit on this.  The theory of hash
> > > > > chains
> > > > > (which I've cut to shorten) is about pure data structures.  The
> > > > > reason for that is that the entire hash chain is supposed to be
> > > > > easily independently verifiable in any environment because
> > > > > anything
> > > > > can compute the hashes of the blocks and links.  This
> > > > > independent
> > > > > verification of the chain is key to formally proving hash
> > > > > chains to
> > > > > be correct.  In your proposal we lose the easy verifiability
> > > > > because the link hash is embedded in the ebpf loader program
> > > > > which
> > > > > has to be disassembled to do the extraction of the hash and
> > > > > verify
> > > > > the loader is actually checking it.
> > > >
> > > > I am not sure I understand your concern. This is something that
> > > > can
> > > > easily be built into tooling / annotations.
> > > >
> > > >     bpftool -S -v <verification_key> <loader> <metadata>
> > > >
> > > > Could you explain what's the use-case for "easy verifiability".
> > >
> > > I mean verifiability of the hash chain link.  Given a signed
> > > program, (i.e. a .h file which is generated by bpftool) which is a
> > > signature over the loader only how would one use simple
> > > cryptographic operations to verify it?
> > >
> >
> > I literally just said it above the hash can be extracted if you
> > really want offline verification. Are you saying this code is hard to
> > write? or is the tooling hard to write? Do you have some definition
> > of "simple cryptographic operations".  All operations use tooling.
>
> As I said, you have a gap in that you not only have to extract the hash
> and verify it against the map (which I agree is fairly simple) but also
> verify the loader program actually checks it correctly.  That latter
> operation is not a simple cryptographic one and represents a security
> gap between this proposal and the hash linked chains you introduced in
> your first email in this thread.

Sure, but I don't see this as being problematic. If it's hard for
folks who do theoretical work, then I think it's okay to push this
effort on them rather than every user.

>
> > > > > I was looking at ways we could use a pure hash chain (i.e.
> > > > > signature over loader and real map hash) and it does strike me
> > > > > that the above ebpf hash verification code is pretty invariant
> > > > > and easy to construct, so it could run as a separate BPF
> > > > > fragment that then jumps to the real loader.  In that case, it
> > > > > could be constructed on the fly in a trusted environment, like
> > > > > the kernel, from the link hash in the signature and the
> > > > > signature could just be Sig(loader || map hash) which can then
> > > > > be
> > > >
> > > > The design I proposed does the same thing:
> > > >
> > > >     Sig(loader || H_metadata)
> > > >
> > > > metadata is actually the data (programs, context etc) that's
> > > > passed in the map. The verification just happens in the loader
> > > > program and the loader || H_metadata is implemented elegantly to
> > > > avoid any separate payloads.
> > >
> > > OK, so I think this is the crux of the problem:  In formal methods
> > > proving the validity of a data based hash link is an easy set of
> > > cryptographic operations.  You can assert that's equivalent to a
> > > signature over a program that verifies the hash, but formally
> > > proving it requires a formal analysis of the program to show that
> > > 1) it contains the correct hash and 2) it correctly checks the hash
> > > against the map.  That makes the task of someone receiving the .h
> > > file containing the signed skeleton way harder: it's easy to prove
> > > the signature matches the loader instructions, but they still have
> > > to prove the instructions contain and verify the correct map hash.
> > >
> >
> > I don't see this as a problem for 2 reasons:
> >
> > 1. It's not hard
>
> it requires disassembling the first 20 or so BPF instructions and
> verifying their operation, so that's harder than simply calculating
> hashes and signatures.
>
> > 2. Your typical user does not want to do formal verification and
> > extract signatures etc.
>
> Users don't want to do formal verification, agreed ... but they do want
> to know that security experts have verified the algorithms they're
> using.
>
> That's why I was thinking, since the loader preamble that verifies the
> hash is easy to construct, that the scheme could use a real hash linked
> chain, which has already been formally verified and is well understood,
> then construct the preamble for the loader you want in a trusted
> environment based on the hashes, meaning there's no security gap.
>
> Regards,
>
> James
>

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-14 18:48           ` KP Singh
@ 2025-05-16 19:49             ` Paul Moore
  2025-05-16 23:49               ` Alexei Starovoitov
  0 siblings, 1 reply; 41+ messages in thread
From: Paul Moore @ 2025-05-16 19:49 UTC (permalink / raw)
  To: KP Singh
  Cc: bboscaccy, James.Bottomley, bpf, code, corbet, davem, dhowells,
	gnoack, herbert, jarkko, jmorris, jstancek, justinstitt, keyrings,
	linux-crypto, linux-doc, linux-kbuild, linux-kernel,
	linux-kselftest, linux-security-module, llvm, masahiroy, mic,
	morbo, nathan, neal, nick.desaulniers+lkml, nicolas, nkapron,
	roberto.sassu, serge, shuah, teknoraver, xiyou.wangcong,
	kysrinivasan, Linus Torvalds

On Wed, May 14, 2025 at 2:48 PM KP Singh <kpsingh@kernel.org> wrote:
> On Wed, May 14, 2025 at 5:06 AM Paul Moore <paul@paul-moore.com> wrote:
> > On Sat, May 10, 2025 at 10:01 PM KP Singh <kpsingh@kernel.org> wrote:
> > >
> >
> > ...
> >
> > > The signature check in the verifier (during BPF_PROG_LOAD):
> > >
> > >     verify_pkcs7_signature(prog->aux->sha, sizeof(prog->aux->sha),
> > > sig_from_bpf_attr, …);
> >
> > I think we still need to clarify the authorization aspect of your
> > proposed design.
> >
> > Working under the assumption that the core BPF kernel code doesn't
> > want to enforce any restrictions, or at least as few as possible ...
>
> The assumption is not true, I should have clarified it in the original
> design. With the UAPI / bpf_attr the bpf syscall is simply denied if
> the signature does not verify, so we don't need any LSM logic for
> this. There is really no point in continuing as signature verification
> is a part of the API contract when the user passes the sig, keyring in
> the bpf syscall.

I think we need some clarification on a few of these details, it would
be good if you could answer the questions below about the
authorization aspects of your design?

* Is the signature validation code in the BPF verifier *always* going
to be enforced when a signature is passed in from userspace?  In other
words, in your design is there going to be either a kernel build time
or runtime configuration knob that could selectively enable (or
disable) signature verification in the BPF verifier?

* In the case where the signature validation code in the BPF verifier
is active, what happens when a signature is *not* passed in from
userspace?  Will the BPF verifier allow the program load to take
place?  Will the load operation be blocked?  Will the load operation
be subject to a more granular policy, and if so, how do you plan to
incorporate that policy decision into the BPF program load path?

-- 
paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-16 19:49             ` Paul Moore
@ 2025-05-16 23:49               ` Alexei Starovoitov
  2025-05-17 15:02                 ` Paul Moore
  0 siblings, 1 reply; 41+ messages in thread
From: Alexei Starovoitov @ 2025-05-16 23:49 UTC (permalink / raw)
  To: Paul Moore
  Cc: KP Singh, Blaise Boscaccy, James Bottomley, bpf, code,
	Jonathan Corbet, David S. Miller, David Howells,
	Günther Noack, Herbert Xu, Jarkko Sakkinen, James Morris,
	Jan Stancek, Justin Stitt, keyrings, Linux Crypto Mailing List,
	open list:DOCUMENTATION, Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang, kysrinivasan, Linus Torvalds

On Fri, May 16, 2025 at 12:49 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Wed, May 14, 2025 at 2:48 PM KP Singh <kpsingh@kernel.org> wrote:
> > On Wed, May 14, 2025 at 5:06 AM Paul Moore <paul@paul-moore.com> wrote:
> > > On Sat, May 10, 2025 at 10:01 PM KP Singh <kpsingh@kernel.org> wrote:
> > > >
> > >
> > > ...
> > >
> > > > The signature check in the verifier (during BPF_PROG_LOAD):
> > > >
> > > >     verify_pkcs7_signature(prog->aux->sha, sizeof(prog->aux->sha),
> > > > sig_from_bpf_attr, …);
> > >
> > > I think we still need to clarify the authorization aspect of your
> > > proposed design.
> > >
> > > Working under the assumption that the core BPF kernel code doesn't
> > > want to enforce any restrictions, or at least as few as possible ...
> >
> > The assumption is not true, I should have clarified it in the original
> > design. With the UAPI / bpf_attr the bpf syscall is simply denied if
> > the signature does not verify, so we don't need any LSM logic for
> > this. There is really no point in continuing as signature verification
> > is a part of the API contract when the user passes the sig, keyring in
> > the bpf syscall.
>
> I think we need some clarification on a few of these details, it would
> be good if you could answer the questions below about the
> authorization aspects of your design?
>
> * Is the signature validation code in the BPF verifier *always* going
> to be enforced when a signature is passed in from userspace?  In other
> words, in your design is there going to be either a kernel build time
> or runtime configuration knob that could selectively enable (or
> disable) signature verification in the BPF verifier?

If there is a signature in union bpf_attr and it's incorrect
the prog_load command will be rejected.
No point in adding a knob to control that.

> * In the case where the signature validation code in the BPF verifier
> is active, what happens when a signature is *not* passed in from
> userspace?  Will the BPF verifier allow the program load to take
> place?  Will the load operation be blocked?  Will the load operation
> be subject to a more granular policy, and if so, how do you plan to
> incorporate that policy decision into the BPF program load path?

If there is no signature the existing loading semantics will remain intact.
We can discuss whether to add a sysctl or cgroup knob to disallow
loading when signature is not present, but it probably should be
a job of trivial LSM:
if (prog_attr doesn't have signature &&
   (task == .. || task is under certain cgroup || whatever))
  disallow.


Note that the prog verification itself is independent of the signature.
If prog fails to pass safety checks it will still be rejected
even if signature is ok.
We're not going to do a verifier bypass.

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-16 23:49               ` Alexei Starovoitov
@ 2025-05-17 15:02                 ` Paul Moore
  2025-05-17 16:13                   ` Alexei Starovoitov
  0 siblings, 1 reply; 41+ messages in thread
From: Paul Moore @ 2025-05-17 15:02 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: KP Singh, Blaise Boscaccy, James Bottomley, bpf, code,
	Jonathan Corbet, David S. Miller, David Howells,
	Günther Noack, Herbert Xu, Jarkko Sakkinen, James Morris,
	Jan Stancek, Justin Stitt, keyrings, Linux Crypto Mailing List,
	open list:DOCUMENTATION, Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang, kysrinivasan, Linus Torvalds

On Fri, May 16, 2025 at 7:49 PM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
> On Fri, May 16, 2025 at 12:49 PM Paul Moore <paul@paul-moore.com> wrote:
> >
> > I think we need some clarification on a few of these details, it would
> > be good if you could answer the questions below about the
> > authorization aspects of your design?
> >
> > * Is the signature validation code in the BPF verifier *always* going
> > to be enforced when a signature is passed in from userspace?  In other
> > words, in your design is there going to be either a kernel build time
> > or runtime configuration knob that could selectively enable (or
> > disable) signature verification in the BPF verifier?
>
> If there is a signature in union bpf_attr and it's incorrect
> the prog_load command will be rejected.
> No point in adding a knob to control that.

I agree that when a signature is provided and that signature check
fails, the BPF load should be rejected.  I'm simply trying to
understand how you envision your design handling all of the cases, not
just this one, as well as what build and runtime options you expect
for controlling various aspects of this behavior.

> > * In the case where the signature validation code in the BPF verifier
> > is active, what happens when a signature is *not* passed in from
> > userspace?  Will the BPF verifier allow the program load to take
> > place?  Will the load operation be blocked?  Will the load operation
> > be subject to a more granular policy, and if so, how do you plan to
> > incorporate that policy decision into the BPF program load path?
>
> If there is no signature the existing loading semantics will remain intact.
> We can discuss whether to add a sysctl or cgroup knob to disallow
> loading when signature is not present ...

As mentioned earlier this week, if the BPF verifier is performing the
signature verification as KP described, we will need a LSM hook after
the verifier to serve as an access control point.  Of course that
doesn't preclude the addition of some type of sysctl/cgroup/whatever
based access control, but the LSM hook would be needed regardless.

> but it probably should be a job of trivial LSM ...

Exactly.  If the LSM is simply verifying the signature validation
state of the BPF program being loaded it seems like an addition to IPE
would be the best option from an upstream, in-tree perspective.
However, with the post verifier LSM hook in place, one could also
supply a BPF LSM to do something similar.

It sounds like we are in agreement on the desirability and need for a
post verifier LSM hook; we'll keep moving forward with this idea
despite KP's earlier objections to the hook.

> Note that the prog verification itself is independent of the signature.
> If prog fails to pass safety checks it will still be rejected
> even if signature is ok.

There is plenty of precedence for a kernel subsystem rejecting a
security relevant operation before a LSM access control hook is
called; the reasons range from discretionary access control issues to
simple matters of resource exhaustion.  The possibility of the BPF
verifier rejecting the program load due to verifier constraints is
reasonable and expected.

> We're not going to do a verifier bypass.

Agreed.  I don't recall anyone ever suggesting that as part of this
recent BPF signature verification effort.

-- 
paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-17 15:02                 ` Paul Moore
@ 2025-05-17 16:13                   ` Alexei Starovoitov
  2025-05-18  5:48                     ` Paul Moore
  0 siblings, 1 reply; 41+ messages in thread
From: Alexei Starovoitov @ 2025-05-17 16:13 UTC (permalink / raw)
  To: Paul Moore
  Cc: KP Singh, Blaise Boscaccy, James Bottomley, bpf, code,
	Jonathan Corbet, David S. Miller, David Howells,
	Günther Noack, Herbert Xu, Jarkko Sakkinen, James Morris,
	Jan Stancek, Justin Stitt, keyrings, Linux Crypto Mailing List,
	open list:DOCUMENTATION, Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang, kysrinivasan, Linus Torvalds

On Sat, May 17, 2025 at 8:03 AM Paul Moore <paul@paul-moore.com> wrote:
>
> On Fri, May 16, 2025 at 7:49 PM Alexei Starovoitov
> <alexei.starovoitov@gmail.com> wrote:
> > On Fri, May 16, 2025 at 12:49 PM Paul Moore <paul@paul-moore.com> wrote:
> > >
> > > I think we need some clarification on a few of these details, it would
> > > be good if you could answer the questions below about the
> > > authorization aspects of your design?
> > >
> > > * Is the signature validation code in the BPF verifier *always* going
> > > to be enforced when a signature is passed in from userspace?  In other
> > > words, in your design is there going to be either a kernel build time
> > > or runtime configuration knob that could selectively enable (or
> > > disable) signature verification in the BPF verifier?
> >
> > If there is a signature in union bpf_attr and it's incorrect
> > the prog_load command will be rejected.
> > No point in adding a knob to control that.
>
> I agree that when a signature is provided and that signature check
> fails, the BPF load should be rejected.  I'm simply trying to
> understand how you envision your design handling all of the cases, not
> just this one, as well as what build and runtime options you expect
> for controlling various aspects of this behavior.
>
> > > * In the case where the signature validation code in the BPF verifier
> > > is active, what happens when a signature is *not* passed in from
> > > userspace?  Will the BPF verifier allow the program load to take
> > > place?  Will the load operation be blocked?  Will the load operation
> > > be subject to a more granular policy, and if so, how do you plan to
> > > incorporate that policy decision into the BPF program load path?
> >
> > If there is no signature the existing loading semantics will remain intact.
> > We can discuss whether to add a sysctl or cgroup knob to disallow
> > loading when signature is not present ...
>
> As mentioned earlier this week, if the BPF verifier is performing the
> signature verification as KP described, we will need a LSM hook after
> the verifier to serve as an access control point.  Of course that
> doesn't preclude the addition of some type of sysctl/cgroup/whatever
> based access control, but the LSM hook would be needed regardless.

No. New hook is not needed.

> > but it probably should be a job of trivial LSM ...
>
> Exactly.  If the LSM is simply verifying the signature validation
> state of the BPF program being loaded it seems like an addition to IPE
> would be the best option from an upstream, in-tree perspective.
> However, with the post verifier LSM hook in place, one could also
> supply a BPF LSM to do something similar.
>
> It sounds like we are in agreement on the desirability and need for a
> post verifier LSM hook; we'll keep moving forward with this idea
> despite KP's earlier objections to the hook.

Don't twist my words please.
We're absolutely _not_ in agreement.
What I described above can be done with the existing hook and
its current set of arguments.
We're not going to move or change the existing hook.

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-17 16:13                   ` Alexei Starovoitov
@ 2025-05-18  5:48                     ` Paul Moore
  2025-05-18 15:52                       ` Alexei Starovoitov
  0 siblings, 1 reply; 41+ messages in thread
From: Paul Moore @ 2025-05-18  5:48 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: KP Singh, Blaise Boscaccy, James Bottomley, bpf, code,
	Jonathan Corbet, David S. Miller, David Howells,
	Günther Noack, Herbert Xu, Jarkko Sakkinen, James Morris,
	Jan Stancek, Justin Stitt, keyrings, Linux Crypto Mailing List,
	open list:DOCUMENTATION, Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang, kysrinivasan, Linus Torvalds

On May 17, 2025 12:13:50 PM Alexei Starovoitov 
<alexei.starovoitov@gmail.com> wrote:
> On Sat, May 17, 2025 at 8:03 AM Paul Moore <paul@paul-moore.com> wrote:
>> On Fri, May 16, 2025 at 7:49 PM Alexei Starovoitov
>> <alexei.starovoitov@gmail.com> wrote:
>>> On Fri, May 16, 2025 at 12:49 PM Paul Moore <paul@paul-moore.com> wrote:
>>>>
>>>> I think we need some clarification on a few of these details, it would
>>>> be good if you could answer the questions below about the
>>>> authorization aspects of your design?
>>>>
>>>> * Is the signature validation code in the BPF verifier *always* going
>>>> to be enforced when a signature is passed in from userspace?  In other
>>>> words, in your design is there going to be either a kernel build time
>>>> or runtime configuration knob that could selectively enable (or
>>>> disable) signature verification in the BPF verifier?
>>>
>>> If there is a signature in union bpf_attr and it's incorrect
>>> the prog_load command will be rejected.
>>> No point in adding a knob to control that.
>>
>> I agree that when a signature is provided and that signature check
>> fails, the BPF load should be rejected.  I'm simply trying to
>> understand how you envision your design handling all of the cases, not
>> just this one, as well as what build and runtime options you expect
>> for controlling various aspects of this behavior.
>>
>>>> * In the case where the signature validation code in the BPF verifier
>>>> is active, what happens when a signature is *not* passed in from
>>>> userspace?  Will the BPF verifier allow the program load to take
>>>> place?  Will the load operation be blocked?  Will the load operation
>>>> be subject to a more granular policy, and if so, how do you plan to
>>>> incorporate that policy decision into the BPF program load path?
>>>
>>> If there is no signature the existing loading semantics will remain intact.
>>> We can discuss whether to add a sysctl or cgroup knob to disallow
>>> loading when signature is not present ...
>>
>> As mentioned earlier this week, if the BPF verifier is performing the
>> signature verification as KP described, we will need a LSM hook after
>> the verifier to serve as an access control point.  Of course that
>> doesn't preclude the addition of some type of sysctl/cgroup/whatever
>> based access control, but the LSM hook would be needed regardless.
>
> No. New hook is not needed.

It would be good for you to explain how the existing LSM hook is sufficient 
to authorize the loading of a BPF program using the signature validation 
state determined in the BPF verifier.

--
paul-moore.com

>




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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-18  5:48                     ` Paul Moore
@ 2025-05-18 15:52                       ` Alexei Starovoitov
  2025-05-18 21:34                         ` Paul Moore
  0 siblings, 1 reply; 41+ messages in thread
From: Alexei Starovoitov @ 2025-05-18 15:52 UTC (permalink / raw)
  To: Paul Moore
  Cc: KP Singh, Blaise Boscaccy, James Bottomley, bpf, code,
	Jonathan Corbet, David S. Miller, David Howells,
	Günther Noack, Herbert Xu, Jarkko Sakkinen, James Morris,
	Jan Stancek, Justin Stitt, keyrings, Linux Crypto Mailing List,
	open list:DOCUMENTATION, Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang, kysrinivasan, Linus Torvalds

On Sat, May 17, 2025 at 10:49 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On May 17, 2025 12:13:50 PM Alexei Starovoitov
> <alexei.starovoitov@gmail.com> wrote:
> > On Sat, May 17, 2025 at 8:03 AM Paul Moore <paul@paul-moore.com> wrote:
> >> On Fri, May 16, 2025 at 7:49 PM Alexei Starovoitov
> >> <alexei.starovoitov@gmail.com> wrote:
> >>> On Fri, May 16, 2025 at 12:49 PM Paul Moore <paul@paul-moore.com> wrote:
> >>>>
> >>>> I think we need some clarification on a few of these details, it would
> >>>> be good if you could answer the questions below about the
> >>>> authorization aspects of your design?
> >>>>
> >>>> * Is the signature validation code in the BPF verifier *always* going
> >>>> to be enforced when a signature is passed in from userspace?  In other
> >>>> words, in your design is there going to be either a kernel build time
> >>>> or runtime configuration knob that could selectively enable (or
> >>>> disable) signature verification in the BPF verifier?
> >>>
> >>> If there is a signature in union bpf_attr and it's incorrect
> >>> the prog_load command will be rejected.
> >>> No point in adding a knob to control that.
> >>
> >> I agree that when a signature is provided and that signature check
> >> fails, the BPF load should be rejected.  I'm simply trying to
> >> understand how you envision your design handling all of the cases, not
> >> just this one, as well as what build and runtime options you expect
> >> for controlling various aspects of this behavior.
> >>
> >>>> * In the case where the signature validation code in the BPF verifier
> >>>> is active, what happens when a signature is *not* passed in from
> >>>> userspace?  Will the BPF verifier allow the program load to take
> >>>> place?  Will the load operation be blocked?  Will the load operation
> >>>> be subject to a more granular policy, and if so, how do you plan to
> >>>> incorporate that policy decision into the BPF program load path?
> >>>
> >>> If there is no signature the existing loading semantics will remain intact.
> >>> We can discuss whether to add a sysctl or cgroup knob to disallow
> >>> loading when signature is not present ...
> >>
> >> As mentioned earlier this week, if the BPF verifier is performing the
> >> signature verification as KP described, we will need a LSM hook after
> >> the verifier to serve as an access control point.  Of course that
> >> doesn't preclude the addition of some type of sysctl/cgroup/whatever
> >> based access control, but the LSM hook would be needed regardless.
> >
> > No. New hook is not needed.
>
> It would be good for you to explain how the existing LSM hook is sufficient
> to authorize the loading of a BPF program using the signature validation
> state determined in the BPF verifier.

I already explained:
.. a job of trivial LSM:
if (prog_attr doesn't have signature &&
   (task == .. || task is under certain cgroup || whatever))
  disallow.

If that's not obvious you have to wait for patches.

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-18 15:52                       ` Alexei Starovoitov
@ 2025-05-18 21:34                         ` Paul Moore
  2025-05-19 22:20                           ` KP Singh
  0 siblings, 1 reply; 41+ messages in thread
From: Paul Moore @ 2025-05-18 21:34 UTC (permalink / raw)
  To: Alexei Starovoitov
  Cc: KP Singh, Blaise Boscaccy, James Bottomley, bpf, code,
	Jonathan Corbet, David S. Miller, David Howells,
	Günther Noack, Herbert Xu, Jarkko Sakkinen, James Morris,
	Jan Stancek, Justin Stitt, keyrings, Linux Crypto Mailing List,
	open list:DOCUMENTATION, Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang, kysrinivasan, Linus Torvalds

On Sun, May 18, 2025 at 11:52 AM Alexei Starovoitov
<alexei.starovoitov@gmail.com> wrote:
> On Sat, May 17, 2025 at 10:49 PM Paul Moore <paul@paul-moore.com> wrote:
> > On May 17, 2025 12:13:50 PM Alexei Starovoitov
> > <alexei.starovoitov@gmail.com> wrote:
> > > On Sat, May 17, 2025 at 8:03 AM Paul Moore <paul@paul-moore.com> wrote:
> > >> On Fri, May 16, 2025 at 7:49 PM Alexei Starovoitov
> > >> <alexei.starovoitov@gmail.com> wrote:
> > >>> On Fri, May 16, 2025 at 12:49 PM Paul Moore <paul@paul-moore.com> wrote:
> > >>>>
> > >>>> I think we need some clarification on a few of these details, it would
> > >>>> be good if you could answer the questions below about the
> > >>>> authorization aspects of your design?
> > >>>>
> > >>>> * Is the signature validation code in the BPF verifier *always* going
> > >>>> to be enforced when a signature is passed in from userspace?  In other
> > >>>> words, in your design is there going to be either a kernel build time
> > >>>> or runtime configuration knob that could selectively enable (or
> > >>>> disable) signature verification in the BPF verifier?
> > >>>
> > >>> If there is a signature in union bpf_attr and it's incorrect
> > >>> the prog_load command will be rejected.
> > >>> No point in adding a knob to control that.
> > >>
> > >> I agree that when a signature is provided and that signature check
> > >> fails, the BPF load should be rejected.  I'm simply trying to
> > >> understand how you envision your design handling all of the cases, not
> > >> just this one, as well as what build and runtime options you expect
> > >> for controlling various aspects of this behavior.
> > >>
> > >>>> * In the case where the signature validation code in the BPF verifier
> > >>>> is active, what happens when a signature is *not* passed in from
> > >>>> userspace?  Will the BPF verifier allow the program load to take
> > >>>> place?  Will the load operation be blocked?  Will the load operation
> > >>>> be subject to a more granular policy, and if so, how do you plan to
> > >>>> incorporate that policy decision into the BPF program load path?
> > >>>
> > >>> If there is no signature the existing loading semantics will remain intact.
> > >>> We can discuss whether to add a sysctl or cgroup knob to disallow
> > >>> loading when signature is not present ...
> > >>
> > >> As mentioned earlier this week, if the BPF verifier is performing the
> > >> signature verification as KP described, we will need a LSM hook after
> > >> the verifier to serve as an access control point.  Of course that
> > >> doesn't preclude the addition of some type of sysctl/cgroup/whatever
> > >> based access control, but the LSM hook would be needed regardless.
> > >
> > > No. New hook is not needed.
> >
> > It would be good for you to explain how the existing LSM hook is sufficient
> > to authorize the loading of a BPF program using the signature validation
> > state determined in the BPF verifier.
>
> I already explained:
> .. a job of trivial LSM:
> if (prog_attr doesn't have signature &&
>    (task == .. || task is under certain cgroup || whatever))
>   disallow.

I read that earlier reply as an example that covers a sample use case,
I didn't realize you were asserting that was the only approach you
were considering.  Perhaps that was the source of confusion earlier,
we may disagree, but I don't intentionally "twist" words; not only is
that rude, it's just stupid in public, archived discussions.

As I mentioned previously, we really need to see an explicit yes/no
flag from the BPF verifier to indicate that the signature on the BPF
program has been validated.  It really should be as simple as adding a
bool to bpf_prog_aux which the BPF verifier sets to true upon
successful signature validation, and then an LSM can use this flag as
input to an access control decision in a hook placed after the
verifier.  Are you objecting to the addition of a flag in the
bpf_prog_aux struct (or some other struct tightly coupled to the BPF
program), the LSM hook after the verifier, or both?  It would also be
helpful if you can elaborate on the technical reasons behind these
objections.

-- 
paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-18 21:34                         ` Paul Moore
@ 2025-05-19 22:20                           ` KP Singh
  2025-05-19 22:58                             ` Paul Moore
  2025-05-19 23:00                             ` Zvi Effron
  0 siblings, 2 replies; 41+ messages in thread
From: KP Singh @ 2025-05-19 22:20 UTC (permalink / raw)
  To: Paul Moore
  Cc: Alexei Starovoitov, Blaise Boscaccy, James Bottomley, bpf, code,
	Jonathan Corbet, David S. Miller, David Howells,
	Günther Noack, Herbert Xu, Jarkko Sakkinen, James Morris,
	Jan Stancek, Justin Stitt, keyrings, Linux Crypto Mailing List,
	open list:DOCUMENTATION, Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang, kysrinivasan, Linus Torvalds

On Sun, May 18, 2025 at 11:34 PM Paul Moore <paul@paul-moore.com> wrote:
>
> On Sun, May 18, 2025 at 11:52 AM Alexei Starovoitov
> <alexei.starovoitov@gmail.com> wrote:
> > On Sat, May 17, 2025 at 10:49 PM Paul Moore <paul@paul-moore.com> wrote:
> > > On May 17, 2025 12:13:50 PM Alexei Starovoitov
> > > <alexei.starovoitov@gmail.com> wrote:
> > > > On Sat, May 17, 2025 at 8:03 AM Paul Moore <paul@paul-moore.com> wrote:
> > > >> On Fri, May 16, 2025 at 7:49 PM Alexei Starovoitov
> > > >> <alexei.starovoitov@gmail.com> wrote:
> > > >>> On Fri, May 16, 2025 at 12:49 PM Paul Moore <paul@paul-moore.com> wrote:
> > > >>>>
> > > >>>> I think we need some clarification on a few of these details, it would
> > > >>>> be good if you could answer the questions below about the
> > > >>>> authorization aspects of your design?
> > > >>>>
> > > >>>> * Is the signature validation code in the BPF verifier *always* going
> > > >>>> to be enforced when a signature is passed in from userspace?  In other
> > > >>>> words, in your design is there going to be either a kernel build time
> > > >>>> or runtime configuration knob that could selectively enable (or
> > > >>>> disable) signature verification in the BPF verifier?
> > > >>>
> > > >>> If there is a signature in union bpf_attr and it's incorrect
> > > >>> the prog_load command will be rejected.
> > > >>> No point in adding a knob to control that.
> > > >>
> > > >> I agree that when a signature is provided and that signature check
> > > >> fails, the BPF load should be rejected.  I'm simply trying to
> > > >> understand how you envision your design handling all of the cases, not
> > > >> just this one, as well as what build and runtime options you expect
> > > >> for controlling various aspects of this behavior.
> > > >>
> > > >>>> * In the case where the signature validation code in the BPF verifier
> > > >>>> is active, what happens when a signature is *not* passed in from
> > > >>>> userspace?  Will the BPF verifier allow the program load to take
> > > >>>> place?  Will the load operation be blocked?  Will the load operation
> > > >>>> be subject to a more granular policy, and if so, how do you plan to
> > > >>>> incorporate that policy decision into the BPF program load path?
> > > >>>
> > > >>> If there is no signature the existing loading semantics will remain intact.
> > > >>> We can discuss whether to add a sysctl or cgroup knob to disallow
> > > >>> loading when signature is not present ...
> > > >>
> > > >> As mentioned earlier this week, if the BPF verifier is performing the
> > > >> signature verification as KP described, we will need a LSM hook after
> > > >> the verifier to serve as an access control point.  Of course that
> > > >> doesn't preclude the addition of some type of sysctl/cgroup/whatever
> > > >> based access control, but the LSM hook would be needed regardless.
> > > >
> > > > No. New hook is not needed.
> > >
> > > It would be good for you to explain how the existing LSM hook is sufficient
> > > to authorize the loading of a BPF program using the signature validation
> > > state determined in the BPF verifier.
> >
> > I already explained:
> > .. a job of trivial LSM:
> > if (prog_attr doesn't have signature &&
> >    (task == .. || task is under certain cgroup || whatever))
> >   disallow.
>
> I read that earlier reply as an example that covers a sample use case,
> I didn't realize you were asserting that was the only approach you
> were considering.  Perhaps that was the source of confusion earlier,
> we may disagree, but I don't intentionally "twist" words; not only is
> that rude, it's just stupid in public, archived discussions.
>
> As I mentioned previously, we really need to see an explicit yes/no
> flag from the BPF verifier to indicate that the signature on the BPF
> program has been validated.  It really should be as simple as adding a
> bool to bpf_prog_aux which the BPF verifier sets to true upon
> successful signature validation, and then an LSM can use this flag as
> input to an access control decision in a hook placed after the
> verifier.  Are you objecting to the addition of a flag in the
> bpf_prog_aux struct (or some other struct tightly coupled to the BPF
> program), the LSM hook after the verifier, or both?  It would also be
> helpful if you can elaborate on the technical reasons behind these
> objections.

Neither the aux field, nor the hook are required because:

* If the signature is passed, it will be enforced, there are no
"runtime aspects" that need to be configurable here.
* What the LSM can specify a policy for is when a signature is not
passed, for this, it does not need an aux field or a signature or the
new hook, existing hooks are sufficient.

- KP

>
> --
> paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-19 22:20                           ` KP Singh
@ 2025-05-19 22:58                             ` Paul Moore
  2025-05-21 22:26                               ` Paul Moore
  2025-05-19 23:00                             ` Zvi Effron
  1 sibling, 1 reply; 41+ messages in thread
From: Paul Moore @ 2025-05-19 22:58 UTC (permalink / raw)
  To: KP Singh
  Cc: Alexei Starovoitov, Blaise Boscaccy, James Bottomley, bpf, code,
	Jonathan Corbet, David S. Miller, David Howells,
	Günther Noack, Herbert Xu, Jarkko Sakkinen, James Morris,
	Jan Stancek, Justin Stitt, keyrings, Linux Crypto Mailing List,
	open list:DOCUMENTATION, Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang, kysrinivasan, Linus Torvalds

On Mon, May 19, 2025 at 6:20 PM KP Singh <kpsingh@kernel.org> wrote:
> On Sun, May 18, 2025 at 11:34 PM Paul Moore <paul@paul-moore.com> wrote:
> > On Sun, May 18, 2025 at 11:52 AM Alexei Starovoitov
> > <alexei.starovoitov@gmail.com> wrote:
> > > On Sat, May 17, 2025 at 10:49 PM Paul Moore <paul@paul-moore.com> wrote:
> > > > On May 17, 2025 12:13:50 PM Alexei Starovoitov
> > > > <alexei.starovoitov@gmail.com> wrote:
> > > > > On Sat, May 17, 2025 at 8:03 AM Paul Moore <paul@paul-moore.com> wrote:
> > > > >> On Fri, May 16, 2025 at 7:49 PM Alexei Starovoitov
> > > > >> <alexei.starovoitov@gmail.com> wrote:
> > > > >>> On Fri, May 16, 2025 at 12:49 PM Paul Moore <paul@paul-moore.com> wrote:
> > > > >>>>
> > > > >>>> I think we need some clarification on a few of these details, it would
> > > > >>>> be good if you could answer the questions below about the
> > > > >>>> authorization aspects of your design?
> > > > >>>>
> > > > >>>> * Is the signature validation code in the BPF verifier *always* going
> > > > >>>> to be enforced when a signature is passed in from userspace?  In other
> > > > >>>> words, in your design is there going to be either a kernel build time
> > > > >>>> or runtime configuration knob that could selectively enable (or
> > > > >>>> disable) signature verification in the BPF verifier?
> > > > >>>
> > > > >>> If there is a signature in union bpf_attr and it's incorrect
> > > > >>> the prog_load command will be rejected.
> > > > >>> No point in adding a knob to control that.
> > > > >>
> > > > >> I agree that when a signature is provided and that signature check
> > > > >> fails, the BPF load should be rejected.  I'm simply trying to
> > > > >> understand how you envision your design handling all of the cases, not
> > > > >> just this one, as well as what build and runtime options you expect
> > > > >> for controlling various aspects of this behavior.
> > > > >>
> > > > >>>> * In the case where the signature validation code in the BPF verifier
> > > > >>>> is active, what happens when a signature is *not* passed in from
> > > > >>>> userspace?  Will the BPF verifier allow the program load to take
> > > > >>>> place?  Will the load operation be blocked?  Will the load operation
> > > > >>>> be subject to a more granular policy, and if so, how do you plan to
> > > > >>>> incorporate that policy decision into the BPF program load path?
> > > > >>>
> > > > >>> If there is no signature the existing loading semantics will remain intact.
> > > > >>> We can discuss whether to add a sysctl or cgroup knob to disallow
> > > > >>> loading when signature is not present ...
> > > > >>
> > > > >> As mentioned earlier this week, if the BPF verifier is performing the
> > > > >> signature verification as KP described, we will need a LSM hook after
> > > > >> the verifier to serve as an access control point.  Of course that
> > > > >> doesn't preclude the addition of some type of sysctl/cgroup/whatever
> > > > >> based access control, but the LSM hook would be needed regardless.
> > > > >
> > > > > No. New hook is not needed.
> > > >
> > > > It would be good for you to explain how the existing LSM hook is sufficient
> > > > to authorize the loading of a BPF program using the signature validation
> > > > state determined in the BPF verifier.
> > >
> > > I already explained:
> > > .. a job of trivial LSM:
> > > if (prog_attr doesn't have signature &&
> > >    (task == .. || task is under certain cgroup || whatever))
> > >   disallow.
> >
> > I read that earlier reply as an example that covers a sample use case,
> > I didn't realize you were asserting that was the only approach you
> > were considering.  Perhaps that was the source of confusion earlier,
> > we may disagree, but I don't intentionally "twist" words; not only is
> > that rude, it's just stupid in public, archived discussions.
> >
> > As I mentioned previously, we really need to see an explicit yes/no
> > flag from the BPF verifier to indicate that the signature on the BPF
> > program has been validated.  It really should be as simple as adding a
> > bool to bpf_prog_aux which the BPF verifier sets to true upon
> > successful signature validation, and then an LSM can use this flag as
> > input to an access control decision in a hook placed after the
> > verifier.  Are you objecting to the addition of a flag in the
> > bpf_prog_aux struct (or some other struct tightly coupled to the BPF
> > program), the LSM hook after the verifier, or both?  It would also be
> > helpful if you can elaborate on the technical reasons behind these
> > objections.
>
> Neither the aux field, nor the hook are required because:
>
> * If the signature is passed, it will be enforced, there are no
> "runtime aspects" that need to be configurable here.
> * What the LSM can specify a policy for is when a signature is not
> passed, for this, it does not need an aux field or a signature or the
> new hook, existing hooks are sufficient.

When the kernel performs a security relevant operation, such as
verifying the signature on a BPF program, where the result of the
operation serves as input to a policy decision, system measurement,
audit event, etc. the LSM hook needs to be located after the security
relevant operation takes place so that the hook is able to properly
take into account the state of the event/system and record the actual
result as opposed to an implied result (this is critical for auditing,
measurement, attestation, etc.).

You explained why you believe the field/hook is not required, but I'm
asking for your *technical*objections*.  I understand that you believe
these changes are not required, but as described above, I happen to
disagree and therefore it would be helpful to understand the technical
reasons why you can't accept the field/hook changes.  Is there a
technical reason which would prevent such changes, or is it simply a
rejection of the use case and requirements above?

-- 
paul-moore.com

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-19 22:20                           ` KP Singh
  2025-05-19 22:58                             ` Paul Moore
@ 2025-05-19 23:00                             ` Zvi Effron
  2025-05-19 23:42                               ` KP Singh
  1 sibling, 1 reply; 41+ messages in thread
From: Zvi Effron @ 2025-05-19 23:00 UTC (permalink / raw)
  To: KP Singh
  Cc: Paul Moore, Alexei Starovoitov, Blaise Boscaccy, James Bottomley,
	bpf, code, Jonathan Corbet, David S. Miller, David Howells,
	Günther Noack, Herbert Xu, Jarkko Sakkinen, James Morris,
	Jan Stancek, Justin Stitt, keyrings, Linux Crypto Mailing List,
	open list:DOCUMENTATION, Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang, kysrinivasan, Linus Torvalds

On Mon, May 19, 2025 at 3:20 PM KP Singh <kpsingh@kernel.org> wrote:
>
> On Sun, May 18, 2025 at 11:34 PM Paul Moore <paul@paul-moore.com> wrote:
> >
> > On Sun, May 18, 2025 at 11:52 AM Alexei Starovoitov
> > <alexei.starovoitov@gmail.com> wrote:
> > > On Sat, May 17, 2025 at 10:49 PM Paul Moore <paul@paul-moore.com> wrote:
> > > > On May 17, 2025 12:13:50 PM Alexei Starovoitov
> > > > <alexei.starovoitov@gmail.com> wrote:
> > > > > On Sat, May 17, 2025 at 8:03 AM Paul Moore <paul@paul-moore.com> wrote:
> > > > >> On Fri, May 16, 2025 at 7:49 PM Alexei Starovoitov
> > > > >> <alexei.starovoitov@gmail.com> wrote:
> > > > >>> On Fri, May 16, 2025 at 12:49 PM Paul Moore <paul@paul-moore.com> wrote:
> > > > >>>>
> > > > >>>> I think we need some clarification on a few of these details, it would
> > > > >>>> be good if you could answer the questions below about the
> > > > >>>> authorization aspects of your design?
> > > > >>>>
> > > > >>>> * Is the signature validation code in the BPF verifier *always* going
> > > > >>>> to be enforced when a signature is passed in from userspace?  In other
> > > > >>>> words, in your design is there going to be either a kernel build time
> > > > >>>> or runtime configuration knob that could selectively enable (or
> > > > >>>> disable) signature verification in the BPF verifier?
> > > > >>>
> > > > >>> If there is a signature in union bpf_attr and it's incorrect
> > > > >>> the prog_load command will be rejected.
> > > > >>> No point in adding a knob to control that.
> > > > >>
> > > > >> I agree that when a signature is provided and that signature check
> > > > >> fails, the BPF load should be rejected.  I'm simply trying to
> > > > >> understand how you envision your design handling all of the cases, not
> > > > >> just this one, as well as what build and runtime options you expect
> > > > >> for controlling various aspects of this behavior.
> > > > >>
> > > > >>>> * In the case where the signature validation code in the BPF verifier
> > > > >>>> is active, what happens when a signature is *not* passed in from
> > > > >>>> userspace?  Will the BPF verifier allow the program load to take
> > > > >>>> place?  Will the load operation be blocked?  Will the load operation
> > > > >>>> be subject to a more granular policy, and if so, how do you plan to
> > > > >>>> incorporate that policy decision into the BPF program load path?
> > > > >>>
> > > > >>> If there is no signature the existing loading semantics will remain intact.
> > > > >>> We can discuss whether to add a sysctl or cgroup knob to disallow
> > > > >>> loading when signature is not present ...
> > > > >>
> > > > >> As mentioned earlier this week, if the BPF verifier is performing the
> > > > >> signature verification as KP described, we will need a LSM hook after
> > > > >> the verifier to serve as an access control point.  Of course that
> > > > >> doesn't preclude the addition of some type of sysctl/cgroup/whatever
> > > > >> based access control, but the LSM hook would be needed regardless.
> > > > >
> > > > > No. New hook is not needed.
> > > >
> > > > It would be good for you to explain how the existing LSM hook is sufficient
> > > > to authorize the loading of a BPF program using the signature validation
> > > > state determined in the BPF verifier.
> > >
> > > I already explained:
> > > .. a job of trivial LSM:
> > > if (prog_attr doesn't have signature &&
> > >    (task == .. || task is under certain cgroup || whatever))
> > >   disallow.
> >
> > I read that earlier reply as an example that covers a sample use case,
> > I didn't realize you were asserting that was the only approach you
> > were considering.  Perhaps that was the source of confusion earlier,
> > we may disagree, but I don't intentionally "twist" words; not only is
> > that rude, it's just stupid in public, archived discussions.
> >
> > As I mentioned previously, we really need to see an explicit yes/no
> > flag from the BPF verifier to indicate that the signature on the BPF
> > program has been validated.  It really should be as simple as adding a
> > bool to bpf_prog_aux which the BPF verifier sets to true upon
> > successful signature validation, and then an LSM can use this flag as
> > input to an access control decision in a hook placed after the
> > verifier.  Are you objecting to the addition of a flag in the
> > bpf_prog_aux struct (or some other struct tightly coupled to the BPF
> > program), the LSM hook after the verifier, or both?  It would also be
> > helpful if you can elaborate on the technical reasons behind these
> > objections.
>
> Neither the aux field, nor the hook are required because:
>
> * If the signature is passed, it will be enforced, there are no
> "runtime aspects" that need to be configurable here.
> * What the LSM can specify a policy for is when a signature is not
> passed, for this, it does not need an aux field or a signature or the
> new hook, existing hooks are sufficient.
>

What about wanting to create a policy that requires signatures under certain
situations and allowing the lack of a signature under others? How is that
implemented with the existing hooks?
As I understand it, all the existing hooks know (would know) is that _if_ there
is a signature _then_ it will be enforced. There is no way to know _whether_
there is a signature.

An example policy I can think of is that most users (with CAP_BPF) must submit
signed programs but some users are exempted. Would that policy be able to be
made with the current hooks?

> - KP
>
> >
> > --
> > paul-moore.com
>

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-19 23:00                             ` Zvi Effron
@ 2025-05-19 23:42                               ` KP Singh
  0 siblings, 0 replies; 41+ messages in thread
From: KP Singh @ 2025-05-19 23:42 UTC (permalink / raw)
  To: Zvi Effron
  Cc: Paul Moore, Alexei Starovoitov, Blaise Boscaccy, James Bottomley,
	bpf, code, Jonathan Corbet, David S. Miller, David Howells,
	Günther Noack, Herbert Xu, Jarkko Sakkinen, James Morris,
	Jan Stancek, Justin Stitt, keyrings, Linux Crypto Mailing List,
	open list:DOCUMENTATION, Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang, kysrinivasan, Linus Torvalds

> > > > > > No. New hook is not needed.

[...]

> > > > >
> > > > > It would be good for you to explain how the existing LSM hook is sufficient
> > > > > to authorize the loading of a BPF program using the signature validation
> > > > > state determined in the BPF verifier.
> > > >
> > > > I already explained:
> > > > .. a job of trivial LSM:
> > > > if (prog_attr doesn't have signature &&
> > > >    (task == .. || task is under certain cgroup || whatever))
> > > >   disallow.
> > >
> > > I read that earlier reply as an example that covers a sample use case,
> > > I didn't realize you were asserting that was the only approach you
> > > were considering.  Perhaps that was the source of confusion earlier,
> > > we may disagree, but I don't intentionally "twist" words; not only is
> > > that rude, it's just stupid in public, archived discussions.
> > >
> > > As I mentioned previously, we really need to see an explicit yes/no
> > > flag from the BPF verifier to indicate that the signature on the BPF
> > > program has been validated.  It really should be as simple as adding a
> > > bool to bpf_prog_aux which the BPF verifier sets to true upon
> > > successful signature validation, and then an LSM can use this flag as
> > > input to an access control decision in a hook placed after the
> > > verifier.  Are you objecting to the addition of a flag in the
> > > bpf_prog_aux struct (or some other struct tightly coupled to the BPF
> > > program), the LSM hook after the verifier, or both?  It would also be
> > > helpful if you can elaborate on the technical reasons behind these
> > > objections.
> >
> > Neither the aux field, nor the hook are required because:
> >
> > * If the signature is passed, it will be enforced, there are no
> > "runtime aspects" that need to be configurable here.
> > * What the LSM can specify a policy for is when a signature is not
> > passed, for this, it does not need an aux field or a signature or the
> > new hook, existing hooks are sufficient.
> >
>
> What about wanting to create a policy that requires signatures under certain
> situations and allowing the lack of a signature under others? How is that
> implemented with the existing hooks?
> As I understand it, all the existing hooks know (would know) is that _if_ there
> is a signature _then_ it will be enforced. There is no way to know _whether_
> there is a signature.
>

The signature is passed in bpf_attr and if there is a signature the
LSM's job is done.

   https://elixir.bootlin.com/linux/v6.14.7/source/kernel/bpf/syscall.c#L5771

 It will be enforced.


- KP

> An example policy I can think of is that most users (with CAP_BPF) must submit
> signed programs but some users are exempted. Would that policy be able to be
> made with the current hooks?
>
> > - KP
> >
> > >
> > > --
> > > paul-moore.com
> >

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

* Re: [PATCH v3 0/4] Introducing Hornet LSM
  2025-05-19 22:58                             ` Paul Moore
@ 2025-05-21 22:26                               ` Paul Moore
  0 siblings, 0 replies; 41+ messages in thread
From: Paul Moore @ 2025-05-21 22:26 UTC (permalink / raw)
  To: KP Singh
  Cc: Alexei Starovoitov, Blaise Boscaccy, James Bottomley, bpf, code,
	Jonathan Corbet, David S. Miller, David Howells,
	Günther Noack, Herbert Xu, Jarkko Sakkinen, James Morris,
	Jan Stancek, Justin Stitt, keyrings, Linux Crypto Mailing List,
	open list:DOCUMENTATION, Linux Kbuild mailing list, LKML,
	open list:KERNEL SELFTEST FRAMEWORK, LSM List, clang-built-linux,
	Masahiro Yamada, Mickaël Salaün, Bill Wendling,
	Nathan Chancellor, Neal Gompa, Nick Desaulniers, Nicolas Schier,
	nkapron, Roberto Sassu, Serge E . Hallyn, Shuah Khan,
	Matteo Croce, Cong Wang, kysrinivasan, Linus Torvalds

On Mon, May 19, 2025 at 6:58 PM Paul Moore <paul@paul-moore.com> wrote:
>
> When the kernel performs a security relevant operation, such as
> verifying the signature on a BPF program, where the result of the
> operation serves as input to a policy decision, system measurement,
> audit event, etc. the LSM hook needs to be located after the security
> relevant operation takes place so that the hook is able to properly
> take into account the state of the event/system and record the actual
> result as opposed to an implied result (this is critical for auditing,
> measurement, attestation, etc.).
>
> You explained why you believe the field/hook is not required, but I'm
> asking for your *technical*objections*.  I understand that you believe
> these changes are not required, but as described above, I happen to
> disagree and therefore it would be helpful to understand the technical
> reasons why you can't accept the field/hook changes.  Is there a
> technical reason which would prevent such changes, or is it simply a
> rejection of the use case and requirements above?

Bubbling this back up to the top of your inbox ...

-- 
paul-moore.com

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

end of thread, other threads:[~2025-05-21 22:26 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-05-02 18:44 [PATCH v3 0/4] Introducing Hornet LSM Blaise Boscaccy
2025-05-02 18:44 ` [PATCH v3 1/4] security: " Blaise Boscaccy
2025-05-04 15:02   ` Paul Moore
2025-05-02 18:44 ` [PATCH v3 2/4] hornet: Introduce sign-ebpf Blaise Boscaccy
2025-05-02 18:44 ` [PATCH v3 3/4] hornet: Add a light skeleton data extractor script Blaise Boscaccy
2025-05-02 18:44 ` [PATCH v3 4/4] selftests/hornet: Add a selftest for the Hornet LSM Blaise Boscaccy
2025-05-02 21:00 ` [PATCH v3 0/4] Introducing " KP Singh
2025-05-04 17:36   ` Paul Moore
2025-05-04 23:25     ` KP Singh
2025-05-05 16:22       ` Paul Moore
2025-05-11  2:01       ` KP Singh
2025-05-14  3:06         ` Paul Moore
2025-05-14 18:48           ` KP Singh
2025-05-16 19:49             ` Paul Moore
2025-05-16 23:49               ` Alexei Starovoitov
2025-05-17 15:02                 ` Paul Moore
2025-05-17 16:13                   ` Alexei Starovoitov
2025-05-18  5:48                     ` Paul Moore
2025-05-18 15:52                       ` Alexei Starovoitov
2025-05-18 21:34                         ` Paul Moore
2025-05-19 22:20                           ` KP Singh
2025-05-19 22:58                             ` Paul Moore
2025-05-21 22:26                               ` Paul Moore
2025-05-19 23:00                             ` Zvi Effron
2025-05-19 23:42                               ` KP Singh
2025-05-14 15:39         ` James Bottomley
2025-05-14 17:17           ` KP Singh
2025-05-14 17:45             ` James Bottomley
2025-05-14 18:35               ` KP Singh
2025-05-14 18:35                 ` KP Singh
2025-05-14 20:31                 ` James Bottomley
2025-05-14 20:41                   ` KP Singh
2025-05-05  9:22     ` Daniel Borkmann
2025-05-05 17:30   ` Blaise Boscaccy
2025-05-05 20:41     ` KP Singh
2025-05-05 21:04       ` Paul Moore
2025-05-07 17:48       ` James Bottomley
2025-05-07 23:21         ` Paul Moore
2025-05-08 17:44           ` Alexei Starovoitov
2025-05-08 19:23             ` Paul Moore
2025-05-11  2:14               ` KP Singh

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).