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