Skip to content

BPF signature verification #5385

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ struct bpf_map_ops {
long (*map_pop_elem)(struct bpf_map *map, void *value);
long (*map_peek_elem)(struct bpf_map *map, void *value);
void *(*map_lookup_percpu_elem)(struct bpf_map *map, void *key, u32 cpu);
int (*map_get_hash)(struct bpf_map *map, u8 *out);

/* funcs called by prog_array and perf_event_array map */
void *(*map_fd_get_ptr)(struct bpf_map *map, struct file *map_file,
Expand Down Expand Up @@ -1592,6 +1593,7 @@ struct bpf_prog_aux {
#ifdef CONFIG_SECURITY
void *security;
#endif
bool signature_verified;
struct bpf_token *token;
struct bpf_prog_offload *offload;
struct btf *btf;
Expand Down
1 change: 1 addition & 0 deletions include/linux/verification.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ enum key_being_used_for {
VERIFYING_KEXEC_PE_SIGNATURE,
VERIFYING_KEY_SIGNATURE,
VERIFYING_KEY_SELF_SIGNATURE,
VERIFYING_EBPF_SIGNATURE,
VERIFYING_UNSPECIFIED_SIGNATURE,
NR__KEY_BEING_USED_FOR
};
Expand Down
4 changes: 4 additions & 0 deletions include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1587,6 +1587,10 @@ union bpf_attr {
* continuous.
*/
__u32 fd_array_cnt;
__aligned_u64 signature; /* program signature */
__u32 signature_size; /* size of program signature */
__aligned_u64 signature_maps; /* maps used in signature */
__u32 signature_maps_size; /* size of maps used in signature */
};

struct { /* anonymous struct used by BPF_OBJ_* commands */
Expand Down
11 changes: 10 additions & 1 deletion kernel/bpf/arraymap.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include <uapi/linux/btf.h>
#include <linux/rcupdate_trace.h>
#include <linux/btf_ids.h>
#include <crypto/sha2.h>

#include "map_in_map.h"

Expand Down Expand Up @@ -426,6 +427,14 @@ static long array_map_delete_elem(struct bpf_map *map, void *key)
return -EINVAL;
}

static int array_map_get_hash(struct bpf_map *map, u8 *out)
{
struct bpf_array *array = container_of(map, struct bpf_array, map);

sha256(array->value, array->elem_size * array->map.max_entries, out);
return 0;
}

static void *array_map_vmalloc_addr(struct bpf_array *array)
{
return (void *)round_down((unsigned long)array, PAGE_SIZE);
Expand Down Expand Up @@ -792,6 +801,7 @@ const struct bpf_map_ops array_map_ops = {
.map_lookup_elem = array_map_lookup_elem,
.map_update_elem = array_map_update_elem,
.map_delete_elem = array_map_delete_elem,
.map_get_hash = array_map_get_hash,
.map_gen_lookup = array_map_gen_lookup,
.map_direct_value_addr = array_map_direct_value_addr,
.map_direct_value_meta = array_map_direct_value_meta,
Expand Down Expand Up @@ -940,7 +950,6 @@ static long fd_array_map_delete_elem(struct bpf_map *map, void *key)
{
return __fd_array_map_delete_elem(map, key, true);
}

static void *prog_fd_array_get_ptr(struct bpf_map *map,
struct file *map_file, int fd)
{
Expand Down
123 changes: 122 additions & 1 deletion kernel/bpf/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@
#include <linux/memcontrol.h>
#include <linux/trace_events.h>
#include <linux/tracepoint.h>
#include <crypto/pkcs7.h>
#include <crypto/sha2.h>

#include <net/netfilter/nf_bpf_link.h>
#include <net/netkit.h>
Expand Down Expand Up @@ -2216,6 +2218,15 @@ static int map_freeze(const union bpf_attr *attr)
return err;
}

static int __map_get_hash(struct bpf_map *map, u8 *out)
{
if (map->ops->map_get_hash) {
map->ops->map_get_hash(map, out);
return 0;
}
return -EINVAL;
}

static const struct bpf_prog_ops * const bpf_prog_types[] = {
#define BPF_PROG_TYPE(_id, _name, prog_ctx_type, kern_ctx_type) \
[_id] = & _name ## _prog_ops,
Expand Down Expand Up @@ -2753,8 +2764,113 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
}
}

static int bpf_check_signature(struct bpf_prog *prog, union bpf_attr *attr, bpfptr_t uattr,
__u32 uattr_size)
{
u64 hash[4];
u64 buffer[8];
int err;
char *signature;
int *used_maps;
int n;
int map_fd;
struct bpf_map *map;

if (!attr->signature)
return 0;

signature = kmalloc(attr->signature_size, GFP_KERNEL);
if (!signature) {
err = -ENOMEM;
goto out;
}

if (copy_from_bpfptr(signature,
make_bpfptr(attr->signature, uattr.is_kernel),
attr->signature_size) != 0) {
err = -EINVAL;
goto free_sig;
}

if (!attr->signature_maps_size) {
sha256((u8 *)prog->insnsi, prog->len * sizeof(struct bpf_insn), (u8 *)&hash);
err = verify_pkcs7_signature(hash, sizeof(hash), signature, attr->signature_size,
VERIFY_USE_SECONDARY_KEYRING,
VERIFYING_EBPF_SIGNATURE,
NULL, NULL);
} else {
used_maps = kmalloc_array(attr->signature_maps_size,
sizeof(*used_maps), GFP_KERNEL);
if (!used_maps) {
err = -ENOMEM;
goto free_sig;
}
n = attr->signature_maps_size;
n--;

err = copy_from_bpfptr_offset(&map_fd, make_bpfptr(attr->fd_array, uattr.is_kernel),
used_maps[n] * sizeof(map_fd),
sizeof(map_fd));
if (err < 0)
goto free_maps;

/* calculate the terminal hash */
CLASS(fd, f)(map_fd);
map = __bpf_map_get(f);
if (IS_ERR(map)) {
err = PTR_ERR(map);
goto free_maps;
}
if (__map_get_hash(map, (u8 *)hash)) {
err = -EINVAL;
goto free_maps;
}

n--;
/* calculate a link in the hash chain */
while (n >= 0) {
memcpy(buffer, hash, sizeof(hash));
err = copy_from_bpfptr_offset(&map_fd,
make_bpfptr(attr->fd_array, uattr.is_kernel),
used_maps[n] * sizeof(map_fd),
sizeof(map_fd));
if (err < 0)
goto free_maps;

CLASS(fd, f)(map_fd);
map = __bpf_map_get(f);
if (!map) {
err = -EINVAL;
goto free_maps;
}
if (__map_get_hash(map, (u8 *)buffer+4)) {
err = -EINVAL;
goto free_maps;
}
sha256((u8 *)buffer, sizeof(buffer), (u8 *)&hash);
n--;
}
/* calculate the root hash and verify it's signature */
sha256((u8 *)prog->insnsi, prog->len * sizeof(struct bpf_insn), (u8 *)&buffer);
memcpy(buffer+4, hash, sizeof(hash));
sha256((u8 *)buffer, sizeof(buffer), (u8 *)&hash);
err = verify_pkcs7_signature(hash, sizeof(hash), signature, attr->signature_size,
VERIFY_USE_SECONDARY_KEYRING,
VERIFYING_EBPF_SIGNATURE,
NULL, NULL);
free_maps:
kfree(used_maps);
}

free_sig:
kfree(signature);
out:
prog->aux->signature_verified = !err;
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 signature_maps_size

static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
{
Expand Down Expand Up @@ -2963,6 +3079,11 @@ static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
if (err < 0)
goto free_prog;

/* run eBPF signature verifier */
err = bpf_check_signature(prog, attr, uattr, uattr_size);
if (err < 0)
goto free_prog;

err = security_bpf_prog_load(prog, attr, token, uattr.is_kernel);
if (err)
goto free_prog_sec;
Expand Down
4 changes: 2 additions & 2 deletions tools/bpf/bpftool/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,8 @@ include $(FEATURES_DUMP)
endif
endif

LIBS = $(LIBBPF) -lelf -lz
LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz
LIBS = $(LIBBPF) -lelf -lz -lcrypto
LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz -lcrypto

ifeq ($(feature-libelf-zstd),1)
LIBS += -lzstd
Expand Down
Loading
Loading