162306a36Sopenharmony_ci/* Extract X.509 certificate in DER form from PKCS#11 or PEM. 262306a36Sopenharmony_ci * 362306a36Sopenharmony_ci * Copyright © 2014-2015 Red Hat, Inc. All Rights Reserved. 462306a36Sopenharmony_ci * Copyright © 2015 Intel Corporation. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: David Howells <dhowells@redhat.com> 762306a36Sopenharmony_ci * David Woodhouse <dwmw2@infradead.org> 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or 1062306a36Sopenharmony_ci * modify it under the terms of the GNU Lesser General Public License 1162306a36Sopenharmony_ci * as published by the Free Software Foundation; either version 2.1 1262306a36Sopenharmony_ci * of the licence, or (at your option) any later version. 1362306a36Sopenharmony_ci */ 1462306a36Sopenharmony_ci#define _GNU_SOURCE 1562306a36Sopenharmony_ci#include <stdio.h> 1662306a36Sopenharmony_ci#include <stdlib.h> 1762306a36Sopenharmony_ci#include <stdint.h> 1862306a36Sopenharmony_ci#include <stdbool.h> 1962306a36Sopenharmony_ci#include <string.h> 2062306a36Sopenharmony_ci#include <err.h> 2162306a36Sopenharmony_ci#include <openssl/bio.h> 2262306a36Sopenharmony_ci#include <openssl/pem.h> 2362306a36Sopenharmony_ci#include <openssl/err.h> 2462306a36Sopenharmony_ci#include <openssl/engine.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* 2762306a36Sopenharmony_ci * OpenSSL 3.0 deprecates the OpenSSL's ENGINE API. 2862306a36Sopenharmony_ci * 2962306a36Sopenharmony_ci * Remove this if/when that API is no longer used 3062306a36Sopenharmony_ci */ 3162306a36Sopenharmony_ci#pragma GCC diagnostic ignored "-Wdeprecated-declarations" 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define PKEY_ID_PKCS7 2 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic __attribute__((noreturn)) 3662306a36Sopenharmony_civoid format(void) 3762306a36Sopenharmony_ci{ 3862306a36Sopenharmony_ci fprintf(stderr, 3962306a36Sopenharmony_ci "Usage: extract-cert <source> <dest>\n"); 4062306a36Sopenharmony_ci exit(2); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic void display_openssl_errors(int l) 4462306a36Sopenharmony_ci{ 4562306a36Sopenharmony_ci const char *file; 4662306a36Sopenharmony_ci char buf[120]; 4762306a36Sopenharmony_ci int e, line; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci if (ERR_peek_error() == 0) 5062306a36Sopenharmony_ci return; 5162306a36Sopenharmony_ci fprintf(stderr, "At main.c:%d:\n", l); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci while ((e = ERR_get_error_line(&file, &line))) { 5462306a36Sopenharmony_ci ERR_error_string(e, buf); 5562306a36Sopenharmony_ci fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line); 5662306a36Sopenharmony_ci } 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void drain_openssl_errors(void) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci const char *file; 6262306a36Sopenharmony_ci int line; 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci if (ERR_peek_error() == 0) 6562306a36Sopenharmony_ci return; 6662306a36Sopenharmony_ci while (ERR_get_error_line(&file, &line)) {} 6762306a36Sopenharmony_ci} 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci#define ERR(cond, fmt, ...) \ 7062306a36Sopenharmony_ci do { \ 7162306a36Sopenharmony_ci bool __cond = (cond); \ 7262306a36Sopenharmony_ci display_openssl_errors(__LINE__); \ 7362306a36Sopenharmony_ci if (__cond) { \ 7462306a36Sopenharmony_ci err(1, fmt, ## __VA_ARGS__); \ 7562306a36Sopenharmony_ci } \ 7662306a36Sopenharmony_ci } while(0) 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_cistatic const char *key_pass; 7962306a36Sopenharmony_cistatic BIO *wb; 8062306a36Sopenharmony_cistatic char *cert_dst; 8162306a36Sopenharmony_cistatic bool verbose; 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic void write_cert(X509 *x509) 8462306a36Sopenharmony_ci{ 8562306a36Sopenharmony_ci char buf[200]; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci if (!wb) { 8862306a36Sopenharmony_ci wb = BIO_new_file(cert_dst, "wb"); 8962306a36Sopenharmony_ci ERR(!wb, "%s", cert_dst); 9062306a36Sopenharmony_ci } 9162306a36Sopenharmony_ci X509_NAME_oneline(X509_get_subject_name(x509), buf, sizeof(buf)); 9262306a36Sopenharmony_ci ERR(!i2d_X509_bio(wb, x509), "%s", cert_dst); 9362306a36Sopenharmony_ci if (verbose) 9462306a36Sopenharmony_ci fprintf(stderr, "Extracted cert: %s\n", buf); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ciint main(int argc, char **argv) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci char *cert_src; 10062306a36Sopenharmony_ci char *verbose_env; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci OpenSSL_add_all_algorithms(); 10362306a36Sopenharmony_ci ERR_load_crypto_strings(); 10462306a36Sopenharmony_ci ERR_clear_error(); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci verbose_env = getenv("KBUILD_VERBOSE"); 10762306a36Sopenharmony_ci if (verbose_env && strchr(verbose_env, '1')) 10862306a36Sopenharmony_ci verbose = true; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci key_pass = getenv("KBUILD_SIGN_PIN"); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci if (argc != 3) 11362306a36Sopenharmony_ci format(); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci cert_src = argv[1]; 11662306a36Sopenharmony_ci cert_dst = argv[2]; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci if (!cert_src[0]) { 11962306a36Sopenharmony_ci /* Invoked with no input; create empty file */ 12062306a36Sopenharmony_ci FILE *f = fopen(cert_dst, "wb"); 12162306a36Sopenharmony_ci ERR(!f, "%s", cert_dst); 12262306a36Sopenharmony_ci fclose(f); 12362306a36Sopenharmony_ci exit(0); 12462306a36Sopenharmony_ci } else if (!strncmp(cert_src, "pkcs11:", 7)) { 12562306a36Sopenharmony_ci ENGINE *e; 12662306a36Sopenharmony_ci struct { 12762306a36Sopenharmony_ci const char *cert_id; 12862306a36Sopenharmony_ci X509 *cert; 12962306a36Sopenharmony_ci } parms; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci parms.cert_id = cert_src; 13262306a36Sopenharmony_ci parms.cert = NULL; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci ENGINE_load_builtin_engines(); 13562306a36Sopenharmony_ci drain_openssl_errors(); 13662306a36Sopenharmony_ci e = ENGINE_by_id("pkcs11"); 13762306a36Sopenharmony_ci ERR(!e, "Load PKCS#11 ENGINE"); 13862306a36Sopenharmony_ci if (ENGINE_init(e)) 13962306a36Sopenharmony_ci drain_openssl_errors(); 14062306a36Sopenharmony_ci else 14162306a36Sopenharmony_ci ERR(1, "ENGINE_init"); 14262306a36Sopenharmony_ci if (key_pass) 14362306a36Sopenharmony_ci ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0), "Set PKCS#11 PIN"); 14462306a36Sopenharmony_ci ENGINE_ctrl_cmd(e, "LOAD_CERT_CTRL", 0, &parms, NULL, 1); 14562306a36Sopenharmony_ci ERR(!parms.cert, "Get X.509 from PKCS#11"); 14662306a36Sopenharmony_ci write_cert(parms.cert); 14762306a36Sopenharmony_ci } else { 14862306a36Sopenharmony_ci BIO *b; 14962306a36Sopenharmony_ci X509 *x509; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci b = BIO_new_file(cert_src, "rb"); 15262306a36Sopenharmony_ci ERR(!b, "%s", cert_src); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci while (1) { 15562306a36Sopenharmony_ci x509 = PEM_read_bio_X509(b, NULL, NULL, NULL); 15662306a36Sopenharmony_ci if (wb && !x509) { 15762306a36Sopenharmony_ci unsigned long err = ERR_peek_last_error(); 15862306a36Sopenharmony_ci if (ERR_GET_LIB(err) == ERR_LIB_PEM && 15962306a36Sopenharmony_ci ERR_GET_REASON(err) == PEM_R_NO_START_LINE) { 16062306a36Sopenharmony_ci ERR_clear_error(); 16162306a36Sopenharmony_ci break; 16262306a36Sopenharmony_ci } 16362306a36Sopenharmony_ci } 16462306a36Sopenharmony_ci ERR(!x509, "%s", cert_src); 16562306a36Sopenharmony_ci write_cert(x509); 16662306a36Sopenharmony_ci } 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci BIO_free(wb); 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci return 0; 17262306a36Sopenharmony_ci} 173