[PATCH v2 3/3] selftests/bpf: Add test for bpf_verify_pkcs7_signature() helper

From: Roberto Sassu
Date: Wed Jun 08 2022 - 07:15:35 EST


Ensure that signature verification is performed successfully from an eBPF
program, with the new bpf_verify_pkcs7_signature() helper.

The test requires access to the kernel modules signing key and the
execution of the sign-file tool with the signing key path passed as
argument.

Signed-off-by: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
---
tools/testing/selftests/bpf/config | 2 +
.../bpf/prog_tests/verify_pkcs7_sig.c | 149 ++++++++++++++++++
.../bpf/progs/test_verify_pkcs7_sig.c | 127 +++++++++++++++
3 files changed, 278 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
create mode 100644 tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c

diff --git a/tools/testing/selftests/bpf/config b/tools/testing/selftests/bpf/config
index 3b3edc0fc8a6..43f92ce5f3f3 100644
--- a/tools/testing/selftests/bpf/config
+++ b/tools/testing/selftests/bpf/config
@@ -57,3 +57,5 @@ CONFIG_FPROBE=y
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_MPTCP=y
+CONFIG_MODULE_SIG_FORMAT=y
+CONFIG_SECONDARY_TRUSTED_KEYRING=y
diff --git a/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
new file mode 100644
index 000000000000..3c85b8cd13d4
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/verify_pkcs7_sig.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <endian.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <test_progs.h>
+
+#include "test_verify_pkcs7_sig.skel.h"
+
+#define MAX_DATA_SIZE 4096
+
+struct data {
+ u8 payload[MAX_DATA_SIZE];
+};
+
+static int populate_data_item(struct data *data_item)
+{
+ struct stat st;
+ char signed_file_template[] = "/tmp/signed_fileXXXXXX";
+ int ret, fd, child_status, child_pid;
+
+ fd = mkstemp(signed_file_template);
+ if (fd == -1)
+ return -errno;
+
+ ret = write(fd, "test", 4);
+
+ close(fd);
+
+ if (ret != 4) {
+ ret = -EIO;
+ goto out;
+ }
+
+ child_pid = fork();
+
+ if (child_pid == -1) {
+ ret = -errno;
+ goto out;
+ }
+
+ if (child_pid == 0)
+ return execlp(env.sign_file_path, env.sign_file_path, "sha256",
+ env.kernel_priv_cert_path,
+ env.kernel_priv_cert_path,
+ signed_file_template, NULL);
+
+ waitpid(child_pid, &child_status, 0);
+
+ ret = WEXITSTATUS(child_status);
+ if (ret)
+ goto out;
+
+ ret = stat(signed_file_template, &st);
+ if (ret == -1) {
+ ret = -errno;
+ goto out;
+ }
+
+ if (st.st_size > sizeof(data_item->payload) - sizeof(u32)) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ *(u32 *)data_item->payload = __cpu_to_be32(st.st_size);
+
+ fd = open(signed_file_template, O_RDONLY);
+ if (fd == -1) {
+ ret = -errno;
+ goto out;
+ }
+
+ ret = read(fd, data_item->payload + sizeof(u32), st.st_size);
+
+ close(fd);
+
+ if (ret != st.st_size) {
+ ret = -EIO;
+ goto out;
+ }
+
+ ret = 0;
+out:
+ unlink(signed_file_template);
+ return ret;
+}
+
+void test_verify_pkcs7_sig(void)
+{
+ struct test_verify_pkcs7_sig *skel = NULL;
+ struct bpf_map *map;
+ struct data data;
+ u32 saved_len;
+ int ret, zero = 0;
+
+ if (!env.sign_file_path || !env.kernel_priv_cert_path) {
+ printf(
+ "%s:SKIP:sign-file and kernel priv key cert paths missing\n",
+ __func__);
+ test__skip();
+ return;
+ }
+
+ skel = test_verify_pkcs7_sig__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "test_verify_pkcs7_sig__open_and_load"))
+ goto close_prog;
+
+ ret = test_verify_pkcs7_sig__attach(skel);
+ if (!ASSERT_OK(ret, "test_verify_pkcs7_sig__attach\n"))
+ goto close_prog;
+
+ map = bpf_object__find_map_by_name(skel->obj, "data_input");
+ if (!ASSERT_OK_PTR(map, "data_input not found"))
+ goto close_prog;
+
+ ret = populate_data_item(&data);
+ if (!ASSERT_OK(ret, "populate_data_item\n"))
+ goto close_prog;
+
+ skel->bss->monitored_pid = getpid();
+
+ ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);
+ if (!ASSERT_OK(ret, "bpf_map_update_elem\n"))
+ goto close_prog;
+
+ saved_len = *(__u32 *)data.payload;
+ *(__u32 *)data.payload = sizeof(data.payload);
+ ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);
+ if (!ASSERT_LT(ret, 0, "bpf_map_update_elem data_input\n"))
+ goto close_prog;
+
+ *(__u32 *)data.payload = saved_len;
+ data.payload[sizeof(__u32)] = 'a';
+ ret = bpf_map_update_elem(bpf_map__fd(map), &zero, &data, BPF_ANY);
+ ASSERT_LT(ret, 0, "bpf_map_update_elem data_input\n");
+close_prog:
+ skel->bss->monitored_pid = 0;
+ test_verify_pkcs7_sig__destroy(skel);
+}
diff --git a/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c b/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c
new file mode 100644
index 000000000000..e72bcb7fb7a9
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/test_verify_pkcs7_sig.c
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * Copyright (C) 2022 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@xxxxxxxxxx>
+ */
+
+#include "vmlinux.h"
+#include <errno.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <bpf/bpf_endian.h>
+
+#define MAX_DATA_SIZE 4096
+
+#ifdef __BIG_ENDIAN__
+#define be32_to_cpu(x) (x)
+#else
+#define be32_to_cpu(x) ___bpf_swab32(x)
+#endif
+
+#define VERIFY_USE_SECONDARY_KEYRING (1UL)
+
+/* In stripped ARM and x86-64 modules, ~ is surprisingly rare. */
+#define MODULE_SIG_STRING "~Module signature appended~\n"
+
+u32 monitored_pid;
+
+struct data {
+ u8 payload[MAX_DATA_SIZE];
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, __u32);
+ __type(value, struct data);
+} data_input SEC(".maps");
+
+char _license[] SEC("license") = "GPL";
+
+static int mod_check_sig(const struct module_signature *ms, size_t file_len)
+{
+ if (!ms)
+ return -ENOENT;
+
+ if (be32_to_cpu(ms->sig_len) >= file_len - sizeof(*ms))
+ return -EBADMSG;
+
+ if (ms->id_type != PKEY_ID_PKCS7)
+ return -ENOPKG;
+
+ if (ms->algo != 0 ||
+ ms->hash != 0 ||
+ ms->signer_len != 0 ||
+ ms->key_id_len != 0 ||
+ ms->__pad[0] != 0 ||
+ ms->__pad[1] != 0 ||
+ ms->__pad[2] != 0)
+ return -EBADMSG;
+
+ return 0;
+}
+
+SEC("lsm.s/bpf")
+int BPF_PROG(bpf, int cmd, union bpf_attr *attr, unsigned int size)
+{
+ const size_t marker_len = sizeof(MODULE_SIG_STRING) - 1;
+ char marker[sizeof(MODULE_SIG_STRING) - 1];
+ struct module_signature ms;
+ struct data *data_ptr;
+ u32 modlen;
+ u32 sig_len;
+ u64 value;
+ u8 *mod;
+ u32 pid;
+ int ret, zero = 0;
+
+ pid = bpf_get_current_pid_tgid() >> 32;
+ if (pid != monitored_pid)
+ return 0;
+
+ data_ptr = bpf_map_lookup_elem(&data_input, &zero);
+ if (!data_ptr)
+ return 0;
+
+ bpf_probe_read(&value, sizeof(value), &attr->value);
+
+ bpf_copy_from_user(data_ptr, sizeof(struct data),
+ (void *)(unsigned long)value);
+
+ modlen = be32_to_cpu(*(u32 *)data_ptr->payload);
+ mod = data_ptr->payload + sizeof(u32);
+
+ if (modlen > sizeof(struct data) - sizeof(u32))
+ return -EINVAL;
+
+ if (modlen <= marker_len)
+ return -ENOENT;
+
+ modlen &= sizeof(struct data) - 1;
+ bpf_probe_read(marker, marker_len, (char *)mod + modlen - marker_len);
+
+ if (bpf_strncmp(marker, marker_len, MODULE_SIG_STRING))
+ return -ENOENT;
+
+ modlen -= marker_len;
+
+ if (modlen <= sizeof(ms))
+ return -EBADMSG;
+
+ bpf_probe_read(&ms, sizeof(ms), (char *)mod + (modlen - sizeof(ms)));
+
+ ret = mod_check_sig(&ms, modlen);
+ if (ret)
+ return ret;
+
+ sig_len = be32_to_cpu(ms.sig_len);
+ modlen -= sig_len + sizeof(ms);
+
+ modlen &= 0x3ff;
+ sig_len &= 0x3ff;
+
+ return bpf_verify_pkcs7_signature(mod, modlen, mod + modlen, sig_len,
+ VERIFY_USE_SECONDARY_KEYRING);
+}
--
2.25.1