KP Singh <kpsingh@xxxxxxxxxx> writes: > This patch extends the BPF_PROG_LOAD command by adding three new fields > to `union bpf_attr` in the user-space API: > > - signature: A pointer to the signature blob. > - signature_size: The size of the signature blob. > - keyring_id: The serial number of a loaded kernel keyring (e.g., > the user or session keyring) containing the trusted public keys. > > When a BPF program is loaded with a signature, the kernel: > > 1. Retrieves the trusted keyring using the provided `keyring_id`. > 2. Verifies the supplied signature against the BPF program's > instruction buffer. > 3. If the signature is valid and was generated by a key in the trusted > keyring, the program load proceeds. > 4. If no signature is provided, the load proceeds as before, allowing > for backward compatibility. LSMs can chose to restrict unsigned > programs and implement a security policy. > 5. If signature verification fails for any reason, > the program is not loaded. > > Signed-off-by: KP Singh <kpsingh@xxxxxxxxxx> > --- > include/linux/bpf.h | 9 +++++++- > include/uapi/linux/bpf.h | 10 +++++++++ > kernel/bpf/syscall.c | 39 +++++++++++++++++++++++++++++++++- > kernel/trace/bpf_trace.c | 6 ++++-- > tools/include/uapi/linux/bpf.h | 10 +++++++++ > tools/lib/bpf/bpf.c | 2 +- > 6 files changed, 71 insertions(+), 5 deletions(-) > > diff --git a/include/linux/bpf.h b/include/linux/bpf.h > index 35f1a633d87a..32a41803d61c 100644 > --- a/include/linux/bpf.h > +++ b/include/linux/bpf.h > @@ -2778,7 +2778,14 @@ bpf_jit_find_kfunc_model(const struct bpf_prog *prog, > int bpf_get_kfunc_addr(const struct bpf_prog *prog, u32 func_id, > u16 btf_fd_idx, u8 **func_addr); > > -struct bpf_core_ctx { > +__bpf_kfunc struct bpf_key *bpf_lookup_user_key(u32 serial, u64 flags); > +__bpf_kfunc struct bpf_key *bpf_lookup_system_key(u64 id); > +__bpf_kfunc void bpf_key_put(struct bpf_key *bkey); > +__bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_p, > + struct bpf_dynptr *sig_p, > + struct bpf_key *trusted_keyring); > + > + struct bpf_core_ctx { > struct bpf_verifier_log *log; > const struct btf *btf; > }; > diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h > index ffd9e11befc2..5f7c82ebe10a 100644 > --- a/include/uapi/linux/bpf.h > +++ b/include/uapi/linux/bpf.h > @@ -1589,6 +1589,16 @@ union bpf_attr { > * continuous. > */ > __u32 fd_array_cnt; > + /* Pointer to a buffer containing the signature of the BPF > + * program. > + */ > + __aligned_u64 signature; > + /* Size of the signature buffer in bytes. */ > + __u32 signature_size; > + /* ID of the kernel keyring to be used for signature > + * verification. > + */ > + __u32 keyring_id; > }; > > struct { /* anonymous struct used by BPF_OBJ_* commands */ > diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c > index c81be07fa4fa..6cd5ba42d946 100644 > --- a/kernel/bpf/syscall.c > +++ b/kernel/bpf/syscall.c > @@ -2782,8 +2782,39 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type) > } > } > > +static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr, bpfptr_t uattr) > +{ > + bpfptr_t usig = make_bpfptr(attr->signature, uattr.is_kernel); > + struct bpf_dynptr_kern sig_ptr, insns_ptr; > + struct bpf_key *key = NULL; > + void *sig; > + int err = 0; > + > + key = bpf_lookup_user_key(attr->keyring_id, 0); > + if (!key) > + return -ENOKEY; > + > + sig = kvmemdup_bpfptr(usig, attr->signature_size); > + if (!sig) { > + bpf_key_put(key); > + return -ENOMEM; > + } > + > + bpf_dynptr_init(&sig_ptr, sig, BPF_DYNPTR_TYPE_LOCAL, 0, > + attr->signature_size); > + bpf_dynptr_init(&insns_ptr, prog->insnsi, BPF_DYNPTR_TYPE_LOCAL, 0, > + prog->len * sizeof(struct bpf_insn)); > + > + err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&insns_ptr, > + (struct bpf_dynptr *)&sig_ptr, key); > + > + bpf_key_put(key); > + kvfree(sig); > + return err; > +} > + > /* last field in 'union bpf_attr' used by this command */ > -#define BPF_PROG_LOAD_LAST_FIELD fd_array_cnt > +#define BPF_PROG_LOAD_LAST_FIELD keyring_id > > static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) > { > @@ -2947,6 +2978,12 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size) > /* 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); > + if (err) > + goto free_prog; > + } > + > prog->orig_prog = NULL; > prog->jited = 0; > > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c > index 132c8be6f635..0cce39e1a9ee 100644 > --- a/kernel/trace/bpf_trace.c > +++ b/kernel/trace/bpf_trace.c > @@ -1351,7 +1351,6 @@ __bpf_kfunc void bpf_key_put(struct bpf_key *bkey) > kfree(bkey); > } > > -#ifdef CONFIG_SYSTEM_DATA_VERIFICATION > /** > * bpf_verify_pkcs7_signature - verify a PKCS#7 signature > * @data_p: data to verify > @@ -1367,6 +1366,7 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_p, > struct bpf_dynptr *sig_p, > struct bpf_key *trusted_keyring) > { > +#ifdef CONFIG_SYSTEM_DATA_VERIFICATION > struct bpf_dynptr_kern *data_ptr = (struct bpf_dynptr_kern *)data_p; > struct bpf_dynptr_kern *sig_ptr = (struct bpf_dynptr_kern *)sig_p; > const void *data, *sig; > @@ -1396,8 +1396,10 @@ __bpf_kfunc int bpf_verify_pkcs7_signature(struct bpf_dynptr *data_p, > trusted_keyring->key, > VERIFYING_UNSPECIFIED_SIGNATURE, NULL, > NULL); > -} The usage for this is no longer unspecified. VERIFYING_BPF_SIGNATURE or similar would add clarity here. > +#else > + return -EOPNOTSUPP; > #endif /* CONFIG_SYSTEM_DATA_VERIFICATION */ > +} > > __bpf_kfunc_end_defs(); > > diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h > index ffd9e11befc2..5f7c82ebe10a 100644 > --- a/tools/include/uapi/linux/bpf.h > +++ b/tools/include/uapi/linux/bpf.h > @@ -1589,6 +1589,16 @@ union bpf_attr { > * continuous. > */ > __u32 fd_array_cnt; > + /* Pointer to a buffer containing the signature of the BPF > + * program. > + */ > + __aligned_u64 signature; > + /* Size of the signature buffer in bytes. */ > + __u32 signature_size; > + /* ID of the kernel keyring to be used for signature > + * verification. > + */ > + __u32 keyring_id; > }; > > struct { /* anonymous struct used by BPF_OBJ_* commands */ > diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c > index 11fa2d64ccca..1a85cfa4282c 100644 > --- a/tools/lib/bpf/bpf.c > +++ b/tools/lib/bpf/bpf.c > @@ -240,7 +240,7 @@ int bpf_prog_load(enum bpf_prog_type prog_type, > const struct bpf_insn *insns, size_t insn_cnt, > struct bpf_prog_load_opts *opts) > { > - const size_t attr_sz = offsetofend(union bpf_attr, fd_array_cnt); > + const size_t attr_sz = offsetofend(union bpf_attr, keyring_id); > void *finfo = NULL, *linfo = NULL; > const char *func_info, *line_info; > __u32 log_size, log_level, attach_prog_fd, attach_btf_obj_fd; > -- > 2.43.0