mirror of https://github.com/torvalds/linux.git
bpftool: Add support for signing BPF programs
Two modes of operation being added:
Add two modes of operation:
* For prog load, allow signing a program immediately before loading. This
is essential for command-line testing and administration.
bpftool prog load -S -k <private_key> -i <identity_cert> fentry_test.bpf.o
* For gen skeleton, embed a pre-generated signature into the C skeleton
file. This supports the use of signed programs in compiled applications.
bpftool gen skeleton -S -k <private_key> -i <identity_cert> fentry_test.bpf.o
Generation of the loader program and its metadata map is implemented in
libbpf (bpf_obj__gen_loader). bpftool generates a skeleton that loads
the program and automates the required steps: freezing the map, creating
an exclusive map, loading, and running. Users can use standard libbpf
APIs directly or integrate loader program generation into their own
toolchains.
Signed-off-by: KP Singh <kpsingh@kernel.org>
Acked-by: Quentin Monnet <qmo@kernel.org>
Link: https://lore.kernel.org/r/20250921160120.9711-5-kpsingh@kernel.org
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
parent
ea923080c1
commit
40863f4d6e
|
|
@ -16,7 +16,7 @@ SYNOPSIS
|
||||||
|
|
||||||
**bpftool** [*OPTIONS*] **gen** *COMMAND*
|
**bpftool** [*OPTIONS*] **gen** *COMMAND*
|
||||||
|
|
||||||
*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } }
|
*OPTIONS* := { |COMMON_OPTIONS| | { **-L** | **--use-loader** } | [ { **-S** | **--sign** } {**-k** <private_key.pem>} **-i** <certificate.x509> ] }
|
||||||
|
|
||||||
*COMMAND* := { **object** | **skeleton** | **help** }
|
*COMMAND* := { **object** | **skeleton** | **help** }
|
||||||
|
|
||||||
|
|
@ -186,6 +186,17 @@ OPTIONS
|
||||||
skeleton). A light skeleton contains a loader eBPF program. It does not use
|
skeleton). A light skeleton contains a loader eBPF program. It does not use
|
||||||
the majority of the libbpf infrastructure, and does not need libelf.
|
the majority of the libbpf infrastructure, and does not need libelf.
|
||||||
|
|
||||||
|
-S, --sign
|
||||||
|
For skeletons, generate a signed skeleton. This option must be used with
|
||||||
|
**-k** and **-i**. Using this flag implicitly enables **--use-loader**.
|
||||||
|
|
||||||
|
-k <private_key.pem>
|
||||||
|
Path to the private key file in PEM format, required for signing.
|
||||||
|
|
||||||
|
-i <certificate.x509>
|
||||||
|
Path to the X.509 certificate file in PEM or DER format, required for
|
||||||
|
signing.
|
||||||
|
|
||||||
EXAMPLES
|
EXAMPLES
|
||||||
========
|
========
|
||||||
**$ cat example1.bpf.c**
|
**$ cat example1.bpf.c**
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ SYNOPSIS
|
||||||
|
|
||||||
*OPTIONS* := { |COMMON_OPTIONS| |
|
*OPTIONS* := { |COMMON_OPTIONS| |
|
||||||
{ **-f** | **--bpffs** } | { **-m** | **--mapcompat** } | { **-n** | **--nomount** } |
|
{ **-f** | **--bpffs** } | { **-m** | **--mapcompat** } | { **-n** | **--nomount** } |
|
||||||
{ **-L** | **--use-loader** } }
|
{ **-L** | **--use-loader** } | [ { **-S** | **--sign** } **-k** <private_key.pem> **-i** <certificate.x509> ] }
|
||||||
|
|
||||||
*COMMANDS* :=
|
*COMMANDS* :=
|
||||||
{ **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** |
|
{ **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** |
|
||||||
|
|
@ -248,6 +248,18 @@ OPTIONS
|
||||||
creating the maps, and loading the programs (see **bpftool prog tracelog**
|
creating the maps, and loading the programs (see **bpftool prog tracelog**
|
||||||
as a way to dump those messages).
|
as a way to dump those messages).
|
||||||
|
|
||||||
|
-S, --sign
|
||||||
|
Enable signing of the BPF program before loading. This option must be
|
||||||
|
used with **-k** and **-i**. Using this flag implicitly enables
|
||||||
|
**--use-loader**.
|
||||||
|
|
||||||
|
-k <private_key.pem>
|
||||||
|
Path to the private key file in PEM format, required when signing.
|
||||||
|
|
||||||
|
-i <certificate.x509>
|
||||||
|
Path to the X.509 certificate file in PEM or DER format, required when
|
||||||
|
signing.
|
||||||
|
|
||||||
EXAMPLES
|
EXAMPLES
|
||||||
========
|
========
|
||||||
**# bpftool prog show**
|
**# bpftool prog show**
|
||||||
|
|
|
||||||
|
|
@ -130,8 +130,8 @@ include $(FEATURES_DUMP)
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
LIBS = $(LIBBPF) -lelf -lz
|
LIBS = $(LIBBPF) -lelf -lz -lcrypto
|
||||||
LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz
|
LIBS_BOOTSTRAP = $(LIBBPF_BOOTSTRAP) -lelf -lz -lcrypto
|
||||||
|
|
||||||
ifeq ($(feature-libelf-zstd),1)
|
ifeq ($(feature-libelf-zstd),1)
|
||||||
LIBS += -lzstd
|
LIBS += -lzstd
|
||||||
|
|
@ -194,7 +194,7 @@ endif
|
||||||
|
|
||||||
BPFTOOL_BOOTSTRAP := $(BOOTSTRAP_OUTPUT)bpftool
|
BPFTOOL_BOOTSTRAP := $(BOOTSTRAP_OUTPUT)bpftool
|
||||||
|
|
||||||
BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o)
|
BOOTSTRAP_OBJS = $(addprefix $(BOOTSTRAP_OUTPUT),main.o common.o json_writer.o gen.o btf.o sign.o)
|
||||||
$(BOOTSTRAP_OBJS): $(LIBBPF_BOOTSTRAP)
|
$(BOOTSTRAP_OBJS): $(LIBBPF_BOOTSTRAP)
|
||||||
|
|
||||||
OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
|
OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@
|
||||||
// Copyright (C) 2017 Facebook
|
// Copyright (C) 2017 Facebook
|
||||||
// Author: Roman Gushchin <guro@fb.com>
|
// Author: Roman Gushchin <guro@fb.com>
|
||||||
|
|
||||||
|
#undef GCC_VERSION
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
#define _XOPEN_SOURCE 500
|
#define _XOPEN_SOURCE 500
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
|
|
||||||
|
|
@ -688,10 +688,17 @@ static void codegen_destroy(struct bpf_object *obj, const char *obj_name)
|
||||||
static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *header_guard)
|
static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *header_guard)
|
||||||
{
|
{
|
||||||
DECLARE_LIBBPF_OPTS(gen_loader_opts, opts);
|
DECLARE_LIBBPF_OPTS(gen_loader_opts, opts);
|
||||||
|
struct bpf_load_and_run_opts sopts = {};
|
||||||
|
char sig_buf[MAX_SIG_SIZE];
|
||||||
|
__u8 prog_sha[SHA256_DIGEST_LENGTH];
|
||||||
struct bpf_map *map;
|
struct bpf_map *map;
|
||||||
|
|
||||||
char ident[256];
|
char ident[256];
|
||||||
int err = 0;
|
int err = 0;
|
||||||
|
|
||||||
|
if (sign_progs)
|
||||||
|
opts.gen_hash = true;
|
||||||
|
|
||||||
err = bpf_object__gen_loader(obj, &opts);
|
err = bpf_object__gen_loader(obj, &opts);
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
@ -701,6 +708,7 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
|
||||||
p_err("failed to load object file");
|
p_err("failed to load object file");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If there was no error during load then gen_loader_opts
|
/* If there was no error during load then gen_loader_opts
|
||||||
* are populated with the loader program.
|
* are populated with the loader program.
|
||||||
*/
|
*/
|
||||||
|
|
@ -780,7 +788,51 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
|
||||||
print_hex(opts.insns, opts.insns_sz);
|
print_hex(opts.insns, opts.insns_sz);
|
||||||
codegen("\
|
codegen("\
|
||||||
\n\
|
\n\
|
||||||
\"; \n\
|
\";\n");
|
||||||
|
|
||||||
|
if (sign_progs) {
|
||||||
|
sopts.insns = opts.insns;
|
||||||
|
sopts.insns_sz = opts.insns_sz;
|
||||||
|
sopts.excl_prog_hash = prog_sha;
|
||||||
|
sopts.excl_prog_hash_sz = sizeof(prog_sha);
|
||||||
|
sopts.signature = sig_buf;
|
||||||
|
sopts.signature_sz = MAX_SIG_SIZE;
|
||||||
|
|
||||||
|
err = bpftool_prog_sign(&sopts);
|
||||||
|
if (err < 0) {
|
||||||
|
p_err("failed to sign program");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen("\
|
||||||
|
\n\
|
||||||
|
static const char opts_sig[] __attribute__((__aligned__(8))) = \"\\\n\
|
||||||
|
");
|
||||||
|
print_hex((const void *)sig_buf, sopts.signature_sz);
|
||||||
|
codegen("\
|
||||||
|
\n\
|
||||||
|
\";\n");
|
||||||
|
|
||||||
|
codegen("\
|
||||||
|
\n\
|
||||||
|
static const char opts_excl_hash[] __attribute__((__aligned__(8))) = \"\\\n\
|
||||||
|
");
|
||||||
|
print_hex((const void *)prog_sha, sizeof(prog_sha));
|
||||||
|
codegen("\
|
||||||
|
\n\
|
||||||
|
\";\n");
|
||||||
|
|
||||||
|
codegen("\
|
||||||
|
\n\
|
||||||
|
opts.signature = (void *)opts_sig; \n\
|
||||||
|
opts.signature_sz = sizeof(opts_sig) - 1; \n\
|
||||||
|
opts.excl_prog_hash = (void *)opts_excl_hash; \n\
|
||||||
|
opts.excl_prog_hash_sz = sizeof(opts_excl_hash) - 1; \n\
|
||||||
|
opts.keyring_id = skel->keyring_id; \n\
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
codegen("\
|
||||||
\n\
|
\n\
|
||||||
opts.ctx = (struct bpf_loader_ctx *)skel; \n\
|
opts.ctx = (struct bpf_loader_ctx *)skel; \n\
|
||||||
opts.data_sz = sizeof(opts_data) - 1; \n\
|
opts.data_sz = sizeof(opts_data) - 1; \n\
|
||||||
|
|
@ -1240,7 +1292,7 @@ static int do_skeleton(int argc, char **argv)
|
||||||
err = -errno;
|
err = -errno;
|
||||||
libbpf_strerror(err, err_buf, sizeof(err_buf));
|
libbpf_strerror(err, err_buf, sizeof(err_buf));
|
||||||
p_err("failed to open BPF object file: %s", err_buf);
|
p_err("failed to open BPF object file: %s", err_buf);
|
||||||
goto out;
|
goto out_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
bpf_object__for_each_map(map, obj) {
|
bpf_object__for_each_map(map, obj) {
|
||||||
|
|
@ -1355,6 +1407,13 @@ static int do_skeleton(int argc, char **argv)
|
||||||
printf("\t} links;\n");
|
printf("\t} links;\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sign_progs) {
|
||||||
|
codegen("\
|
||||||
|
\n\
|
||||||
|
__s32 keyring_id; \n\
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
if (btf) {
|
if (btf) {
|
||||||
err = codegen_datasecs(obj, obj_name);
|
err = codegen_datasecs(obj, obj_name);
|
||||||
if (err)
|
if (err)
|
||||||
|
|
@ -1552,6 +1611,7 @@ static int do_skeleton(int argc, char **argv)
|
||||||
err = 0;
|
err = 0;
|
||||||
out:
|
out:
|
||||||
bpf_object__close(obj);
|
bpf_object__close(obj);
|
||||||
|
out_obj:
|
||||||
if (obj_data)
|
if (obj_data)
|
||||||
munmap(obj_data, mmap_sz);
|
munmap(obj_data, mmap_sz);
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
@ -1930,7 +1990,7 @@ static int do_help(int argc, char **argv)
|
||||||
" %1$s %2$s help\n"
|
" %1$s %2$s help\n"
|
||||||
"\n"
|
"\n"
|
||||||
" " HELP_SPEC_OPTIONS " |\n"
|
" " HELP_SPEC_OPTIONS " |\n"
|
||||||
" {-L|--use-loader} }\n"
|
" {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ]}\n"
|
||||||
"",
|
"",
|
||||||
bin_name, "gen");
|
bin_name, "gen");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,9 @@ bool relaxed_maps;
|
||||||
bool use_loader;
|
bool use_loader;
|
||||||
struct btf *base_btf;
|
struct btf *base_btf;
|
||||||
struct hashmap *refs_table;
|
struct hashmap *refs_table;
|
||||||
|
bool sign_progs;
|
||||||
|
const char *private_key_path;
|
||||||
|
const char *cert_path;
|
||||||
|
|
||||||
static void __noreturn clean_and_exit(int i)
|
static void __noreturn clean_and_exit(int i)
|
||||||
{
|
{
|
||||||
|
|
@ -448,6 +451,7 @@ int main(int argc, char **argv)
|
||||||
{ "nomount", no_argument, NULL, 'n' },
|
{ "nomount", no_argument, NULL, 'n' },
|
||||||
{ "debug", no_argument, NULL, 'd' },
|
{ "debug", no_argument, NULL, 'd' },
|
||||||
{ "use-loader", no_argument, NULL, 'L' },
|
{ "use-loader", no_argument, NULL, 'L' },
|
||||||
|
{ "sign", no_argument, NULL, 'S' },
|
||||||
{ "base-btf", required_argument, NULL, 'B' },
|
{ "base-btf", required_argument, NULL, 'B' },
|
||||||
{ 0 }
|
{ 0 }
|
||||||
};
|
};
|
||||||
|
|
@ -474,7 +478,7 @@ int main(int argc, char **argv)
|
||||||
bin_name = "bpftool";
|
bin_name = "bpftool";
|
||||||
|
|
||||||
opterr = 0;
|
opterr = 0;
|
||||||
while ((opt = getopt_long(argc, argv, "VhpjfLmndB:l",
|
while ((opt = getopt_long(argc, argv, "VhpjfLmndSi:k:B:l",
|
||||||
options, NULL)) >= 0) {
|
options, NULL)) >= 0) {
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
case 'V':
|
case 'V':
|
||||||
|
|
@ -520,6 +524,16 @@ int main(int argc, char **argv)
|
||||||
case 'L':
|
case 'L':
|
||||||
use_loader = true;
|
use_loader = true;
|
||||||
break;
|
break;
|
||||||
|
case 'S':
|
||||||
|
sign_progs = true;
|
||||||
|
use_loader = true;
|
||||||
|
break;
|
||||||
|
case 'k':
|
||||||
|
private_key_path = optarg;
|
||||||
|
break;
|
||||||
|
case 'i':
|
||||||
|
cert_path = optarg;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
p_err("unrecognized option '%s'", argv[optind - 1]);
|
p_err("unrecognized option '%s'", argv[optind - 1]);
|
||||||
if (json_output)
|
if (json_output)
|
||||||
|
|
@ -534,6 +548,16 @@ int main(int argc, char **argv)
|
||||||
if (argc < 0)
|
if (argc < 0)
|
||||||
usage();
|
usage();
|
||||||
|
|
||||||
|
if (sign_progs && (private_key_path == NULL || cert_path == NULL)) {
|
||||||
|
p_err("-i <identity_x509_cert> and -k <private_key> must be supplied with -S for signing");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!sign_progs && (private_key_path != NULL || cert_path != NULL)) {
|
||||||
|
p_err("--sign (or -S) must be explicitly passed with -i <identity_x509_cert> and -k <private_key> to sign the programs");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
if (version_requested)
|
if (version_requested)
|
||||||
ret = do_version(argc, argv);
|
ret = do_version(argc, argv);
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,14 @@
|
||||||
|
|
||||||
/* BFD and kernel.h both define GCC_VERSION, differently */
|
/* BFD and kernel.h both define GCC_VERSION, differently */
|
||||||
#undef GCC_VERSION
|
#undef GCC_VERSION
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <bpf/skel_internal.h>
|
||||||
#include <linux/bpf.h>
|
#include <linux/bpf.h>
|
||||||
#include <linux/compiler.h>
|
#include <linux/compiler.h>
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
|
|
@ -52,6 +57,7 @@ static inline void *u64_to_ptr(__u64 ptr)
|
||||||
})
|
})
|
||||||
|
|
||||||
#define ERR_MAX_LEN 1024
|
#define ERR_MAX_LEN 1024
|
||||||
|
#define MAX_SIG_SIZE 4096
|
||||||
|
|
||||||
#define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
|
#define BPF_TAG_FMT "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
|
||||||
|
|
||||||
|
|
@ -85,6 +91,9 @@ extern bool relaxed_maps;
|
||||||
extern bool use_loader;
|
extern bool use_loader;
|
||||||
extern struct btf *base_btf;
|
extern struct btf *base_btf;
|
||||||
extern struct hashmap *refs_table;
|
extern struct hashmap *refs_table;
|
||||||
|
extern bool sign_progs;
|
||||||
|
extern const char *private_key_path;
|
||||||
|
extern const char *cert_path;
|
||||||
|
|
||||||
void __printf(1, 2) p_err(const char *fmt, ...);
|
void __printf(1, 2) p_err(const char *fmt, ...);
|
||||||
void __printf(1, 2) p_info(const char *fmt, ...);
|
void __printf(1, 2) p_info(const char *fmt, ...);
|
||||||
|
|
@ -284,4 +293,6 @@ struct kernel_config_option {
|
||||||
int read_kernel_config(const struct kernel_config_option *requested_options,
|
int read_kernel_config(const struct kernel_config_option *requested_options,
|
||||||
size_t num_options, char **out_values,
|
size_t num_options, char **out_values,
|
||||||
const char *define_prefix);
|
const char *define_prefix);
|
||||||
|
int bpftool_prog_sign(struct bpf_load_and_run_opts *opts);
|
||||||
|
__u32 register_session_key(const char *key_der_path);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/perf_event.h>
|
#include <linux/perf_event.h>
|
||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
|
#include <linux/keyctl.h>
|
||||||
|
|
||||||
#include <bpf/bpf.h>
|
#include <bpf/bpf.h>
|
||||||
#include <bpf/btf.h>
|
#include <bpf/btf.h>
|
||||||
|
|
@ -1930,6 +1931,8 @@ static int try_loader(struct gen_loader_opts *gen)
|
||||||
{
|
{
|
||||||
struct bpf_load_and_run_opts opts = {};
|
struct bpf_load_and_run_opts opts = {};
|
||||||
struct bpf_loader_ctx *ctx;
|
struct bpf_loader_ctx *ctx;
|
||||||
|
char sig_buf[MAX_SIG_SIZE];
|
||||||
|
__u8 prog_sha[SHA256_DIGEST_LENGTH];
|
||||||
int ctx_sz = sizeof(*ctx) + 64 * max(sizeof(struct bpf_map_desc),
|
int ctx_sz = sizeof(*ctx) + 64 * max(sizeof(struct bpf_map_desc),
|
||||||
sizeof(struct bpf_prog_desc));
|
sizeof(struct bpf_prog_desc));
|
||||||
int log_buf_sz = (1u << 24) - 1;
|
int log_buf_sz = (1u << 24) - 1;
|
||||||
|
|
@ -1953,6 +1956,26 @@ static int try_loader(struct gen_loader_opts *gen)
|
||||||
opts.insns = gen->insns;
|
opts.insns = gen->insns;
|
||||||
opts.insns_sz = gen->insns_sz;
|
opts.insns_sz = gen->insns_sz;
|
||||||
fds_before = count_open_fds();
|
fds_before = count_open_fds();
|
||||||
|
|
||||||
|
if (sign_progs) {
|
||||||
|
opts.excl_prog_hash = prog_sha;
|
||||||
|
opts.excl_prog_hash_sz = sizeof(prog_sha);
|
||||||
|
opts.signature = sig_buf;
|
||||||
|
opts.signature_sz = MAX_SIG_SIZE;
|
||||||
|
opts.keyring_id = KEY_SPEC_SESSION_KEYRING;
|
||||||
|
|
||||||
|
err = bpftool_prog_sign(&opts);
|
||||||
|
if (err < 0) {
|
||||||
|
p_err("failed to sign program");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = register_session_key(cert_path);
|
||||||
|
if (err < 0) {
|
||||||
|
p_err("failed to add session key");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
err = bpf_load_and_run(&opts);
|
err = bpf_load_and_run(&opts);
|
||||||
fd_delta = count_open_fds() - fds_before;
|
fd_delta = count_open_fds() - fds_before;
|
||||||
if (err < 0 || verifier_logs) {
|
if (err < 0 || verifier_logs) {
|
||||||
|
|
@ -1961,6 +1984,7 @@ static int try_loader(struct gen_loader_opts *gen)
|
||||||
fprintf(stderr, "loader prog leaked %d FDs\n",
|
fprintf(stderr, "loader prog leaked %d FDs\n",
|
||||||
fd_delta);
|
fd_delta);
|
||||||
}
|
}
|
||||||
|
out:
|
||||||
free(log_buf);
|
free(log_buf);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
@ -1988,6 +2012,9 @@ static int do_loader(int argc, char **argv)
|
||||||
goto err_close_obj;
|
goto err_close_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sign_progs)
|
||||||
|
gen.gen_hash = true;
|
||||||
|
|
||||||
err = bpf_object__gen_loader(obj, &gen);
|
err = bpf_object__gen_loader(obj, &gen);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_close_obj;
|
goto err_close_obj;
|
||||||
|
|
@ -2562,7 +2589,7 @@ static int do_help(int argc, char **argv)
|
||||||
" METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n"
|
" METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n"
|
||||||
" " HELP_SPEC_OPTIONS " |\n"
|
" " HELP_SPEC_OPTIONS " |\n"
|
||||||
" {-f|--bpffs} | {-m|--mapcompat} | {-n|--nomount} |\n"
|
" {-f|--bpffs} | {-m|--mapcompat} | {-n|--nomount} |\n"
|
||||||
" {-L|--use-loader} }\n"
|
" {-L|--use-loader} | [ {-S|--sign } {-k} <private_key.pem> {-i} <certificate.x509> ] \n"
|
||||||
"",
|
"",
|
||||||
bin_name, argv[-2]);
|
bin_name, argv[-2]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,212 @@
|
||||||
|
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2025 Google LLC.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
#define _GNU_SOURCE
|
||||||
|
#endif
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <err.h>
|
||||||
|
#include <openssl/opensslv.h>
|
||||||
|
#include <openssl/bio.h>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#include <openssl/cms.h>
|
||||||
|
#include <linux/keyctl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
#include <bpf/skel_internal.h>
|
||||||
|
|
||||||
|
#include "main.h"
|
||||||
|
|
||||||
|
#define OPEN_SSL_ERR_BUF_LEN 256
|
||||||
|
|
||||||
|
static void display_openssl_errors(int l)
|
||||||
|
{
|
||||||
|
char buf[OPEN_SSL_ERR_BUF_LEN];
|
||||||
|
const char *file;
|
||||||
|
const char *data;
|
||||||
|
unsigned long e;
|
||||||
|
int flags;
|
||||||
|
int line;
|
||||||
|
|
||||||
|
while ((e = ERR_get_error_all(&file, &line, NULL, &data, &flags))) {
|
||||||
|
ERR_error_string_n(e, buf, sizeof(buf));
|
||||||
|
if (data && (flags & ERR_TXT_STRING)) {
|
||||||
|
p_err("OpenSSL %s: %s:%d: %s", buf, file, line, data);
|
||||||
|
} else {
|
||||||
|
p_err("OpenSSL %s: %s:%d", buf, file, line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#define DISPLAY_OSSL_ERR(cond) \
|
||||||
|
do { \
|
||||||
|
bool __cond = (cond); \
|
||||||
|
if (__cond && ERR_peek_error()) \
|
||||||
|
display_openssl_errors(__LINE__);\
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static EVP_PKEY *read_private_key(const char *pkey_path)
|
||||||
|
{
|
||||||
|
EVP_PKEY *private_key = NULL;
|
||||||
|
BIO *b;
|
||||||
|
|
||||||
|
b = BIO_new_file(pkey_path, "rb");
|
||||||
|
private_key = PEM_read_bio_PrivateKey(b, NULL, NULL, NULL);
|
||||||
|
BIO_free(b);
|
||||||
|
DISPLAY_OSSL_ERR(!private_key);
|
||||||
|
return private_key;
|
||||||
|
}
|
||||||
|
|
||||||
|
static X509 *read_x509(const char *x509_name)
|
||||||
|
{
|
||||||
|
unsigned char buf[2];
|
||||||
|
X509 *x509 = NULL;
|
||||||
|
BIO *b;
|
||||||
|
int n;
|
||||||
|
|
||||||
|
b = BIO_new_file(x509_name, "rb");
|
||||||
|
if (!b)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
/* Look at the first two bytes of the file to determine the encoding */
|
||||||
|
n = BIO_read(b, buf, 2);
|
||||||
|
if (n != 2)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (BIO_reset(b) != 0)
|
||||||
|
goto cleanup;
|
||||||
|
|
||||||
|
if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
|
||||||
|
/* Assume raw DER encoded X.509 */
|
||||||
|
x509 = d2i_X509_bio(b, NULL);
|
||||||
|
else
|
||||||
|
/* Assume PEM encoded X.509 */
|
||||||
|
x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
BIO_free(b);
|
||||||
|
DISPLAY_OSSL_ERR(!x509);
|
||||||
|
return x509;
|
||||||
|
}
|
||||||
|
|
||||||
|
__u32 register_session_key(const char *key_der_path)
|
||||||
|
{
|
||||||
|
unsigned char *der_buf = NULL;
|
||||||
|
X509 *x509 = NULL;
|
||||||
|
int key_id = -1;
|
||||||
|
int der_len;
|
||||||
|
|
||||||
|
if (!key_der_path)
|
||||||
|
return key_id;
|
||||||
|
x509 = read_x509(key_der_path);
|
||||||
|
if (!x509)
|
||||||
|
goto cleanup;
|
||||||
|
der_len = i2d_X509(x509, &der_buf);
|
||||||
|
if (der_len < 0)
|
||||||
|
goto cleanup;
|
||||||
|
key_id = syscall(__NR_add_key, "asymmetric", key_der_path, der_buf,
|
||||||
|
(size_t)der_len, KEY_SPEC_SESSION_KEYRING);
|
||||||
|
cleanup:
|
||||||
|
X509_free(x509);
|
||||||
|
OPENSSL_free(der_buf);
|
||||||
|
DISPLAY_OSSL_ERR(key_id == -1);
|
||||||
|
return key_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
|
||||||
|
{
|
||||||
|
BIO *bd_in = NULL, *bd_out = NULL;
|
||||||
|
EVP_PKEY *private_key = NULL;
|
||||||
|
CMS_ContentInfo *cms = NULL;
|
||||||
|
long actual_sig_len = 0;
|
||||||
|
X509 *x509 = NULL;
|
||||||
|
int err = 0;
|
||||||
|
|
||||||
|
bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
|
||||||
|
if (!bd_in) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
private_key = read_private_key(private_key_path);
|
||||||
|
if (!private_key) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
x509 = read_x509(cert_path);
|
||||||
|
if (!x509) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
cms = CMS_sign(NULL, NULL, NULL, NULL,
|
||||||
|
CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED |
|
||||||
|
CMS_STREAM);
|
||||||
|
if (!cms) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!CMS_add1_signer(cms, x509, private_key, EVP_sha256(),
|
||||||
|
CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
|
||||||
|
CMS_USE_KEYID | CMS_NOATTR)) {
|
||||||
|
err = -EINVAL;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CMS_final(cms, bd_in, NULL, CMS_NOCERTS | CMS_BINARY) != 1) {
|
||||||
|
err = -EIO;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
if (!bd_out) {
|
||||||
|
err = -ENOMEM;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!i2d_CMS_bio_stream(bd_out, cms, NULL, 0)) {
|
||||||
|
err = -EIO;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
actual_sig_len = BIO_get_mem_data(bd_out, NULL);
|
||||||
|
if (actual_sig_len <= 0) {
|
||||||
|
err = -EIO;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((size_t)actual_sig_len > opts->signature_sz) {
|
||||||
|
err = -ENOSPC;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BIO_read(bd_out, opts->signature, actual_sig_len) != actual_sig_len) {
|
||||||
|
err = -EIO;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
opts->signature_sz = actual_sig_len;
|
||||||
|
cleanup:
|
||||||
|
BIO_free(bd_out);
|
||||||
|
CMS_ContentInfo_free(cms);
|
||||||
|
X509_free(x509);
|
||||||
|
EVP_PKEY_free(private_key);
|
||||||
|
BIO_free(bd_in);
|
||||||
|
DISPLAY_OSSL_ERR(err < 0);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue