diff options
| author | Ingo Molnar <mingo@kernel.org> | 2015-09-13 12:25:35 +0300 | 
|---|---|---|
| committer | Ingo Molnar <mingo@kernel.org> | 2015-09-13 12:25:35 +0300 | 
| commit | d2bb1d42b95fa88f092623bbb8ed533f316b6a3c (patch) | |
| tree | fb796db809a266906fa358f24f1c07ced4df33f0 /scripts/sign-file.c | |
| parent | 3bd7617596df560e2cb22ad97888cb42dae39d02 (diff) | |
| parent | 6ff33f3902c3b1c5d0db6b1e2c70b6d76fba357f (diff) | |
| download | linux-d2bb1d42b95fa88f092623bbb8ed533f316b6a3c.tar.xz | |
Merge tag 'v4.3-rc1' into perf/core, to refresh the tree
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'scripts/sign-file.c')
| -rwxr-xr-x | scripts/sign-file.c | 260 | 
1 files changed, 260 insertions, 0 deletions
diff --git a/scripts/sign-file.c b/scripts/sign-file.c new file mode 100755 index 000000000000..058bba3103e2 --- /dev/null +++ b/scripts/sign-file.c @@ -0,0 +1,260 @@ +/* Sign a module file using the given key. + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stdbool.h> +#include <string.h> +#include <getopt.h> +#include <err.h> +#include <arpa/inet.h> +#include <openssl/bio.h> +#include <openssl/evp.h> +#include <openssl/pem.h> +#include <openssl/cms.h> +#include <openssl/err.h> +#include <openssl/engine.h> + +struct module_signature { +	uint8_t		algo;		/* Public-key crypto algorithm [0] */ +	uint8_t		hash;		/* Digest algorithm [0] */ +	uint8_t		id_type;	/* Key identifier type [PKEY_ID_PKCS7] */ +	uint8_t		signer_len;	/* Length of signer's name [0] */ +	uint8_t		key_id_len;	/* Length of key identifier [0] */ +	uint8_t		__pad[3]; +	uint32_t	sig_len;	/* Length of signature data */ +}; + +#define PKEY_ID_PKCS7 2 + +static char magic_number[] = "~Module signature appended~\n"; + +static __attribute__((noreturn)) +void format(void) +{ +	fprintf(stderr, +		"Usage: scripts/sign-file [-dp] <hash algo> <key> <x509> <module> [<dest>]\n"); +	exit(2); +} + +static void display_openssl_errors(int l) +{ +	const char *file; +	char buf[120]; +	int e, line; + +	if (ERR_peek_error() == 0) +		return; +	fprintf(stderr, "At main.c:%d:\n", l); + +	while ((e = ERR_get_error_line(&file, &line))) { +		ERR_error_string(e, buf); +		fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line); +	} +} + +static void drain_openssl_errors(void) +{ +	const char *file; +	int line; + +	if (ERR_peek_error() == 0) +		return; +	while (ERR_get_error_line(&file, &line)) {} +} + +#define ERR(cond, fmt, ...)				\ +	do {						\ +		bool __cond = (cond);			\ +		display_openssl_errors(__LINE__);	\ +		if (__cond) {				\ +			err(1, fmt, ## __VA_ARGS__);	\ +		}					\ +	} while(0) + +static const char *key_pass; + +static int pem_pw_cb(char *buf, int len, int w, void *v) +{ +	int pwlen; + +	if (!key_pass) +		return -1; + +	pwlen = strlen(key_pass); +	if (pwlen >= len) +		return -1; + +	strcpy(buf, key_pass); + +	/* If it's wrong, don't keep trying it. */ +	key_pass = NULL; + +	return pwlen; +} + +int main(int argc, char **argv) +{ +	struct module_signature sig_info = { .id_type = PKEY_ID_PKCS7 }; +	char *hash_algo = NULL; +	char *private_key_name, *x509_name, *module_name, *dest_name; +	bool save_cms = false, replace_orig; +	bool sign_only = false; +	unsigned char buf[4096]; +	unsigned long module_size, cms_size; +	unsigned int use_keyid = 0, use_signed_attrs = CMS_NOATTR; +	const EVP_MD *digest_algo; +	EVP_PKEY *private_key; +	CMS_ContentInfo *cms; +	X509 *x509; +	BIO *b, *bd = NULL, *bm; +	int opt, n; + +	OpenSSL_add_all_algorithms(); +	ERR_load_crypto_strings(); +	ERR_clear_error(); + +	key_pass = getenv("KBUILD_SIGN_PIN"); + +	do { +		opt = getopt(argc, argv, "dpk"); +		switch (opt) { +		case 'p': save_cms = true; break; +		case 'd': sign_only = true; save_cms = true; break; +		case 'k': use_keyid = CMS_USE_KEYID; break; +		case -1: break; +		default: format(); +		} +	} while (opt != -1); + +	argc -= optind; +	argv += optind; +	if (argc < 4 || argc > 5) +		format(); + +	hash_algo = argv[0]; +	private_key_name = argv[1]; +	x509_name = argv[2]; +	module_name = argv[3]; +	if (argc == 5) { +		dest_name = argv[4]; +		replace_orig = false; +	} else { +		ERR(asprintf(&dest_name, "%s.~signed~", module_name) < 0, +		    "asprintf"); +		replace_orig = true; +	} + +	/* Read the private key and the X.509 cert the PKCS#7 message +	 * will point to. +	 */ +	if (!strncmp(private_key_name, "pkcs11:", 7)) { +		ENGINE *e; + +		ENGINE_load_builtin_engines(); +		drain_openssl_errors(); +		e = ENGINE_by_id("pkcs11"); +		ERR(!e, "Load PKCS#11 ENGINE"); +		if (ENGINE_init(e)) +			drain_openssl_errors(); +		else +			ERR(1, "ENGINE_init"); +		if (key_pass) +			ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0), "Set PKCS#11 PIN"); +		private_key = ENGINE_load_private_key(e, private_key_name, NULL, +						      NULL); +		ERR(!private_key, "%s", private_key_name); +	} else { +		b = BIO_new_file(private_key_name, "rb"); +		ERR(!b, "%s", private_key_name); +		private_key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb, NULL); +		ERR(!private_key, "%s", private_key_name); +		BIO_free(b); +	} + +	b = BIO_new_file(x509_name, "rb"); +	ERR(!b, "%s", x509_name); +	x509 = d2i_X509_bio(b, NULL); /* Binary encoded X.509 */ +	if (!x509) { +		ERR(BIO_reset(b) != 1, "%s", x509_name); +		x509 = PEM_read_bio_X509(b, NULL, NULL, NULL); /* PEM encoded X.509 */ +		if (x509) +			drain_openssl_errors(); +	} +	BIO_free(b); +	ERR(!x509, "%s", x509_name); + +	/* Open the destination file now so that we can shovel the module data +	 * across as we read it. +	 */ +	if (!sign_only) { +		bd = BIO_new_file(dest_name, "wb"); +		ERR(!bd, "%s", dest_name); +	} + +	/* Digest the module data. */ +	OpenSSL_add_all_digests(); +	display_openssl_errors(__LINE__); +	digest_algo = EVP_get_digestbyname(hash_algo); +	ERR(!digest_algo, "EVP_get_digestbyname"); + +	bm = BIO_new_file(module_name, "rb"); +	ERR(!bm, "%s", module_name); + +	/* Load the CMS message from the digest buffer. */ +	cms = CMS_sign(NULL, NULL, NULL, NULL, +		       CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED | CMS_STREAM); +	ERR(!cms, "CMS_sign"); + +	ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo, +			     CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP | +			     use_keyid | use_signed_attrs), +	    "CMS_sign_add_signer"); +	ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) < 0, +	    "CMS_final"); + +	if (save_cms) { +		char *cms_name; + +		ERR(asprintf(&cms_name, "%s.p7s", module_name) < 0, "asprintf"); +		b = BIO_new_file(cms_name, "wb"); +		ERR(!b, "%s", cms_name); +		ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) < 0, "%s", cms_name); +		BIO_free(b); +	} + +	if (sign_only) +		return 0; + +	/* Append the marker and the PKCS#7 message to the destination file */ +	ERR(BIO_reset(bm) < 0, "%s", module_name); +	while ((n = BIO_read(bm, buf, sizeof(buf))), +	       n > 0) { +		ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name); +	} +	ERR(n < 0, "%s", module_name); +	module_size = BIO_number_written(bd); + +	ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) < 0, "%s", dest_name); +	cms_size = BIO_number_written(bd) - module_size; +	sig_info.sig_len = htonl(cms_size); +	ERR(BIO_write(bd, &sig_info, sizeof(sig_info)) < 0, "%s", dest_name); +	ERR(BIO_write(bd, magic_number, sizeof(magic_number) - 1) < 0, "%s", dest_name); + +	ERR(BIO_free(bd) < 0, "%s", dest_name); + +	/* Finally, if we're signing in place, replace the original. */ +	if (replace_orig) +		ERR(rename(dest_name, module_name) < 0, "%s", dest_name); + +	return 0; +}  | 
