Blame view

scripts/sign-file.c 9.72 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.
   */
41693d1c0   Marc-Antoine Perennou   sign-file: fix bu...
44
  #if OPENSSL_VERSION_NUMBER < 0x10000000L || defined(OPENSSL_NO_CMS)
283e8ba2d   David Howells   MODSIGN: Change f...
45
46
47
48
49
50
51
  #define USE_PKCS7
  #endif
  #ifndef USE_PKCS7
  #include <openssl/cms.h>
  #else
  #include <openssl/pkcs7.h>
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
  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...
73
74
75
  	fprintf(stderr,
  		"       scripts/sign-file -s <raw sig> <hash algo> <x509> <module> [<dest>]
  ");
bc1c373dd   David Howells   MODSIGN: Provide ...
76
77
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
  	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...
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
  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...
135
136
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
  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...
172
  	unsigned char buf[2];
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
173
174
  	X509 *x509;
  	BIO *b;
9552c7aeb   David Howells   modsign: Make sig...
175
  	int n;
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
176
177
178
  
  	b = BIO_new_file(x509_name, "rb");
  	ERR(!b, "%s", x509_name);
9552c7aeb   David Howells   modsign: Make sig...
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
  
  	/* 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...
194
  	}
9552c7aeb   David Howells   modsign: Make sig...
195
196
197
198
199
200
201
202
203
  
  	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...
204
205
206
207
208
  	BIO_free(b);
  	ERR(!x509, "%s", x509_name);
  
  	return x509;
  }
bc1c373dd   David Howells   MODSIGN: Provide ...
209
210
211
212
  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...
213
214
  	char *private_key_name = NULL, *raw_sig_name = NULL;
  	char *x509_name, *module_name, *dest_name;
283e8ba2d   David Howells   MODSIGN: Change f...
215
  	bool save_sig = false, replace_orig;
23dfbbabb   Luis R. Rodriguez   sign-file: Add op...
216
  	bool sign_only = false;
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
217
  	bool raw_sig = false;
bc1c373dd   David Howells   MODSIGN: Provide ...
218
  	unsigned char buf[4096];
283e8ba2d   David Howells   MODSIGN: Change f...
219
220
  	unsigned long module_size, sig_size;
  	unsigned int use_signed_attrs;
bc1c373dd   David Howells   MODSIGN: Provide ...
221
222
  	const EVP_MD *digest_algo;
  	EVP_PKEY *private_key;
283e8ba2d   David Howells   MODSIGN: Change f...
223
  #ifndef USE_PKCS7
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
224
  	CMS_ContentInfo *cms = NULL;
283e8ba2d   David Howells   MODSIGN: Change f...
225
226
  	unsigned int use_keyid = 0;
  #else
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
227
  	PKCS7 *pkcs7 = NULL;
283e8ba2d   David Howells   MODSIGN: Change f...
228
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
229
  	X509 *x509;
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
230
  	BIO *bd, *bm;
bc1c373dd   David Howells   MODSIGN: Provide ...
231
  	int opt, n;
af1eb2913   David Woodhouse   modsign: Allow pa...
232
  	OpenSSL_add_all_algorithms();
bc1c373dd   David Howells   MODSIGN: Provide ...
233
234
  	ERR_load_crypto_strings();
  	ERR_clear_error();
af1eb2913   David Woodhouse   modsign: Allow pa...
235
  	key_pass = getenv("KBUILD_SIGN_PIN");
283e8ba2d   David Howells   MODSIGN: Change f...
236
237
238
239
240
  #ifndef USE_PKCS7
  	use_signed_attrs = CMS_NOATTR;
  #else
  	use_signed_attrs = PKCS7_NOATTR;
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
241
  	do {
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
242
  		opt = getopt(argc, argv, "sdpk");
bc1c373dd   David Howells   MODSIGN: Provide ...
243
  		switch (opt) {
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
244
  		case 's': raw_sig = true; break;
283e8ba2d   David Howells   MODSIGN: Change f...
245
246
247
  		case 'p': save_sig = true; break;
  		case 'd': sign_only = true; save_sig = true; break;
  #ifndef USE_PKCS7
ed8c20762   David Howells   sign-file: Genera...
248
  		case 'k': use_keyid = CMS_USE_KEYID; break;
283e8ba2d   David Howells   MODSIGN: Change f...
249
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
250
251
252
253
254
255
256
257
258
  		case -1: break;
  		default: format();
  		}
  	} while (opt != -1);
  
  	argc -= optind;
  	argv += optind;
  	if (argc < 4 || argc > 5)
  		format();
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
259
260
261
262
263
264
265
  	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 ...
266
267
  	x509_name = argv[2];
  	module_name = argv[3];
efcae7c93   Alex Yashchenko   sign-file: Fix in...
268
  	if (argc == 5 && strcmp(argv[3], argv[4]) != 0) {
bc1c373dd   David Howells   MODSIGN: Provide ...
269
270
271
272
273
274
275
  		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...
276
277
278
279
280
281
282
283
  #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...
284
  	/* Open the module file */
bc1c373dd   David Howells   MODSIGN: Provide ...
285
286
  	bm = BIO_new_file(module_name, "rb");
  	ERR(!bm, "%s", module_name);
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
287
288
289
290
291
292
293
294
295
296
297
298
  	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...
299
  #ifndef USE_PKCS7
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
300
301
302
303
304
305
306
307
308
309
310
311
312
  		/* 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 ...
313

283e8ba2d   David Howells   MODSIGN: Change f...
314
  #else
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
315
316
317
318
  		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...
319
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
320

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

e5a2e3c84   Juerg Haefliger   scripts/sign-file...
325
326
327
328
  			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...
329
  #ifndef USE_PKCS7
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
330
331
  			ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) < 0,
  			    "%s", sig_file_name);
283e8ba2d   David Howells   MODSIGN: Change f...
332
  #else
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
333
334
  			ERR(i2d_PKCS7_bio(b, pkcs7) < 0,
  			    "%s", sig_file_name);
283e8ba2d   David Howells   MODSIGN: Change f...
335
  #endif
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
336
337
338
339
340
341
342
  			BIO_free(b);
  		}
  
  		if (sign_only) {
  			BIO_free(bm);
  			return 0;
  		}
bc1c373dd   David Howells   MODSIGN: Provide ...
343
  	}
e5a2e3c84   Juerg Haefliger   scripts/sign-file...
344
345
346
347
348
  	/* 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...
349

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