]>
Commit | Line | Data |
---|---|---|
fab430be AT |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Copyright 2018 Linaro Limited | |
4 | * Author: AKASHI Takahiro | |
5 | */ | |
6 | ||
7 | #include <getopt.h> | |
16abff24 | 8 | #include <pe.h> |
fab430be | 9 | #include <stdbool.h> |
9e63786e | 10 | #include <stdint.h> |
fab430be AT |
11 | #include <stdio.h> |
12 | #include <stdlib.h> | |
13 | #include <string.h> | |
14 | #include <linux/types.h> | |
322c813f | 15 | |
fab430be AT |
16 | #include <sys/stat.h> |
17 | #include <sys/types.h> | |
d9612f44 | 18 | #include <uuid/uuid.h> |
fab430be | 19 | |
16abff24 AT |
20 | #include <gnutls/gnutls.h> |
21 | #include <gnutls/pkcs7.h> | |
22 | #include <gnutls/abstract.h> | |
fab430be | 23 | |
16abff24 | 24 | #include "eficapsule.h" |
fab430be AT |
25 | |
26 | static const char *tool_name = "mkeficapsule"; | |
27 | ||
28 | efi_guid_t efi_guid_fm_capsule = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; | |
16abff24 AT |
29 | efi_guid_t efi_guid_cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID; |
30 | ||
6984077d | 31 | static const char *opts_short = "g:i:I:v:p:c:m:o:dhARD"; |
6da9271a SG |
32 | |
33 | enum { | |
34 | CAPSULE_NORMAL_BLOB = 0, | |
35 | CAPSULE_ACCEPT, | |
36 | CAPSULE_REVERT, | |
37 | } capsule_type; | |
fab430be AT |
38 | |
39 | static struct option options[] = { | |
d9612f44 | 40 | {"guid", required_argument, NULL, 'g'}, |
fab430be AT |
41 | {"index", required_argument, NULL, 'i'}, |
42 | {"instance", required_argument, NULL, 'I'}, | |
000806f7 | 43 | {"fw-version", required_argument, NULL, 'v'}, |
16abff24 AT |
44 | {"private-key", required_argument, NULL, 'p'}, |
45 | {"certificate", required_argument, NULL, 'c'}, | |
46 | {"monotonic-count", required_argument, NULL, 'm'}, | |
47 | {"dump-sig", no_argument, NULL, 'd'}, | |
6da9271a SG |
48 | {"fw-accept", no_argument, NULL, 'A'}, |
49 | {"fw-revert", no_argument, NULL, 'R'}, | |
f65ee99b | 50 | {"capoemflag", required_argument, NULL, 'o'}, |
6984077d | 51 | {"dump-capsule", no_argument, NULL, 'D'}, |
fab430be AT |
52 | {"help", no_argument, NULL, 'h'}, |
53 | {NULL, 0, NULL, 0}, | |
54 | }; | |
55 | ||
56 | static void print_usage(void) | |
57 | { | |
d9612f44 | 58 | fprintf(stderr, "Usage: %s [options] <image blob> <output file>\n" |
9e63786e AT |
59 | "Options:\n" |
60 | ||
d9612f44 | 61 | "\t-g, --guid <guid string> guid for image blob type\n" |
9e63786e AT |
62 | "\t-i, --index <index> update image index\n" |
63 | "\t-I, --instance <instance> update hardware instance\n" | |
000806f7 | 64 | "\t-v, --fw-version <version> firmware version\n" |
16abff24 AT |
65 | "\t-p, --private-key <privkey file> private key file\n" |
66 | "\t-c, --certificate <cert file> signer's certificate file\n" | |
67 | "\t-m, --monotonic-count <count> monotonic count\n" | |
68 | "\t-d, --dump_sig dump signature (*.p7)\n" | |
6da9271a SG |
69 | "\t-A, --fw-accept firmware accept capsule, requires GUID, no image blob\n" |
70 | "\t-R, --fw-revert firmware revert capsule, takes no GUID, no image blob\n" | |
f65ee99b | 71 | "\t-o, --capoemflag Capsule OEM Flag, an integer between 0x0000 and 0xffff\n" |
6984077d | 72 | "\t-D, --dump-capsule dump the contents of the capsule headers\n" |
9e63786e AT |
73 | "\t-h, --help print a help message\n", |
74 | tool_name); | |
fab430be AT |
75 | } |
76 | ||
16abff24 AT |
77 | /** |
78 | * auth_context - authentication context | |
79 | * @key_file: Path to a private key file | |
80 | * @cert_file: Path to a certificate file | |
81 | * @image_data: Pointer to firmware data | |
82 | * @image_size: Size of firmware data | |
83 | * @auth: Authentication header | |
84 | * @sig_data: Signature data | |
85 | * @sig_size: Size of signature data | |
86 | * | |
87 | * Data structure used in create_auth_data(). @key_file through | |
88 | * @image_size are input parameters. @auth, @sig_data and @sig_size | |
89 | * are filled in by create_auth_data(). | |
90 | */ | |
91 | struct auth_context { | |
92 | char *key_file; | |
93 | char *cert_file; | |
94 | uint8_t *image_data; | |
95 | size_t image_size; | |
96 | struct efi_firmware_image_authentication auth; | |
97 | uint8_t *sig_data; | |
98 | size_t sig_size; | |
99 | }; | |
100 | ||
101 | static int dump_sig; | |
102 | ||
9e63786e AT |
103 | /** |
104 | * read_bin_file - read a firmware binary file | |
105 | * @bin: Path to a firmware binary file | |
106 | * @data: Pointer to pointer of allocated buffer | |
107 | * @bin_size: Size of allocated buffer | |
108 | * | |
109 | * Read out a content of binary, @bin, into @data. | |
110 | * A caller should free @data. | |
111 | * | |
112 | * Return: | |
113 | * * 0 - on success | |
114 | * * -1 - on failure | |
115 | */ | |
16abff24 | 116 | static int read_bin_file(char *bin, uint8_t **data, off_t *bin_size) |
fab430be | 117 | { |
9e63786e | 118 | FILE *g; |
fab430be | 119 | struct stat bin_stat; |
9e63786e | 120 | void *buf; |
fab430be | 121 | size_t size; |
9e63786e | 122 | int ret = 0; |
fab430be AT |
123 | |
124 | g = fopen(bin, "r"); | |
125 | if (!g) { | |
df1ce60f | 126 | fprintf(stderr, "cannot open %s\n", bin); |
fab430be AT |
127 | return -1; |
128 | } | |
129 | if (stat(bin, &bin_stat) < 0) { | |
df1ce60f | 130 | fprintf(stderr, "cannot determine the size of %s\n", bin); |
9e63786e AT |
131 | ret = -1; |
132 | goto err; | |
133 | } | |
134 | if (bin_stat.st_size > SIZE_MAX) { | |
135 | fprintf(stderr, "file size is too large for malloc: %s\n", bin); | |
136 | ret = -1; | |
137 | goto err; | |
fab430be | 138 | } |
9e63786e AT |
139 | buf = malloc(bin_stat.st_size); |
140 | if (!buf) { | |
df1ce60f AT |
141 | fprintf(stderr, "cannot allocate memory: %zx\n", |
142 | (size_t)bin_stat.st_size); | |
9e63786e AT |
143 | ret = -1; |
144 | goto err; | |
145 | } | |
146 | ||
147 | size = fread(buf, 1, bin_stat.st_size, g); | |
148 | if (size < bin_stat.st_size) { | |
149 | fprintf(stderr, "read failed (%zx)\n", size); | |
150 | ret = -1; | |
151 | goto err; | |
fab430be | 152 | } |
9e63786e AT |
153 | |
154 | *data = buf; | |
155 | *bin_size = bin_stat.st_size; | |
156 | err: | |
157 | fclose(g); | |
158 | ||
159 | return ret; | |
160 | } | |
161 | ||
162 | /** | |
163 | * write_capsule_file - write a capsule file | |
164 | * @bin: FILE stream | |
165 | * @data: Pointer to data | |
166 | * @bin_size: Size of data | |
167 | * | |
168 | * Write out data, @data, with the size @bin_size. | |
169 | * | |
170 | * Return: | |
171 | * * 0 - on success | |
172 | * * -1 - on failure | |
173 | */ | |
174 | static int write_capsule_file(FILE *f, void *data, size_t size, const char *msg) | |
175 | { | |
176 | size_t size_written; | |
177 | ||
178 | size_written = fwrite(data, 1, size, f); | |
179 | if (size_written < size) { | |
180 | fprintf(stderr, "%s: write failed (%zx != %zx)\n", msg, | |
181 | size_written, size); | |
182 | return -1; | |
183 | } | |
184 | ||
185 | return 0; | |
186 | } | |
187 | ||
16abff24 AT |
188 | /** |
189 | * create_auth_data - compose authentication data in capsule | |
190 | * @auth_context: Pointer to authentication context | |
191 | * | |
192 | * Fill up an authentication header (.auth) and signature data (.sig_data) | |
193 | * in @auth_context, using library functions from openssl. | |
194 | * All the parameters in @auth_context must be filled in by a caller. | |
195 | * | |
196 | * Return: | |
197 | * * 0 - on success | |
198 | * * -1 - on failure | |
199 | */ | |
200 | static int create_auth_data(struct auth_context *ctx) | |
201 | { | |
202 | gnutls_datum_t cert; | |
203 | gnutls_datum_t key; | |
204 | off_t file_size; | |
205 | gnutls_privkey_t pkey; | |
206 | gnutls_x509_crt_t x509; | |
207 | gnutls_pkcs7_t pkcs7; | |
208 | gnutls_datum_t data; | |
209 | gnutls_datum_t signature; | |
210 | int ret; | |
211 | ||
212 | ret = read_bin_file(ctx->cert_file, &cert.data, &file_size); | |
213 | if (ret < 0) | |
214 | return -1; | |
215 | if (file_size > UINT_MAX) | |
216 | return -1; | |
217 | cert.size = file_size; | |
218 | ||
219 | ret = read_bin_file(ctx->key_file, &key.data, &file_size); | |
16abff24 AT |
220 | if (ret < 0) |
221 | return -1; | |
222 | if (file_size > UINT_MAX) | |
223 | return -1; | |
224 | key.size = file_size; | |
225 | ||
226 | /* | |
227 | * For debugging, | |
228 | * gnutls_global_set_time_function(mytime); | |
229 | * gnutls_global_set_log_function(tls_log_func); | |
230 | * gnutls_global_set_log_level(6); | |
231 | */ | |
232 | ||
233 | ret = gnutls_privkey_init(&pkey); | |
234 | if (ret < 0) { | |
235 | fprintf(stderr, "error in gnutls_privkey_init(): %s\n", | |
236 | gnutls_strerror(ret)); | |
237 | return -1; | |
238 | } | |
239 | ||
240 | ret = gnutls_x509_crt_init(&x509); | |
241 | if (ret < 0) { | |
242 | fprintf(stderr, "error in gnutls_x509_crt_init(): %s\n", | |
243 | gnutls_strerror(ret)); | |
244 | return -1; | |
245 | } | |
246 | ||
247 | /* load a private key */ | |
248 | ret = gnutls_privkey_import_x509_raw(pkey, &key, GNUTLS_X509_FMT_PEM, | |
249 | 0, 0); | |
250 | if (ret < 0) { | |
251 | fprintf(stderr, | |
252 | "error in gnutls_privkey_import_x509_raw(): %s\n", | |
253 | gnutls_strerror(ret)); | |
254 | return -1; | |
255 | } | |
256 | ||
257 | /* load x509 certificate */ | |
258 | ret = gnutls_x509_crt_import(x509, &cert, GNUTLS_X509_FMT_PEM); | |
259 | if (ret < 0) { | |
260 | fprintf(stderr, "error in gnutls_x509_crt_import(): %s\n", | |
261 | gnutls_strerror(ret)); | |
262 | return -1; | |
263 | } | |
264 | ||
265 | /* generate a PKCS #7 structure */ | |
266 | ret = gnutls_pkcs7_init(&pkcs7); | |
267 | if (ret < 0) { | |
268 | fprintf(stderr, "error in gnutls_pkcs7_init(): %s\n", | |
269 | gnutls_strerror(ret)); | |
270 | return -1; | |
271 | } | |
272 | ||
273 | /* sign */ | |
274 | /* | |
275 | * Data should have | |
276 | * * firmware image | |
277 | * * monotonic count | |
278 | * in this order! | |
279 | * See EDK2's FmpAuthenticatedHandlerRsa2048Sha256() | |
280 | */ | |
281 | data.size = ctx->image_size + sizeof(ctx->auth.monotonic_count); | |
282 | data.data = malloc(data.size); | |
283 | if (!data.data) { | |
284 | fprintf(stderr, "allocating memory (0x%x) failed\n", data.size); | |
285 | return -1; | |
286 | } | |
287 | memcpy(data.data, ctx->image_data, ctx->image_size); | |
288 | memcpy(data.data + ctx->image_size, &ctx->auth.monotonic_count, | |
289 | sizeof(ctx->auth.monotonic_count)); | |
290 | ||
291 | ret = gnutls_pkcs7_sign(pkcs7, x509, pkey, &data, NULL, NULL, | |
292 | GNUTLS_DIG_SHA256, | |
293 | /* GNUTLS_PKCS7_EMBED_DATA? */ | |
294 | GNUTLS_PKCS7_INCLUDE_CERT | | |
295 | GNUTLS_PKCS7_INCLUDE_TIME); | |
296 | if (ret < 0) { | |
297 | fprintf(stderr, "error in gnutls_pkcs7)sign(): %s\n", | |
298 | gnutls_strerror(ret)); | |
299 | return -1; | |
300 | } | |
301 | ||
302 | /* export */ | |
303 | ret = gnutls_pkcs7_export2(pkcs7, GNUTLS_X509_FMT_DER, &signature); | |
304 | if (ret < 0) { | |
305 | fprintf(stderr, "error in gnutls_pkcs7_export2: %s\n", | |
306 | gnutls_strerror(ret)); | |
307 | return -1; | |
308 | } | |
309 | ctx->sig_data = signature.data; | |
310 | ctx->sig_size = signature.size; | |
311 | ||
312 | /* fill auth_info */ | |
313 | ctx->auth.auth_info.hdr.dwLength = sizeof(ctx->auth.auth_info) | |
314 | + ctx->sig_size; | |
315 | ctx->auth.auth_info.hdr.wRevision = WIN_CERT_REVISION_2_0; | |
316 | ctx->auth.auth_info.hdr.wCertificateType = WIN_CERT_TYPE_EFI_GUID; | |
317 | memcpy(&ctx->auth.auth_info.cert_type, &efi_guid_cert_type_pkcs7, | |
318 | sizeof(efi_guid_cert_type_pkcs7)); | |
319 | ||
320 | /* | |
321 | * For better clean-ups, | |
322 | * gnutls_pkcs7_deinit(pkcs7); | |
323 | * gnutls_privkey_deinit(pkey); | |
324 | * gnutls_x509_crt_deinit(x509); | |
325 | * free(cert.data); | |
326 | * free(key.data); | |
327 | * if error | |
328 | * gnutls_free(signature.data); | |
329 | */ | |
330 | ||
331 | return 0; | |
332 | } | |
333 | ||
334 | /** | |
335 | * dump_signature - dump out a signature | |
336 | * @path: Path to a capsule file | |
337 | * @signature: Signature data | |
338 | * @sig_size: Size of signature data | |
339 | * | |
340 | * Signature data pointed to by @signature will be saved into | |
341 | * a file whose file name is @path with ".p7" suffix. | |
342 | * | |
343 | * Return: | |
344 | * * 0 - on success | |
345 | * * -1 - on failure | |
346 | */ | |
347 | static int dump_signature(const char *path, uint8_t *signature, size_t sig_size) | |
348 | { | |
349 | char *sig_path; | |
350 | FILE *f; | |
351 | size_t size; | |
352 | int ret = -1; | |
353 | ||
354 | sig_path = malloc(strlen(path) + 3 + 1); | |
355 | if (!sig_path) | |
356 | return ret; | |
357 | ||
358 | sprintf(sig_path, "%s.p7", path); | |
359 | f = fopen(sig_path, "w"); | |
360 | if (!f) | |
361 | goto err; | |
362 | ||
363 | size = fwrite(signature, 1, sig_size, f); | |
364 | if (size == sig_size) | |
365 | ret = 0; | |
366 | ||
367 | fclose(f); | |
368 | err: | |
369 | free(sig_path); | |
370 | return ret; | |
371 | } | |
372 | ||
373 | /** | |
374 | * free_sig_data - free out signature data | |
375 | * @ctx: Pointer to authentication context | |
376 | * | |
377 | * Free signature data allocated in create_auth_data(). | |
378 | */ | |
379 | static void free_sig_data(struct auth_context *ctx) | |
380 | { | |
381 | if (ctx->sig_size) | |
382 | gnutls_free(ctx->sig_data); | |
383 | } | |
384 | ||
9e63786e AT |
385 | /** |
386 | * create_fwbin - create an uefi capsule file | |
387 | * @path: Path to a created capsule file | |
388 | * @bin: Path to a firmware binary to encapsulate | |
389 | * @guid: GUID of related FMP driver | |
390 | * @index: Index number in capsule | |
391 | * @instance: Instance number in capsule | |
392 | * @mcount: Monotonic count in authentication information | |
393 | * @private_file: Path to a private key file | |
394 | * @cert_file: Path to a certificate file | |
f65ee99b | 395 | * @oemflags: Capsule OEM Flags, bits 0-15 |
9e63786e AT |
396 | * |
397 | * This function actually does the job of creating an uefi capsule file. | |
398 | * All the arguments must be supplied. | |
399 | * If either @private_file ror @cert_file is NULL, the capsule file | |
400 | * won't be signed. | |
401 | * | |
402 | * Return: | |
403 | * * 0 - on success | |
404 | * * -1 - on failure | |
405 | */ | |
406 | static int create_fwbin(char *path, char *bin, efi_guid_t *guid, | |
16abff24 | 407 | unsigned long index, unsigned long instance, |
000806f7 | 408 | struct fmp_payload_header_params *fmp_ph_params, |
f65ee99b SG |
409 | uint64_t mcount, char *privkey_file, char *cert_file, |
410 | uint16_t oemflags) | |
9e63786e AT |
411 | { |
412 | struct efi_capsule_header header; | |
413 | struct efi_firmware_management_capsule_header capsule; | |
414 | struct efi_firmware_management_capsule_image_header image; | |
16abff24 | 415 | struct auth_context auth_context; |
9e63786e | 416 | FILE *f; |
000806f7 | 417 | uint8_t *data, *new_data, *buf; |
9e63786e | 418 | off_t bin_size; |
16abff24 | 419 | uint64_t offset; |
9e63786e | 420 | int ret; |
000806f7 | 421 | struct fmp_payload_header payload_header; |
9e63786e AT |
422 | |
423 | #ifdef DEBUG | |
16abff24 AT |
424 | fprintf(stderr, "For output: %s\n", path); |
425 | fprintf(stderr, "\tbin: %s\n\ttype: %pUl\n", bin, guid); | |
426 | fprintf(stderr, "\tindex: %lu\n\tinstance: %lu\n", index, instance); | |
9e63786e | 427 | #endif |
16abff24 | 428 | auth_context.sig_size = 0; |
9e63786e AT |
429 | f = NULL; |
430 | data = NULL; | |
000806f7 | 431 | new_data = NULL; |
9e63786e AT |
432 | ret = -1; |
433 | ||
434 | /* | |
435 | * read a firmware binary | |
436 | */ | |
437 | if (read_bin_file(bin, &data, &bin_size)) | |
438 | goto err; | |
439 | ||
000806f7 MK |
440 | buf = data; |
441 | ||
442 | /* insert fmp payload header right before the payload */ | |
443 | if (fmp_ph_params->have_header) { | |
444 | new_data = malloc(bin_size + sizeof(payload_header)); | |
445 | if (!new_data) | |
446 | goto err; | |
447 | ||
448 | payload_header.signature = FMP_PAYLOAD_HDR_SIGNATURE; | |
449 | payload_header.header_size = sizeof(payload_header); | |
450 | payload_header.fw_version = fmp_ph_params->fw_version; | |
451 | payload_header.lowest_supported_version = 0; /* not used */ | |
452 | memcpy(new_data, &payload_header, sizeof(payload_header)); | |
453 | memcpy(new_data + sizeof(payload_header), data, bin_size); | |
454 | buf = new_data; | |
455 | bin_size += sizeof(payload_header); | |
456 | } | |
457 | ||
16abff24 AT |
458 | /* first, calculate signature to determine its size */ |
459 | if (privkey_file && cert_file) { | |
460 | auth_context.key_file = privkey_file; | |
461 | auth_context.cert_file = cert_file; | |
462 | auth_context.auth.monotonic_count = mcount; | |
000806f7 | 463 | auth_context.image_data = buf; |
16abff24 AT |
464 | auth_context.image_size = bin_size; |
465 | ||
466 | if (create_auth_data(&auth_context)) { | |
467 | fprintf(stderr, "Signing firmware image failed\n"); | |
468 | goto err; | |
469 | } | |
470 | ||
471 | if (dump_sig && | |
472 | dump_signature(path, auth_context.sig_data, | |
473 | auth_context.sig_size)) { | |
474 | fprintf(stderr, "Creating signature file failed\n"); | |
475 | goto err; | |
476 | } | |
477 | } | |
478 | ||
9e63786e AT |
479 | /* |
480 | * write a capsule file | |
481 | */ | |
fab430be AT |
482 | f = fopen(path, "w"); |
483 | if (!f) { | |
df1ce60f | 484 | fprintf(stderr, "cannot open %s\n", path); |
9e63786e | 485 | goto err; |
fab430be | 486 | } |
9e63786e AT |
487 | |
488 | /* | |
489 | * capsule file header | |
490 | */ | |
fab430be AT |
491 | header.capsule_guid = efi_guid_fm_capsule; |
492 | header.header_size = sizeof(header); | |
450596f2 AT |
493 | /* TODO: The current implementation ignores flags */ |
494 | header.flags = CAPSULE_FLAGS_PERSIST_ACROSS_RESET; | |
f65ee99b SG |
495 | if (oemflags) |
496 | header.flags |= oemflags; | |
fab430be | 497 | header.capsule_image_size = sizeof(header) |
16abff24 | 498 | + sizeof(capsule) + sizeof(uint64_t) |
fab430be | 499 | + sizeof(image) |
9e63786e | 500 | + bin_size; |
16abff24 AT |
501 | if (auth_context.sig_size) |
502 | header.capsule_image_size += sizeof(auth_context.auth) | |
503 | + auth_context.sig_size; | |
9e63786e AT |
504 | if (write_capsule_file(f, &header, sizeof(header), |
505 | "Capsule header")) | |
506 | goto err; | |
fab430be | 507 | |
9e63786e AT |
508 | /* |
509 | * firmware capsule header | |
510 | * This capsule has only one firmware capsule image. | |
511 | */ | |
fab430be AT |
512 | capsule.version = 0x00000001; |
513 | capsule.embedded_driver_count = 0; | |
514 | capsule.payload_item_count = 1; | |
9e63786e AT |
515 | if (write_capsule_file(f, &capsule, sizeof(capsule), |
516 | "Firmware capsule header")) | |
517 | goto err; | |
518 | ||
16abff24 | 519 | offset = sizeof(capsule) + sizeof(uint64_t); |
9e63786e AT |
520 | if (write_capsule_file(f, &offset, sizeof(offset), |
521 | "Offset to capsule image")) | |
522 | goto err; | |
fab430be | 523 | |
9e63786e AT |
524 | /* |
525 | * firmware capsule image header | |
526 | */ | |
fab430be AT |
527 | image.version = 0x00000003; |
528 | memcpy(&image.update_image_type_id, guid, sizeof(*guid)); | |
529 | image.update_image_index = index; | |
f7cd8b7b AT |
530 | image.reserved[0] = 0; |
531 | image.reserved[1] = 0; | |
532 | image.reserved[2] = 0; | |
9e63786e | 533 | image.update_image_size = bin_size; |
16abff24 AT |
534 | if (auth_context.sig_size) |
535 | image.update_image_size += sizeof(auth_context.auth) | |
536 | + auth_context.sig_size; | |
fab430be AT |
537 | image.update_vendor_code_size = 0; /* none */ |
538 | image.update_hardware_instance = instance; | |
539 | image.image_capsule_support = 0; | |
16abff24 AT |
540 | if (auth_context.sig_size) |
541 | image.image_capsule_support |= CAPSULE_SUPPORT_AUTHENTICATION; | |
9e63786e AT |
542 | if (write_capsule_file(f, &image, sizeof(image), |
543 | "Firmware capsule image header")) | |
544 | goto err; | |
fab430be | 545 | |
16abff24 AT |
546 | /* |
547 | * signature | |
548 | */ | |
549 | if (auth_context.sig_size) { | |
550 | if (write_capsule_file(f, &auth_context.auth, | |
551 | sizeof(auth_context.auth), | |
552 | "Authentication header")) | |
553 | goto err; | |
554 | ||
555 | if (write_capsule_file(f, auth_context.sig_data, | |
556 | auth_context.sig_size, "Signature")) | |
557 | goto err; | |
558 | } | |
559 | ||
9e63786e AT |
560 | /* |
561 | * firmware binary | |
562 | */ | |
000806f7 | 563 | if (write_capsule_file(f, buf, bin_size, "Firmware binary")) |
9e63786e | 564 | goto err; |
fab430be | 565 | |
9e63786e AT |
566 | ret = 0; |
567 | err: | |
568 | if (f) | |
569 | fclose(f); | |
16abff24 | 570 | free_sig_data(&auth_context); |
fab430be | 571 | free(data); |
000806f7 | 572 | free(new_data); |
fab430be | 573 | |
9e63786e | 574 | return ret; |
fab430be AT |
575 | } |
576 | ||
d9612f44 AT |
577 | /** |
578 | * convert_uuid_to_guid() - convert UUID to GUID | |
579 | * @buf: UUID binary | |
580 | * | |
581 | * UUID and GUID have the same data structure, but their binary | |
582 | * formats are different due to the endianness. See lib/uuid.c. | |
583 | * Since uuid_parse() can handle only UUID, this function must | |
584 | * be called to get correct data for GUID when parsing a string. | |
585 | * | |
586 | * The correct data will be returned in @buf. | |
587 | */ | |
588 | void convert_uuid_to_guid(unsigned char *buf) | |
589 | { | |
590 | unsigned char c; | |
591 | ||
592 | c = buf[0]; | |
593 | buf[0] = buf[3]; | |
594 | buf[3] = c; | |
595 | c = buf[1]; | |
596 | buf[1] = buf[2]; | |
597 | buf[2] = c; | |
598 | ||
599 | c = buf[4]; | |
600 | buf[4] = buf[5]; | |
601 | buf[5] = c; | |
602 | ||
603 | c = buf[6]; | |
604 | buf[6] = buf[7]; | |
605 | buf[7] = c; | |
606 | } | |
607 | ||
6da9271a SG |
608 | static int create_empty_capsule(char *path, efi_guid_t *guid, bool fw_accept) |
609 | { | |
610 | struct efi_capsule_header header = { 0 }; | |
611 | FILE *f = NULL; | |
612 | int ret = -1; | |
613 | efi_guid_t fw_accept_guid = FW_ACCEPT_OS_GUID; | |
614 | efi_guid_t fw_revert_guid = FW_REVERT_OS_GUID; | |
615 | efi_guid_t capsule_guid; | |
616 | ||
617 | f = fopen(path, "w"); | |
618 | if (!f) { | |
619 | fprintf(stderr, "cannot open %s\n", path); | |
620 | goto err; | |
621 | } | |
622 | ||
623 | capsule_guid = fw_accept ? fw_accept_guid : fw_revert_guid; | |
624 | ||
625 | memcpy(&header.capsule_guid, &capsule_guid, sizeof(efi_guid_t)); | |
626 | header.header_size = sizeof(header); | |
627 | header.flags = 0; | |
628 | ||
629 | header.capsule_image_size = fw_accept ? | |
630 | sizeof(header) + sizeof(efi_guid_t) : sizeof(header); | |
631 | ||
632 | if (write_capsule_file(f, &header, sizeof(header), | |
633 | "Capsule header")) | |
634 | goto err; | |
635 | ||
636 | if (fw_accept) { | |
637 | if (write_capsule_file(f, guid, sizeof(*guid), | |
638 | "FW Accept Capsule Payload")) | |
639 | goto err; | |
640 | } | |
641 | ||
642 | ret = 0; | |
643 | ||
644 | err: | |
645 | if (f) | |
646 | fclose(f); | |
647 | ||
648 | return ret; | |
649 | } | |
650 | ||
6984077d SG |
651 | static void print_guid(void *ptr) |
652 | { | |
653 | int i; | |
654 | efi_guid_t *guid = ptr; | |
655 | const uint8_t seq[] = { | |
656 | 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, | |
657 | '-', 8, 9, '-', 10, 11, 12, 13, 14, 15 }; | |
658 | ||
659 | for (i = 0; i < ARRAY_SIZE(seq); i++) { | |
660 | if (seq[i] == '-') | |
661 | putchar(seq[i]); | |
662 | else | |
663 | printf("%02X", guid->b[seq[i]]); | |
664 | } | |
665 | ||
666 | printf("\n"); | |
667 | } | |
668 | ||
669 | static uint32_t dump_fmp_payload_header( | |
670 | struct fmp_payload_header *fmp_payload_hdr) | |
671 | { | |
672 | if (fmp_payload_hdr->signature == FMP_PAYLOAD_HDR_SIGNATURE) { | |
673 | printf("--------\n"); | |
674 | printf("FMP_PAYLOAD_HDR.SIGNATURE\t\t\t: %08X\n", | |
675 | FMP_PAYLOAD_HDR_SIGNATURE); | |
676 | printf("FMP_PAYLOAD_HDR.HEADER_SIZE\t\t\t: %08X\n", | |
677 | fmp_payload_hdr->header_size); | |
678 | printf("FMP_PAYLOAD_HDR.FW_VERSION\t\t\t: %08X\n", | |
679 | fmp_payload_hdr->fw_version); | |
680 | printf("FMP_PAYLOAD_HDR.LOWEST_SUPPORTED_VERSION\t: %08X\n", | |
681 | fmp_payload_hdr->lowest_supported_version); | |
682 | return fmp_payload_hdr->header_size; | |
683 | } | |
684 | ||
685 | return 0; | |
686 | } | |
687 | ||
688 | static void dump_capsule_auth_header( | |
689 | struct efi_firmware_image_authentication *capsule_auth_hdr) | |
690 | { | |
691 | printf("EFI_FIRMWARE_IMAGE_AUTH.MONOTONIC_COUNT\t\t: %08lX\n", | |
692 | capsule_auth_hdr->monotonic_count); | |
693 | printf("EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.dwLENGTH\t: %08X\n", | |
694 | capsule_auth_hdr->auth_info.hdr.dwLength); | |
695 | printf("EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wREVISION\t: %08X\n", | |
696 | capsule_auth_hdr->auth_info.hdr.wRevision); | |
697 | printf("EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.HDR.wCERTTYPE\t: %08X\n", | |
698 | capsule_auth_hdr->auth_info.hdr.wCertificateType); | |
699 | printf("EFI_FIRMWARE_IMAGE_AUTH.AUTH_INFO.CERT_TYPE\t: "); | |
700 | print_guid(&capsule_auth_hdr->auth_info.cert_type); | |
701 | } | |
702 | ||
703 | static void dump_fmp_capsule_image_header( | |
704 | struct efi_firmware_management_capsule_image_header *image_hdr) | |
705 | { | |
706 | void *capsule_auth_hdr; | |
707 | void *fmp_payload_hdr; | |
708 | uint64_t signature_size = 0; | |
709 | uint32_t payload_size = 0; | |
710 | uint32_t fmp_payload_hdr_size = 0; | |
711 | struct efi_firmware_image_authentication *auth_hdr; | |
712 | ||
713 | printf("--------\n"); | |
714 | printf("FMP_CAPSULE_IMAGE_HDR.VERSION\t\t\t: %08X\n", | |
715 | image_hdr->version); | |
716 | printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_TYPE_ID\t: "); | |
717 | print_guid(&image_hdr->update_image_type_id); | |
718 | printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_INDEX\t: %08X\n", | |
719 | image_hdr->update_image_index); | |
720 | printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_IMAGE_SIZE\t\t: %08X\n", | |
721 | image_hdr->update_image_size); | |
722 | printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_VENDOR_CODE_SIZE\t: %08X\n", | |
723 | image_hdr->update_vendor_code_size); | |
724 | printf("FMP_CAPSULE_IMAGE_HDR.UPDATE_HARDWARE_INSTANCE\t: %08lX\n", | |
725 | image_hdr->update_hardware_instance); | |
726 | printf("FMP_CAPSULE_IMAGE_HDR.IMAGE_CAPSULE_SUPPORT\t: %08lX\n", | |
727 | image_hdr->image_capsule_support); | |
728 | ||
729 | printf("--------\n"); | |
730 | if (image_hdr->image_capsule_support & CAPSULE_SUPPORT_AUTHENTICATION) { | |
731 | capsule_auth_hdr = (char *)image_hdr + sizeof(*image_hdr); | |
732 | dump_capsule_auth_header(capsule_auth_hdr); | |
733 | ||
734 | auth_hdr = capsule_auth_hdr; | |
735 | signature_size = sizeof(auth_hdr->monotonic_count) + | |
736 | auth_hdr->auth_info.hdr.dwLength; | |
737 | fmp_payload_hdr = (char *)capsule_auth_hdr + signature_size; | |
738 | } else { | |
739 | printf("Capsule Authentication Not Enabled\n"); | |
740 | fmp_payload_hdr = (char *)image_hdr + sizeof(*image_hdr); | |
741 | } | |
742 | ||
743 | fmp_payload_hdr_size = dump_fmp_payload_header(fmp_payload_hdr); | |
744 | ||
745 | payload_size = image_hdr->update_image_size - signature_size - | |
746 | fmp_payload_hdr_size; | |
747 | printf("--------\n"); | |
748 | printf("Payload Image Size\t\t\t\t: %08X\n", payload_size); | |
749 | } | |
750 | ||
751 | static void dump_fmp_header( | |
752 | struct efi_firmware_management_capsule_header *fmp_hdr) | |
753 | { | |
754 | int i; | |
755 | void *capsule_image_hdr; | |
756 | ||
757 | printf("EFI_FMP_HDR.VERSION\t\t\t\t: %08X\n", fmp_hdr->version); | |
758 | printf("EFI_FMP_HDR.EMBEDDED_DRIVER_COUNT\t\t: %08X\n", | |
759 | fmp_hdr->embedded_driver_count); | |
760 | printf("EFI_FMP_HDR.PAYLOAD_ITEM_COUNT\t\t\t: %08X\n", | |
761 | fmp_hdr->payload_item_count); | |
762 | ||
763 | /* | |
764 | * We currently don't support Embedded Drivers. | |
765 | * Only worry about the payload items. | |
766 | */ | |
767 | for (i = 0; i < fmp_hdr->payload_item_count; i++) { | |
768 | capsule_image_hdr = (char *)fmp_hdr + | |
769 | fmp_hdr->item_offset_list[i]; | |
770 | dump_fmp_capsule_image_header(capsule_image_hdr); | |
771 | } | |
772 | } | |
773 | ||
774 | static void dump_capsule_header(struct efi_capsule_header *capsule_hdr) | |
775 | { | |
776 | printf("EFI_CAPSULE_HDR.CAPSULE_GUID\t\t\t: "); | |
777 | print_guid((void *)&capsule_hdr->capsule_guid); | |
778 | printf("EFI_CAPSULE_HDR.HEADER_SIZE\t\t\t: %08X\n", | |
779 | capsule_hdr->header_size); | |
780 | printf("EFI_CAPSULE_HDR.FLAGS\t\t\t\t: %08X\n", capsule_hdr->flags); | |
781 | printf("EFI_CAPSULE_HDR.CAPSULE_IMAGE_SIZE\t\t: %08X\n", | |
782 | capsule_hdr->capsule_image_size); | |
783 | } | |
784 | ||
785 | static void normal_capsule_dump(void *capsule_buf) | |
786 | { | |
787 | void *fmp_hdr; | |
788 | struct efi_capsule_header *hdr = capsule_buf; | |
789 | ||
790 | dump_capsule_header(hdr); | |
791 | printf("--------\n"); | |
792 | ||
793 | fmp_hdr = (char *)capsule_buf + sizeof(*hdr); | |
794 | dump_fmp_header(fmp_hdr); | |
795 | } | |
796 | ||
797 | static void empty_capsule_dump(void *capsule_buf) | |
798 | { | |
799 | efi_guid_t *accept_image_guid; | |
800 | struct efi_capsule_header *hdr = capsule_buf; | |
801 | efi_guid_t efi_empty_accept_capsule = FW_ACCEPT_OS_GUID; | |
802 | ||
803 | dump_capsule_header(hdr); | |
804 | ||
805 | if (!memcmp(&efi_empty_accept_capsule, &hdr->capsule_guid, | |
806 | sizeof(efi_guid_t))) { | |
807 | accept_image_guid = (void *)(char *)capsule_buf + | |
808 | sizeof(struct efi_capsule_header); | |
809 | printf("--------\n"); | |
810 | printf("ACCEPT_IMAGE_GUID\t\t\t\t: "); | |
811 | print_guid(accept_image_guid); | |
812 | } | |
813 | } | |
814 | ||
815 | static void dump_capsule_contents(char *capsule_file) | |
816 | { | |
817 | int fd; | |
818 | char *ptr; | |
819 | efi_guid_t efi_fmp_guid = EFI_FIRMWARE_MANAGEMENT_CAPSULE_ID_GUID; | |
820 | efi_guid_t efi_empty_accept_capsule = FW_ACCEPT_OS_GUID; | |
821 | efi_guid_t efi_empty_revert_capsule = FW_REVERT_OS_GUID; | |
822 | struct stat sbuf; | |
823 | ||
824 | if (!capsule_file) { | |
825 | fprintf(stderr, "No capsule file provided\n"); | |
826 | exit(EXIT_FAILURE); | |
827 | } | |
828 | ||
829 | if ((fd = open(capsule_file, O_RDONLY)) < 0) { | |
830 | fprintf(stderr, "Error opening capsule file: %s\n", | |
831 | capsule_file); | |
832 | exit(EXIT_FAILURE); | |
833 | } | |
834 | ||
835 | if (fstat(fd, &sbuf) < 0) { | |
836 | fprintf(stderr, "Can't stat capsule file: %s\n", capsule_file); | |
837 | exit(EXIT_FAILURE); | |
838 | } | |
839 | ||
840 | if ((ptr = mmap(0, sbuf.st_size, PROT_READ, MAP_SHARED, fd, 0)) | |
841 | == MAP_FAILED) { | |
842 | fprintf(stderr, "Can't mmap capsule file: %s\n", capsule_file); | |
843 | exit(EXIT_FAILURE); | |
844 | } | |
845 | ||
846 | if (!memcmp(&efi_fmp_guid, ptr, sizeof(efi_guid_t))) { | |
847 | normal_capsule_dump(ptr); | |
848 | } else if (!memcmp(&efi_empty_accept_capsule, ptr, | |
849 | sizeof(efi_guid_t)) || | |
850 | !memcmp(&efi_empty_revert_capsule, ptr, | |
851 | sizeof(efi_guid_t))) { | |
852 | empty_capsule_dump(ptr); | |
853 | } else { | |
854 | fprintf(stderr, "Unable to decode the capsule file: %s\n", | |
855 | capsule_file); | |
856 | exit(EXIT_FAILURE); | |
857 | } | |
858 | } | |
859 | ||
16abff24 AT |
860 | /** |
861 | * main - main entry function of mkeficapsule | |
862 | * @argc: Number of arguments | |
863 | * @argv: Array of pointers to arguments | |
864 | * | |
865 | * Create an uefi capsule file, optionally signing it. | |
866 | * Parse all the arguments and pass them on to create_fwbin(). | |
867 | * | |
868 | * Return: | |
869 | * * 0 - on success | |
870 | * * -1 - on failure | |
fab430be AT |
871 | */ |
872 | int main(int argc, char **argv) | |
873 | { | |
fab430be | 874 | efi_guid_t *guid; |
d9612f44 | 875 | unsigned char uuid_buf[16]; |
fab430be | 876 | unsigned long index, instance; |
16abff24 | 877 | uint64_t mcount; |
f65ee99b | 878 | unsigned long oemflags; |
6984077d | 879 | bool capsule_dump; |
16abff24 | 880 | char *privkey_file, *cert_file; |
fab430be | 881 | int c, idx; |
000806f7 | 882 | struct fmp_payload_header_params fmp_ph_params = { 0 }; |
fab430be | 883 | |
fab430be AT |
884 | guid = NULL; |
885 | index = 0; | |
886 | instance = 0; | |
16abff24 AT |
887 | mcount = 0; |
888 | privkey_file = NULL; | |
889 | cert_file = NULL; | |
6984077d | 890 | capsule_dump = false; |
16abff24 | 891 | dump_sig = 0; |
6da9271a | 892 | capsule_type = CAPSULE_NORMAL_BLOB; |
f65ee99b | 893 | oemflags = 0; |
fab430be | 894 | for (;;) { |
16abff24 | 895 | c = getopt_long(argc, argv, opts_short, options, &idx); |
fab430be AT |
896 | if (c == -1) |
897 | break; | |
898 | ||
899 | switch (c) { | |
d9612f44 AT |
900 | case 'g': |
901 | if (guid) { | |
902 | fprintf(stderr, | |
903 | "Image type already specified\n"); | |
904 | exit(EXIT_FAILURE); | |
905 | } | |
906 | if (uuid_parse(optarg, uuid_buf)) { | |
907 | fprintf(stderr, "Wrong guid format\n"); | |
908 | exit(EXIT_FAILURE); | |
909 | } | |
910 | convert_uuid_to_guid(uuid_buf); | |
911 | guid = (efi_guid_t *)uuid_buf; | |
912 | break; | |
fab430be AT |
913 | case 'i': |
914 | index = strtoul(optarg, NULL, 0); | |
915 | break; | |
916 | case 'I': | |
917 | instance = strtoul(optarg, NULL, 0); | |
918 | break; | |
000806f7 MK |
919 | case 'v': |
920 | fmp_ph_params.fw_version = strtoul(optarg, NULL, 0); | |
921 | fmp_ph_params.have_header = true; | |
922 | break; | |
16abff24 AT |
923 | case 'p': |
924 | if (privkey_file) { | |
925 | fprintf(stderr, | |
926 | "Private Key already specified\n"); | |
927 | exit(EXIT_FAILURE); | |
928 | } | |
929 | privkey_file = optarg; | |
930 | break; | |
931 | case 'c': | |
932 | if (cert_file) { | |
933 | fprintf(stderr, | |
934 | "Certificate file already specified\n"); | |
935 | exit(EXIT_FAILURE); | |
936 | } | |
937 | cert_file = optarg; | |
938 | break; | |
939 | case 'm': | |
940 | mcount = strtoul(optarg, NULL, 0); | |
941 | break; | |
942 | case 'd': | |
943 | dump_sig = 1; | |
944 | break; | |
6da9271a SG |
945 | case 'A': |
946 | if (capsule_type) { | |
947 | fprintf(stderr, | |
948 | "Select either of Accept or Revert capsule generation\n"); | |
949 | exit(1); | |
950 | } | |
951 | capsule_type = CAPSULE_ACCEPT; | |
952 | break; | |
953 | case 'R': | |
954 | if (capsule_type) { | |
955 | fprintf(stderr, | |
956 | "Select either of Accept or Revert capsule generation\n"); | |
957 | exit(1); | |
958 | } | |
959 | capsule_type = CAPSULE_REVERT; | |
960 | break; | |
f65ee99b SG |
961 | case 'o': |
962 | oemflags = strtoul(optarg, NULL, 0); | |
963 | if (oemflags > 0xffff) { | |
964 | fprintf(stderr, | |
965 | "oemflags must be between 0x0 and 0xffff\n"); | |
966 | exit(1); | |
967 | } | |
968 | break; | |
6984077d SG |
969 | case 'D': |
970 | capsule_dump = true; | |
971 | break; | |
6da9271a | 972 | default: |
fab430be | 973 | print_usage(); |
16abff24 | 974 | exit(EXIT_SUCCESS); |
fab430be AT |
975 | } |
976 | } | |
977 | ||
6984077d SG |
978 | if (capsule_dump) { |
979 | if (argc != optind + 1) { | |
980 | fprintf(stderr, "Must provide the capsule file to parse\n"); | |
981 | exit(EXIT_FAILURE); | |
982 | } | |
983 | dump_capsule_contents(argv[argc - 1]); | |
984 | exit(EXIT_SUCCESS); | |
985 | } | |
986 | ||
16abff24 | 987 | /* check necessary parameters */ |
6da9271a SG |
988 | if ((capsule_type == CAPSULE_NORMAL_BLOB && |
989 | ((argc != optind + 2) || !guid || | |
990 | ((privkey_file && !cert_file) || | |
991 | (!privkey_file && cert_file)))) || | |
992 | (capsule_type != CAPSULE_NORMAL_BLOB && | |
993 | ((argc != optind + 1) || | |
994 | ((capsule_type == CAPSULE_ACCEPT) && !guid) || | |
995 | ((capsule_type == CAPSULE_REVERT) && guid)))) { | |
fab430be | 996 | print_usage(); |
d33f3181 | 997 | exit(EXIT_FAILURE); |
fab430be AT |
998 | } |
999 | ||
6da9271a SG |
1000 | if (capsule_type != CAPSULE_NORMAL_BLOB) { |
1001 | if (create_empty_capsule(argv[argc - 1], guid, | |
1002 | capsule_type == CAPSULE_ACCEPT) < 0) { | |
1003 | fprintf(stderr, "Creating empty capsule failed\n"); | |
1004 | exit(EXIT_FAILURE); | |
1005 | } | |
1006 | } else if (create_fwbin(argv[argc - 1], argv[argc - 2], guid, | |
000806f7 | 1007 | index, instance, &fmp_ph_params, mcount, privkey_file, |
f65ee99b | 1008 | cert_file, (uint16_t)oemflags) < 0) { |
df1ce60f | 1009 | fprintf(stderr, "Creating firmware capsule failed\n"); |
d33f3181 | 1010 | exit(EXIT_FAILURE); |
fab430be AT |
1011 | } |
1012 | ||
d33f3181 | 1013 | exit(EXIT_SUCCESS); |
fab430be | 1014 | } |