mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 08:38:45 +02:00 
			
		
		
		
	scripts/sign-file.c: Add support for signing with a raw signature
This patch adds support for signing a kernel module with a raw
detached PKCS#7 signature/message.
The signature is not converted and is simply appended to the module so
it needs to be in the right format. Using openssl, a valid signature can
be generated like this:
  $ openssl smime -sign -nocerts -noattr -binary -in <module> -inkey \
    <key> -signer <x509> -outform der -out <raw sig>
The resulting raw signature from the above command is (more or less)
identical to the raw signature that sign-file itself can produce like
this:
  $ scripts/sign-file -d <hash algo> <key> <x509> <module>
Signed-off-by: Juerg Haefliger <juerg.haefliger@hpe.com>
Signed-off-by: David Howells <dhowells@redhat.com>
			
			
This commit is contained in:
		
							parent
							
								
									a1f2bdf338
								
							
						
					
					
						commit
						e5a2e3c847
					
				
					 1 changed files with 153 additions and 97 deletions
				
			
		|  | @ -2,9 +2,11 @@ | |||
|  * | ||||
|  * Copyright © 2014-2015 Red Hat, Inc. All Rights Reserved. | ||||
|  * Copyright © 2015      Intel Corporation. | ||||
|  * Copyright © 2016      Hewlett Packard Enterprise Development LP | ||||
|  * | ||||
|  * Authors: David Howells <dhowells@redhat.com> | ||||
|  *          David Woodhouse <dwmw2@infradead.org> | ||||
|  *          Juerg Haefliger <juerg.haefliger@hpe.com> | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public License | ||||
|  | @ -67,6 +69,8 @@ void format(void) | |||
| { | ||||
| 	fprintf(stderr, | ||||
| 		"Usage: scripts/sign-file [-dp] <hash algo> <key> <x509> <module> [<dest>]\n"); | ||||
| 	fprintf(stderr, | ||||
| 		"       scripts/sign-file -s <raw sig> <hash algo> <x509> <module> [<dest>]\n"); | ||||
| 	exit(2); | ||||
| } | ||||
| 
 | ||||
|  | @ -126,26 +130,84 @@ static int pem_pw_cb(char *buf, int len, int w, void *v) | |||
| 	return pwlen; | ||||
| } | ||||
| 
 | ||||
| static EVP_PKEY *read_private_key(const char *private_key_name) | ||||
| { | ||||
| 	EVP_PKEY *private_key; | ||||
| 
 | ||||
| 	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 { | ||||
| 		BIO *b; | ||||
| 
 | ||||
| 		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); | ||||
| 	} | ||||
| 
 | ||||
| 	return private_key; | ||||
| } | ||||
| 
 | ||||
| static X509 *read_x509(const char *x509_name) | ||||
| { | ||||
| 	X509 *x509; | ||||
| 	BIO *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); | ||||
| 
 | ||||
| 	return x509; | ||||
| } | ||||
| 
 | ||||
| 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; | ||||
| 	char *private_key_name = NULL, *raw_sig_name = NULL; | ||||
| 	char *x509_name, *module_name, *dest_name; | ||||
| 	bool save_sig = false, replace_orig; | ||||
| 	bool sign_only = false; | ||||
| 	bool raw_sig = false; | ||||
| 	unsigned char buf[4096]; | ||||
| 	unsigned long module_size, sig_size; | ||||
| 	unsigned int use_signed_attrs; | ||||
| 	const EVP_MD *digest_algo; | ||||
| 	EVP_PKEY *private_key; | ||||
| #ifndef USE_PKCS7 | ||||
| 	CMS_ContentInfo *cms; | ||||
| 	CMS_ContentInfo *cms = NULL; | ||||
| 	unsigned int use_keyid = 0; | ||||
| #else | ||||
| 	PKCS7 *pkcs7; | ||||
| 	PKCS7 *pkcs7 = NULL; | ||||
| #endif | ||||
| 	X509 *x509; | ||||
| 	BIO *b, *bd = NULL, *bm; | ||||
| 	BIO *bd, *bm; | ||||
| 	int opt, n; | ||||
| 	OpenSSL_add_all_algorithms(); | ||||
| 	ERR_load_crypto_strings(); | ||||
|  | @ -160,8 +222,9 @@ int main(int argc, char **argv) | |||
| #endif | ||||
| 
 | ||||
| 	do { | ||||
| 		opt = getopt(argc, argv, "dpk"); | ||||
| 		opt = getopt(argc, argv, "sdpk"); | ||||
| 		switch (opt) { | ||||
| 		case 's': raw_sig = true; break; | ||||
| 		case 'p': save_sig = true; break; | ||||
| 		case 'd': sign_only = true; save_sig = true; break; | ||||
| #ifndef USE_PKCS7 | ||||
|  | @ -177,8 +240,13 @@ int main(int argc, char **argv) | |||
| 	if (argc < 4 || argc > 5) | ||||
| 		format(); | ||||
| 
 | ||||
| 	hash_algo = argv[0]; | ||||
| 	private_key_name = argv[1]; | ||||
| 	if (raw_sig) { | ||||
| 		raw_sig_name = argv[0]; | ||||
| 		hash_algo = argv[1]; | ||||
| 	} else { | ||||
| 		hash_algo = argv[0]; | ||||
| 		private_key_name = argv[1]; | ||||
| 	} | ||||
| 	x509_name = argv[2]; | ||||
| 	module_name = argv[3]; | ||||
| 	if (argc == 5) { | ||||
|  | @ -198,101 +266,74 @@ int main(int argc, char **argv) | |||
| 	} | ||||
| #endif | ||||
| 
 | ||||
| 	/* 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; | ||||
| 	/* Open the module file */ | ||||
| 	bm = BIO_new_file(module_name, "rb"); | ||||
| 	ERR(!bm, "%s", module_name); | ||||
| 
 | ||||
| 		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); | ||||
| 	} | ||||
| 	if (!raw_sig) { | ||||
| 		/* Read the private key and the X.509 cert the PKCS#7 message
 | ||||
| 		 * will point to. | ||||
| 		 */ | ||||
| 		private_key = read_private_key(private_key_name); | ||||
| 		x509 = read_x509(x509_name); | ||||
| 
 | ||||
| 	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(); | ||||
| 		/* 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"); | ||||
| 
 | ||||
| #ifndef USE_PKCS7 | ||||
| 		/* Load the signature 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_add1_signer"); | ||||
| 		ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) < 0, | ||||
| 		    "CMS_final"); | ||||
| 
 | ||||
| #else | ||||
| 		pkcs7 = PKCS7_sign(x509, private_key, NULL, bm, | ||||
| 				   PKCS7_NOCERTS | PKCS7_BINARY | | ||||
| 				   PKCS7_DETACHED | use_signed_attrs); | ||||
| 		ERR(!pkcs7, "PKCS7_sign"); | ||||
| #endif | ||||
| 
 | ||||
| 		if (save_sig) { | ||||
| 			char *sig_file_name; | ||||
| 			BIO *b; | ||||
| 
 | ||||
| 			ERR(asprintf(&sig_file_name, "%s.p7s", module_name) < 0, | ||||
| 			    "asprintf"); | ||||
| 			b = BIO_new_file(sig_file_name, "wb"); | ||||
| 			ERR(!b, "%s", sig_file_name); | ||||
| #ifndef USE_PKCS7 | ||||
| 			ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) < 0, | ||||
| 			    "%s", sig_file_name); | ||||
| #else | ||||
| 			ERR(i2d_PKCS7_bio(b, pkcs7) < 0, | ||||
| 			    "%s", sig_file_name); | ||||
| #endif | ||||
| 			BIO_free(b); | ||||
| 		} | ||||
| 
 | ||||
| 		if (sign_only) { | ||||
| 			BIO_free(bm); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| 	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); | ||||
| 
 | ||||
| #ifndef USE_PKCS7 | ||||
| 	/* Load the signature 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_add1_signer"); | ||||
| 	ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) < 0, | ||||
| 	    "CMS_final"); | ||||
| 
 | ||||
| #else | ||||
| 	pkcs7 = PKCS7_sign(x509, private_key, NULL, bm, | ||||
| 			   PKCS7_NOCERTS | PKCS7_BINARY | | ||||
| 			   PKCS7_DETACHED | use_signed_attrs); | ||||
| 	ERR(!pkcs7, "PKCS7_sign"); | ||||
| #endif | ||||
| 
 | ||||
| 	if (save_sig) { | ||||
| 		char *sig_file_name; | ||||
| 
 | ||||
| 		ERR(asprintf(&sig_file_name, "%s.p7s", module_name) < 0, | ||||
| 		    "asprintf"); | ||||
| 		b = BIO_new_file(sig_file_name, "wb"); | ||||
| 		ERR(!b, "%s", sig_file_name); | ||||
| #ifndef USE_PKCS7 | ||||
| 		ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) < 0, | ||||
| 		    "%s", sig_file_name); | ||||
| #else | ||||
| 		ERR(i2d_PKCS7_bio(b, pkcs7) < 0, | ||||
| 			"%s", sig_file_name); | ||||
| #endif | ||||
| 		BIO_free(b); | ||||
| 	} | ||||
| 
 | ||||
| 	if (sign_only) | ||||
| 		return 0; | ||||
| 	bd = BIO_new_file(dest_name, "wb"); | ||||
| 	ERR(!bd, "%s", dest_name); | ||||
| 
 | ||||
| 	/* Append the marker and the PKCS#7 message to the destination file */ | ||||
| 	ERR(BIO_reset(bm) < 0, "%s", module_name); | ||||
|  | @ -300,14 +341,29 @@ int main(int argc, char **argv) | |||
| 	       n > 0) { | ||||
| 		ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name); | ||||
| 	} | ||||
| 	BIO_free(bm); | ||||
| 	ERR(n < 0, "%s", module_name); | ||||
| 	module_size = BIO_number_written(bd); | ||||
| 
 | ||||
| 	if (!raw_sig) { | ||||
| #ifndef USE_PKCS7 | ||||
| 	ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) < 0, "%s", dest_name); | ||||
| 		ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) < 0, "%s", dest_name); | ||||
| #else | ||||
| 	ERR(i2d_PKCS7_bio(bd, pkcs7) < 0, "%s", dest_name); | ||||
| 		ERR(i2d_PKCS7_bio(bd, pkcs7) < 0, "%s", dest_name); | ||||
| #endif | ||||
| 	} else { | ||||
| 		BIO *b; | ||||
| 
 | ||||
| 		/* Read the raw signature file and write the data to the
 | ||||
| 		 * destination file | ||||
| 		 */ | ||||
| 		b = BIO_new_file(raw_sig_name, "rb"); | ||||
| 		ERR(!b, "%s", raw_sig_name); | ||||
| 		while ((n = BIO_read(b, buf, sizeof(buf))), n > 0) | ||||
| 			ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name); | ||||
| 		BIO_free(b); | ||||
| 	} | ||||
| 
 | ||||
| 	sig_size = BIO_number_written(bd) - module_size; | ||||
| 	sig_info.sig_len = htonl(sig_size); | ||||
| 	ERR(BIO_write(bd, &sig_info, sizeof(sig_info)) < 0, "%s", dest_name); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Juerg Haefliger
						Juerg Haefliger