Blame view

scripts/sign-file.c 8.14 KB
bc1c373dd   David Howells   MODSIGN: Provide ...
1
2
  /* Sign a module file using the given key.
   *
09a77a885   David Woodhouse   modsign: Fix GPL/...
3
4
5
6
7
   * Copyright © 2014-2015 Red Hat, Inc. All Rights Reserved.
   * Copyright © 2015      Intel Corporation.
   *
   * Authors: David Howells <dhowells@redhat.com>
   *          David Woodhouse <dwmw2@infradead.org>
bc1c373dd   David Howells   MODSIGN: Provide ...
8
9
   *
   * This program is free software; you can redistribute it and/or
09a77a885   David Woodhouse   modsign: Fix GPL/...
10
11
12
   * 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 ...
13
14
15
16
17
18
19
20
21
22
   */
  #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...
23
  #include <openssl/opensslv.h>
bc1c373dd   David Howells   MODSIGN: Provide ...
24
25
26
  #include <openssl/bio.h>
  #include <openssl/evp.h>
  #include <openssl/pem.h>
bc1c373dd   David Howells   MODSIGN: Provide ...
27
  #include <openssl/err.h>
6e3e281f3   David Woodhouse   modsign: Allow si...
28
  #include <openssl/engine.h>
bc1c373dd   David Howells   MODSIGN: Provide ...
29

283e8ba2d   David Howells   MODSIGN: Change f...
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  /*
   * 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.
   */
  #if OPENSSL_VERSION_NUMBER < 0x10000000L
  #define USE_PKCS7
  #endif
  #ifndef USE_PKCS7
  #include <openssl/cms.h>
  #else
  #include <openssl/pkcs7.h>
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
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
  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>]
  ");
  	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...
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
  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;
  }
bc1c373dd   David Howells   MODSIGN: Provide ...
130
131
132
133
134
  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;
283e8ba2d   David Howells   MODSIGN: Change f...
135
  	bool save_sig = false, replace_orig;
23dfbbabb   Luis R. Rodriguez   sign-file: Add op...
136
  	bool sign_only = false;
bc1c373dd   David Howells   MODSIGN: Provide ...
137
  	unsigned char buf[4096];
283e8ba2d   David Howells   MODSIGN: Change f...
138
139
  	unsigned long module_size, sig_size;
  	unsigned int use_signed_attrs;
bc1c373dd   David Howells   MODSIGN: Provide ...
140
141
  	const EVP_MD *digest_algo;
  	EVP_PKEY *private_key;
283e8ba2d   David Howells   MODSIGN: Change f...
142
  #ifndef USE_PKCS7
ed8c20762   David Howells   sign-file: Genera...
143
  	CMS_ContentInfo *cms;
283e8ba2d   David Howells   MODSIGN: Change f...
144
145
146
147
  	unsigned int use_keyid = 0;
  #else
  	PKCS7 *pkcs7;
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
148
  	X509 *x509;
23dfbbabb   Luis R. Rodriguez   sign-file: Add op...
149
  	BIO *b, *bd = NULL, *bm;
bc1c373dd   David Howells   MODSIGN: Provide ...
150
  	int opt, n;
af1eb2913   David Woodhouse   modsign: Allow pa...
151
  	OpenSSL_add_all_algorithms();
bc1c373dd   David Howells   MODSIGN: Provide ...
152
153
  	ERR_load_crypto_strings();
  	ERR_clear_error();
af1eb2913   David Woodhouse   modsign: Allow pa...
154
  	key_pass = getenv("KBUILD_SIGN_PIN");
283e8ba2d   David Howells   MODSIGN: Change f...
155
156
157
158
159
  #ifndef USE_PKCS7
  	use_signed_attrs = CMS_NOATTR;
  #else
  	use_signed_attrs = PKCS7_NOATTR;
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
160
  	do {
ed8c20762   David Howells   sign-file: Genera...
161
  		opt = getopt(argc, argv, "dpk");
bc1c373dd   David Howells   MODSIGN: Provide ...
162
  		switch (opt) {
283e8ba2d   David Howells   MODSIGN: Change f...
163
164
165
  		case 'p': save_sig = true; break;
  		case 'd': sign_only = true; save_sig = true; break;
  #ifndef USE_PKCS7
ed8c20762   David Howells   sign-file: Genera...
166
  		case 'k': use_keyid = CMS_USE_KEYID; break;
283e8ba2d   David Howells   MODSIGN: Change f...
167
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
  		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;
  	}
283e8ba2d   David Howells   MODSIGN: Change f...
190
191
192
193
194
195
196
197
  #ifdef USE_PKCS7
  	if (strcmp(hash_algo, "sha1") != 0) {
  		fprintf(stderr, "sign-file: %s only supports SHA1 signing
  ",
  			OPENSSL_VERSION_TEXT);
  		exit(3);
  	}
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
198
199
200
  	/* Read the private key and the X.509 cert the PKCS#7 message
  	 * will point to.
  	 */
6e3e281f3   David Woodhouse   modsign: Allow si...
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
  	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);
  	}
bc1c373dd   David Howells   MODSIGN: Provide ...
224
225
226
227
228
  
  	b = BIO_new_file(x509_name, "rb");
  	ERR(!b, "%s", x509_name);
  	x509 = d2i_X509_bio(b, NULL); /* Binary encoded X.509 */
  	if (!x509) {
e9a5e8cc5   David Howells   sign-file: Fix wa...
229
  		ERR(BIO_reset(b) != 1, "%s", x509_name);
bc1c373dd   David Howells   MODSIGN: Provide ...
230
231
232
233
234
235
236
237
238
239
  		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.
  	 */
23dfbbabb   Luis R. Rodriguez   sign-file: Add op...
240
241
242
243
  	if (!sign_only) {
  		bd = BIO_new_file(dest_name, "wb");
  		ERR(!bd, "%s", dest_name);
  	}
bc1c373dd   David Howells   MODSIGN: Provide ...
244
245
246
247
248
249
250
251
252
  
  	/* 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);
283e8ba2d   David Howells   MODSIGN: Change f...
253
254
  #ifndef USE_PKCS7
  	/* Load the signature message from the digest buffer. */
ed8c20762   David Howells   sign-file: Genera...
255
256
257
  	cms = CMS_sign(NULL, NULL, NULL, NULL,
  		       CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED | CMS_STREAM);
  	ERR(!cms, "CMS_sign");
bc1c373dd   David Howells   MODSIGN: Provide ...
258

ed8c20762   David Howells   sign-file: Genera...
259
  	ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo,
99db44350   David Howells   PKCS#7: Appropria...
260
261
  			     CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP |
  			     use_keyid | use_signed_attrs),
283e8ba2d   David Howells   MODSIGN: Change f...
262
  	    "CMS_add1_signer");
ed8c20762   David Howells   sign-file: Genera...
263
264
  	ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) < 0,
  	    "CMS_final");
bc1c373dd   David Howells   MODSIGN: Provide ...
265

283e8ba2d   David Howells   MODSIGN: Change f...
266
267
268
269
270
271
  #else
  	pkcs7 = PKCS7_sign(x509, private_key, NULL, bm,
  			   PKCS7_NOCERTS | PKCS7_BINARY |
  			   PKCS7_DETACHED | use_signed_attrs);
  	ERR(!pkcs7, "PKCS7_sign");
  #endif
bc1c373dd   David Howells   MODSIGN: Provide ...
272

283e8ba2d   David Howells   MODSIGN: Change f...
273
274
275
276
277
278
279
280
281
282
283
284
285
286
  	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
bc1c373dd   David Howells   MODSIGN: Provide ...
287
288
  		BIO_free(b);
  	}
23dfbbabb   Luis R. Rodriguez   sign-file: Add op...
289
290
  	if (sign_only)
  		return 0;
bc1c373dd   David Howells   MODSIGN: Provide ...
291
292
293
294
295
296
297
298
  	/* 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);
283e8ba2d   David Howells   MODSIGN: Change f...
299
  #ifndef USE_PKCS7
ed8c20762   David Howells   sign-file: Genera...
300
  	ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) < 0, "%s", dest_name);
283e8ba2d   David Howells   MODSIGN: Change f...
301
302
303
304
305
  #else
  	ERR(i2d_PKCS7_bio(bd, pkcs7) < 0, "%s", dest_name);
  #endif
  	sig_size = BIO_number_written(bd) - module_size;
  	sig_info.sig_len = htonl(sig_size);
bc1c373dd   David Howells   MODSIGN: Provide ...
306
307
308
309
310
311
312
313
314
315
316
  	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;
  }