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