Skip to content
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
6 changes: 6 additions & 0 deletions include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,12 @@ union bpf_attr {
* verification.
*/
__s32 keyring_id;
/* Pointer to a buffer containing the maps used in the signature
* hash chain of the BPF program.
*/
__aligned_u64 signature_maps;
/* Size of the signature maps buffer. */
__u32 signature_maps_size;
};

struct { /* anonymous struct used by BPF_OBJ_* commands */
Expand Down
71 changes: 67 additions & 4 deletions kernel/bpf/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -2800,14 +2800,35 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
}
}

static inline int bpf_map_get_hash(int map_fd, void *buffer)
{
struct bpf_map *map;

CLASS(fd, f)(map_fd);
map = __bpf_map_get(f);
if (IS_ERR(map))
return PTR_ERR(map);

if (!map->ops->map_get_hash)
return -EINVAL;

return map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, buffer);
}

static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr,
bool is_kernel)
{
bpfptr_t usig = make_bpfptr(attr->signature, is_kernel);
struct bpf_dynptr_kern sig_ptr, insns_ptr;
bpfptr_t umaps;
struct bpf_dynptr_kern sig_ptr, insns_ptr, hash_ptr;
struct bpf_key *key = NULL;
void *sig;
int *maps;
int map_fd;
int err = 0;
u64 buffer[SHA256_DIGEST_SIZE * 2 / sizeof(u64)];
u64 hash[SHA256_DIGEST_SIZE / sizeof(u64)];
int n;

if (system_keyring_id_check(attr->keyring_id) == 0)
key = bpf_lookup_system_key(attr->keyring_id);
Expand All @@ -2828,16 +2849,58 @@ static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr
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);
if (!attr->signature_maps_size) {
err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&insns_ptr,
(struct bpf_dynptr *)&sig_ptr, key);
} else {
bpf_dynptr_init(&hash_ptr, hash, BPF_DYNPTR_TYPE_LOCAL, 0,
sizeof(hash));
umaps = make_bpfptr(attr->signature_maps, is_kernel);
maps = kvmemdup_bpfptr(umaps, attr->signature_maps_size * sizeof(*maps));
if (!maps) {
err = -ENOMEM;
goto out;
}
/* Process the map array in reverse order to generate a hash chain
h(n) = sha256(h(n + 1), sha256(map(n)))
h(n_len) = sha256(map(n_len)) */
for (n = attr->signature_maps_size - 1; n >= 0; n--) {
err = copy_from_bpfptr_offset(&map_fd,
make_bpfptr(attr->fd_array, is_kernel),
maps[n] * sizeof(map_fd),
sizeof(map_fd));
if (err)
goto free_maps;

if (n == attr->signature_maps_size - 1)
err = bpf_map_get_hash(map_fd, hash);
else {
memcpy(buffer, hash, sizeof(hash));
err = bpf_map_get_hash(map_fd, buffer + ARRAY_SIZE(hash));
sha256((u8 *)buffer, sizeof(buffer), (u8 *)&hash);
}
if (err)
goto free_maps;
}
/* Calculate final hash with program instructions
f_hash = sha256(sha256(prog), h(0)) */
sha256((u8 *)prog->insnsi, prog->len * sizeof(struct bpf_insn), (u8 *)&buffer);
memcpy(buffer + ARRAY_SIZE(hash), hash, sizeof(hash));
sha256((u8 *)buffer, sizeof(buffer), (u8 *)&hash);
err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&hash_ptr,
(struct bpf_dynptr *)&sig_ptr, key);

free_maps:
kvfree(maps);
}
out:
bpf_key_put(key);
kvfree(sig);
return err;
}

/* last field in 'union bpf_attr' used by this command */
#define BPF_PROG_LOAD_LAST_FIELD keyring_id
#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
7 changes: 6 additions & 1 deletion tools/bpf/bpftool/Documentation/bpftool-gen.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ SYNOPSIS

**bpftool** [*OPTIONS*] **gen** *COMMAND*

*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } | [ { **-S** | **--sign** } {**-k** <private_key.pem>} **-i** <certificate.x509> ] }
*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } | [ { **-S** | **--sign** } { **-M** | **--sign-maps** } {**-k** <private_key.pem>} **-i** <certificate.x509> ] }

*COMMAND* := { **object** | **skeleton** | **help** }

Expand Down Expand Up @@ -190,6 +190,11 @@ OPTIONS
For skeletons, generate a signed skeleton. This option must be used with
**-k** and **-i**. Using this flag implicitly enables **--use-loader**.

-M --sign-maps
For skeletons, generate a signed skeleton that includes a hash chain for the
skeletons maps. This option must be used with **-k** and **-i**. Using this
flag implicitly enables **--use-loader** and **--sign**.

-k <private_key.pem>
Path to the private key file in PEM format, required for signing.

Expand Down
27 changes: 26 additions & 1 deletion tools/bpf/bpftool/gen.c
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,9 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
if (sign_progs)
opts.gen_hash = true;

if (sign_maps)
opts.sign_maps = true;

err = bpf_object__gen_loader(obj, &opts);
if (err)
return err;
Expand Down Expand Up @@ -793,6 +796,8 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
if (sign_progs) {
sopts.insns = opts.insns;
sopts.insns_sz = opts.insns_sz;
sopts.data = opts.data;
sopts.data_sz = opts.data_sz;
sopts.excl_prog_hash = prog_sha;
sopts.excl_prog_hash_sz = sizeof(prog_sha);
sopts.signature = sig_buf;
Expand Down Expand Up @@ -822,6 +827,13 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
\n\
\";\n");

if (sign_maps) {
codegen("\
\n\
static const int opts_signature_maps[1] __attribute__((__aligned__(8))) = {0}; \n\
");
}

codegen("\
\n\
opts.signature = (void *)opts_sig; \n\
Expand All @@ -830,6 +842,19 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
opts.excl_prog_hash_sz = sizeof(opts_excl_hash) - 1; \n\
opts.keyring_id = skel->keyring_id; \n\
");
if (sign_maps) {
codegen("\
\n\
opts.signature_maps = (void *)opts_signature_maps; \n\
opts.signature_maps_sz = 1; \n\
");
} else {
codegen("\
\n\
opts.signature_maps = (void *)NULL; \n\
opts.signature_maps_sz = 0; \n\
");
}
}

codegen("\
Expand Down Expand Up @@ -1990,7 +2015,7 @@ static int do_help(int argc, char **argv)
" %1$s %2$s help\n"
"\n"
" " HELP_SPEC_OPTIONS " |\n"
" {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ]}\n"
" {-L|--use-loader} | [ {-S|--sign } {-M|--sign-maps } {-k} <private_key.pem> {-i} <certificate.x509> ]}\n"
"",
bin_name, "gen");

Expand Down
9 changes: 8 additions & 1 deletion tools/bpf/bpftool/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ bool use_loader;
struct btf *base_btf;
struct hashmap *refs_table;
bool sign_progs;
bool sign_maps;
const char *private_key_path;
const char *cert_path;

Expand Down Expand Up @@ -452,6 +453,7 @@ int main(int argc, char **argv)
{ "debug", no_argument, NULL, 'd' },
{ "use-loader", no_argument, NULL, 'L' },
{ "sign", no_argument, NULL, 'S' },
{ "sign-maps", no_argument, NULL, 'M' },
{ "base-btf", required_argument, NULL, 'B' },
{ 0 }
};
Expand All @@ -478,7 +480,7 @@ int main(int argc, char **argv)
bin_name = "bpftool";

opterr = 0;
while ((opt = getopt_long(argc, argv, "VhpjfLmndSi:k:B:l",
while ((opt = getopt_long(argc, argv, "VhpjfLmndSMi:k:B:l",
options, NULL)) >= 0) {
switch (opt) {
case 'V':
Expand Down Expand Up @@ -528,6 +530,11 @@ int main(int argc, char **argv)
sign_progs = true;
use_loader = true;
break;
case 'M':
sign_maps = true;
sign_progs = true;
use_loader = true;
break;
case 'k':
private_key_path = optarg;
break;
Expand Down
1 change: 1 addition & 0 deletions tools/bpf/bpftool/main.h
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ extern bool use_loader;
extern struct btf *base_btf;
extern struct hashmap *refs_table;
extern bool sign_progs;
extern bool sign_maps;
extern const char *private_key_path;
extern const char *cert_path;

Expand Down
17 changes: 14 additions & 3 deletions tools/bpf/bpftool/sign.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <errno.h>

#include <bpf/skel_internal.h>
#include <bpf/libbpf_internal.h>

#include "main.h"

Expand Down Expand Up @@ -131,8 +132,18 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
long actual_sig_len = 0;
X509 *x509 = NULL;
int err = 0;

bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
unsigned char hash[SHA256_DIGEST_LENGTH * 2];
unsigned char term[SHA256_DIGEST_LENGTH];

if (sign_maps) {
libbpf_sha256(opts->insns, opts->insns_sz, hash, SHA256_DIGEST_LENGTH);
libbpf_sha256(opts->data, opts->data_sz, hash + SHA256_DIGEST_LENGTH,
SHA256_DIGEST_LENGTH);
libbpf_sha256(hash, sizeof(hash), term, sizeof(term));
bd_in = BIO_new_mem_buf(term, sizeof(term));
} else {
bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
}
if (!bd_in) {
err = -ENOMEM;
goto cleanup;
Expand Down Expand Up @@ -173,7 +184,7 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
EVP_Digest(opts->insns, opts->insns_sz, opts->excl_prog_hash,
&opts->excl_prog_hash_sz, EVP_sha256(), NULL);

bd_out = BIO_new(BIO_s_mem());
bd_out = BIO_new(BIO_s_mem());
if (!bd_out) {
err = -ENOMEM;
goto cleanup;
Expand Down
6 changes: 6 additions & 0 deletions tools/include/uapi/linux/bpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1621,6 +1621,12 @@ union bpf_attr {
* verification.
*/
__s32 keyring_id;
/* Pointer to a buffer containing the maps used in the signature
* hash chain of the BPF program.
*/
__aligned_u64 signature_maps;
/* Size of the signature maps buffer. */
__u32 signature_maps_size;
};

struct { /* anonymous struct used by BPF_OBJ_* commands */
Expand Down
3 changes: 2 additions & 1 deletion tools/lib/bpf/libbpf.h
Original file line number Diff line number Diff line change
Expand Up @@ -1858,9 +1858,10 @@ struct gen_loader_opts {
__u32 data_sz;
__u32 insns_sz;
bool gen_hash;
bool sign_maps;
};

#define gen_loader_opts__last_field gen_hash
#define gen_loader_opts__last_field sign_maps
LIBBPF_API int bpf_object__gen_loader(struct bpf_object *obj,
struct gen_loader_opts *opts);

Expand Down
6 changes: 5 additions & 1 deletion tools/lib/bpf/skel_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,8 @@ struct bpf_load_and_run_opts {
__s32 keyring_id;
void *excl_prog_hash;
__u32 excl_prog_hash_sz;
const int *signature_maps;
__u32 signature_maps_sz;
};

long kern_sys_bpf(__u32 cmd, void *attr, __u32 attr_size);
Expand Down Expand Up @@ -352,7 +354,7 @@ static inline int skel_map_freeze(int fd)

static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
{
const size_t prog_load_attr_sz = offsetofend(union bpf_attr, keyring_id);
const size_t prog_load_attr_sz = offsetofend(union bpf_attr, signature_maps_size);
const size_t test_run_attr_sz = offsetofend(union bpf_attr, test);
int map_fd = -1, prog_fd = -1, key = 0, err;
union bpf_attr attr;
Expand Down Expand Up @@ -395,6 +397,8 @@ static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
#ifndef __KERNEL__
attr.signature = (long) opts->signature;
attr.signature_size = opts->signature_sz;
attr.signature_maps = (long) opts->signature_maps;
attr.signature_maps_size = opts->signature_maps_sz;
#else
if (opts->signature || opts->signature_sz)
pr_warn("signatures are not supported from bpf_preload\n");
Expand Down
18 changes: 16 additions & 2 deletions tools/testing/selftests/bpf/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -500,12 +500,14 @@ LSKELS := fexit_sleep.c trace_printk.c trace_vprintk.c map_ptr_kern.c \
core_kern.c core_kern_overflow.c test_ringbuf.c \
test_ringbuf_n.c test_ringbuf_map_key.c test_ringbuf_write.c

LSKELS_SIGNED := fentry_test.c fexit_test.c atomics.c
LSKELS_SIGNED := fentry_test.c fexit_test.c

LSKELS_SIGNED_MAPS := atomics.c

# Generate both light skeleton and libbpf skeleton for these
LSKELS_EXTRA := test_ksyms_module.c test_ksyms_weak.c kfunc_call_test.c \
kfunc_call_test_subprog.c
SKEL_BLACKLIST += $$(LSKELS) $$(LSKELS_SIGNED)
SKEL_BLACKLIST += $$(LSKELS) $$(LSKELS_SIGNED) $$(LSKELS_SIGNED_MAPS)

test_static_linked.skel.h-deps := test_static_linked1.bpf.o test_static_linked2.bpf.o
linked_funcs.skel.h-deps := linked_funcs1.bpf.o linked_funcs2.bpf.o
Expand Down Expand Up @@ -537,6 +539,7 @@ HEADERS_FOR_BPF_OBJS := $(wildcard $(BPFDIR)/*.bpf.h) \
define DEFINE_TEST_RUNNER

LSKEL_SIGN := -S -k $(PRIVATE_KEY) -i $(VERIFICATION_CERT)
LSKEL_SIGN_MAPS := -S -M -k $(PRIVATE_KEY) -i $(VERIFICATION_CERT)
TRUNNER_OUTPUT := $(OUTPUT)$(if $2,/)$2
TRUNNER_BINARY := $1$(if $2,-)$2
TRUNNER_TEST_OBJS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.test.o, \
Expand All @@ -553,6 +556,7 @@ TRUNNER_BPF_SKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.skel.h, \
TRUNNER_BPF_LSKELS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS) $$(LSKELS_EXTRA))
TRUNNER_BPF_SKELS_LINKED := $$(addprefix $$(TRUNNER_OUTPUT)/,$(LINKED_SKELS))
TRUNNER_BPF_LSKELS_SIGNED := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS_SIGNED))
TRUNNER_BPF_LSKELS_SIGNED_MAPS := $$(patsubst %.c,$$(TRUNNER_OUTPUT)/%.lskel.h, $$(LSKELS_SIGNED_MAPS))
TEST_GEN_FILES += $$(TRUNNER_BPF_OBJS)

# Evaluate rules now with extra TRUNNER_XXX variables above already defined
Expand Down Expand Up @@ -616,6 +620,15 @@ $(TRUNNER_BPF_LSKELS_SIGNED): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
$(Q)$$(BPFTOOL) gen skeleton $(LSKEL_SIGN) $$(<:.o=.llinked3.o) name $$(notdir $$(<:.bpf.o=_lskel)) > $$@
$(Q)rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)

$(TRUNNER_BPF_LSKELS_SIGNED_MAPS): %.lskel.h: %.bpf.o $(BPFTOOL) | $(TRUNNER_OUTPUT)
$$(call msg,GEN-SKEL,$(TRUNNER_BINARY) (signed),$$@)
$(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked1.o) $$<
$(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked2.o) $$(<:.o=.llinked1.o)
$(Q)$$(BPFTOOL) gen object $$(<:.o=.llinked3.o) $$(<:.o=.llinked2.o)
$(Q)diff $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)
$(Q)$$(BPFTOOL) gen skeleton $(LSKEL_SIGN_MAPS) $$(<:.o=.llinked3.o) name $$(notdir $$(<:.bpf.o=_lskel)) > $$@
$(Q)rm -f $$(<:.o=.llinked1.o) $$(<:.o=.llinked2.o) $$(<:.o=.llinked3.o)

$(LINKED_BPF_OBJS): %: $(TRUNNER_OUTPUT)/%

# .SECONDEXPANSION here allows to correctly expand %-deps variables as prerequisites
Expand Down Expand Up @@ -666,6 +679,7 @@ $(TRUNNER_TEST_OBJS:.o=.d): $(TRUNNER_OUTPUT)/%.test.d: \
$(TRUNNER_BPF_SKELS) \
$(TRUNNER_BPF_LSKELS) \
$(TRUNNER_BPF_LSKELS_SIGNED) \
$(TRUNNER_BPF_LSKELS_SIGNED_MAPS) \
$(TRUNNER_BPF_SKELS_LINKED) \
$$(BPFOBJ) | $(TRUNNER_OUTPUT)

Expand Down
Loading