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