Re: [PATCH v5 10/10] ipe: Add BPF program load policy enforcement via Hornet integration

Fan Wu
2026-04-21T00:27:46Z

On Mon, Apr 20, 2026 at 2:27 PM Blaise Boscaccy
<bboscaccy@linux.microsoft.com> wrote:
>
> Add support for the bpf_prog_load_post_integrity LSM hook, enabling IPE
> to make policy decisions about BPF program loading based on integrity
> verdicts provided by the Hornet LSM.
>
> New policy operation:
> op=BPF_PROG_LOAD - Matches BPF program load events
>
> New policy properties:
> bpf_signature=NONE - No Verdict
> bpf_signature=OK - Program signature and map hashes verified
> bpf_signature=UNSIGNED - No signature provided
> bpf_signature=PARTIALSIG - Signature OK but no map hash data
> bpf_signature=UNKNOWNKEY - Cert not trusted
> bpf_signature=UNEXPECTED - An unexpected hash value was encountered
> bpf_signature=FAULT - System error during verification
> bpf_signature=BADSIG - Signature or map hash verification failed
> bpf_keyring=BUILTIN - Program was signed using a builtin keyring
> bpf_keyring=SECONDARY - Program was signed using the secondary keyring
> bpf_keyring=PLATFORM - Program was signed using the platform keyring
> bpf_kernel=TRUE - Program originated from kernelspace
> bpf_kernel=FALSE - Program originated from userspace
>
> These properties map directly to the lsm_integrity_verdict enum values
> provided by the Hornet LSM through security_bpf_prog_load_post_integrity.
>
> The feature is gated on CONFIG_IPE_PROP_BPF_SIGNATURE which depends on
> CONFIG_SECURITY_HORNET.
>
> Signed-off-by: Blaise Boscaccy <bboscaccy@linux.microsoft.com>
> ---

...

> diff --git a/security/ipe/hooks.c b/security/ipe/hooks.c
> index 0ae54a880405a..03541e5bb7f60 100644
> --- a/security/ipe/hooks.c
> +++ b/security/ipe/hooks.c
> @@ -340,3 +340,40 @@ int ipe_inode_setintegrity(const struct inode *inode,
> return -EINVAL;
> }
> #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
> +
> +#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
> +/**
> + * ipe_bpf_prog_load_post_integrity() - ipe security hook for BPF program load.
> + * @prog: Supplies the BPF program being loaded.
> + * @attr: Supplies the bpf syscall attributes.
> + * @token: Supplies the BPF token, if any.
> + * @kernel: Whether the call originated from the kernel.
> + * @lsmid: Supplies the LSM ID of the integrity provider.
> + * @verdict: Supplies the integrity verdict from the provider (e.g. Hornet).
> + *
> + * This LSM hook is called after an integrity verification LSM (such as Hornet)
> + * has evaluated a BPF program's cryptographic signature. IPE uses the verdict
> + * to make a policy-based allow/deny decision.
> + *
> + * Return:
> + * * %0 - Success
> + * * %-EACCES - Did not pass IPE policy
> + */
> +int ipe_bpf_prog_load_post_integrity(struct bpf_prog *prog,
> + union bpf_attr *attr,
> + struct bpf_token *token,
> + bool kernel,
> + const struct lsm_id *lsmid,
> + enum lsm_integrity_verdict verdict)
> +{
> + struct ipe_eval_ctx ctx = IPE_EVAL_CTX_INIT;
> +
> + ctx.op = IPE_OP_BPF_PROG_LOAD;
> + ctx.hook = IPE_HOOK_BPF_PROG_LOAD;
> + ctx.bpf_verdict = verdict;
> + ctx.bpf_keyring_id = attr->keyring_id;
> + ctx.bpf_kernel = kernel;
> +
> + return ipe_evaluate_event(&ctx);
> +}
> +#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
> diff --git a/security/ipe/hooks.h b/security/ipe/hooks.h
> index 07db373327402..95b74f7899750 100644
> --- a/security/ipe/hooks.h
> +++ b/security/ipe/hooks.h
> @@ -10,6 +10,7 @@
> #include <linux/security.h>
> #include <linux/blk_types.h>
> #include <linux/fsverity.h>
> +#include <linux/bpf.h>
>
> enum ipe_hook_type {
> IPE_HOOK_BPRM_CHECK = 0,
> @@ -18,6 +19,7 @@ enum ipe_hook_type {
> IPE_HOOK_MPROTECT,
> IPE_HOOK_KERNEL_READ,
> IPE_HOOK_KERNEL_LOAD,
> + IPE_HOOK_BPF_PROG_LOAD,
> __IPE_HOOK_MAX
> };
>
> @@ -52,4 +54,13 @@ int ipe_inode_setintegrity(const struct inode *inode, enum lsm_integrity_type ty
> const void *value, size_t size);
> #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
>
> +#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
> +int ipe_bpf_prog_load_post_integrity(struct bpf_prog *prog,
> + union bpf_attr *attr,
> + struct bpf_token *token,
> + bool kernel,
> + const struct lsm_id *lsmid,
> + enum lsm_integrity_verdict verdict);
> +#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
> +
> #endif /* _IPE_HOOKS_H */
> diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
> index 495bb765de1b8..6502d4ddc641c 100644
> --- a/security/ipe/ipe.c
> +++ b/security/ipe/ipe.c
> @@ -60,6 +60,9 @@ static struct security_hook_list ipe_hooks[] __ro_after_init = {
> #ifdef CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG
> LSM_HOOK_INIT(inode_setintegrity, ipe_inode_setintegrity),
> #endif /* CONFIG_IPE_PROP_FS_VERITY_BUILTIN_SIG */
> +#ifdef CONFIG_IPE_PROP_BPF_SIGNATURE
> + LSM_HOOK_INIT(bpf_prog_load_post_integrity, ipe_bpf_prog_load_post_integrity),
> +#endif /* CONFIG_IPE_PROP_BPF_SIGNATURE */
> };
>

IPE only registers bpf_prog_load_post_integrity(), which fires only
when Hornet calls security_bpf_prog_load_post_integrity. If Hornet
isn't enabled via lsm=, this hook never triggers, and an IPE policy
like "DEFAULT op=BPF_PROG_LOAD action=DENY" silently allows every
load.

I think IPE should register bpf_prog_load() as well, post_integrity()
should only store the verdict to a per-prog blob, and bpf_prog_load
reads it and evaluates policy.

-Fan