aboutsummaryrefslogtreecommitdiffstats
path: root/Documentation
diff options
authorMark Brown <broonie@kernel.org>2026-05-29 22:43:00 +0100
committerMark Brown <broonie@kernel.org>2026-05-29 22:43:00 +0100
commitc6c23350fbf64731e47f4d1576ac2ba6c03b1ed5 (patch)
treeec124a98ce774bf0bc31f9c73359122d65ed06e9 /Documentation
parente114941bfd16ae83f7075511371dad18aa62ad73 (diff)
parentc67f6e6d2c141f202211fa9fea844f577eb233a8 (diff)
downloadlinux-next-history-c6c23350fbf64731e47f4d1576ac2ba6c03b1ed5.tar.gz
Merge branch 'next' of https://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/lsm.git
Diffstat (limited to 'Documentation')
-rw-r--r--Documentation/admin-guide/LSM/Hornet.rst323
-rw-r--r--Documentation/admin-guide/LSM/index.rst1
-rw-r--r--Documentation/admin-guide/LSM/ipe.rst162
-rw-r--r--Documentation/security/ipe.rst68
4 files changed, 553 insertions, 1 deletions
diff --git a/Documentation/admin-guide/LSM/Hornet.rst b/Documentation/admin-guide/LSM/Hornet.rst
new file mode 100644
index 0000000000000..0ade4c17374c6
--- /dev/null
+++ b/Documentation/admin-guide/LSM/Hornet.rst
@@ -0,0 +1,323 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+======
+Hornet
+======
+
+Hornet is a Linux Security Module that provides extensible signature
+verification for eBPF programs. This is selectable at build-time with
+``CONFIG_SECURITY_HORNET``.
+
+Overview
+========
+
+Hornet addresses concerns from users who require strict audit trails and
+verification guarantees for eBPF programs, especially in
+security-sensitive environments. Many production systems need assurance
+that only authorized, unmodified eBPF programs are loaded into the
+kernel. Hornet provides this assurance through cryptographic signature
+verification.
+
+When an eBPF program is loaded via the ``bpf()`` syscall, Hornet
+verifies a PKCS#7 signature attached to the program instructions. The
+signature is checked against whichever keyring was specified by the user
+existing kernel cryptographic infrastructure. In addition to signing the
+program bytecode, Hornet supports signing SHA-256 hashes of associated
+BPF maps, enabling integrity verification of map contents at load time
+and at runtime.
+
+After verification, Hornet classifies the program into one of the
+following integrity states and passes the result to a downstream LSM hook
+(``bpf_prog_load_post_integrity``), allowing other security modules to
+make policy decisions based on the verification outcome:
+
+``LSM_INT_VERDICT_OK``
+ The program signature and all map hashes verified successfully.
+
+``LSM_INT_VERDICT_UNSIGNED``
+ No signature was provided with the program.
+
+``LSM_INT_VERDICT_PARTIALSIG``
+ The program signature verified, but the signature did not contain
+ hornet map hash data.
+
+``LSM_INT_VERDICT_UNKNOWNKEY``
+ The keyring requested by the user is invalid.
+
+``LSM_INT_VERDICT_FAULT``
+ A system error occurred during verification.
+
+``LSM_INT_VERDICT_UNEXPECTED``
+ An unexpected map hash value was encountered.
+
+``LSM_INT_VERDICT_BADSIG``
+ The signature or a map hash failed verification.
+
+Hornet itself does not enforce a policy on whether unsigned or partially
+signed programs should be rejected. It delegates that decision to
+downstream LSMs via the ``bpf_prog_load_post_integrity`` hook, making it
+a composable building block in a larger security architecture.
+
+Use Cases
+=========
+
+- **Locked-down production environments**: Ensure only eBPF programs
+ signed by a trusted authority can be loaded, preventing unauthorized
+ or tampered programs from running in the kernel.
+
+- **Audit and compliance**: Provide cryptographic evidence that loaded
+ eBPF programs match their expected build artifacts, supporting
+ compliance requirements in regulated industries.
+
+- **Supply chain integrity**: Verify that eBPF programs and their
+ associated map data have not been modified since they were built and
+ signed, protecting against supply chain attacks.
+
+Threat Model
+============
+
+Hornet protects against the following threats:
+
+- **Unauthorized eBPF program loading**: Programs that have not been
+ signed by a trusted key will be reported as unsigned or badly signed.
+
+- **Tampering with program instructions**: Any modification to the eBPF
+ bytecode after signing will cause signature verification to fail.
+
+- **Tampering with map data**: When map hashes are included in the
+ signature, Hornet verifies that frozen BPF maps match their expected
+ SHA-256 hashes at load time. Maps are also re-verified before program
+ execution via ``BPF_PROG_RUN``.
+
+Hornet does **not** protect against:
+
+- Compromise of the signing key itself.
+- Attacks that occur after a program has been loaded and verified.
+- Programs loaded by the kernel itself (kernel-internal loads bypass
+ the ``BPF_PROG_RUN`` map check).
+
+Known Limitations
+=================
+
+- Hornet requires programs to use :doc:`light skeletons
+ </bpf/libbpf/libbpf_naming_convention>` (lskels) for the signing
+ workflow, as the tooling operates on lskel-generated headers.
+
+- A maximum of 64 maps per program can be tracked for hash
+ verification.
+
+- Map hash verification requires the maps to be frozen before loading.
+ Maps that are not frozen at load time will cause verification to fail
+ when their hashes are included in the signature.
+
+- The only hashing algorithm available is SHA256 due to it be hardcoded
+ in the bpf subsystem.
+
+- Hornet guarantees that the signed program runs only with signed map
+ data. It does not guarantee positional binding of maps to specific
+ fd_array slots.
+
+- BPF_MAP_TYPE_PROG_ARRAY maps must be frozen for Hornet to verify
+ them. Unfrozen prog array maps are not covered by verification.
+
+Configuration
+=============
+
+Build Configuration
+-------------------
+
+Enable Hornet by setting the following kernel configuration option::
+
+ CONFIG_SECURITY_HORNET=y
+
+This option is found under :menuselection:`Security options --> Hornet
+support` and depends on ``CONFIG_SECURITY``.
+
+When enabled, Hornet is included in the default LSM initialization order
+and will appear in ``/sys/kernel/security/lsm``.
+
+Architecture
+============
+
+Signature Verification Flow
+---------------------------
+
+The following describes what happens when a userspace program calls
+``bpf(BPF_PROG_LOAD, ...)`` with a signature attached:
+
+1. The ``bpf_prog_load_integrity`` LSM hook is invoked.
+
+2. Hornet reads the signature from the userspace buffer specified by
+ ``attr->signature`` (with length ``attr->signature_size``).
+
+3. The PKCS#7 signature is verified against the program instructions
+ using ``verify_pkcs7_message_sig()`` with the user specified keyring.
+
+4. The PKCS#7 message is parsed and its trust chain is validated via
+ ``validate_pkcs7_trust()``.
+
+5. Hornet extracts the authenticated attribute identified by
+ ``OID_hornet_data`` (OID ``2.25.316487325684022475439036912669789383960``)
+ from the PKCS#7 message. This attribute contains an ASN.1-encoded set
+ of map index/hash pairs.
+
+6. For each map hash entry, Hornet retrieves the corresponding BPF map
+ via its file descriptor, confirms it is frozen, computes its SHA-256
+ hash, and compares it against the signed hash.
+
+7. The resulting integrity verdict is passed to the
+ ``bpf_prog_load_post_integrity`` hook so that downstream LSMs can
+ enforce policy.
+
+Runtime Map Verification
+------------------------
+
+When ``bpf(BPF_PROG_RUN, ...)`` is called from userspace, Hornet
+re-verifies the hashes of all maps associated with the program. This
+ensures that map contents have not been modified between program load
+and execution. If any map hash no longer matches, the ``BPF_PROG_RUN``
+command is denied.
+
+Userspace Interface
+-------------------
+
+Signatures are passed to the kernel through fields in ``union bpf_attr``
+when using the ``BPF_PROG_LOAD`` command:
+
+``signature``
+ A pointer to a userspace buffer containing the PKCS#7 signature.
+
+``signature_size``
+ The size of the signature buffer in bytes.
+
+ASN.1 Schema
+------------
+
+Map hashes are encoded as a signed attribute in the PKCS#7 message using
+the following ASN.1 schema::
+
+ HornetData ::= SET OF Map
+
+ Map ::= SEQUENCE {
+ index INTEGER,
+ sha OCTET STRING
+ }
+
+Each ``Map`` entry contains the index of the map in the program's
+``fd_array`` and its expected SHA-256 hash. A zero-length ``sha`` field
+indicates that the map at that index should be skipped during
+verification.
+
+Tooling
+=======
+
+Helper scripts and a signature generation tool are provided in
+``scripts/hornet/`` to support the development of signed eBPF light
+skeletons.
+
+gen_sig
+-------
+
+``gen_sig`` is a C program (using OpenSSL) that creates a PKCS#7
+signature over eBPF program instructions and optionally includes
+SHA-256 hashes of BPF maps as signed attributes.
+
+Usage::
+
+ gen_sig --data <instructions.bin> \
+ --cert <signer.crt> \
+ --key <signer.key> \
+ [--pass <passphrase>] \
+ --out <signature.p7b> \
+ [--add <mapfile.bin>:<index> ...]
+
+``--data``
+ Path to the binary file containing eBPF program instructions to sign.
+
+``--cert``
+ Path to the signing certificate (PEM or DER format).
+
+``--key``
+ Path to the private key (PEM or DER format).
+
+``--pass``
+ Optional passphrase for the private key.
+
+``--out``
+ Path to write the output PKCS#7 signature.
+
+``--add``
+ Attach a map hash as a signed attribute. The argument is a path to a
+ binary map file followed by a colon and the map's index in the
+ ``fd_array``. This option may be specified multiple times.
+
+extract-skel.sh
+---------------
+
+Extracts a named field from an autogenerated eBPF lskel header file.
+Used internally by other helper scripts.
+
+extract-insn.sh
+---------------
+
+Extracts the eBPF program instructions (``opts_insn``) from an lskel
+header into a binary file suitable for signing with ``gen_sig``.
+
+extract-map.sh
+--------------
+
+Extracts the map data (``opts_data``) from an lskel header into a
+binary file suitable for hashing with ``gen_sig``.
+
+write-sig.sh
+------------
+
+Replaces the signature data in an lskel header with a new signature
+from a binary file. This is used to embed a freshly generated signature
+back into the header after signing.
+
+Signing Workflow
+================
+
+A typical workflow for building and signing an eBPF light skeleton is:
+
+1. **Compile the eBPF program**::
+
+ clang -O2 -target bpf -c program.bpf.c -o program.bpf.o
+
+2. **Generate the light skeleton header** using ``bpftool``::
+
+ bpftool gen skeleton -S program.bpf.o > loader.h
+
+3. **Extract instructions and map data** from the generated header::
+
+ scripts/hornet/extract-insn.sh loader.h > insn.bin
+ scripts/hornet/extract-map.sh loader.h > map.bin
+
+4. **Generate the signature** with ``gen_sig``::
+
+ scripts/hornet/gen_sig \
+ --key signing_key.pem \
+ --cert signing_key.x509 \
+ --data insn.bin \
+ --add map.bin:0 \
+ --out sig.bin
+
+5. **Embed the signature** back into the header::
+
+ scripts/hornet/write-sig.sh loader.h sig.bin > signed_loader.h
+
+6. **Build the loader program** using the signed header::
+
+ cc -o loader loader.c -lbpf
+
+The resulting loader program will pass the embedded signature to the
+kernel when loading the eBPF program, enabling Hornet to verify it.
+
+Testing
+=======
+
+Self-tests are provided in ``tools/testing/selftests/hornet/``. The test
+suite builds a minimal eBPF program (``trivial.bpf.c``), signs it using
+the workflow described above, and verifies that the signed program loads
+successfully.
diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
index b44ef68f6e4da..57f6e9fbe5fd1 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/Documentation/admin-guide/LSM/ipe.rst b/Documentation/admin-guide/LSM/ipe.rst
index a756d81585317..d68ba9d98859e 100644
--- a/Documentation/admin-guide/LSM/ipe.rst
+++ b/Documentation/admin-guide/LSM/ipe.rst
@@ -559,7 +559,8 @@ policy. Two properties are built-into the policy parser: 'op' and 'action'.
The other properties are used to restrict immutable security properties
about the files being evaluated. Currently those properties are:
'``boot_verified``', '``dmverity_signature``', '``dmverity_roothash``',
-'``fsverity_signature``', '``fsverity_digest``'. A description of all
+'``fsverity_signature``', '``fsverity_digest``', '``bpf_signature``',
+'``bpf_keyring``', '``bpf_kernel``'. A description of all
properties supported by IPE are listed below:
op
@@ -603,6 +604,14 @@ as the first token. IPE supports the following operations:
Controls loading IMA certificates through the Kconfigs,
``CONFIG_IMA_X509_PATH`` and ``CONFIG_EVM_X509_PATH``.
+ ``BPF_PROG_LOAD``:
+
+ Pertains to BPF programs being loaded via the ``bpf()`` syscall.
+ This operation is used in conjunction with the ``bpf_signature``,
+ ``bpf_keyring``, and ``bpf_kernel`` properties to control BPF
+ program loading based on integrity verification provided by the
+ Hornet LSM.
+
action
~~~~~~
@@ -713,6 +722,105 @@ fsverity_signature
fsverity_signature=(TRUE|FALSE)
+bpf_signature
+~~~~~~~~~~~~~
+
+ This property can be utilized for authorization of BPF program loads based
+ on the integrity verdict provided by the Hornet LSM. When a BPF program is
+ loaded, Hornet performs cryptographic verification of the program's PKCS#7
+ signature (if present) and passes an integrity verdict to IPE via the
+ ``security_bpf_prog_load_post_integrity`` hook. IPE can then allow or deny
+ the load based on the verdict.
+
+ This property depends on ``SECURITY_HORNET`` and is controlled by the
+ ``IPE_PROP_BPF_SIGNATURE`` config option.
+ The format of this property is::
+
+ bpf_signature=(NONE|OK|UNSIGNED|PARTIALSIG|UNKNOWNKEY|UNEXPECTED|FAULT|BADSIG)
+
+ The possible values correspond to the integrity verdicts from Hornet:
+
+ ``NONE``
+
+ No integrity verdict was set (default/uninitialized).
+
+ ``OK``
+
+ The BPF program's signature and all map hashes were successfully
+ verified.
+
+ ``UNSIGNED``
+
+ No signature was provided with the BPF program.
+
+ ``PARTIALSIG``
+
+ The program signature was verified, but no authenticated map hash
+ data was present.
+
+ ``UNKNOWNKEY``
+
+ The keyring requested by the user is invalid.
+
+ ``UNEXPECTED``
+
+ An unexpected map hash value was encountered during verification.
+
+ ``FAULT``
+
+ A system error occurred during signature verification.
+
+ ``BADSIG``
+
+ The signature or hash verification failed.
+
+bpf_keyring
+~~~~~~~~~~~~
+
+ This property can be utilized for authorization of BPF program loads based
+ on the keyring specified in the ``bpf_attr`` during the ``BPF_PROG_LOAD``
+ syscall. This allows policies to restrict which keyring must be used for
+ signature verification of BPF programs.
+
+ This property shares the ``IPE_PROP_BPF_SIGNATURE`` config option with
+ ``bpf_signature``.
+ The format of this property is::
+
+ bpf_keyring=(BUILTIN|SECONDARY|PLATFORM)
+
+ The possible values correspond to the system keyrings:
+
+ ``BUILTIN``
+
+ The builtin trusted keyring (``.builtin_trusted_keys``), which
+ contains keys embedded at kernel compile time.
+
+ ``SECONDARY``
+
+ The secondary trusted keyring (``.secondary_trusted_keys``), which
+ includes both builtin trusted keys and keys added at runtime.
+
+ ``PLATFORM``
+
+ The platform keyring (``.platform``), which contains keys provided
+ by the platform firmware (e.g. UEFI db keys).
+
+bpf_kernel
+~~~~~~~~~~
+
+ This property can be utilized for authorization of BPF program loads based
+ on whether the load originated from kernel space or user space. The BPF
+ light skeleton infrastructure performs a secondary kernel-originated program
+ load that will not carry a signature. This property allows policies to
+ permit such kernel-originated loads while still requiring signatures for
+ user-space loads.
+
+ This property shares the ``IPE_PROP_BPF_SIGNATURE`` config option with
+ ``bpf_signature``.
+ The format of this property is::
+
+ bpf_kernel=(TRUE|FALSE)
+
Policy Examples
---------------
@@ -788,6 +896,58 @@ Allow execution of a specific fs-verity file
op=EXECUTE fsverity_digest=sha256:fd88f2b8824e197f850bf4c5109bea5cf0ee38104f710843bb72da796ba5af9e action=ALLOW
+Allow only signed BPF programs
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ policy_name=Allow_Signed_BPF policy_version=0.0.0
+ DEFAULT action=ALLOW
+
+ DEFAULT op=BPF_PROG_LOAD action=DENY
+ op=BPF_PROG_LOAD bpf_kernel=TRUE action=ALLOW
+ op=BPF_PROG_LOAD bpf_signature=OK action=ALLOW
+
+This policy allows all other operations but restricts BPF program loading
+to only programs that either originate from kernel space (e.g. light skeleton
+reloads) or have a valid signature verified by the Hornet LSM. Unsigned or
+improperly signed BPF programs from user space will be denied.
+
+Allow signed BPF programs from a specific keyring
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ policy_name=Allow_BPF_Builtin_Keyring policy_version=0.0.0
+ DEFAULT action=ALLOW
+
+ DEFAULT op=BPF_PROG_LOAD action=DENY
+ op=BPF_PROG_LOAD bpf_kernel=TRUE action=ALLOW
+ op=BPF_PROG_LOAD bpf_signature=OK bpf_keyring=BUILTIN action=ALLOW
+
+This policy further restricts BPF program loading to only accept programs
+whose signatures were verified using the builtin trusted keyring. Programs
+signed against the secondary or platform keyrings will be denied, providing
+tighter control over which signing keys are acceptable.
+
+Allow signed BPF programs with relaxed partial signatures
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+ policy_name=Allow_BPF_Partial policy_version=0.0.0
+ DEFAULT action=ALLOW
+
+ DEFAULT op=BPF_PROG_LOAD action=DENY
+ op=BPF_PROG_LOAD bpf_kernel=TRUE action=ALLOW
+ op=BPF_PROG_LOAD bpf_signature=OK action=ALLOW
+ op=BPF_PROG_LOAD bpf_signature=PARTIALSIG action=ALLOW
+
+This policy allows BPF programs that have been fully verified (``OK``) as
+well as programs with a valid program signature but without authenticated
+map hash data (``PARTIALSIG``). This can be useful during development or
+for programs that do not use maps.
+
Additional Information
----------------------
diff --git a/Documentation/security/ipe.rst b/Documentation/security/ipe.rst
index 5eb3e6265fbde..c51dcb16a377b 100644
--- a/Documentation/security/ipe.rst
+++ b/Documentation/security/ipe.rst
@@ -412,6 +412,73 @@ a standard securityfs policy tree::
The policy is stored in the ``->i_private`` data of the MyPolicy inode.
+BPF/Hornet Integration
+~~~~~~~~~~~~~~~~~~~~~~
+
+IPE integrates with the Hornet LSM to enforce integrity policies on BPF
+program loading. Hornet performs cryptographic verification of BPF program
+signatures (PKCS#7 with authenticated attributes containing map hashes)
+and produces an ``enum lsm_integrity_verdict``. IPE acts as the policy
+enforcer: it stores Hornet's verdict in a per-program LSM blob and later
+evaluates it against the active IPE policy when the BPF program load is
+finalized.
+
+Enforcement is split across two LSM hooks so that signature verification
+and policy evaluation are cleanly separated. This also lets IPE evaluate
+policy uniformly even when no integrity provider ran.
+
+The hook flow is:
+
+ 1. User space (or the kernel, via the BPF light skeleton) invokes
+ ``BPF_PROG_LOAD`` through the ``bpf()`` syscall.
+ 2. Hornet's ``bpf_prog_load_integrity`` hook runs first and calls
+ ``hornet_check_program()`` to verify the program's PKCS#7 signature
+ against ``attr->keyring_id`` and to check the signed map hashes
+ (decoded from the ``OID_hornet_data`` authenticated attribute)
+ against the live hashes of the frozen maps referenced from
+ ``attr->fd_array``. The function produces one of
+ ``LSM_INT_VERDICT_OK``, ``LSM_INT_VERDICT_UNSIGNED``,
+ ``LSM_INT_VERDICT_BADSIG``, ``LSM_INT_VERDICT_PARTIALSIG``,
+ ``LSM_INT_VERDICT_UNKNOWNKEY``, ``LSM_INT_VERDICT_UNEXPECTED``, or
+ ``LSM_INT_VERDICT_FAULT``.
+ 3. Hornet calls ``security_bpf_prog_load_post_integrity()`` with the
+ resulting verdict and its ``lsm_id``. IPE's
+ ``ipe_bpf_prog_load_post_integrity`` handler does **not** enforce
+ policy here; it only stashes the verdict in IPE's per-program blob
+ and returns ``0``. This keeps the integrity step decoupled from
+ policy evaluation and allows multiple providers to coexist without
+ short-circuiting each other.
+ 4. The core BPF load path then invokes the standard ``bpf_prog_load``
+ LSM hook. IPE's ``ipe_bpf_prog_load`` reads the verdict back out of
+ the per-program blob, populates an ``ipe_eval_ctx``, and calls
+ ``ipe_evaluate_event()`` against the active policy's
+ ``BPF_PROG_LOAD`` rules. A deny verdict returns ``-EACCES`` and
+ aborts the load.
+
+If no integrity provider populated the blob (e.g. Hornet is not enabled,
+or the load came from a path Hornet does not cover), the verdict defaults
+to ``LSM_INT_VERDICT_NONE`` and IPE evaluates accordingly. Policy can
+therefore express "deny anything Hornet did not vouch for".
+
+Three properties are available for BPF policy rules:
+
+ - ``bpf_signature``: Matches against the integrity verdict (OK, UNSIGNED,
+ BADSIG, etc.)
+ - ``bpf_keyring``: Matches against the keyring specified in ``bpf_attr``
+ (BUILTIN, SECONDARY, PLATFORM)
+ - ``bpf_kernel``: Matches whether the load originated from kernel space
+ (TRUE/FALSE). This is important because the BPF light skeleton
+ infrastructure performs a secondary kernel-originated program load that
+ does not carry a signature.
+
+All three properties are gated on ``CONFIG_IPE_PROP_BPF_SIGNATURE`` which
+depends on ``CONFIG_SECURITY_HORNET``.
+
+The evaluation context (``struct ipe_eval_ctx``) carries three BPF-specific
+fields: ``bpf_verdict`` (the integrity verdict enum), ``bpf_keyring_id``
+(the ``s32`` keyring ID from ``bpf_attr``), and ``bpf_kernel`` (bool
+indicating kernel origin).
+
Tests
-----
@@ -439,6 +506,7 @@ IPE has KUnit Tests for the policy parser. Recommended kunitconfig::
CONFIG_IPE_PROP_DM_VERITY_SIGNATURE=y
CONFIG_IPE_PROP_FS_VERITY=y
CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG=y
+ CONFIG_IPE_PROP_BPF_SIGNATURE=y
CONFIG_SECURITY_IPE_KUNIT_TEST=y
In addition, IPE has a python based integration