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