bpf: Expose signature verdict via bpf_prog_aux

BPF_PROG_LOAD verifies the loader signature but does not record the
outcome on the BPF program. [BPF] LSMs and audit can read attr->signature
and attr->keyring_id to infer "was this signed, and if so, against which
keyring".

Add prog->aux->sig (verdict + keyring_{type,serial}), populated by
bpf_prog_load before the LSM hook. keyring_type classifies the keyring
the load referenced (builtin, secondary, platform or user), while
keyring_serial records the serial of the keyring the signature was
actually validated against. System keyrings carry a pseudo key pointer
with no user-visible serial and are reported as 0, as are unsigned loads.
Failed verifications reject the load before the hook runs, so it observes
only either UNSIGNED or VERIFIED.

Signed-off-by: KP Singh <kpsingh@kernel.org>
Co-developed-by: Daniel Borkmann <daniel@iogearbox.net>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/r/20260605213518.544262-1-daniel@iogearbox.net
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
KP Singh 2026-06-05 23:35:17 +02:00 committed by Alexei Starovoitov
parent ba033497f1
commit 63a673e8a4
2 changed files with 59 additions and 15 deletions

View File

@ -32,6 +32,7 @@
#include <linux/static_call.h>
#include <linux/memcontrol.h>
#include <linux/cfi.h>
#include <linux/key.h>
#include <asm/rqspinlock.h>
struct bpf_verifier_env;
@ -1674,6 +1675,19 @@ struct bpf_stream_stage {
int len;
};
enum bpf_sig_verdict {
BPF_SIG_UNSIGNED = 0,
BPF_SIG_VERIFIED,
};
enum bpf_sig_keyring {
BPF_SIG_KEYRING_NONE = 0,
BPF_SIG_KEYRING_BUILTIN,
BPF_SIG_KEYRING_SECONDARY,
BPF_SIG_KEYRING_PLATFORM,
BPF_SIG_KEYRING_USER,
};
struct bpf_prog_aux {
atomic64_t refcnt;
u32 used_map_cnt;
@ -1716,6 +1730,11 @@ struct bpf_prog_aux {
bool changes_pkt_data;
bool might_sleep;
bool kprobe_write_ctx;
struct {
s32 keyring_serial;
u8 keyring_type;
u8 verdict;
} sig;
u64 prog_array_member_cnt; /* counts how many times as member of prog_array */
struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cnt */
struct bpf_arena *arena;
@ -3697,8 +3716,14 @@ static inline int bpf_fd_reuseport_array_update_elem(struct bpf_map *map,
#endif /* CONFIG_BPF_SYSCALL */
#endif /* defined(CONFIG_INET) && defined(CONFIG_BPF_SYSCALL) */
#if defined(CONFIG_KEYS) && defined(CONFIG_BPF_SYSCALL)
#ifdef CONFIG_KEYS
struct bpf_key {
struct key *key;
bool has_ref;
};
#endif /* CONFIG_KEYS */
#if defined(CONFIG_KEYS) && defined(CONFIG_BPF_SYSCALL)
struct bpf_key *bpf_lookup_user_key(s32 serial, u64 flags);
struct bpf_key *bpf_lookup_system_key(u64 id);
void bpf_key_put(struct bpf_key *bkey);
@ -3706,6 +3731,10 @@ int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_p,
const struct bpf_dynptr *sig_p,
struct bpf_key *trusted_keyring);
static inline s32 bpf_key_serial(const struct bpf_key *key)
{
return key->has_ref ? key->key->serial : 0;
}
#else
static inline struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags)
{
@ -3727,6 +3756,11 @@ static inline int bpf_verify_pkcs7_signature(const struct bpf_dynptr *data_p,
{
return -EOPNOTSUPP;
}
static inline s32 bpf_key_serial(const struct bpf_key *key)
{
return 0;
}
#endif /* defined(CONFIG_KEYS) && defined(CONFIG_BPF_SYSCALL) */
/* verifier prototypes for helper functions called from eBPF programs */
@ -4002,15 +4036,6 @@ static inline void bpf_cgroup_atype_get(u32 attach_btf_id, int cgroup_atype) {}
static inline void bpf_cgroup_atype_put(int cgroup_atype) {}
#endif /* CONFIG_BPF_LSM */
struct key;
#ifdef CONFIG_KEYS
struct bpf_key {
struct key *key;
bool has_ref;
};
#endif /* CONFIG_KEYS */
static inline bool type_is_alloc(u32 type)
{
return type & MEM_ALLOC;

View File

@ -2871,8 +2871,22 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
}
}
static enum bpf_sig_keyring bpf_classify_keyring(s32 keyring_id)
{
switch (keyring_id) {
case 0:
return BPF_SIG_KEYRING_BUILTIN;
case (s32)(unsigned long)VERIFY_USE_SECONDARY_KEYRING:
return BPF_SIG_KEYRING_SECONDARY;
case (s32)(unsigned long)VERIFY_USE_PLATFORM_KEYRING:
return BPF_SIG_KEYRING_PLATFORM;
default:
return BPF_SIG_KEYRING_USER;
}
}
static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr,
bool is_kernel)
bool is_kernel, s32 *keyring_serial)
{
bpfptr_t usig = make_bpfptr(attr->signature, is_kernel);
struct bpf_dynptr_kern sig_ptr, insns_ptr;
@ -2908,7 +2922,8 @@ static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr
err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&insns_ptr,
(struct bpf_dynptr *)&sig_ptr, key);
if (!err)
*keyring_serial = bpf_key_serial(key);
bpf_key_put(key);
kvfree(sig);
return err;
@ -3095,13 +3110,17 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, struct bpf_log_at
/* eBPF programs must be GPL compatible to use GPL-ed functions */
prog->gpl_compatible = license_is_gpl_compatible(license) ? 1 : 0;
if (attr->signature) {
err = bpf_prog_verify_signature(prog, attr, uattr.is_kernel);
err = bpf_prog_verify_signature(prog, attr, uattr.is_kernel,
&prog->aux->sig.keyring_serial);
if (err)
goto free_prog;
prog->aux->sig.keyring_type = bpf_classify_keyring(attr->keyring_id);
prog->aux->sig.verdict = BPF_SIG_VERIFIED;
} else {
prog->aux->sig.keyring_type = BPF_SIG_KEYRING_NONE;
prog->aux->sig.verdict = BPF_SIG_UNSIGNED;
}
prog->orig_prog = NULL;
prog->jited = 0;