selftests/bpf: Inspect the signature verdict exposed to BPF LSM

Add a minimal BPF LSM program on lsm/bpf_prog_load that, for loads on
the monitored thread, reads back prog->aux->sig.{verdict,keyring_type,
keyring_serial}, and a signed_loader subtest that drives the same
gen_loader loader through the hook twice: i) /unsigned/ where the LSM
must observe UNSIGNED, no keyring and serial 0; ii) /signed/ where the
very same insns signed against the session keyring must be observed as
VERIFIED with a user keyring, and the recorded keyring_serial must be
equal to the resolved session keyring serial. Loading (not running) the
loader is sufficient since the verdict is attached at load time.

  # LDLIBS=-static PKG_CONFIG='pkg-config --static' ./vmtest.sh -- ./test_progs -t signed_loader
  [    1.970530] clocksource: Switched to clocksource tsc
  #405/1   signed_loader/metadata_check_shape:OK
  #405/2   signed_loader/metadata_match:OK
  #405/3   signed_loader/metadata_sha_mismatch:OK
  #405/4   signed_loader/metadata_not_exclusive:OK
  #405/5   signed_loader/metadata_hash_not_computed:OK
  #405/6   signed_loader/signature_enforced:OK
  #405/7   signed_loader/signature_too_large:OK
  #405/8   signed_loader/signature_bad_keyring:OK
  #405/9   signed_loader/metadata_ctx_max_entries_ignored:OK
  #405/10  signed_loader/metadata_ctx_initial_value_ignored:OK
  #405/11  signed_loader/signature_authenticates_insns:OK
  #405/12  signed_loader/hash_requires_frozen:OK
  #405/13  signed_loader/no_update_after_freeze:OK
  #405/14  signed_loader/freeze_writable_mmap:OK
  #405/15  signed_loader/no_writable_mmap_frozen:OK
  #405/16  signed_loader/map_hash_matches_libbpf:OK
  #405/17  signed_loader/map_hash_multi_element:OK
  #405/18  signed_loader/map_hash_bad_size:OK
  #405/19  signed_loader/map_hash_unsupported_type:OK
  #405/20  signed_loader/lsm_signature_verdict:OK
  #405     signed_loader:OK
  Summary: 1/20 PASSED, 0 SKIPPED, 0 FAILED

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Link: https://lore.kernel.org/r/20260605213518.544262-2-daniel@iogearbox.net
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
Daniel Borkmann 2026-06-05 23:35:18 +02:00 committed by Alexei Starovoitov
parent 63a673e8a4
commit 8ddce41679
2 changed files with 152 additions and 0 deletions

View File

@ -17,9 +17,23 @@
#include "test_signed_loader.skel.h"
#include "test_signed_loader_map.skel.h"
#include "test_signed_loader_data.skel.h"
#include "test_signed_loader_lsm.skel.h"
#define SIG_MATCH_INSNS 33 /* excl (5) + 4 * sha-dword (7) */
enum {
BPF_SIG_UNSIGNED = 0,
BPF_SIG_VERIFIED,
};
enum {
BPF_SIG_KEYRING_NONE = 0,
BPF_SIG_KEYRING_BUILTIN,
BPF_SIG_KEYRING_SECONDARY,
BPF_SIG_KEYRING_PLATFORM,
BPF_SIG_KEYRING_USER,
};
static int load_loader(const void *insns, __u32 insns_sz, int map_fd,
const void *sig, __u32 sig_sz, __s32 keyring_id)
{
@ -970,6 +984,112 @@ static void map_hash_unsupported_type(void)
close(fd);
}
static int setup_meta_map(const struct gen_loader_fixture *f)
{
LIBBPF_OPTS(bpf_map_create_opts, mopts,
.excl_prog_hash = f->excl,
.excl_prog_hash_size = sizeof(f->excl));
__u32 key = 0;
int fd;
fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, "__loader.map", 4,
f->data_sz, 1, &mopts);
if (fd < 0)
return -errno;
if (bpf_map_update_elem(fd, &key, f->blob, 0) || bpf_map_freeze(fd)) {
close(fd);
return -errno;
}
return fd;
}
static void lsm_signature_verdict(void)
{
char dir_tmpl[] = "/tmp/signed_loader_lsmXXXXXX", *dir = NULL;
struct test_signed_loader_lsm *lsm = NULL;
int map_fd = -1, prog_fd = -1;
bool have_fixture = false;
struct gen_loader_fixture f;
__u32 sig_sz = 8192;
__s32 ses_serial;
__u8 sig[8192];
lsm = test_signed_loader_lsm__open_and_load();
if (!ASSERT_OK_PTR(lsm, "lsm_skel_load"))
return;
lsm->bss->monitored_tid = sys_gettid();
if (!ASSERT_OK(test_signed_loader_lsm__attach(lsm), "lsm_attach"))
goto out;
have_fixture = true;
if (gen_loader_fixture_init(&f) != 0)
goto out;
map_fd = setup_meta_map(&f);
if (!ASSERT_OK_FD(map_fd, "meta_map_unsigned"))
goto out;
lsm->bss->seen = 0;
prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, NULL, 0, 0);
close(map_fd);
map_fd = -1;
if (!ASSERT_OK_FD(prog_fd, "unsigned loader load"))
goto out;
close(prog_fd);
prog_fd = -1;
if (!ASSERT_NEQ(lsm->bss->seen, 0, "bpf LSM in the active LSM set"))
goto out;
ASSERT_EQ(lsm->bss->seen, 1, "unsigned: one observed load");
ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_UNSIGNED, "unsigned verdict");
ASSERT_EQ(lsm->bss->sig_keyring_type, BPF_SIG_KEYRING_NONE, "unsigned keyring type");
ASSERT_EQ(lsm->bss->sig_keyring_serial, 0, "unsigned: no keyring serial");
syscall(__NR_request_key, "keyring", "_uid.0", NULL,
KEY_SPEC_SESSION_KEYRING);
dir = mkdtemp(dir_tmpl);
if (!ASSERT_OK_PTR(dir, "mkdtemp"))
goto out;
if (!ASSERT_OK(run_setup("setup", dir), "verify_sig_setup")) {
rmdir(dir);
dir = NULL;
goto out;
}
if (!ASSERT_OK(sign_buf(dir, f.gopts.insns, f.gopts.insns_sz, sig,
&sig_sz), "sign-file"))
goto out;
map_fd = setup_meta_map(&f);
if (!ASSERT_OK_FD(map_fd, "meta_map_signed"))
goto out;
lsm->bss->seen = 0;
prog_fd = load_loader(f.gopts.insns, f.gopts.insns_sz, map_fd, sig,
sig_sz, KEY_SPEC_SESSION_KEYRING);
close(map_fd);
map_fd = -1;
if (!ASSERT_OK_FD(prog_fd, "signed loader load"))
goto out;
close(prog_fd);
prog_fd = -1;
ses_serial = syscall(__NR_keyctl, KEYCTL_GET_KEYRING_ID,
KEY_SPEC_SESSION_KEYRING, 0);
ASSERT_EQ(lsm->bss->seen, 1, "signed: one observed load");
ASSERT_EQ(lsm->bss->sig_verdict, BPF_SIG_VERIFIED, "signed verdict");
ASSERT_EQ(lsm->bss->sig_keyring_type, BPF_SIG_KEYRING_USER, "signed keyring type");
ASSERT_GT(ses_serial, 0, "session keyring serial resolved");
ASSERT_EQ(lsm->bss->sig_keyring_serial, ses_serial,
"signed: validated against session keyring");
out:
if (map_fd >= 0)
close(map_fd);
if (prog_fd >= 0)
close(prog_fd);
if (have_fixture)
gen_loader_fixture_fini(&f);
if (dir)
run_setup("cleanup", dir);
test_signed_loader_lsm__destroy(lsm);
}
void test_signed_loader(void)
{
if (test__start_subtest("metadata_check_shape"))
@ -1010,4 +1130,6 @@ void test_signed_loader(void)
map_hash_bad_size();
if (test__start_subtest("map_hash_unsupported_type"))
map_hash_unsupported_type();
if (test__start_subtest("lsm_signature_verdict"))
lsm_signature_verdict();
}

View File

@ -0,0 +1,30 @@
// SPDX-License-Identifier: GPL-2.0
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
char _license[] SEC("license") = "GPL";
__u32 monitored_tid;
int sig_keyring_serial;
int sig_keyring_type;
int sig_verdict;
int seen;
SEC("lsm/bpf_prog_load")
int BPF_PROG(inspect_prog_load, struct bpf_prog *prog, union bpf_attr *attr,
struct bpf_token *token, bool kernel)
{
__u32 tid = bpf_get_current_pid_tgid() & 0xffffffff;
if (!monitored_tid || tid != monitored_tid)
return 0;
seen++;
sig_keyring_serial = prog->aux->sig.keyring_serial;
sig_keyring_type = prog->aux->sig.keyring_type;
sig_verdict = prog->aux->sig.verdict;
return 0;
}