diff options
author | Juerg Haefliger <juerg.haefliger@hpe.com> | 2016-02-04 06:09:25 -0500 |
---|---|---|
committer | David Howells <dhowells@redhat.com> | 2016-02-18 12:20:48 -0500 |
commit | e5a2e3c8478215aea5b4c58e6154f1b6b170b0ca (patch) | |
tree | 27033f09f9cb85c971a52bca150318d46f60dcc5 | |
parent | a1f2bdf338f15dbad10ee6362891ebf79244858b (diff) |
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>
-rwxr-xr-x | scripts/sign-file.c | 236 |
1 files changed, 146 insertions, 90 deletions
diff --git a/scripts/sign-file.c b/scripts/sign-file.c index a0b806d2b31d..80b7f7f933d6 100755 --- a/scripts/sign-file.c +++ b/scripts/sign-file.c | |||
@@ -2,9 +2,11 @@ | |||
2 | * | 2 | * |
3 | * Copyright © 2014-2015 Red Hat, Inc. All Rights Reserved. | 3 | * Copyright © 2014-2015 Red Hat, Inc. All Rights Reserved. |
4 | * Copyright © 2015 Intel Corporation. | 4 | * Copyright © 2015 Intel Corporation. |
5 | * Copyright © 2016 Hewlett Packard Enterprise Development LP | ||
5 | * | 6 | * |
6 | * Authors: David Howells <dhowells@redhat.com> | 7 | * Authors: David Howells <dhowells@redhat.com> |
7 | * David Woodhouse <dwmw2@infradead.org> | 8 | * David Woodhouse <dwmw2@infradead.org> |
9 | * Juerg Haefliger <juerg.haefliger@hpe.com> | ||
8 | * | 10 | * |
9 | * This program is free software; you can redistribute it and/or | 11 | * This program is free software; you can redistribute it and/or |
10 | * modify it under the terms of the GNU Lesser General Public License | 12 | * modify it under the terms of the GNU Lesser General Public License |
@@ -67,6 +69,8 @@ void format(void) | |||
67 | { | 69 | { |
68 | fprintf(stderr, | 70 | fprintf(stderr, |
69 | "Usage: scripts/sign-file [-dp] <hash algo> <key> <x509> <module> [<dest>]\n"); | 71 | "Usage: scripts/sign-file [-dp] <hash algo> <key> <x509> <module> [<dest>]\n"); |
72 | fprintf(stderr, | ||
73 | " scripts/sign-file -s <raw sig> <hash algo> <x509> <module> [<dest>]\n"); | ||
70 | exit(2); | 74 | exit(2); |
71 | } | 75 | } |
72 | 76 | ||
@@ -126,26 +130,84 @@ static int pem_pw_cb(char *buf, int len, int w, void *v) | |||
126 | return pwlen; | 130 | return pwlen; |
127 | } | 131 | } |
128 | 132 | ||
133 | static EVP_PKEY *read_private_key(const char *private_key_name) | ||
134 | { | ||
135 | EVP_PKEY *private_key; | ||
136 | |||
137 | if (!strncmp(private_key_name, "pkcs11:", 7)) { | ||
138 | ENGINE *e; | ||
139 | |||
140 | ENGINE_load_builtin_engines(); | ||
141 | drain_openssl_errors(); | ||
142 | e = ENGINE_by_id("pkcs11"); | ||
143 | ERR(!e, "Load PKCS#11 ENGINE"); | ||
144 | if (ENGINE_init(e)) | ||
145 | drain_openssl_errors(); | ||
146 | else | ||
147 | ERR(1, "ENGINE_init"); | ||
148 | if (key_pass) | ||
149 | ERR(!ENGINE_ctrl_cmd_string(e, "PIN", key_pass, 0), | ||
150 | "Set PKCS#11 PIN"); | ||
151 | private_key = ENGINE_load_private_key(e, private_key_name, | ||
152 | NULL, NULL); | ||
153 | ERR(!private_key, "%s", private_key_name); | ||
154 | } else { | ||
155 | BIO *b; | ||
156 | |||
157 | b = BIO_new_file(private_key_name, "rb"); | ||
158 | ERR(!b, "%s", private_key_name); | ||
159 | private_key = PEM_read_bio_PrivateKey(b, NULL, pem_pw_cb, | ||
160 | NULL); | ||
161 | ERR(!private_key, "%s", private_key_name); | ||
162 | BIO_free(b); | ||
163 | } | ||
164 | |||
165 | return private_key; | ||
166 | } | ||
167 | |||
168 | static X509 *read_x509(const char *x509_name) | ||
169 | { | ||
170 | X509 *x509; | ||
171 | BIO *b; | ||
172 | |||
173 | b = BIO_new_file(x509_name, "rb"); | ||
174 | ERR(!b, "%s", x509_name); | ||
175 | x509 = d2i_X509_bio(b, NULL); /* Binary encoded X.509 */ | ||
176 | if (!x509) { | ||
177 | ERR(BIO_reset(b) != 1, "%s", x509_name); | ||
178 | x509 = PEM_read_bio_X509(b, NULL, NULL, | ||
179 | NULL); /* PEM encoded X.509 */ | ||
180 | if (x509) | ||
181 | drain_openssl_errors(); | ||
182 | } | ||
183 | BIO_free(b); | ||
184 | ERR(!x509, "%s", x509_name); | ||
185 | |||
186 | return x509; | ||
187 | } | ||
188 | |||
129 | int main(int argc, char **argv) | 189 | int main(int argc, char **argv) |
130 | { | 190 | { |
131 | struct module_signature sig_info = { .id_type = PKEY_ID_PKCS7 }; | 191 | struct module_signature sig_info = { .id_type = PKEY_ID_PKCS7 }; |
132 | char *hash_algo = NULL; | 192 | char *hash_algo = NULL; |
133 | char *private_key_name, *x509_name, *module_name, *dest_name; | 193 | char *private_key_name = NULL, *raw_sig_name = NULL; |
194 | char *x509_name, *module_name, *dest_name; | ||
134 | bool save_sig = false, replace_orig; | 195 | bool save_sig = false, replace_orig; |
135 | bool sign_only = false; | 196 | bool sign_only = false; |
197 | bool raw_sig = false; | ||
136 | unsigned char buf[4096]; | 198 | unsigned char buf[4096]; |
137 | unsigned long module_size, sig_size; | 199 | unsigned long module_size, sig_size; |
138 | unsigned int use_signed_attrs; | 200 | unsigned int use_signed_attrs; |
139 | const EVP_MD *digest_algo; | 201 | const EVP_MD *digest_algo; |
140 | EVP_PKEY *private_key; | 202 | EVP_PKEY *private_key; |
141 | #ifndef USE_PKCS7 | 203 | #ifndef USE_PKCS7 |
142 | CMS_ContentInfo *cms; | 204 | CMS_ContentInfo *cms = NULL; |
143 | unsigned int use_keyid = 0; | 205 | unsigned int use_keyid = 0; |
144 | #else | 206 | #else |
145 | PKCS7 *pkcs7; | 207 | PKCS7 *pkcs7 = NULL; |
146 | #endif | 208 | #endif |
147 | X509 *x509; | 209 | X509 *x509; |
148 | BIO *b, *bd = NULL, *bm; | 210 | BIO *bd, *bm; |
149 | int opt, n; | 211 | int opt, n; |
150 | OpenSSL_add_all_algorithms(); | 212 | OpenSSL_add_all_algorithms(); |
151 | ERR_load_crypto_strings(); | 213 | ERR_load_crypto_strings(); |
@@ -160,8 +222,9 @@ int main(int argc, char **argv) | |||
160 | #endif | 222 | #endif |
161 | 223 | ||
162 | do { | 224 | do { |
163 | opt = getopt(argc, argv, "dpk"); | 225 | opt = getopt(argc, argv, "sdpk"); |
164 | switch (opt) { | 226 | switch (opt) { |
227 | case 's': raw_sig = true; break; | ||
165 | case 'p': save_sig = true; break; | 228 | case 'p': save_sig = true; break; |
166 | case 'd': sign_only = true; save_sig = true; break; | 229 | case 'd': sign_only = true; save_sig = true; break; |
167 | #ifndef USE_PKCS7 | 230 | #ifndef USE_PKCS7 |
@@ -177,8 +240,13 @@ int main(int argc, char **argv) | |||
177 | if (argc < 4 || argc > 5) | 240 | if (argc < 4 || argc > 5) |
178 | format(); | 241 | format(); |
179 | 242 | ||
180 | hash_algo = argv[0]; | 243 | if (raw_sig) { |
181 | private_key_name = argv[1]; | 244 | raw_sig_name = argv[0]; |
245 | hash_algo = argv[1]; | ||
246 | } else { | ||
247 | hash_algo = argv[0]; | ||
248 | private_key_name = argv[1]; | ||
249 | } | ||
182 | x509_name = argv[2]; | 250 | x509_name = argv[2]; |
183 | module_name = argv[3]; | 251 | module_name = argv[3]; |
184 | if (argc == 5) { | 252 | if (argc == 5) { |
@@ -198,101 +266,74 @@ int main(int argc, char **argv) | |||
198 | } | 266 | } |
199 | #endif | 267 | #endif |
200 | 268 | ||
201 | /* Read the private key and the X.509 cert the PKCS#7 message | 269 | /* Open the module file */ |
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"); | 270 | bm = BIO_new_file(module_name, "rb"); |
255 | ERR(!bm, "%s", module_name); | 271 | ERR(!bm, "%s", module_name); |
256 | 272 | ||
273 | if (!raw_sig) { | ||
274 | /* Read the private key and the X.509 cert the PKCS#7 message | ||
275 | * will point to. | ||
276 | */ | ||
277 | private_key = read_private_key(private_key_name); | ||
278 | x509 = read_x509(x509_name); | ||
279 | |||
280 | /* Digest the module data. */ | ||
281 | OpenSSL_add_all_digests(); | ||
282 | display_openssl_errors(__LINE__); | ||
283 | digest_algo = EVP_get_digestbyname(hash_algo); | ||
284 | ERR(!digest_algo, "EVP_get_digestbyname"); | ||
285 | |||
257 | #ifndef USE_PKCS7 | 286 | #ifndef USE_PKCS7 |
258 | /* Load the signature message from the digest buffer. */ | 287 | /* Load the signature message from the digest buffer. */ |
259 | cms = CMS_sign(NULL, NULL, NULL, NULL, | 288 | cms = CMS_sign(NULL, NULL, NULL, NULL, |
260 | CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | CMS_DETACHED | CMS_STREAM); | 289 | CMS_NOCERTS | CMS_PARTIAL | CMS_BINARY | |
261 | ERR(!cms, "CMS_sign"); | 290 | CMS_DETACHED | CMS_STREAM); |
262 | 291 | ERR(!cms, "CMS_sign"); | |
263 | ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo, | 292 | |
264 | CMS_NOCERTS | CMS_BINARY | CMS_NOSMIMECAP | | 293 | ERR(!CMS_add1_signer(cms, x509, private_key, digest_algo, |
265 | use_keyid | use_signed_attrs), | 294 | CMS_NOCERTS | CMS_BINARY | |
266 | "CMS_add1_signer"); | 295 | CMS_NOSMIMECAP | use_keyid | |
267 | ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) < 0, | 296 | use_signed_attrs), |
268 | "CMS_final"); | 297 | "CMS_add1_signer"); |
298 | ERR(CMS_final(cms, bm, NULL, CMS_NOCERTS | CMS_BINARY) < 0, | ||
299 | "CMS_final"); | ||
269 | 300 | ||
270 | #else | 301 | #else |
271 | pkcs7 = PKCS7_sign(x509, private_key, NULL, bm, | 302 | pkcs7 = PKCS7_sign(x509, private_key, NULL, bm, |
272 | PKCS7_NOCERTS | PKCS7_BINARY | | 303 | PKCS7_NOCERTS | PKCS7_BINARY | |
273 | PKCS7_DETACHED | use_signed_attrs); | 304 | PKCS7_DETACHED | use_signed_attrs); |
274 | ERR(!pkcs7, "PKCS7_sign"); | 305 | ERR(!pkcs7, "PKCS7_sign"); |
275 | #endif | 306 | #endif |
276 | 307 | ||
277 | if (save_sig) { | 308 | if (save_sig) { |
278 | char *sig_file_name; | 309 | char *sig_file_name; |
310 | BIO *b; | ||
279 | 311 | ||
280 | ERR(asprintf(&sig_file_name, "%s.p7s", module_name) < 0, | 312 | ERR(asprintf(&sig_file_name, "%s.p7s", module_name) < 0, |
281 | "asprintf"); | 313 | "asprintf"); |
282 | b = BIO_new_file(sig_file_name, "wb"); | 314 | b = BIO_new_file(sig_file_name, "wb"); |
283 | ERR(!b, "%s", sig_file_name); | 315 | ERR(!b, "%s", sig_file_name); |
284 | #ifndef USE_PKCS7 | 316 | #ifndef USE_PKCS7 |
285 | ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) < 0, | 317 | ERR(i2d_CMS_bio_stream(b, cms, NULL, 0) < 0, |
286 | "%s", sig_file_name); | 318 | "%s", sig_file_name); |
287 | #else | 319 | #else |
288 | ERR(i2d_PKCS7_bio(b, pkcs7) < 0, | 320 | ERR(i2d_PKCS7_bio(b, pkcs7) < 0, |
289 | "%s", sig_file_name); | 321 | "%s", sig_file_name); |
290 | #endif | 322 | #endif |
291 | BIO_free(b); | 323 | BIO_free(b); |
324 | } | ||
325 | |||
326 | if (sign_only) { | ||
327 | BIO_free(bm); | ||
328 | return 0; | ||
329 | } | ||
292 | } | 330 | } |
293 | 331 | ||
294 | if (sign_only) | 332 | /* Open the destination file now so that we can shovel the module data |
295 | return 0; | 333 | * across as we read it. |
334 | */ | ||
335 | bd = BIO_new_file(dest_name, "wb"); | ||
336 | ERR(!bd, "%s", dest_name); | ||
296 | 337 | ||
297 | /* Append the marker and the PKCS#7 message to the destination file */ | 338 | /* Append the marker and the PKCS#7 message to the destination file */ |
298 | ERR(BIO_reset(bm) < 0, "%s", module_name); | 339 | ERR(BIO_reset(bm) < 0, "%s", module_name); |
@@ -300,14 +341,29 @@ int main(int argc, char **argv) | |||
300 | n > 0) { | 341 | n > 0) { |
301 | ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name); | 342 | ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name); |
302 | } | 343 | } |
344 | BIO_free(bm); | ||
303 | ERR(n < 0, "%s", module_name); | 345 | ERR(n < 0, "%s", module_name); |
304 | module_size = BIO_number_written(bd); | 346 | module_size = BIO_number_written(bd); |
305 | 347 | ||
348 | if (!raw_sig) { | ||
306 | #ifndef USE_PKCS7 | 349 | #ifndef USE_PKCS7 |
307 | ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) < 0, "%s", dest_name); | 350 | ERR(i2d_CMS_bio_stream(bd, cms, NULL, 0) < 0, "%s", dest_name); |
308 | #else | 351 | #else |
309 | ERR(i2d_PKCS7_bio(bd, pkcs7) < 0, "%s", dest_name); | 352 | ERR(i2d_PKCS7_bio(bd, pkcs7) < 0, "%s", dest_name); |
310 | #endif | 353 | #endif |
354 | } else { | ||
355 | BIO *b; | ||
356 | |||
357 | /* Read the raw signature file and write the data to the | ||
358 | * destination file | ||
359 | */ | ||
360 | b = BIO_new_file(raw_sig_name, "rb"); | ||
361 | ERR(!b, "%s", raw_sig_name); | ||
362 | while ((n = BIO_read(b, buf, sizeof(buf))), n > 0) | ||
363 | ERR(BIO_write(bd, buf, n) < 0, "%s", dest_name); | ||
364 | BIO_free(b); | ||
365 | } | ||
366 | |||
311 | sig_size = BIO_number_written(bd) - module_size; | 367 | sig_size = BIO_number_written(bd) - module_size; |
312 | sig_info.sig_len = htonl(sig_size); | 368 | sig_info.sig_len = htonl(sig_size); |
313 | ERR(BIO_write(bd, &sig_info, sizeof(sig_info)) < 0, "%s", dest_name); | 369 | ERR(BIO_write(bd, &sig_info, sizeof(sig_info)) < 0, "%s", dest_name); |