xref: /openbmc/linux/scripts/sign-file.c (revision a8fe58ce)
1 /* Sign a module file using the given key.
2  *
3  * Copyright © 2014-2015 Red Hat, Inc. All Rights Reserved.
4  * Copyright © 2015      Intel Corporation.
5  *
6  * Authors: David Howells <dhowells@redhat.com>
7  *          David Woodhouse <dwmw2@infradead.org>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the licence, or (at your option) any later version.
13  */
14 #define _GNU_SOURCE
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <stdint.h>
18 #include <stdbool.h>
19 #include <string.h>
20 #include <getopt.h>
21 #include <err.h>
22 #include <arpa/inet.h>
23 #include <openssl/opensslv.h>
24 #include <openssl/bio.h>
25 #include <openssl/evp.h>
26 #include <openssl/pem.h>
27 #include <openssl/err.h>
28 #include <openssl/engine.h>
29 
30 /*
31  * Use CMS if we have openssl-1.0.0 or newer available - otherwise we have to
32  * assume that it's not available and its header file is missing and that we
33  * should use PKCS#7 instead.  Switching to the older PKCS#7 format restricts
34  * the options we have on specifying the X.509 certificate we want.
35  *
36  * Further, older versions of OpenSSL don't support manually adding signers to
37  * the PKCS#7 message so have to accept that we get a certificate included in
38  * the signature message.  Nor do such older versions of OpenSSL support
39  * signing with anything other than SHA1 - so we're stuck with that if such is
40  * the case.
41  */
42 #if OPENSSL_VERSION_NUMBER < 0x10000000L
43 #define USE_PKCS7
44 #endif
45 #ifndef USE_PKCS7
46 #include <openssl/cms.h>
47 #else
48 #include <openssl/pkcs7.h>
49 #endif
50 
51 struct module_signature {
52 	uint8_t		algo;		/* Public-key crypto algorithm [0] */
53 	uint8_t		hash;		/* Digest algorithm [0] */
54 	uint8_t		id_type;	/* Key identifier type [PKEY_ID_PKCS7] */
55 	uint8_t		signer_len;	/* Length of signer's name [0] */
56 	uint8_t		key_id_len;	/* Length of key identifier [0] */
57 	uint8_t		__pad[3];
58 	uint32_t	sig_len;	/* Length of signature data */
59 };
60 
61 #define PKEY_ID_PKCS7 2
62 
63 static char magic_number[] = "~Module signature appended~\n";
64 
65 static __attribute__((noreturn))
66 void format(void)
67 {
68 	fprintf(stderr,
69 		"Usage: scripts/sign-file [-dp] <hash algo> <key> <x509> <module> [<dest>]\n");
70 	exit(2);
71 }
72 
73 static void display_openssl_errors(int l)
74 {
75 	const char *file;
76 	char buf[120];
77 	int e, line;
78 
79 	if (ERR_peek_error() == 0)
80 		return;
81 	fprintf(stderr, "At main.c:%d:\n", l);
82 
83 	while ((e = ERR_get_error_line(&file, &line))) {
84 		ERR_error_string(e, buf);
85 		fprintf(stderr, "- SSL %s: %s:%d\n", buf, file, line);
86 	}
87 }
88 
89 static void drain_openssl_errors(void)
90 {
91 	const char *file;
92 	int line;
93 
94 	if (ERR_peek_error() == 0)
95 		return;
96 	while (ERR_get_error_line(&file, &line)) {}
97 }
98 
99 #define ERR(cond, fmt, ...)				\
100 	do {						\
101 		bool __cond = (cond);			\
102 		display_openssl_errors(__LINE__);	\
103 		if (__cond) {				\
104 			err(1, fmt, ## __VA_ARGS__);	\
105 		}					\
106 	} while(0)
107 
108 static const char *key_pass;
109 
110 static int pem_pw_cb(char *buf, int len, int w, void *v)
111 {
112 	int pwlen;
113 
114 	if (!key_pass)
115 		return -1;
116 
117 	pwlen = strlen(key_pass);
118 	if (pwlen >= len)
119 		return -1;
120 
121 	strcpy(buf, key_pass);
122 
123 	/* If it's wrong, don't keep trying it. */
124 	key_pass = NULL;
125 
126 	return pwlen;
127 }
128 
129 int main(int argc, char **argv)
130 {
131 	struct module_signature sig_info = { .id_type = PKEY_ID_PKCS7 };
132 	char *hash_algo = NULL;
133 	char *private_key_name, *x509_name, *module_name, *dest_name;
134 	bool save_sig = false, replace_orig;
135 	bool sign_only = false;
136 	unsigned char buf[4096];
137 	unsigned long module_size, sig_size;
138 	unsigned int use_signed_attrs;
139 	const EVP_MD *digest_algo;
140 	EVP_PKEY *private_key;
141 #ifndef USE_PKCS7
142 	CMS_ContentInfo *cms;
143 	unsigned int use_keyid = 0;
144 #else
145 	PKCS7 *pkcs7;
146 #endif
147 	X509 *x509;
148 	BIO *b, *bd = NULL, *bm;
149 	int opt, n;
150 	OpenSSL_add_all_algorithms();
151 	ERR_load_crypto_strings();
152 	ERR_clear_error();
153 
154 	key_pass = getenv("KBUILD_SIGN_PIN");
155 
156 #ifndef USE_PKCS7
157 	use_signed_attrs = CMS_NOATTR;
158 #else
159 	use_signed_attrs = PKCS7_NOATTR;
160 #endif
161 
162 	do {
163 		opt = getopt(argc, argv, "dpk");
164 		switch (opt) {
165 		case 'p': save_sig = true; break;
166 		case 'd': sign_only = true; save_sig = true; break;
167 #ifndef USE_PKCS7
168 		case 'k': use_keyid = CMS_USE_KEYID; break;
169 #endif
170 		case -1: break;
171 		default: format();
172 		}
173 	} while (opt != -1);
174 
175 	argc -= optind;
176 	argv += optind;
177 	if (argc < 4 || argc > 5)
178 		format();
179 
180 	hash_algo = argv[0];
181 	private_key_name = argv[1];
182 	x509_name = argv[2];
183 	module_name = argv[3];
184 	if (argc == 5) {
185 		dest_name = argv[4];
186 		replace_orig = false;
187 	} else {
188 		ERR(asprintf(&dest_name, "%s.~signed~", module_name) < 0,
189 		    "asprintf");
190 		replace_orig = true;
191 	}
192 
193 #ifdef USE_PKCS7
194 	if (strcmp(hash_algo, "sha1") != 0) {
195 		fprintf(stderr, "sign-file: %s only supports SHA1 signing\n",
196 			OPENSSL_VERSION_TEXT);
197 		exit(3);
198 	}
199 #endif
200 
201 	/* Read the private key and the X.509 cert the PKCS#7 message
202 	 * will point to.
203 	 */
204 	if (!strncmp(private_key_name, "pkcs11:", 7)) {
205 		ENGINE *e;
206 
207 		ENGINE_load_builtin_engines();
208 		drain_openssl_errors();
209 		e = ENGINE_by_id("pkcs11");
210 		ERR(!e, "Load PKCS#11 ENGINE");
211 		if (ENGINE_init(e))
212 			drain_openssl_errors();
213 		else
214 			ERR(1, "ENGINE_init");
215 		if (key_pass)
216 			ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0), "Set PKCS#11 PIN");
217 		private_key = ENGINE_load_private_key(e, private_key_name, NULL,
218 						      NULL);
219 		ERR(!private_key, "%s", private_key_name);
220 	} else {
221 		b = BIO_new_file(private_key_name, "rb");
222 		ERR(!b, "%s", private_key_name);
223 		private_key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb, NULL);
224 		ERR(!private_key, "%s", private_key_name);
225 		BIO_free(b);
226 	}
227 
228 	b = BIO_new_file(x509_name, "rb");
229 	ERR(!b, "%s", x509_name);
230 	x509 = d2i_X509_bio(b, NULL); /* Binary encoded X.509 */
231 	if (!x509) {
232 		ERR(BIO_reset(b) != 1, "%s", x509_name);
233 		x509 = PEM_read_bio_X509(b, NULL, NULL, NULL); /* PEM encoded X.509 */
234 		if (x509)
235 			drain_openssl_errors();
236 	}
237 	BIO_free(b);
238 	ERR(!x509, "%s", x509_name);
239 
240 	/* Open the destination file now so that we can shovel the module data
241 	 * across as we read it.
242 	 */
243 	if (!sign_only) {
244 		bd = BIO_new_file(dest_name, "wb");
245 		ERR(!bd, "%s", dest_name);
246 	}
247 
248 	/* Digest the module data. */
249 	OpenSSL_add_all_digests();
250 	display_openssl_errors(__LINE__);
251 	digest_algo = EVP_get_digestbyname(hash_algo);
252 	ERR(!digest_algo, "EVP_get_digestbyname");
253 
254 	bm = BIO_new_file(module_name, "rb");
255 	ERR(!bm, "%s", module_name);
256 
257 #ifndef USE_PKCS7
258 	/* Load the signature message from the digest buffer. */
259 	cms = CMS_sign(NULL, NULL, NULL, NULL,
260 		       CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED | CMS_STREAM);
261 	ERR(!cms, "CMS_sign");
262 
263 	ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo,
264 			     CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
265 			     use_keyid | use_signed_attrs),
266 	    "CMS_add1_signer");
267 	ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) < 0,
268 	    "CMS_final");
269 
270 #else
271 	pkcs7 = PKCS7_sign(x509, private_key, NULL, bm,
272 			   PKCS7_NOCERTS | PKCS7_BINARY |
273 			   PKCS7_DETACHED | use_signed_attrs);
274 	ERR(!pkcs7, "PKCS7_sign");
275 #endif
276 
277 	if (save_sig) {
278 		char *sig_file_name;
279 
280 		ERR(asprintf(&sig_file_name, "%s.p7s", module_name) < 0,
281 		    "asprintf");
282 		b = BIO_new_file(sig_file_name, "wb");
283 		ERR(!b, "%s", sig_file_name);
284 #ifndef USE_PKCS7
285 		ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) < 0,
286 		    "%s", sig_file_name);
287 #else
288 		ERR(i2d_PKCS7_bio(b, pkcs7) < 0,
289 			"%s", sig_file_name);
290 #endif
291 		BIO_free(b);
292 	}
293 
294 	if (sign_only)
295 		return 0;
296 
297 	/* Append the marker and the PKCS#7 message to the destination file */
298 	ERR(BIO_reset(bm) < 0, "%s", module_name);
299 	while ((n = BIO_read(bm, buf, sizeof(buf))),
300 	       n > 0) {
301 		ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name);
302 	}
303 	ERR(n < 0, "%s", module_name);
304 	module_size = BIO_number_written(bd);
305 
306 #ifndef USE_PKCS7
307 	ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) < 0, "%s", dest_name);
308 #else
309 	ERR(i2d_PKCS7_bio(bd, pkcs7) < 0, "%s", dest_name);
310 #endif
311 	sig_size = BIO_number_written(bd) - module_size;
312 	sig_info.sig_len = htonl(sig_size);
313 	ERR(BIO_write(bd, &sig_info, sizeof(sig_info)) < 0, "%s", dest_name);
314 	ERR(BIO_write(bd, magic_number, sizeof(magic_number) - 1) < 0, "%s", dest_name);
315 
316 	ERR(BIO_free(bd) < 0, "%s", dest_name);
317 
318 	/* Finally, if we're signing in place, replace the original. */
319 	if (replace_orig)
320 		ERR(rename(dest_name, module_name) < 0, "%s", dest_name);
321 
322 	return 0;
323 }
324