[PATCH bpf-next 03/13] lsm: Add LSM hook security_bprm_enforce_policy_kptr
Justin Suess <[email protected]> Thu, 30 Jul 2026 22:20:36 -0400
| Newsgroups | gmane.linux.kernel.lsm,gmane.linux.kernel.bpf,gmane.linux.kernel |
|---|---|
| Message-ID | <[email protected]> |
Add a generic LSM hook enforcing an LSM policy object on the credentials prepared for an execution: security_bprm_enforce_policy_kptr(lsmid, bprm, &policy, flags) The policy object is obtained from the owning LSM through security_policy_kptr_from_fd(), travels in that LSM's member of union lsm_policy_kptr, and is handed back only to that same LSM: the shim uses the same targeted dispatch by @lsmid as the policy kptr lifetime hooks, and returns -EOPNOTSUPP when no active LSM matches. This is the first policy operation backed by the BPF-owned LSM kfuncs: it lets a sleepable LSM BPF program attached to bprm_creds_for_exec() or bprm_creds_from_file() arrange for the executed task to start confined by a policy created through the LSM's own userspace API, e.g. a Landlock ruleset applied to a binprm. The BPF-facing kfunc keeps the policy pointer strongly BTF-typed all the way to the union member the implementing LSM reads back. The hook contract is LSM agnostic: any LSM with a notion of a per-task policy object can implement it, with its own semantics for how the policy composes with restrictions the credentials already carry and for the meaning of @flags, unsupported values of which it must reject with -EINVAL. Implementations can rely on being called only between the preparation and the commitment of the bprm's credentials. Like the policy kptr lifetime hooks, this hook is excluded from the "bpf" LSM's attachment points, as the targeted dispatch makes an attachment there unreachable. Cc: Paul Moore <[email protected]> Cc: Casey Schaufler <[email protected]> Signed-off-by: Justin Suess <[email protected]> --- include/linux/lsm_hook_defs.h | 2 ++ include/linux/security.h | 12 ++++++++++ kernel/bpf/bpf_lsm.c | 1 + security/security.c | 41 +++++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+) diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h index 0800622e317f..a70edbd7b761 100644 --- a/include/linux/lsm_hook_defs.h +++ b/include/linux/lsm_hook_defs.h @@ -455,6 +455,8 @@ LSM_HOOK(int, 0, bpf_token_capable, const struct bpf_token *token, int cap) LSM_HOOK(int, -EOPNOTSUPP, policy_kptr_from_fd, int fd, union lsm_policy_kptr *policy) LSM_HOOK(void, LSM_RET_VOID, policy_kptr_put, union lsm_policy_kptr *policy) +LSM_HOOK(int, -EOPNOTSUPP, bprm_enforce_policy_kptr, struct linux_binprm *bprm, + union lsm_policy_kptr *policy, u32 flags) #endif /* CONFIG_BPF_SYSCALL */ LSM_HOOK(int, 0, locked_down, enum lockdown_reason what) diff --git a/include/linux/security.h b/include/linux/security.h index 5017a335918c..40dfa96b6a71 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -2332,6 +2332,10 @@ extern int security_bpf_token_capable(const struct bpf_token *token, int cap); extern int security_policy_kptr_from_fd(u64 lsmid, int fd, union lsm_policy_kptr *policy); extern void security_policy_kptr_put(u64 lsmid, union lsm_policy_kptr *policy); +extern int security_bprm_enforce_policy_kptr(u64 lsmid, + struct linux_binprm *bprm, + union lsm_policy_kptr *policy, + u32 flags); #else static inline int security_bpf(int cmd, union bpf_attr *attr, unsigned int size, bool kernel) @@ -2396,6 +2400,14 @@ static inline void security_policy_kptr_put(u64 lsmid, union lsm_policy_kptr *policy) { } + +static inline int security_bprm_enforce_policy_kptr(u64 lsmid, + struct linux_binprm *bprm, + union lsm_policy_kptr *policy, + u32 flags) +{ + return -EOPNOTSUPP; +} #endif /* CONFIG_SECURITY */ #endif /* CONFIG_BPF_SYSCALL */ diff --git a/kernel/bpf/bpf_lsm.c b/kernel/bpf/bpf_lsm.c index e9059d43e92c..d847a180489f 100644 --- a/kernel/bpf/bpf_lsm.c +++ b/kernel/bpf/bpf_lsm.c @@ -58,6 +58,7 @@ BTF_ID(func, bpf_lsm_ismaclabel) BTF_ID(func, bpf_lsm_file_alloc_security) BTF_ID(func, bpf_lsm_policy_kptr_from_fd) BTF_ID(func, bpf_lsm_policy_kptr_put) +BTF_ID(func, bpf_lsm_bprm_enforce_policy_kptr) BTF_SET_END(bpf_lsm_disabled_hooks) /* List of LSM hooks that should operate on 'current' cgroup regardless diff --git a/security/security.c b/security/security.c index fd535bd00c24..e9d8c9492bdb 100644 --- a/security/security.c +++ b/security/security.c @@ -5506,6 +5506,47 @@ void security_policy_kptr_put(u64 lsmid, union lsm_policy_kptr *policy) WARN_ON_ONCE(1); } +/** + * security_bprm_enforce_policy_kptr() - Enforce a policy on exec credentials + * @lsmid: LSM_ID_* value of the LSM owning @policy + * @bprm: execution context providing the prepared credentials to restrict + * @policy: the policy object to enforce, in the member of the LSM + * identified by @lsmid + * @flags: LSM-specific enforcement flags + * + * Ask the LSM identified by @lsmid to restrict the credentials + * prepared in @bprm with @policy, so that the executed task starts + * confined by it. @policy must have been obtained from the same LSM + * with security_policy_kptr_from_fd(); the hook borrows the + * reference and the caller remains responsible for releasing it. + * Only the hook implementation of the LSM identified by @lsmid is + * called: an LSM never receives a policy object meant for another LSM. + * + * This hook may only be called from an exec security context where + * @bprm's credentials are prepared but not yet committed, i.e. from a + * bprm_creds_for_exec() or bprm_creds_from_file() hook. + * + * How @policy composes with restrictions the credentials already + * carry is defined by the implementing LSM, as is the meaning of + * @flags, unsupported values of which it must reject with -EINVAL. + * + * Return: Returns 0 on success, -EOPNOTSUPP if the LSM does not + * implement the hook, negative values on other failures. + */ +int security_bprm_enforce_policy_kptr(u64 lsmid, struct linux_binprm *bprm, + union lsm_policy_kptr *policy, u32 flags) +{ + struct lsm_static_call *scall; + + lsm_for_each_hook(scall, bprm_enforce_policy_kptr) { + if (scall->hl->lsmid->id != lsmid) + continue; + return scall->hl->hook.bprm_enforce_policy_kptr(bprm, policy, + flags); + } + return LSM_RET_DEFAULT(bprm_enforce_policy_kptr); +} + /** * security_bpf_map_free() - Free a bpf map's LSM blob * @map: bpf map -- 2.54.0