mirror of https://github.com/torvalds/linux.git
218 lines
4.5 KiB
C
218 lines
4.5 KiB
C
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
|
/*
|
|
* Copyright (C) 2025 Google LLC.
|
|
*/
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include <string.h>
|
|
#include <getopt.h>
|
|
#include <err.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>
|
|
#include <linux/keyctl.h>
|
|
#include <errno.h>
|
|
|
|
#include <bpf/skel_internal.h>
|
|
|
|
#include "main.h"
|
|
|
|
#define OPEN_SSL_ERR_BUF_LEN 256
|
|
|
|
/* Use deprecated in 3.0 ERR_get_error_line_data for openssl < 3 */
|
|
#if !defined(OPENSSL_VERSION_MAJOR) || (OPENSSL_VERSION_MAJOR < 3)
|
|
#define ERR_get_error_all(file, line, func, data, flags) \
|
|
ERR_get_error_line_data(file, line, data, flags)
|
|
#endif
|
|
|
|
static void display_openssl_errors(int l)
|
|
{
|
|
char buf[OPEN_SSL_ERR_BUF_LEN];
|
|
const char *file;
|
|
const char *data;
|
|
unsigned long e;
|
|
int flags;
|
|
int line;
|
|
|
|
while ((e = ERR_get_error_all(&file, &line, NULL, &data, &flags))) {
|
|
ERR_error_string_n(e, buf, sizeof(buf));
|
|
if (data && (flags & ERR_TXT_STRING)) {
|
|
p_err("OpenSSL %s: %s:%d: %s", buf, file, line, data);
|
|
} else {
|
|
p_err("OpenSSL %s: %s:%d", buf, file, line);
|
|
}
|
|
}
|
|
}
|
|
|
|
#define DISPLAY_OSSL_ERR(cond) \
|
|
do { \
|
|
bool __cond = (cond); \
|
|
if (__cond && ERR_peek_error()) \
|
|
display_openssl_errors(__LINE__);\
|
|
} while (0)
|
|
|
|
static EVP_PKEY *read_private_key(const char *pkey_path)
|
|
{
|
|
EVP_PKEY *private_key = NULL;
|
|
BIO *b;
|
|
|
|
b = BIO_new_file(pkey_path, "rb");
|
|
private_key = PEM_read_bio_PrivateKey(b, NULL, NULL, NULL);
|
|
BIO_free(b);
|
|
DISPLAY_OSSL_ERR(!private_key);
|
|
return private_key;
|
|
}
|
|
|
|
static X509 *read_x509(const char *x509_name)
|
|
{
|
|
unsigned char buf[2];
|
|
X509 *x509 = NULL;
|
|
BIO *b;
|
|
int n;
|
|
|
|
b = BIO_new_file(x509_name, "rb");
|
|
if (!b)
|
|
goto cleanup;
|
|
|
|
/* Look at the first two bytes of the file to determine the encoding */
|
|
n = BIO_read(b, buf, 2);
|
|
if (n != 2)
|
|
goto cleanup;
|
|
|
|
if (BIO_reset(b) != 0)
|
|
goto cleanup;
|
|
|
|
if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
|
|
/* Assume raw DER encoded X.509 */
|
|
x509 = d2i_X509_bio(b, NULL);
|
|
else
|
|
/* Assume PEM encoded X.509 */
|
|
x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
|
|
|
|
cleanup:
|
|
BIO_free(b);
|
|
DISPLAY_OSSL_ERR(!x509);
|
|
return x509;
|
|
}
|
|
|
|
__u32 register_session_key(const char *key_der_path)
|
|
{
|
|
unsigned char *der_buf = NULL;
|
|
X509 *x509 = NULL;
|
|
int key_id = -1;
|
|
int der_len;
|
|
|
|
if (!key_der_path)
|
|
return key_id;
|
|
x509 = read_x509(key_der_path);
|
|
if (!x509)
|
|
goto cleanup;
|
|
der_len = i2d_X509(x509, &der_buf);
|
|
if (der_len < 0)
|
|
goto cleanup;
|
|
key_id = syscall(__NR_add_key, "asymmetric", key_der_path, der_buf,
|
|
(size_t)der_len, KEY_SPEC_SESSION_KEYRING);
|
|
cleanup:
|
|
X509_free(x509);
|
|
OPENSSL_free(der_buf);
|
|
DISPLAY_OSSL_ERR(key_id == -1);
|
|
return key_id;
|
|
}
|
|
|
|
int bpftool_prog_sign(struct bpf_load_and_run_opts *opts)
|
|
{
|
|
BIO *bd_in = NULL, *bd_out = NULL;
|
|
EVP_PKEY *private_key = NULL;
|
|
CMS_ContentInfo *cms = NULL;
|
|
long actual_sig_len = 0;
|
|
X509 *x509 = NULL;
|
|
int err = 0;
|
|
|
|
bd_in = BIO_new_mem_buf(opts->insns, opts->insns_sz);
|
|
if (!bd_in) {
|
|
err = -ENOMEM;
|
|
goto cleanup;
|
|
}
|
|
|
|
private_key = read_private_key(private_key_path);
|
|
if (!private_key) {
|
|
err = -EINVAL;
|
|
goto cleanup;
|
|
}
|
|
|
|
x509 = read_x509(cert_path);
|
|
if (!x509) {
|
|
err = -EINVAL;
|
|
goto cleanup;
|
|
}
|
|
|
|
cms = CMS_sign(NULL, NULL, NULL, NULL,
|
|
CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED |
|
|
CMS_STREAM);
|
|
if (!cms) {
|
|
err = -EINVAL;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!CMS_add1_signer(cms, x509, private_key, EVP_sha256(),
|
|
CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
|
|
CMS_USE_KEYID | CMS_NOATTR)) {
|
|
err = -EINVAL;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (CMS_final(cms, bd_in, NULL, CMS_NOCERTS | CMS_BINARY) != 1) {
|
|
err = -EIO;
|
|
goto cleanup;
|
|
}
|
|
|
|
EVP_Digest(opts->insns, opts->insns_sz, opts->excl_prog_hash,
|
|
&opts->excl_prog_hash_sz, EVP_sha256(), NULL);
|
|
|
|
bd_out = BIO_new(BIO_s_mem());
|
|
if (!bd_out) {
|
|
err = -ENOMEM;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (!i2d_CMS_bio_stream(bd_out, cms, NULL, 0)) {
|
|
err = -EIO;
|
|
goto cleanup;
|
|
}
|
|
|
|
actual_sig_len = BIO_get_mem_data(bd_out, NULL);
|
|
if (actual_sig_len <= 0) {
|
|
err = -EIO;
|
|
goto cleanup;
|
|
}
|
|
|
|
if ((size_t)actual_sig_len > opts->signature_sz) {
|
|
err = -ENOSPC;
|
|
goto cleanup;
|
|
}
|
|
|
|
if (BIO_read(bd_out, opts->signature, actual_sig_len) != actual_sig_len) {
|
|
err = -EIO;
|
|
goto cleanup;
|
|
}
|
|
|
|
opts->signature_sz = actual_sig_len;
|
|
cleanup:
|
|
BIO_free(bd_out);
|
|
CMS_ContentInfo_free(cms);
|
|
X509_free(x509);
|
|
EVP_PKEY_free(private_key);
|
|
BIO_free(bd_in);
|
|
DISPLAY_OSSL_ERR(err < 0);
|
|
return err;
|
|
}
|