[PATCH v3 11/12] bpftool: Add support for signing BPF programs

Blaise Boscaccy bboscaccy at linux.microsoft.com
Mon Aug 18 20:37:52 UTC 2025


KP Singh <kpsingh at kernel.org> writes:

> On Thu, Aug 14, 2025 at 6:51 PM Blaise Boscaccy
> <bboscaccy at linux.microsoft.com> wrote:
>>
>> KP Singh <kpsingh at kernel.org> writes:
>>
>> > 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 at kernel.org>
>> > ---
>> >  .../bpf/bpftool/Documentation/bpftool-gen.rst |  16 +-
>> >  .../bpftool/Documentation/bpftool-prog.rst    |  18 +-
>> >  tools/bpf/bpftool/Makefile                    |   6 +-
>> >  tools/bpf/bpftool/cgroup.c                    |   4 +
>> >  tools/bpf/bpftool/gen.c                       |  60 ++++-
>> >  tools/bpf/bpftool/main.c                      |  26 ++-
>> >  tools/bpf/bpftool/main.h                      |  11 +
>> >  tools/bpf/bpftool/prog.c                      |  27 ++-
>> >  tools/bpf/bpftool/sign.c                      | 212 ++++++++++++++++++
>> >  9 files changed, 367 insertions(+), 13 deletions(-)
>> >  create mode 100644 tools/bpf/bpftool/sign.c
>> >
>> > diff --git a/tools/bpf/bpftool/Documentation/bpftool-gen.rst b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
>> > index ca860fd97d8d..cef469d758ed 100644
>> > --- a/tools/bpf/bpftool/Documentation/bpftool-gen.rst
>> > +++ b/tools/bpf/bpftool/Documentation/bpftool-gen.rst
>> > @@ -16,7 +16,8 @@ SYNOPSIS
>> >
>> >  **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** }
>> >
>> > @@ -186,6 +187,19 @@ OPTIONS
>> >      skeleton). A light skeleton contains a loader eBPF program. It does not use
>> >      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**.
>> > +    See the "Signed Skeletons" section in the description of the
>> > +    **gen skeleton** command for more details.
>> > +
>> > +-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
>> >  ========
>> >  **$ cat example1.bpf.c**
>> > diff --git a/tools/bpf/bpftool/Documentation/bpftool-prog.rst b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
>> > index f69fd92df8d8..55b812761df2 100644
>> > --- a/tools/bpf/bpftool/Documentation/bpftool-prog.rst
>> > +++ b/tools/bpf/bpftool/Documentation/bpftool-prog.rst
>> > @@ -16,9 +16,9 @@ SYNOPSIS
>> >
>> >  **bpftool** [*OPTIONS*] **prog** *COMMAND*
>> >
>> > -*OPTIONS* := { |COMMON_OPTIONS| |
>> > -{ **-f** | **--bpffs** } | { **-m** | **--mapcompat** } | { **-n** | **--nomount** } |
>> > -{ **-L** | **--use-loader** } }
>> > +*OPTIONS* := { |COMMON_OPTIONS| [ { **-f** | **--bpffs** } ] [ { **-m** | **--mapcompat** } ]
>> > +[ { **-n** | **--nomount** } ] [ { **-L** | **--use-loader** } ]
>> > +[ { { **-S** | **--sign** } **-k** <private_key.pem> **-i** <certificate.x509> } ] }
>> >
>> >  *COMMANDS* :=
>> >  { **show** | **list** | **dump xlated** | **dump jited** | **pin** | **load** |
>> > @@ -248,6 +248,18 @@ OPTIONS
>> >      creating the maps, and loading the programs (see **bpftool prog tracelog**
>> >      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
>> >  ========
>> >  **# bpftool prog show**
>> > diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile
>> > index 9e9a5f006cd2..586d1b2595d1 100644
>> > --- a/tools/bpf/bpftool/Makefile
>> > +++ b/tools/bpf/bpftool/Makefile
>> > @@ -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
>> > @@ -194,7 +194,7 @@ endif
>> >
>> >  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)
>> >
>> >  OBJS = $(patsubst %.c,$(OUTPUT)%.o,$(SRCS)) $(OUTPUT)disasm.o
>> > diff --git a/tools/bpf/bpftool/cgroup.c b/tools/bpf/bpftool/cgroup.c
>> > index 944ebe21a216..ec356deb27c9 100644
>> > --- a/tools/bpf/bpftool/cgroup.c
>> > +++ b/tools/bpf/bpftool/cgroup.c
>> > @@ -2,6 +2,10 @@
>> >  // Copyright (C) 2017 Facebook
>> >  // Author: Roman Gushchin <guro at fb.com>
>> >
>> > +#undef GCC_VERSION
>> > +#ifndef _GNU_SOURCE
>> > +#define _GNU_SOURCE
>> > +#endif
>> >  #define _XOPEN_SOURCE 500
>> >  #include <errno.h>
>> >  #include <fcntl.h>
>> > diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
>> > index 67a60114368f..427468c9e9c2 100644
>> > --- a/tools/bpf/bpftool/gen.c
>> > +++ b/tools/bpf/bpftool/gen.c
>> > @@ -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)
>> >  {
>> >       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;
>> > +
>> >       char ident[256];
>> >       int err = 0;
>> >
>> > +     if (sign_progs)
>> > +             opts.gen_hash = true;
>> > +
>> >       err = bpf_object__gen_loader(obj, &opts);
>> >       if (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");
>> >               goto out;
>> >       }
>> > +
>> >       /* If there was no error during load then gen_loader_opts
>> >        * are populated with the loader program.
>> >        */
>> > @@ -780,8 +788,51 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
>> >       print_hex(opts.insns, opts.insns_sz);
>> >       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;
>> > +             sopts.keyring_id = KEY_SPEC_SESSION_KEYRING;
>> > +
>>
>> This still has the session keyring hardcoded.
>
> We can do this for now:
>
> diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
> index 427468c9e9c2..694e61f1909e 100644
> --- a/tools/bpf/bpftool/gen.c
> +++ b/tools/bpf/bpftool/gen.c
> @@ -797,7 +797,6 @@ static int gen_trace(struct bpf_object *obj, const
> char *obj_name, const char *h
>                 sopts.excl_prog_hash_sz = sizeof(prog_sha);
>                 sopts.signature = sig_buf;
>                 sopts.signature_sz = MAX_SIG_SIZE;
> -               sopts.keyring_id = KEY_SPEC_SESSION_KEYRING;
>
>                 err = bpftool_prog_sign(&sopts);
>                 if (err < 0)
> @@ -827,7 +826,7 @@ static int gen_trace(struct bpf_object *obj, const
> char *obj_name, const char *h
>                         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 = KEY_SPEC_SESSION_KEYRING;
>          \n\
> +                       opts.keyring_id = skel->keyring_id;
>          \n\
>                 ");
>         }
>
> @@ -1406,6 +1405,13 @@ static int do_skeleton(int argc, char **argv)
>                 printf("\t} links;\n");
>         }
>
> +       if (sign_progs) {
> +               codegen("\
> +               \n\
> +                       __s32 keyring_id;                                  \n\
> +               ");
> +       }
> +
>         if (btf) {
>                 err = codegen_datasecs(obj, obj_name);
>                 if (err)
> diff --git a/tools/testing/selftests/bpf/prog_tests/atomics.c
> b/tools/testing/selftests/bpf/prog_tests/atomics.c
> index 13e101f370a1..92b5f378bfb8 100644
> --- a/tools/testing/selftests/bpf/prog_tests/atomics.c
> +++ b/tools/testing/selftests/bpf/prog_tests/atomics.c
> @@ -165,11 +165,17 @@ static void test_xchg(struct atomics_lskel *skel)
>  void test_atomics(void)
>  {
>         struct atomics_lskel *skel;
> +       int err;
>
> -       skel = atomics_lskel__open_and_load();
> -       if (!ASSERT_OK_PTR(skel, "atomics skeleton load"))
> +       skel = atomics_lskel__open();
> +       if (!ASSERT_OK_PTR(skel, "atomics skeleton open"))
>                 return;
>
> +       skel->keyring_id = KEY_SPEC_SESSION_KEYRING;
> +       err = atomics_lskel__load(skel);
> +       if (!ASSERT_OK(err, "atomics skeleton load"))
> +               goto cleanup;
> +
>         if (skel->data->skip_tests) {
>                 printf("%s:SKIP:no ENABLE_ATOMICS_TESTS (missing Clang
> BPF atomics support)",
>                        __func__);
> - KP
>

That should work. 

>>
>> > +             err = bpftool_prog_sign(&sopts);
>> > +             if (err < 0)
>> > +                     return err;
>> > +
>> > +             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 = KEY_SPEC_SESSION_KEYRING;             \n\
>> > +             ");
>>
>> And here.
>>
>> > +     }
>> > +
>> > +     codegen("\
>> > +             \n\
>> >                       opts.ctx = (struct bpf_loader_ctx *)skel;           \n\
>> >                       opts.data_sz = sizeof(opts_data) - 1;               \n\
>> >                       opts.data = (void *)opts_data;                      \n\
>> > @@ -1240,7 +1291,7 @@ static int do_skeleton(int argc, char **argv)
>> >               err = -errno;
>> >               libbpf_strerror(err, err_buf, sizeof(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) {
>> > @@ -1552,6 +1603,7 @@ static int do_skeleton(int argc, char **argv)
>> >       err = 0;
>> >  out:
>> >       bpf_object__close(obj);
>> > +out_obj:
>> >       if (obj_data)
>> >               munmap(obj_data, mmap_sz);
>> >       close(fd);
>> > @@ -1930,7 +1982,7 @@ static int do_help(int argc, char **argv)
>> >               "       %1$s %2$s help\n"
>> >               "\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");
>> >
>> > diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
>> > index 0f1183b2ed0a..c78eb80b9c94 100644
>> > --- a/tools/bpf/bpftool/main.c
>> > +++ b/tools/bpf/bpftool/main.c
>> > @@ -33,6 +33,9 @@ bool relaxed_maps;
>> >  bool use_loader;
>> >  struct btf *base_btf;
>> >  struct hashmap *refs_table;
>> > +bool sign_progs;
>> > +const char *private_key_path;
>> > +const char *cert_path;
>> >
>> >  static void __noreturn clean_and_exit(int i)
>> >  {
>> > @@ -448,6 +451,7 @@ int main(int argc, char **argv)
>> >               { "nomount",    no_argument,    NULL,   'n' },
>> >               { "debug",      no_argument,    NULL,   'd' },
>> >               { "use-loader", no_argument,    NULL,   'L' },
>> > +             { "sign",       no_argument,    NULL,   'S' },
>> >               { "base-btf",   required_argument, NULL, 'B' },
>> >               { 0 }
>> >       };
>> > @@ -474,7 +478,7 @@ int main(int argc, char **argv)
>> >       bin_name = "bpftool";
>> >
>> >       opterr = 0;
>> > -     while ((opt = getopt_long(argc, argv, "VhpjfLmndB:l",
>> > +     while ((opt = getopt_long(argc, argv, "VhpjfLmndSi:k:B:l",
>> >                                 options, NULL)) >= 0) {
>> >               switch (opt) {
>> >               case 'V':
>> > @@ -520,6 +524,16 @@ int main(int argc, char **argv)
>> >               case 'L':
>> >                       use_loader = true;
>> >                       break;
>> > +             case 'S':
>> > +                     sign_progs = true;
>> > +                     use_loader = true;
>> > +                     break;
>> > +             case 'k':
>> > +                     private_key_path = optarg;
>> > +                     break;
>> > +             case 'i':
>> > +                     cert_path = optarg;
>> > +                     break;
>> >               default:
>> >                       p_err("unrecognized option '%s'", argv[optind - 1]);
>> >                       if (json_output)
>> > @@ -534,6 +548,16 @@ int main(int argc, char **argv)
>> >       if (argc < 0)
>> >               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("-i <identity_x509_cert> and -k <private> also need --sign to be used for sign programs");
>> > +             return -EINVAL;
>> > +     }
>> > +
>> >       if (version_requested)
>> >               ret = do_version(argc, argv);
>> >       else
>> > diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
>> > index a2bb0714b3d6..f7f5b39b66c8 100644
>> > --- a/tools/bpf/bpftool/main.h
>> > +++ b/tools/bpf/bpftool/main.h
>> > @@ -6,9 +6,14 @@
>> >
>> >  /* BFD and kernel.h both define GCC_VERSION, differently */
>> >  #undef GCC_VERSION
>> > +#ifndef _GNU_SOURCE
>> > +#define _GNU_SOURCE
>> > +#endif
>> >  #include <stdbool.h>
>> >  #include <stdio.h>
>> > +#include <errno.h>
>> >  #include <stdlib.h>
>> > +#include <bpf/skel_internal.h>
>> >  #include <linux/bpf.h>
>> >  #include <linux/compiler.h>
>> >  #include <linux/kernel.h>
>> > @@ -52,6 +57,7 @@ static inline void *u64_to_ptr(__u64 ptr)
>> >       })
>> >
>> >  #define ERR_MAX_LEN  1024
>> > +#define MAX_SIG_SIZE 4096
>> >
>> >  #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 struct btf *base_btf;
>> >  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_info(const char *fmt, ...);
>> > @@ -275,4 +284,6 @@ int pathname_concat(char *buf, int buf_sz, const char *path,
>> >  /* print netfilter bpf_link info */
>> >  void netfilter_dump_plain(const struct bpf_link_info *info);
>> >  void netfilter_dump_json(const struct bpf_link_info *info, json_writer_t *wtr);
>> > +int bpftool_prog_sign(struct bpf_load_and_run_opts *opts);
>> > +__u32 register_session_key(const char *key_der_path);
>> >  #endif
>> > diff --git a/tools/bpf/bpftool/prog.c b/tools/bpf/bpftool/prog.c
>> > index 9722d841abc0..82b8da084504 100644
>> > --- a/tools/bpf/bpftool/prog.c
>> > +++ b/tools/bpf/bpftool/prog.c
>> > @@ -23,6 +23,7 @@
>> >  #include <linux/err.h>
>> >  #include <linux/perf_event.h>
>> >  #include <linux/sizes.h>
>> > +#include <linux/keyctl.h>
>> >
>> >  #include <bpf/bpf.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_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),
>> >                                            sizeof(struct bpf_prog_desc));
>> >       int log_buf_sz = (1u << 24) - 1;
>> > @@ -1953,6 +1956,24 @@ static int try_loader(struct gen_loader_opts *gen)
>> >       opts.insns = gen->insns;
>> >       opts.insns_sz = gen->insns_sz;
>> >       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;
>> > +
>>
>> And here as well.
>
> The "load -S" command loads and signs the program in one go, so this
> is purely for debugging and not how one would use signing. Session key
> is fine here. What we really want is flexibility when using skeletons.
>
> - KP
>
>
>
>>
>> > +             err = bpftool_prog_sign(&opts);
>> > +             if (err < 0)
>> > +                     return err;
>> > +
>> > +             err = register_session_key(cert_path);
>> > +             if (err < 0) {
>> > +                     p_err("failed to add session key");
>> > +                     goto out;
>> > +             }
>> > +     }
>> >       err = bpf_load_and_run(&opts);
>> >       fd_delta = count_open_fds() - fds_before;
>> >       if (err < 0 || verifier_logs) {
>> > @@ -1961,6 +1982,7 @@ static int try_loader(struct gen_loader_opts *gen)
>> >                       fprintf(stderr, "loader prog leaked %d FDs\n",
>> >                               fd_delta);
>> >       }
>> > +out:
>> >       free(log_buf);
>> >       return err;
>> >  }
>> > @@ -1988,6 +2010,9 @@ static int do_loader(int argc, char **argv)
>> >               goto err_close_obj;
>> >       }
>> >
>> > +     if (sign_progs)
>> > +             gen.gen_hash = true;
>> > +
>> >       err = bpf_object__gen_loader(obj, &gen);
>> >       if (err)
>> >               goto err_close_obj;
>> > @@ -2562,7 +2587,7 @@ static int do_help(int argc, char **argv)
>> >               "       METRIC := { cycles | instructions | l1d_loads | llc_misses | itlb_misses | dtlb_misses }\n"
>> >               "       " HELP_SPEC_OPTIONS " |\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]);
>> >
>> > diff --git a/tools/bpf/bpftool/sign.c b/tools/bpf/bpftool/sign.c
>> > new file mode 100644
>> > index 000000000000..b29d825bb1d4
>> > --- /dev/null
>> > +++ b/tools/bpf/bpftool/sign.c
>> > @@ -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;
>> > +}
>> > --
>> > 2.43.0



More information about the Linux-security-module-archive mailing list