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....
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.