Blame view

scripts/sign-file.c 9.76 KB
bc1c373dd   David Howells   MODSIGN: Provide ...
1
2
  /* Sign a module file using the given key.
   *
9552c7aeb   David Howells   modsign: Make sig...
3
   * Copyright © 2014-2016 Red Hat, Inc. All Rights Reserved.
09a77a885   David Woodhouse   modsign: Fix GPL/...
4
   * Copyright © 2015      Intel Corporation.
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
5
   * Copyright © 2016      Hewlett Packard Enterprise Development LP
09a77a885   David Woodhouse   modsign: Fix GPL/...
6
7
8
   *
   * Authors: David Howells <dhowells@redhat.com>
   *          David Woodhouse <dwmw2@infradead.org>
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
9
   *          Juerg Haefliger <juerg.haefliger@hpe.com>
bc1c373dd   David Howells   MODSIGN: Provide ...
10
11
   *
   * This program is free software; you can redistribute it and/or
09a77a885   David Woodhouse   modsign: Fix GPL/...
12
13
14
   * modify it under the terms of the GNU Lesser General Public License
   * as published by the Free Software Foundation; either version 2.1
   * of the licence, or (at your option) any later version.
bc1c373dd   David Howells   MODSIGN: Provide ...
15
16
17
18
19
20
21
22
23
24
   */
  #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>
283e8ba2d   David Howells   MODSIGN: Change f...
25
  #include <openssl/opensslv.h>
bc1c373dd   David Howells   MODSIGN: Provide ...
26
27
28
  #include <openssl/bio.h>
  #include <openssl/evp.h>
  #include <openssl/pem.h>
bc1c373dd   David Howells   MODSIGN: Provide ...
29
  #include <openssl/err.h>
6e3e281f3   David Woodhouse   modsign: Allow si...
30
  #include <openssl/engine.h>
bc1c373dd   David Howells   MODSIGN: Provide ...
31

283e8ba2d   David Howells   MODSIGN: Change f...
32
33
34
35
36
37
38
39
40
41
42
43
  /*
   * Use CMS if we have openssl-1.0.0 or newer available - otherwise we have to
   * assume that it's not available and its header file is missing and that we
   * should use PKCS#7 instead.  Switching to the older PKCS#7 format restricts
   * the options we have on specifying the X.509 certificate we want.
   *
   * Further, older versions of OpenSSL don't support manually adding signers to
   * the PKCS#7 message so have to accept that we get a certificate included in
   * the signature message.  Nor do such older versions of OpenSSL support
   * signing with anything other than SHA1 - so we're stuck with that if such is
   * the case.
   */
f86880175   Felix Fietkau   sign-file: fix bu...
44
45
46
  #if defined(LIBRESSL_VERSION_NUMBER) || \
  	OPENSSL_VERSION_NUMBER < 0x10000000L || \
  	defined(OPENSSL_NO_CMS)
283e8ba2d   David Howells   MODSIGN: Change f...
47
48
49
50
51
52
53
  #define USE_PKCS7
  #endif
  #ifndef USE_PKCS7
  #include <openssl/cms.h>
  #else
  #include <openssl/pkcs7.h>
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  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~
  ";
  
  static __attribute__((noreturn))
  void format(void)
  {
  	fprintf(stderr,
  		"Usage: scripts/sign-file [-dp] <hash algo> <key> <x509> <module> [<dest>]
  ");
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
75
76
77
  	fprintf(stderr,
  		"       scripts/sign-file -s <raw sig> <hash algo> <x509> <module> [<dest>]
  ");
bc1c373dd   David Howells   MODSIGN: Provide ...
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
  	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:
  ", l);
  
  	while ((e = ERR_get_error_line(&file, &line))) {
  		ERR_error_string(e, buf);
  		fprintf(stderr, "- SSL %s: %s:%d
  ", 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)
af1eb2913   David Woodhouse   modsign: Allow pa...
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
  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;
  }
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
  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)
  {
9552c7aeb   David Howells   modsign: Make sig...
174
  	unsigned char buf[2];
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
175
176
  	X509 *x509;
  	BIO *b;
9552c7aeb   David Howells   modsign: Make sig...
177
  	int n;
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
178
179
180
  
  	b = BIO_new_file(x509_name, "rb");
  	ERR(!b, "%s", x509_name);
9552c7aeb   David Howells   modsign: Make sig...
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
  
  	/* Look at the first two bytes of the file to determine the encoding */
  	n = BIO_read(b, buf, 2);
  	if (n != 2) {
  		if (BIO_should_retry(b)) {
  			fprintf(stderr, "%s: Read wanted retry
  ", x509_name);
  			exit(1);
  		}
  		if (n >= 0) {
  			fprintf(stderr, "%s: Short read
  ", x509_name);
  			exit(1);
  		}
  		ERR(1, "%s", x509_name);
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
196
  	}
9552c7aeb   David Howells   modsign: Make sig...
197
198
199
200
201
202
203
204
205
  
  	ERR(BIO_reset(b) != 0, "%s", x509_name);
  
  	if (buf[0] == 0x30 && buf[1] >= 0x81 && buf[1] <= 0x84)
  		/* Assume raw DER encoded X.509 */
  		x509 = d2i_X509_bio(b, NULL);
  	else
  		/* Assume PEM encoded X.509 */
  		x509 = PEM_read_bio_X509(b, NULL, NULL, NULL);
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
206
207
208
209
210
  	BIO_free(b);
  	ERR(!x509, "%s", x509_name);
  
  	return x509;
  }
bc1c373dd   David Howells   MODSIGN: Provide ...
211
212
213
214
  int main(int argc, char **argv)
  {
  	struct module_signature sig_info = { .id_type = PKEY_ID_PKCS7 };
  	char *hash_algo = NULL;
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
215
216
  	char *private_key_name = NULL, *raw_sig_name = NULL;
  	char *x509_name, *module_name, *dest_name;
283e8ba2d   David Howells   MODSIGN: Change f...
217
  	bool save_sig = false, replace_orig;
23dfbbabb   Luis R. Rodriguez   sign-file: Add op...
218
  	bool sign_only = false;
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
219
  	bool raw_sig = false;
bc1c373dd   David Howells   MODSIGN: Provide ...
220
  	unsigned char buf[4096];
283e8ba2d   David Howells   MODSIGN: Change f...
221
222
  	unsigned long module_size, sig_size;
  	unsigned int use_signed_attrs;
bc1c373dd   David Howells   MODSIGN: Provide ...
223
224
  	const EVP_MD *digest_algo;
  	EVP_PKEY *private_key;
283e8ba2d   David Howells   MODSIGN: Change f...
225
  #ifndef USE_PKCS7
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
226
  	CMS_ContentInfo *cms = NULL;
283e8ba2d   David Howells   MODSIGN: Change f...
227
228
  	unsigned int use_keyid = 0;
  #else
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
229
  	PKCS7 *pkcs7 = NULL;
283e8ba2d   David Howells   MODSIGN: Change f...
230
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
231
  	X509 *x509;
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
232
  	BIO *bd, *bm;
bc1c373dd   David Howells   MODSIGN: Provide ...
233
  	int opt, n;
af1eb2913   David Woodhouse   modsign: Allow pa...
234
  	OpenSSL_add_all_algorithms();
bc1c373dd   David Howells   MODSIGN: Provide ...
235
236
  	ERR_load_crypto_strings();
  	ERR_clear_error();
af1eb2913   David Woodhouse   modsign: Allow pa...
237
  	key_pass = getenv("KBUILD_SIGN_PIN");
283e8ba2d   David Howells   MODSIGN: Change f...
238
239
240
241
242
  #ifndef USE_PKCS7
  	use_signed_attrs = CMS_NOATTR;
  #else
  	use_signed_attrs = PKCS7_NOATTR;
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
243
  	do {
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
244
  		opt = getopt(argc, argv, "sdpk");
bc1c373dd   David Howells   MODSIGN: Provide ...
245
  		switch (opt) {
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
246
  		case 's': raw_sig = true; break;
283e8ba2d   David Howells   MODSIGN: Change f...
247
248
249
  		case 'p': save_sig = true; break;
  		case 'd': sign_only = true; save_sig = true; break;
  #ifndef USE_PKCS7
ed8c20762   David Howells   sign-file: Genera...
250
  		case 'k': use_keyid = CMS_USE_KEYID; break;
283e8ba2d   David Howells   MODSIGN: Change f...
251
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
252
253
254
255
256
257
258
259
260
  		case -1: break;
  		default: format();
  		}
  	} while (opt != -1);
  
  	argc -= optind;
  	argv += optind;
  	if (argc < 4 || argc > 5)
  		format();
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
261
262
263
264
265
266
267
  	if (raw_sig) {
  		raw_sig_name = argv[0];
  		hash_algo = argv[1];
  	} else {
  		hash_algo = argv[0];
  		private_key_name = argv[1];
  	}
bc1c373dd   David Howells   MODSIGN: Provide ...
268
269
  	x509_name = argv[2];
  	module_name = argv[3];
efcae7c93   Alex Yashchenko   sign-file: Fix in...
270
  	if (argc == 5 && strcmp(argv[3], argv[4]) != 0) {
bc1c373dd   David Howells   MODSIGN: Provide ...
271
272
273
274
275
276
277
  		dest_name = argv[4];
  		replace_orig = false;
  	} else {
  		ERR(asprintf(&dest_name, "%s.~signed~", module_name) < 0,
  		    "asprintf");
  		replace_orig = true;
  	}
283e8ba2d   David Howells   MODSIGN: Change f...
278
279
280
281
282
283
284
285
  #ifdef USE_PKCS7
  	if (strcmp(hash_algo, "sha1") != 0) {
  		fprintf(stderr, "sign-file: %s only supports SHA1 signing
  ",
  			OPENSSL_VERSION_TEXT);
  		exit(3);
  	}
  #endif
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
286
  	/* Open the module file */
bc1c373dd   David Howells   MODSIGN: Provide ...
287
288
  	bm = BIO_new_file(module_name, "rb");
  	ERR(!bm, "%s", module_name);
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
289
290
291
292
293
294
295
296
297
298
299
300
  	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);
  
  		/* 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");
283e8ba2d   David Howells   MODSIGN: Change f...
301
  #ifndef USE_PKCS7
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
302
303
304
305
306
307
308
309
310
311
312
313
314
  		/* 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");
bc1c373dd   David Howells   MODSIGN: Provide ...
315

283e8ba2d   David Howells   MODSIGN: Change f...
316
  #else
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
317
318
319
320
  		pkcs7 = PKCS7_sign(x509, private_key, NULL, bm,
  				   PKCS7_NOCERTS | PKCS7_BINARY |
  				   PKCS7_DETACHED | use_signed_attrs);
  		ERR(!pkcs7, "PKCS7_sign");
283e8ba2d   David Howells   MODSIGN: Change f...
321
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
322

e5a2e3c84   Juerg Haefliger   scripts/sign-file...
323
324
325
  		if (save_sig) {
  			char *sig_file_name;
  			BIO *b;
283e8ba2d   David Howells   MODSIGN: Change f...
326

e5a2e3c84   Juerg Haefliger   scripts/sign-file...
327
328
329
330
  			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);
283e8ba2d   David Howells   MODSIGN: Change f...
331
  #ifndef USE_PKCS7
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
332
333
  			ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) < 0,
  			    "%s", sig_file_name);
283e8ba2d   David Howells   MODSIGN: Change f...
334
  #else
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
335
336
  			ERR(i2d_PKCS7_bio(b, pkcs7) < 0,
  			    "%s", sig_file_name);
283e8ba2d   David Howells   MODSIGN: Change f...
337
  #endif
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
338
339
340
341
342
343
344
  			BIO_free(b);
  		}
  
  		if (sign_only) {
  			BIO_free(bm);
  			return 0;
  		}
bc1c373dd   David Howells   MODSIGN: Provide ...
345
  	}
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
346
347
348
349
350
  	/* Open the destination file now so that we can shovel the module data
  	 * across as we read it.
  	 */
  	bd = BIO_new_file(dest_name, "wb");
  	ERR(!bd, "%s", dest_name);
23dfbbabb   Luis R. Rodriguez   sign-file: Add op...
351

bc1c373dd   David Howells   MODSIGN: Provide ...
352
353
354
355
356
357
  	/* 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);
  	}
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
358
  	BIO_free(bm);
bc1c373dd   David Howells   MODSIGN: Provide ...
359
360
  	ERR(n < 0, "%s", module_name);
  	module_size = BIO_number_written(bd);
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
361
  	if (!raw_sig) {
283e8ba2d   David Howells   MODSIGN: Change f...
362
  #ifndef USE_PKCS7
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
363
  		ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) < 0, "%s", dest_name);
283e8ba2d   David Howells   MODSIGN: Change f...
364
  #else
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
365
  		ERR(i2d_PKCS7_bio(bd, pkcs7) < 0, "%s", dest_name);
283e8ba2d   David Howells   MODSIGN: Change f...
366
  #endif
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
367
368
369
370
371
372
373
374
375
376
377
378
  	} 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);
  	}
283e8ba2d   David Howells   MODSIGN: Change f...
379
380
  	sig_size = BIO_number_written(bd) - module_size;
  	sig_info.sig_len = htonl(sig_size);
bc1c373dd   David Howells   MODSIGN: Provide ...
381
382
383
384
385
386
387
388
389
390
391
  	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;
  }