lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20250528215037.2081066-4-bboscaccy@linux.microsoft.com>
Date: Wed, 28 May 2025 14:49:05 -0700
From: Blaise Boscaccy <bboscaccy@...ux.microsoft.com>
To: Paul Moore <paul@...l-moore.com>,
	bboscaccy@...ux.microsoft.com,
	jarkko@...nel.org,
	zeffron@...tgames.com,
	xiyou.wangcong@...il.com,
	kysrinivasan@...il.com,
	code@...icks.com,
	linux-security-module@...r.kernel.org,
	roberto.sassu@...wei.com,
	James.Bottomley@...senpartnership.com,
	Alexei Starovoitov <ast@...nel.org>,
	Daniel Borkmann <daniel@...earbox.net>,
	John Fastabend <john.fastabend@...il.com>,
	Andrii Nakryiko <andrii@...nel.org>,
	Martin KaFai Lau <martin.lau@...ux.dev>,
	Eduard Zingerman <eddyz87@...il.com>,
	Song Liu <song@...nel.org>,
	Yonghong Song <yonghong.song@...ux.dev>,
	KP Singh <kpsingh@...nel.org>,
	Stanislav Fomichev <sdf@...ichev.me>,
	Hao Luo <haoluo@...gle.com>,
	Jiri Olsa <jolsa@...nel.org>,
	David Howells <dhowells@...hat.com>,
	Lukas Wunner <lukas@...ner.de>,
	Ignat Korchagin <ignat@...udflare.com>,
	Quentin Monnet <qmo@...nel.org>,
	Jason Xing <kerneljasonxing@...il.com>,
	Willem de Bruijn <willemb@...gle.com>,
	Anton Protopopov <aspsk@...valent.com>,
	Jordan Rome <linux@...danrome.com>,
	Martin Kelly <martin.kelly@...wdstrike.com>,
	Alan Maguire <alan.maguire@...cle.com>,
	Matteo Croce <teknoraver@...a.com>,
	bpf@...r.kernel.org,
	linux-kernel@...r.kernel.org,
	keyrings@...r.kernel.org,
	linux-crypto@...r.kernel.org
Subject: [PATCH 3/3] bpftool: Allow signing of light-skeleton programs

This introduces hash-chain signature support into bpftool. The
signature generated code was adapted from sign-file and follows a
similar set of command line switches.  The algorithm used here
supports the signature of both the loader program and it's map
containing the target program.

Signed-off-by: Blaise Boscaccy <bboscaccy@...ux.microsoft.com>
---
 tools/bpf/bpftool/Makefile |   4 +-
 tools/bpf/bpftool/common.c | 204 +++++++++++++++++++++++++++++++++++++
 tools/bpf/bpftool/gen.c    |  66 +++++++++++-
 tools/bpf/bpftool/main.c   |  24 ++++-
 tools/bpf/bpftool/main.h   |  23 +++++
 tools/lib/bpf/libbpf.h     |   4 +
 6 files changed, 321 insertions(+), 4 deletions(-)

diff --git a/tools/bpf/bpftool/Makefile b/tools/bpf/bpftool/Makefile
index 9e9a5f006cd2..b17e295f4954 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
diff --git a/tools/bpf/bpftool/common.c b/tools/bpf/bpftool/common.c
index ecfa790adc13..e6cfb855fc8a 100644
--- a/tools/bpf/bpftool/common.c
+++ b/tools/bpf/bpftool/common.c
@@ -5,6 +5,7 @@
 #define _GNU_SOURCE
 #endif
 #include <ctype.h>
+#include <err.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <ftw.h>
@@ -31,6 +32,24 @@
 #include <bpf/libbpf.h> /* libbpf_num_possible_cpus */
 #include <bpf/btf.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>
+#if OPENSSL_VERSION_MAJOR >= 3
+# define USE_PKCS11_PROVIDER
+# include <openssl/provider.h>
+# include <openssl/store.h>
+#else
+# if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+#  define USE_PKCS11_ENGINE
+#  include <openssl/engine.h>
+# endif
+#endif
+#include "../../scripts/ssl-common.h"
+
 #include "main.h"
 
 #ifndef BPF_FS_MAGIC
@@ -1181,3 +1200,188 @@ int pathname_concat(char *buf, int buf_sz, const char *path,
 
 	return 0;
 }
+
+static const char *key_pass;
+
+static int pem_pw_cb(char *buf, int len, int w, void *v)
+{
+	int pwlen;
+
+	if (!key_pass)
+		return -1;
+
+	pwlen = strlen(key_pass);
+	if (pwlen >= len)
+		return -1;
+
+	strcpy(buf, key_pass);
+
+	/* If it's wrong, don't keep trying it. */
+	key_pass = NULL;
+
+	return pwlen;
+}
+
+static EVP_PKEY *read_private_key_pkcs11(const char *private_key_name)
+{
+	EVP_PKEY *pk = NULL;
+#ifdef USE_PKCS11_PROVIDER
+	OSSL_STORE_CTX *store;
+
+	if (!OSSL_PROVIDER_try_load(NULL, "pkcs11", true))
+		ERR(1, "OSSL_PROVIDER_try_load(pkcs11)");
+	if (!OSSL_PROVIDER_try_load(NULL, "default", true))
+		ERR(1, "OSSL_PROVIDER_try_load(default)");
+
+	store = OSSL_STORE_open(private_key_name, NULL, NULL, NULL, NULL);
+	ERR(!store, "OSSL_STORE_open");
+
+	while (!OSSL_STORE_eof(store)) {
+		OSSL_STORE_INFO *info = OSSL_STORE_load(store);
+
+		if (!info) {
+			drain_openssl_errors(__LINE__, 0);
+			continue;
+		}
+		if (OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PKEY) {
+			pk = OSSL_STORE_INFO_get1_PKEY(info);
+			ERR(!pk, "OSSL_STORE_INFO_get1_PKEY");
+		}
+		OSSL_STORE_INFO_free(info);
+		if (pk)
+			break;
+	}
+	OSSL_STORE_close(store);
+#elif defined(USE_PKCS11_ENGINE)
+	ENGINE *e;
+
+	ENGINE_load_builtin_engines();
+	drain_openssl_errors(__LINE__, 1);
+	e = ENGINE_by_id("pkcs11");
+	ERR(!e, "Load PKCS#11 ENGINE");
+	if (ENGINE_init(e))
+		drain_openssl_errors(__LINE__, 1);
+	else
+		ERR(1, "ENGINE_init");
+	if (key_pass)
+		ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0), "Set PKCS#11 PIN");
+	pk = ENGINE_load_private_key(e, private_key_name, NULL, NULL);
+	ERR(!pk, "%s", private_key_name);
+#else
+	fprintf(stderr, "no pkcs11 engine/provider available\n");
+	exit(1);
+#endif
+	return pk;
+}
+
+EVP_PKEY *read_private_key(const char *private_key_name)
+{
+	if (!strncmp(private_key_name, "pkcs11:", 7)) {
+		return read_private_key_pkcs11(private_key_name);
+	} else {
+		EVP_PKEY *pk;
+		BIO *b;
+
+		b = BIO_new_file(private_key_name, "rb");
+		ERR(!b, "%s", private_key_name);
+		pk = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb,
+					     NULL);
+		ERR(!pk, "%s", private_key_name);
+		BIO_free(b);
+
+		return pk;
+	}
+}
+
+X509 *read_x509(const char *x509_name)
+{
+	unsigned char buf[2];
+	X509 *x509_cert;
+	BIO *b;
+	int n;
+
+	b = BIO_new_file(x509_name, "rb");
+	ERR(!b, "%s", x509_name);
+
+	/* Look at the first two bytes of the file to determine the encoding */
+	n = BIO_read(b, buf, 2);
+	if (n != 2) {
+		if (BIO_should_retry(b)) {
+			fprintf(stderr, "%s: Read wanted retry\n", x509_name);
+			exit(1);
+		}
+		if (n >= 0) {
+			fprintf(stderr, "%s: Short read\n", x509_name);
+			exit(1);
+		}
+		ERR(1, "%s", x509_name);
+	}
+
+	ERR(BIO_reset(b) != 0, "%s", x509_name);
+
+	if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
+		/* Assume raw DER encoded X.509 */
+		x509_cert = d2i_X509_bio(b, NULL);
+	else
+		/* Assume PEM encoded X.509 */
+		x509_cert = PEM_read_bio_X509(b, NULL, NULL, NULL);
+
+	BIO_free(b);
+	ERR(!x509_cert, "%s", x509_name);
+
+	return x509_cert;
+}
+
+BIO* generate_signature(const void *buffer, size_t length)
+{
+	const EVP_MD *digest_algo;
+	unsigned int use_signed_attrs;
+#ifndef USE_PKCS7
+	CMS_ContentInfo *cms = NULL;
+	unsigned int use_keyid = 0;
+#else
+	PKCS7 *pkcs7 = NULL;
+#endif
+	BIO *mem = BIO_new_mem_buf(buffer, length);
+	BIO *bd = BIO_new(BIO_s_mem());
+
+#ifndef USE_PKCS7
+	use_signed_attrs = CMS_NOATTR;
+#else
+	use_signed_attrs = PKCS7_NOATTR;
+#endif
+	/* Digest the module data. */
+	OpenSSL_add_all_digests();
+	drain_openssl_errors(__LINE__, 0);
+	digest_algo = EVP_get_digestbyname(hash_algo);
+	ERR(!digest_algo, "EVP_get_digestbyname");
+
+#ifndef USE_PKCS7
+	/* Load the signature message from the digest buffer. */
+	cms = CMS_sign(NULL, NULL, NULL, NULL,
+		       CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY |
+		       CMS_DETACHED | CMS_STREAM);
+	ERR(!cms, "CMS_sign");
+
+	ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo,
+			     CMS_NOCERTS | CMS_BINARY |
+			     CMS_NOSMIMECAP | use_keyid |
+			     use_signed_attrs),
+	    "CMS_add1_signer");
+	ERR(CMS_final(cms, mem, NULL, CMS_NOCERTS | CMS_BINARY) != 1,
+	    "CMS_final");
+
+#else
+	pkcs7 = PKCS7_sign(x509, private_key, NULL, mem,
+			   PKCS7_NOCERTS | PKCS7_BINARY |
+			   PKCS7_DETACHED | use_signed_attrs);
+	ERR(!pkcs7, "PKCS7_sign");
+#endif
+
+#ifndef USE_PKCS7
+	ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) != 1, "%s", "bpftool");
+#else
+	ERR(i2d_PKCS7_bio(bd, pkcs7) != 1, "%s", "bpftool");
+#endif
+	return bd;
+}
diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
index 67a60114368f..318b1f36d869 100644
--- a/tools/bpf/bpftool/gen.c
+++ b/tools/bpf/bpftool/gen.c
@@ -20,6 +20,7 @@
 #include <sys/stat.h>
 #include <sys/mman.h>
 #include <bpf/btf.h>
+#include <openssl/sha.h>
 
 #include "json_writer.h"
 #include "main.h"
@@ -493,6 +494,30 @@ static size_t bpf_map_mmap_sz(const struct bpf_map *map)
 	return map_sz;
 }
 
+static int sign_loader_and_map(struct gen_loader_opts *opts)
+{
+	BIO *bo;
+	BUF_MEM *bptr;
+	unsigned char hash[SHA256_DIGEST_LENGTH  * 2];
+	unsigned char term[SHA256_DIGEST_LENGTH];
+
+	if (!x509)
+		return 0;
+
+	SHA256((const unsigned char *)opts->insns, opts->insns_sz, hash);
+	SHA256((const unsigned char *)opts->data, opts->data_sz, hash + SHA256_DIGEST_LENGTH);
+	SHA256(hash, sizeof(hash), term);
+
+	bo = generate_signature(term, sizeof(term));
+	if (IS_ERR(bo))
+		return -EINVAL;
+	BIO_get_mem_ptr(bo, &bptr);
+	opts->signature = bptr->data;
+	opts->signature_sz = bptr->length;
+
+	return 0;
+}
+
 /* Emit type size asserts for all top-level fields in memory-mapped internal maps. */
 static void codegen_asserts(struct bpf_object *obj, const char *obj_name)
 {
@@ -701,6 +726,11 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
 		p_err("failed to load object file");
 		goto out;
 	}
+	err = sign_loader_and_map(&opts);
+	if (err) {
+		p_err("failed to sign loader");
+		goto out;
+	}
 	/* If there was no error during load then gen_loader_opts
 	 * are populated with the loader program.
 	 */
@@ -778,20 +808,54 @@ static int gen_trace(struct bpf_object *obj, const char *obj_name, const char *h
 			static const char opts_insn[] __attribute__((__aligned__(8))) = \"\\\n\
 		");
 	print_hex(opts.insns, opts.insns_sz);
-	codegen("\
+	if (opts.signature) {
+		codegen("\
 		\n\
 		\";							    \n\
+			static const char opts_signature[] __attribute__((__aligned__(8))) = \"\\\n\
+		");
+		print_hex(opts.signature, opts.signature_sz);
+		codegen("\
+		\n\
+		\";							    \n\
+			static const int opts_signature_maps[1] __attribute__((__aligned__(8))) = {0}; \n\
+		");
+		codegen("\
+		\n\
 									    \n\
 			opts.ctx = (struct bpf_loader_ctx *)skel;	    \n\
 			opts.data_sz = sizeof(opts_data) - 1;		    \n\
 			opts.data = (void *)opts_data;			    \n\
 			opts.insns_sz = sizeof(opts_insn) - 1;		    \n\
 			opts.insns = (void *)opts_insn;			    \n\
+			opts.signature_sz = sizeof(opts_signature) - 1;	    \n\
+			opts.signature = (void *)opts_signature;	    \n\
+			opts.signature_maps_sz = 1;	                    \n\
+			opts.signature_maps = (void *)opts_signature_maps;  \n\
 									    \n\
 			err = bpf_load_and_run(&opts);			    \n\
 			if (err < 0)					    \n\
 				return err;				    \n\
 		");
+
+	} else {
+		codegen("\
+		\n\
+		\";							    \n\
+									    \n\
+			opts.ctx = (struct bpf_loader_ctx *)skel;	    \n\
+			opts.data_sz = sizeof(opts_data) - 1;		    \n\
+			opts.data = (void *)opts_data;			    \n\
+			opts.insns_sz = sizeof(opts_insn) - 1;		    \n\
+			opts.insns = (void *)opts_insn;			    \n\
+			opts.signature_sz  = 0;		                    \n\
+			opts.signature = NULL;			            \n\
+									    \n\
+			err = bpf_load_and_run(&opts);			    \n\
+			if (err < 0)					    \n\
+				return err;				    \n\
+		");
+	}
 	bpf_object__for_each_map(map, obj) {
 		const char *mmap_flags;
 
diff --git a/tools/bpf/bpftool/main.c b/tools/bpf/bpftool/main.c
index cd5963cb6058..01020e5f37c2 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;
+const char *hash_algo;
+EVP_PKEY *private_key;
+X509 *x509;
 
 static void __noreturn clean_and_exit(int i)
 {
@@ -473,7 +476,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, "VhpjfLmndB:lH:lP:lX:l",
 				  options, NULL)) >= 0) {
 		switch (opt) {
 		case 'V':
@@ -519,6 +522,25 @@ int main(int argc, char **argv)
 		case 'L':
 			use_loader = true;
 			break;
+		case 'H':
+			hash_algo = optarg;
+			break;
+		case 'P':
+			private_key = read_private_key(optarg);
+			if (!private_key) {
+				p_err("failed to parse private key '%s': %d\n",
+				      optarg, -errno);
+				return -1;
+			}
+			break;
+		case 'X':
+			x509 = read_x509(optarg);
+			if (!x509) {
+				p_err("failed to parse x509 '%s': %d\n",
+				      optarg, -errno);
+				return -1;
+			}
+			break;
 		default:
 			p_err("unrecognized option '%s'", argv[optind - 1]);
 			if (json_output)
diff --git a/tools/bpf/bpftool/main.h b/tools/bpf/bpftool/main.h
index 9eb764fe4cc8..2f4aee1a8da7 100644
--- a/tools/bpf/bpftool/main.h
+++ b/tools/bpf/bpftool/main.h
@@ -16,6 +16,22 @@
 #include <bpf/hashmap.h>
 #include <bpf/libbpf.h>
 
+#include <openssl/opensslv.h>
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+#if OPENSSL_VERSION_MAJOR >= 3
+# define USE_PKCS11_PROVIDER
+# include <openssl/provider.h>
+# include <openssl/store.h>
+#else
+# if !defined(OPENSSL_NO_ENGINE) && !defined(OPENSSL_NO_DEPRECATED_3_0)
+#  define USE_PKCS11_ENGINE
+#  include <openssl/engine.h>
+# endif
+#endif
+
 #include "json_writer.h"
 
 /* Make sure we do not use kernel-only integer typedefs */
@@ -84,6 +100,9 @@ extern bool relaxed_maps;
 extern bool use_loader;
 extern struct btf *base_btf;
 extern struct hashmap *refs_table;
+extern const char *hash_algo;
+extern EVP_PKEY *private_key;
+extern X509 *x509;
 
 void __printf(1, 2) p_err(const char *fmt, ...);
 void __printf(1, 2) p_info(const char *fmt, ...);
@@ -271,4 +290,8 @@ 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);
+
+X509 *read_x509(const char *x509_name);
+EVP_PKEY *read_private_key(const char *private_key_name);
+BIO *generate_signature(const void *buffer, size_t length);
 #endif
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index e0605403f977..c6c67e8931a6 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -1782,8 +1782,12 @@ struct gen_loader_opts {
 	size_t sz; /* size of this struct, for forward/backward compatibility */
 	const char *data;
 	const char *insns;
+	const char *signature;
+	const int *signature_maps;
 	__u32 data_sz;
 	__u32 insns_sz;
+	__u32 signature_sz;
+	__u32 signature_maps_sz;
 };
 
 #define gen_loader_opts__last_field insns_sz
-- 
2.48.1


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ