diff options
| author | Mark Brown <broonie@kernel.org> | 2026-05-29 22:43:00 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-05-29 22:43:00 +0100 |
| commit | c6c23350fbf64731e47f4d1576ac2ba6c03b1ed5 (patch) | |
| tree | ec124a98ce774bf0bc31f9c73359122d65ed06e9 /Documentation | |
| parent | e114941bfd16ae83f7075511371dad18aa62ad73 (diff) | |
| parent | c67f6e6d2c141f202211fa9fea844f577eb233a8 (diff) | |
| download | linux-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.rst | 323 | ||||
| -rw-r--r-- | Documentation/admin-guide/LSM/index.rst | 1 | ||||
| -rw-r--r-- | Documentation/admin-guide/LSM/ipe.rst | 162 | ||||
| -rw-r--r-- | Documentation/security/ipe.rst | 68 |
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 |
