From: Matteo Croce <mcroce@xxxxxxxxxxxxx> When loading a BPF program, pass a signature which is used to validate the instructions. The signature type is the same used to validate the kernel modules. This happens when loading a program with, respectively, an invalid and a valid signature: # ./core-bad [ 8524.417567] Invalid BPF signature for '__loader.prog': -EKEYREJECTED failed to open and/or load BPF object # ./core-ok Signed-off-by: Matteo Croce <mcroce@xxxxxxxxxxxxx> --- crypto/asymmetric_keys/asymmetric_type.c | 1 + crypto/asymmetric_keys/pkcs7_verify.c | 7 +++- include/linux/verification.h | 1 + include/uapi/linux/bpf.h | 2 + kernel/bpf/Kconfig | 8 ++++ kernel/bpf/syscall.c | 47 +++++++++++++++++++++--- 6 files changed, 59 insertions(+), 7 deletions(-) diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index ad8af3d70ac0..e4f2fee19c5f 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -26,6 +26,7 @@ const char *const key_being_used_for[NR__KEY_BEING_USED_FOR] = { [VERIFYING_KEY_SIGNATURE] = "key sig", [VERIFYING_KEY_SELF_SIGNATURE] = "key self sig", [VERIFYING_UNSPECIFIED_SIGNATURE] = "unspec sig", + [VERIFYING_BPF_SIGNATURE] = "bpf sig", }; EXPORT_SYMBOL_GPL(key_being_used_for); diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 0b4d07aa8811..ab645f23c021 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -411,12 +411,15 @@ int pkcs7_verify(struct pkcs7_message *pkcs7, switch (usage) { case VERIFYING_MODULE_SIGNATURE: + case VERIFYING_BPF_SIGNATURE: if (pkcs7->data_type != OID_data) { - pr_warn("Invalid module sig (not pkcs7-data)\n"); + pr_warn("Invalid %s (not pkcs7-data)\n", + key_being_used_for[usage]); return -EKEYREJECTED; } if (pkcs7->have_authattrs) { - pr_warn("Invalid module sig (has authattrs)\n"); + pr_warn("Invalid %s (has authattrs)\n", + key_being_used_for[usage]); return -EKEYREJECTED; } break; diff --git a/include/linux/verification.h b/include/linux/verification.h index a655923335ae..71482644eea0 100644 --- a/include/linux/verification.h +++ b/include/linux/verification.h @@ -27,6 +27,7 @@ enum key_being_used_for { VERIFYING_KEY_SIGNATURE, VERIFYING_KEY_SELF_SIGNATURE, VERIFYING_UNSPECIFIED_SIGNATURE, + VERIFYING_BPF_SIGNATURE, NR__KEY_BEING_USED_FOR }; extern const char *const key_being_used_for[NR__KEY_BEING_USED_FOR]; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index c26871263f1f..bbb4435c7586 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -1346,6 +1346,8 @@ union bpf_attr { __aligned_u64 fd_array; /* array of FDs */ __aligned_u64 core_relos; __u32 core_relo_rec_size; /* sizeof(struct bpf_core_relo) */ + __aligned_u64 signature; /* instruction's signature */ + __u32 sig_len; /* signature size */ }; struct { /* anonymous struct used by BPF_OBJ_* commands */ diff --git a/kernel/bpf/Kconfig b/kernel/bpf/Kconfig index d24d518ddd63..735979bb8672 100644 --- a/kernel/bpf/Kconfig +++ b/kernel/bpf/Kconfig @@ -79,6 +79,14 @@ config BPF_UNPRIV_DEFAULT_OFF If you are unsure how to answer this question, answer Y. +config BPF_SIG + bool "BPF signature verification" + select SYSTEM_DATA_VERIFICATION + depends on BPF_SYSCALL + help + Check BPF programs for valid signatures upon load: the signature + is passed via the bpf() syscall together with the instructions. + source "kernel/bpf/preload/Kconfig" config BPF_LSM diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index b3ada4085f85..5aaa74a72b46 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -32,6 +32,10 @@ #include <linux/rcupdate_trace.h> #include <linux/memcontrol.h> +#ifdef CONFIG_BPF_SIG +#include <linux/verification.h> +#endif + #define IS_FD_ARRAY(map) ((map)->map_type == BPF_MAP_TYPE_PERF_EVENT_ARRAY || \ (map)->map_type == BPF_MAP_TYPE_CGROUP_ARRAY || \ (map)->map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS) @@ -2184,7 +2188,7 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type) } /* last field in 'union bpf_attr' used by this command */ -#define BPF_PROG_LOAD_LAST_FIELD core_relo_rec_size +#define BPF_PROG_LOAD_LAST_FIELD sig_len static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr) { @@ -2302,6 +2306,43 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr) bpf_prog_insn_size(prog)) != 0) goto free_prog_sec; + err = bpf_obj_name_cpy(prog->aux->name, attr->prog_name, + sizeof(attr->prog_name)); + if (err < 0) + goto free_prog_sec; + +#ifdef CONFIG_BPF_SIG + if (attr->sig_len) { + char *signature; + + signature = kmalloc(attr->sig_len, GFP_USER); + if (!signature) { + err = -ENOMEM; + goto free_prog_sec; + } + + if (copy_from_user(signature, (char *)attr->signature, attr->sig_len)) { + err = -EFAULT; + kfree(signature); + goto free_prog_sec; + } + + err = verify_pkcs7_signature(prog->insns, + prog->len * sizeof(struct bpf_insn), + signature, attr->sig_len, + VERIFY_USE_SECONDARY_KEYRING, + VERIFYING_BPF_SIGNATURE, + NULL, NULL); + kfree(signature); + + if (err) { + pr_warn("Invalid BPF signature for '%s': %pe\n", + prog->aux->name, ERR_PTR(err)); + goto free_prog_sec; + } + } +#endif + prog->orig_prog = NULL; prog->jited = 0; @@ -2320,10 +2361,6 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr) goto free_prog_sec; prog->aux->load_time = ktime_get_boottime_ns(); - err = bpf_obj_name_cpy(prog->aux->name, attr->prog_name, - sizeof(attr->prog_name)); - if (err < 0) - goto free_prog_sec; /* run eBPF verifier */ err = bpf_check(&prog, attr, uattr); -- 2.33.1