xref: /linux/tools/bpf/bpftool/sign.c (revision 40863f4d6ef2c34bb00dd1070dfaf9d5f27a497e)
1 // SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
2 /*
3  * Copyright (C) 2025 Google LLC.
4  */
5 
6 #ifndef _GNU_SOURCE
7 #define _GNU_SOURCE
8 #endif
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <stdint.h>
12 #include <stdbool.h>
13 #include <string.h>
14 #include <string.h>
15 #include <getopt.h>
16 #include <err.h>
17 #include <openssl/opensslv.h>
18 #include <openssl/bio.h>
19 #include <openssl/evp.h>
20 #include <openssl/pem.h>
21 #include <openssl/err.h>
22 #include <openssl/cms.h>
23 #include <linux/keyctl.h>
24 #include <errno.h>
25 
26 #include <bpf/skel_internal.h>
27 
28 #include "main.h"
29 
30 #define OPEN_SSL_ERR_BUF_LEN 256
31 
32 static void display_openssl_errors(int l)
33 {
34 	char buf[OPEN_SSL_ERR_BUF_LEN];
35 	const char *file;
36 	const char *data;
37 	unsigned long e;
38 	int flags;
39 	int line;
40 
41 	while ((e = ERR_get_error_all(&file, &line, NULL, &data, &flags))) {
42 		ERR_error_string_n(e, buf, sizeof(buf));
43 		if (data && (flags & ERR_TXT_STRING)) {
44 			p_err("OpenSSL %s: %s:%d: %s", buf, file, line, data);
45 		} else {
46 			p_err("OpenSSL %s: %s:%d", buf, file, line);
47 		}
48 	}
49 }
50 
51 #define DISPLAY_OSSL_ERR(cond)				 \
52 	do {						 \
53 		bool __cond = (cond);			 \
54 		if (__cond && ERR_peek_error())		 \
55 			display_openssl_errors(__LINE__);\
56 	} while (0)
57 
58 static EVP_PKEY *read_private_key(const char *pkey_path)
59 {
60 	EVP_PKEY *private_key = NULL;
61 	BIO *b;
62 
63 	b = BIO_new_file(pkey_path, "rb");
64 	private_key = PEM_read_bio_PrivateKey(b, NULL, NULL, NULL);
65 	BIO_free(b);
66 	DISPLAY_OSSL_ERR(!private_key);
67 	return private_key;
68 }
69 
70 static X509 *read_x509(const char *x509_name)
71 {
72 	unsigned char buf[2];
73 	X509 *x509 = NULL;
74 	BIO *b;
75 	int n;
76 
77 	b = BIO_new_file(x509_name, "rb");
78 	if (!b)
79 		goto cleanup;
80 
81 	/* Look at the first two bytes of the file to determine the encoding */
82 	n = BIO_read(b, buf, 2);
83 	if (n != 2)
84 		goto cleanup;
85 
86 	if (BIO_reset(b) != 0)
87 		goto cleanup;
88 
89 	if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
90 		/* Assume raw DER encoded X.509 */
91 		x509 = d2i_X509_bio(b, NULL);
92 	else
93 		/* Assume PEM encoded X.509 */
94 		x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
95 
96 cleanup:
97 	BIO_free(b);
98 	DISPLAY_OSSL_ERR(!x509);
99 	return x509;
100 }
101 
102 __u32 register_session_key(const char *key_der_path)
103 {
104 	unsigned char *der_buf = NULL;
105 	X509 *x509 = NULL;
106 	int key_id = -1;
107 	int der_len;
108 
109 	if (!key_der_path)
110 		return key_id;
111 	x509 = read_x509(key_der_path);
112 	if (!x509)
113 		goto cleanup;
114 	der_len = i2d_X509(x509, &der_buf);
115 	if (der_len < 0)
116 		goto cleanup;
117 	key_id = syscall(__NR_add_key, "asymmetric", key_der_path, der_buf,
118 			     (size_t)der_len, KEY_SPEC_SESSION_KEYRING);
119 cleanup:
120 	X509_free(x509);
121 	OPENSSL_free(der_buf);
122 	DISPLAY_OSSL_ERR(key_id == -1);
123 	return key_id;
124 }
125 
126 int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
127 {
128 	BIO *bd_in = NULL, *bd_out = NULL;
129 	EVP_PKEY *private_key = NULL;
130 	CMS_ContentInfo *cms = NULL;
131 	long actual_sig_len = 0;
132 	X509 *x509 = NULL;
133 	int err = 0;
134 
135 	bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
136 	if (!bd_in) {
137 		err = -ENOMEM;
138 		goto cleanup;
139 	}
140 
141 	private_key = read_private_key(private_key_path);
142 	if (!private_key) {
143 		err = -EINVAL;
144 		goto cleanup;
145 	}
146 
147 	x509 = read_x509(cert_path);
148 	if (!x509) {
149 		err = -EINVAL;
150 		goto cleanup;
151 	}
152 
153 	cms = CMS_sign(NULL, NULL, NULL, NULL,
154 		       CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED |
155 			       CMS_STREAM);
156 	if (!cms) {
157 		err = -EINVAL;
158 		goto cleanup;
159 	}
160 
161 	if (!CMS_add1_signer(cms, x509, private_key, EVP_sha256(),
162 			     CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
163 			     CMS_USE_KEYID | CMS_NOATTR)) {
164 		err = -EINVAL;
165 		goto cleanup;
166 	}
167 
168 	if (CMS_final(cms, bd_in, NULL, CMS_NOCERTS | CMS_BINARY) != 1) {
169 		err = -EIO;
170 		goto cleanup;
171 	}
172 
173 	EVP_Digest(opts->insns, opts->insns_sz, opts->excl_prog_hash,
174 		   &opts->excl_prog_hash_sz, EVP_sha256(), NULL);
175 
176 		bd_out = BIO_new(BIO_s_mem());
177 	if (!bd_out) {
178 		err = -ENOMEM;
179 		goto cleanup;
180 	}
181 
182 	if (!i2d_CMS_bio_stream(bd_out, cms, NULL, 0)) {
183 		err = -EIO;
184 		goto cleanup;
185 	}
186 
187 	actual_sig_len = BIO_get_mem_data(bd_out, NULL);
188 	if (actual_sig_len <= 0) {
189 		err = -EIO;
190 		goto cleanup;
191 	}
192 
193 	if ((size_t)actual_sig_len > opts->signature_sz) {
194 		err = -ENOSPC;
195 		goto cleanup;
196 	}
197 
198 	if (BIO_read(bd_out, opts->signature, actual_sig_len) != actual_sig_len) {
199 		err = -EIO;
200 		goto cleanup;
201 	}
202 
203 	opts->signature_sz = actual_sig_len;
204 cleanup:
205 	BIO_free(bd_out);
206 	CMS_ContentInfo_free(cms);
207 	X509_free(x509);
208 	EVP_PKEY_free(private_key);
209 	BIO_free(bd_in);
210 	DISPLAY_OSSL_ERR(err < 0);
211 	return err;
212 }
213