Skip to content

Commit 753746c

Browse files
Blaise BoscaccyKernel Patches Daemon
authored andcommitted
bpf: Add hash chain signature support for arbitrary maps
This patch introduces hash chain support for signature verification of arbitrary bpf map objects which was described here: https://lore.kernel.org/linux-security-module/[email protected]/ The UAPI is extended to allow for in-kernel checking of maps passed in via the fd_array. A hash chain is constructed from the maps, in order specified by the signature_maps field. The hash chain is terminated with the hash of the program itself. Signed-off-by: Blaise Boscaccy <[email protected]>
1 parent 3561924 commit 753746c

File tree

10 files changed

+143
-12
lines changed

10 files changed

+143
-12
lines changed

include/uapi/linux/bpf.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,6 +1621,12 @@ union bpf_attr {
16211621
* verification.
16221622
*/
16231623
__s32 keyring_id;
1624+
/* Pointer to a buffer containing the maps used in the signature
1625+
* hash chain of the BPF program.
1626+
*/
1627+
__aligned_u64 signature_maps;
1628+
/* Size of the signature maps buffer. */
1629+
__u32 signature_maps_size;
16241630
};
16251631

16261632
struct { /* anonymous struct used by BPF_OBJ_* commands */

kernel/bpf/syscall.c

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2802,14 +2802,35 @@ static bool is_perfmon_prog_type(enum bpf_prog_type prog_type)
28022802
}
28032803
}
28042804

2805+
static inline int bpf_map_get_hash(int map_fd, void *buffer)
2806+
{
2807+
struct bpf_map *map;
2808+
2809+
CLASS(fd, f)(map_fd);
2810+
map = __bpf_map_get(f);
2811+
if (IS_ERR(map))
2812+
return PTR_ERR(map);
2813+
2814+
if (!map->ops->map_get_hash)
2815+
return -EINVAL;
2816+
2817+
return map->ops->map_get_hash(map, SHA256_DIGEST_SIZE, buffer);
2818+
}
2819+
28052820
static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr,
28062821
bool is_kernel)
28072822
{
28082823
bpfptr_t usig = make_bpfptr(attr->signature, is_kernel);
2809-
struct bpf_dynptr_kern sig_ptr, insns_ptr;
2824+
bpfptr_t umaps;
2825+
struct bpf_dynptr_kern sig_ptr, insns_ptr, hash_ptr;
28102826
struct bpf_key *key = NULL;
28112827
void *sig;
2828+
int *maps;
2829+
int map_fd;
28122830
int err = 0;
2831+
u64 buffer[SHA256_DIGEST_SIZE * 2 / sizeof(u64)];
2832+
u64 hash[SHA256_DIGEST_SIZE / sizeof(u64)];
2833+
int n;
28132834

28142835
if (system_keyring_id_check(attr->keyring_id) == 0)
28152836
key = bpf_lookup_system_key(attr->keyring_id);
@@ -2830,16 +2851,60 @@ static int bpf_prog_verify_signature(struct bpf_prog *prog, union bpf_attr *attr
28302851
bpf_dynptr_init(&insns_ptr, prog->insnsi, BPF_DYNPTR_TYPE_LOCAL, 0,
28312852
prog->len * sizeof(struct bpf_insn));
28322853

2833-
err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&insns_ptr,
2834-
(struct bpf_dynptr *)&sig_ptr, key);
2854+
if (!attr->signature_maps_size) {
2855+
err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&insns_ptr,
2856+
(struct bpf_dynptr *)&sig_ptr, key);
2857+
} else {
2858+
bpf_dynptr_init(&hash_ptr, hash, BPF_DYNPTR_TYPE_LOCAL, 0,
2859+
sizeof(hash));
2860+
umaps = make_bpfptr(attr->signature_maps, is_kernel);
2861+
maps = kvmemdup_bpfptr(umaps, attr->signature_maps_size * sizeof(*maps));
2862+
if (!maps) {
2863+
err = -ENOMEM;
2864+
goto out;
2865+
}
2866+
/* Process the map array in reverse order to generate a hash chain
2867+
* h(n) = sha256(h(n + 1), sha256(map(n)))
2868+
* h(n_len) = sha256(map(n_len))
2869+
*/
2870+
for (n = attr->signature_maps_size - 1; n >= 0; n--) {
2871+
err = copy_from_bpfptr_offset(&map_fd,
2872+
make_bpfptr(attr->fd_array, is_kernel),
2873+
maps[n] * sizeof(map_fd),
2874+
sizeof(map_fd));
2875+
if (err)
2876+
goto free_maps;
2877+
2878+
if (n == attr->signature_maps_size - 1)
2879+
err = bpf_map_get_hash(map_fd, hash);
2880+
else {
2881+
memcpy(buffer, hash, sizeof(hash));
2882+
err = bpf_map_get_hash(map_fd, buffer + ARRAY_SIZE(hash));
2883+
sha256((u8 *)buffer, sizeof(buffer), (u8 *)&hash);
2884+
}
2885+
if (err)
2886+
goto free_maps;
2887+
}
2888+
/* Calculate final hash with program instructions
2889+
* f_hash = sha256(sha256(prog), h(0))
2890+
*/
2891+
sha256((u8 *)prog->insnsi, prog->len * sizeof(struct bpf_insn), (u8 *)&buffer);
2892+
memcpy(buffer + ARRAY_SIZE(hash), hash, sizeof(hash));
2893+
sha256((u8 *)buffer, sizeof(buffer), (u8 *)&hash);
2894+
err = bpf_verify_pkcs7_signature((struct bpf_dynptr *)&hash_ptr,
2895+
(struct bpf_dynptr *)&sig_ptr, key);
28352896

2897+
free_maps:
2898+
kvfree(maps);
2899+
}
2900+
out:
28362901
bpf_key_put(key);
28372902
kvfree(sig);
28382903
return err;
28392904
}
28402905

28412906
/* last field in 'union bpf_attr' used by this command */
2842-
#define BPF_PROG_LOAD_LAST_FIELD keyring_id
2907+
#define BPF_PROG_LOAD_LAST_FIELD signature_maps_size
28432908

28442909
static int bpf_prog_load(union bpf_attr *attr, bpfptr_t uattr, u32 uattr_size)
28452910
{

tools/bpf/bpftool/Documentation/bpftool-gen.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ SYNOPSIS
1616

1717
**bpftool** [*OPTIONS*] **gen** *COMMAND*
1818

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

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

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

193+
-M --sign-maps
194+
For skeletons, generate a signed skeleton that includes a hash chain for the
195+
skeletons maps. This option must be used with **-k** and **-i**. Using this
196+
flag implicitly enables **--use-loader** and **--sign**.
197+
193198
-k <private_key.pem>
194199
Path to the private key file in PEM format, required for signing.
195200

tools/bpf/bpftool/gen.c

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,9 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
699699
if (sign_progs)
700700
opts.gen_hash = true;
701701

702+
if (sign_maps)
703+
opts.sign_maps = true;
704+
702705
err = bpf_object__gen_loader(obj, &opts);
703706
if (err)
704707
return err;
@@ -793,6 +796,8 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
793796
if (sign_progs) {
794797
sopts.insns = opts.insns;
795798
sopts.insns_sz = opts.insns_sz;
799+
sopts.data = opts.data;
800+
sopts.data_sz = opts.data_sz;
796801
sopts.excl_prog_hash = prog_sha;
797802
sopts.excl_prog_hash_sz = sizeof(prog_sha);
798803
sopts.signature = sig_buf;
@@ -822,6 +827,13 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
822827
\n\
823828
\";\n");
824829

830+
if (sign_maps) {
831+
codegen("\
832+
\n\
833+
static const int opts_signature_maps[1] __attribute__((__aligned__(8))) = {0}; \n\
834+
");
835+
}
836+
825837
codegen("\
826838
\n\
827839
opts.signature = (void *)opts_sig; \n\
@@ -830,6 +842,19 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
830842
opts.excl_prog_hash_sz = sizeof(opts_excl_hash) - 1; \n\
831843
opts.keyring_id = skel->keyring_id; \n\
832844
");
845+
if (sign_maps) {
846+
codegen("\
847+
\n\
848+
opts.signature_maps = (void *)opts_signature_maps; \n\
849+
opts.signature_maps_sz = 1; \n\
850+
");
851+
} else {
852+
codegen("\
853+
\n\
854+
opts.signature_maps = (void *)NULL; \n\
855+
opts.signature_maps_sz = 0; \n\
856+
");
857+
}
833858
}
834859

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

tools/bpf/bpftool/main.c

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ bool use_loader;
3434
struct btf *base_btf;
3535
struct hashmap *refs_table;
3636
bool sign_progs;
37+
bool sign_maps;
3738
const char *private_key_path;
3839
const char *cert_path;
3940

@@ -452,6 +453,7 @@ int main(int argc, char **argv)
452453
{ "debug", no_argument, NULL, 'd' },
453454
{ "use-loader", no_argument, NULL, 'L' },
454455
{ "sign", no_argument, NULL, 'S' },
456+
{ "sign-maps", no_argument, NULL, 'M' },
455457
{ "base-btf", required_argument, NULL, 'B' },
456458
{ 0 }
457459
};
@@ -478,7 +480,7 @@ int main(int argc, char **argv)
478480
bin_name = "bpftool";
479481

480482
opterr = 0;
481-
while ((opt = getopt_long(argc, argv, "VhpjfLmndSi:k:B:l",
483+
while ((opt = getopt_long(argc, argv, "VhpjfLmndSMi:k:B:l",
482484
options, NULL)) >= 0) {
483485
switch (opt) {
484486
case 'V':
@@ -528,6 +530,11 @@ int main(int argc, char **argv)
528530
sign_progs = true;
529531
use_loader = true;
530532
break;
533+
case 'M':
534+
sign_maps = true;
535+
sign_progs = true;
536+
use_loader = true;
537+
break;
531538
case 'k':
532539
private_key_path = optarg;
533540
break;

tools/bpf/bpftool/main.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ extern bool use_loader;
9292
extern struct btf *base_btf;
9393
extern struct hashmap *refs_table;
9494
extern bool sign_progs;
95+
extern bool sign_maps;
9596
extern const char *private_key_path;
9697
extern const char *cert_path;
9798

tools/bpf/bpftool/sign.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <errno.h>
2424

2525
#include <bpf/skel_internal.h>
26+
#include <bpf/libbpf_internal.h>
2627

2728
#include "main.h"
2829

@@ -130,8 +131,18 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
130131
long actual_sig_len = 0;
131132
X509 *x509 = NULL;
132133
int err = 0;
133-
134-
bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
134+
unsigned char hash[SHA256_DIGEST_LENGTH * 2];
135+
unsigned char term[SHA256_DIGEST_LENGTH];
136+
137+
if (sign_maps) {
138+
libbpf_sha256(opts->insns, opts->insns_sz, hash, SHA256_DIGEST_LENGTH);
139+
libbpf_sha256(opts->data, opts->data_sz, hash + SHA256_DIGEST_LENGTH,
140+
SHA256_DIGEST_LENGTH);
141+
libbpf_sha256(hash, sizeof(hash), term, sizeof(term));
142+
bd_in = BIO_new_mem_buf(term, sizeof(term));
143+
} else {
144+
bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
145+
}
135146
if (!bd_in) {
136147
err = -ENOMEM;
137148
goto cleanup;
@@ -172,7 +183,7 @@ int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
172183
EVP_Digest(opts->insns, opts->insns_sz, opts->excl_prog_hash,
173184
&opts->excl_prog_hash_sz, EVP_sha256(), NULL);
174185

175-
bd_out = BIO_new(BIO_s_mem());
186+
bd_out = BIO_new(BIO_s_mem());
176187
if (!bd_out) {
177188
err = -ENOMEM;
178189
goto cleanup;

tools/include/uapi/linux/bpf.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1621,6 +1621,12 @@ union bpf_attr {
16211621
* verification.
16221622
*/
16231623
__s32 keyring_id;
1624+
/* Pointer to a buffer containing the maps used in the signature
1625+
* hash chain of the BPF program.
1626+
*/
1627+
__aligned_u64 signature_maps;
1628+
/* Size of the signature maps buffer. */
1629+
__u32 signature_maps_size;
16241630
};
16251631

16261632
struct { /* anonymous struct used by BPF_OBJ_* commands */

tools/lib/bpf/libbpf.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1858,9 +1858,10 @@ struct gen_loader_opts {
18581858
__u32 data_sz;
18591859
__u32 insns_sz;
18601860
bool gen_hash;
1861+
bool sign_maps;
18611862
};
18621863

1863-
#define gen_loader_opts__last_field gen_hash
1864+
#define gen_loader_opts__last_field sign_maps
18641865
LIBBPF_API int bpf_object__gen_loader(struct bpf_object *obj,
18651866
struct gen_loader_opts *opts);
18661867

tools/lib/bpf/skel_internal.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ struct bpf_load_and_run_opts {
7474
__s32 keyring_id;
7575
void *excl_prog_hash;
7676
__u32 excl_prog_hash_sz;
77+
const int *signature_maps;
78+
__u32 signature_maps_sz;
7779
};
7880

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

353355
static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
354356
{
355-
const size_t prog_load_attr_sz = offsetofend(union bpf_attr, keyring_id);
357+
const size_t prog_load_attr_sz = offsetofend(union bpf_attr, signature_maps_size);
356358
const size_t test_run_attr_sz = offsetofend(union bpf_attr, test);
357359
int map_fd = -1, prog_fd = -1, key = 0, err;
358360
union bpf_attr attr;
@@ -395,6 +397,8 @@ static inline int bpf_load_and_run(struct bpf_load_and_run_opts *opts)
395397
#ifndef __KERNEL__
396398
attr.signature = (long) opts->signature;
397399
attr.signature_size = opts->signature_sz;
400+
attr.signature_maps = (long) opts->signature_maps;
401+
attr.signature_maps_size = opts->signature_maps_sz;
398402
#else
399403
if (opts->signature || opts->signature_sz)
400404
pr_warn("signatures are not supported from bpf_preload\n");

0 commit comments

Comments
 (0)