master
c 671 lines 19.5 KB
Raw
1 /*
2 * EIF (Enclave Image Format) related helpers
3 *
4 * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com>
5 *
6 * This work is licensed under the terms of the GNU GPL, version 2 or
7 * (at your option) any later version. See the COPYING file in the
8 * top-level directory.
9 */
10
11 #include "qemu/osdep.h"
12 #include "qemu/bswap.h"
13 #include "qapi/error.h"
14 #include "crypto/hash.h"
15 #include "crypto/x509-utils.h"
16 #include <zlib.h> /* for crc32 */
17 #include <cbor.h>
18
19 #include "hw/core/eif.h"
20
21 static const char *section_type_to_string(uint16_t type)
22 {
23 const char *str;
24 switch (type) {
25 case EIF_SECTION_INVALID:
26 str = "invalid";
27 break;
28 case EIF_SECTION_KERNEL:
29 str = "kernel";
30 break;
31 case EIF_SECTION_CMDLINE:
32 str = "cmdline";
33 break;
34 case EIF_SECTION_RAMDISK:
35 str = "ramdisk";
36 break;
37 case EIF_SECTION_SIGNATURE:
38 str = "signature";
39 break;
40 case EIF_SECTION_METADATA:
41 str = "metadata";
42 break;
43 default:
44 str = "unknown";
45 break;
46 }
47
48 return str;
49 }
50
51 static bool read_eif_header(FILE *f, EifHeader *header, uint32_t *crc,
52 Error **errp)
53 {
54 size_t got;
55 size_t header_size = sizeof(*header);
56
57 got = fread(header, 1, header_size, f);
58 if (got != header_size) {
59 error_setg(errp, "Failed to read EIF header");
60 return false;
61 }
62
63 if (memcmp(header->magic, ".eif", 4) != 0) {
64 error_setg(errp, "Invalid EIF image. Magic mismatch.");
65 return false;
66 }
67
68 /* Exclude header->eif_crc32 field from CRC calculation */
69 *crc = crc32(*crc, (uint8_t *)header, header_size - 4);
70
71 header->version = be16_to_cpu(header->version);
72 header->flags = be16_to_cpu(header->flags);
73 header->default_memory = be64_to_cpu(header->default_memory);
74 header->default_cpus = be64_to_cpu(header->default_cpus);
75 header->reserved = be16_to_cpu(header->reserved);
76 header->section_cnt = be16_to_cpu(header->section_cnt);
77
78 for (int i = 0; i < MAX_SECTIONS; ++i) {
79 header->section_offsets[i] = be64_to_cpu(header->section_offsets[i]);
80 }
81
82 for (int i = 0; i < MAX_SECTIONS; ++i) {
83 header->section_sizes[i] = be64_to_cpu(header->section_sizes[i]);
84 if (header->section_sizes[i] > SSIZE_MAX) {
85 error_setg(errp, "Invalid EIF image. Section size out of bounds");
86 return false;
87 }
88 }
89
90 header->unused = be32_to_cpu(header->unused);
91 header->eif_crc32 = be32_to_cpu(header->eif_crc32);
92 return true;
93 }
94
95 static bool read_eif_section_header(FILE *f, EifSectionHeader *section_header,
96 uint32_t *crc, Error **errp)
97 {
98 size_t got;
99 size_t section_header_size = sizeof(*section_header);
100
101 got = fread(section_header, 1, section_header_size, f);
102 if (got != section_header_size) {
103 error_setg(errp, "Failed to read EIF section header");
104 return false;
105 }
106
107 *crc = crc32(*crc, (uint8_t *)section_header, section_header_size);
108
109 section_header->section_type = be16_to_cpu(section_header->section_type);
110 section_header->flags = be16_to_cpu(section_header->flags);
111 section_header->section_size = be64_to_cpu(section_header->section_size);
112 return true;
113 }
114
115 /*
116 * Upon success, the caller is responsible for unlinking and freeing *tmp_path.
117 */
118 static bool get_tmp_file(const char *template, char **tmp_path, Error **errp)
119 {
120 int tmp_fd;
121
122 *tmp_path = NULL;
123 tmp_fd = g_file_open_tmp(template, tmp_path, NULL);
124 if (tmp_fd < 0 || *tmp_path == NULL) {
125 error_setg(errp, "Failed to create temporary file for template %s",
126 template);
127 return false;
128 }
129
130 close(tmp_fd);
131 return true;
132 }
133
134 static void safe_fclose(FILE *f)
135 {
136 if (f) {
137 fclose(f);
138 }
139 }
140
141 static void safe_unlink(char *f)
142 {
143 if (f) {
144 unlink(f);
145 }
146 }
147
148 /*
149 * Upon success, the caller is reponsible for unlinking and freeing *kernel_path
150 */
151 static bool read_eif_kernel(FILE *f, uint64_t size, char **kernel_path,
152 QCryptoHash *hash0, QCryptoHash *hash1,
153 uint32_t *crc, Error **errp)
154 {
155 size_t got;
156 FILE *tmp_file = NULL;
157 uint8_t *kernel = g_try_malloc(size);
158 if (!kernel) {
159 error_setg(errp, "Out of memory reading kernel section");
160 goto cleanup;
161 }
162
163 *kernel_path = NULL;
164 if (!get_tmp_file("eif-kernel-XXXXXX", kernel_path, errp)) {
165 goto cleanup;
166 }
167
168 tmp_file = fopen(*kernel_path, "wb");
169 if (tmp_file == NULL) {
170 error_setg_errno(errp, errno, "Failed to open temporary file %s",
171 *kernel_path);
172 goto cleanup;
173 }
174
175 got = fread(kernel, 1, size, f);
176 if ((uint64_t) got != size) {
177 error_setg(errp, "Failed to read EIF kernel section data");
178 goto cleanup;
179 }
180
181 got = fwrite(kernel, 1, size, tmp_file);
182 if ((uint64_t) got != size) {
183 error_setg(errp, "Failed to write EIF kernel section data to temporary"
184 " file");
185 goto cleanup;
186 }
187
188 *crc = crc32(*crc, kernel, size);
189 if (qcrypto_hash_update(hash0, (char *)kernel, size, errp) != 0 ||
190 qcrypto_hash_update(hash1, (char *)kernel, size, errp) != 0) {
191 goto cleanup;
192 }
193 g_free(kernel);
194 fclose(tmp_file);
195
196 return true;
197
198 cleanup:
199 safe_fclose(tmp_file);
200
201 safe_unlink(*kernel_path);
202 g_free(*kernel_path);
203 *kernel_path = NULL;
204
205 g_free(kernel);
206 return false;
207 }
208
209 static bool read_eif_cmdline(FILE *f, uint64_t size, char *cmdline,
210 QCryptoHash *hash0, QCryptoHash *hash1,
211 uint32_t *crc, Error **errp)
212 {
213 size_t got = fread(cmdline, 1, size, f);
214 if ((uint64_t) got != size) {
215 error_setg(errp, "Failed to read EIF cmdline section data");
216 return false;
217 }
218
219 *crc = crc32(*crc, (uint8_t *)cmdline, size);
220 if (qcrypto_hash_update(hash0, cmdline, size, errp) != 0 ||
221 qcrypto_hash_update(hash1, cmdline, size, errp) != 0) {
222 return false;
223 }
224 return true;
225 }
226
227 static bool read_eif_ramdisk(FILE *eif, FILE *initrd, uint64_t size,
228 QCryptoHash *hash0, QCryptoHash *h, uint32_t *crc,
229 Error **errp)
230 {
231 size_t got;
232 bool ret = false;
233 uint8_t *ramdisk = g_try_malloc(size);
234 if (!ramdisk) {
235 error_setg(errp, "Out of memory reading initrd section");
236 goto cleanup;
237 }
238
239 got = fread(ramdisk, 1, size, eif);
240 if ((uint64_t) got != size) {
241 error_setg(errp, "Failed to read EIF ramdisk section data");
242 goto cleanup;
243 }
244
245 got = fwrite(ramdisk, 1, size, initrd);
246 if ((uint64_t) got != size) {
247 error_setg(errp, "Failed to write EIF ramdisk data to temporary file");
248 goto cleanup;
249 }
250
251 *crc = crc32(*crc, ramdisk, size);
252 if (qcrypto_hash_update(hash0, (char *)ramdisk, size, errp) != 0 ||
253 qcrypto_hash_update(h, (char *)ramdisk, size, errp) != 0) {
254 goto cleanup;
255 }
256 ret = true;
257
258 cleanup:
259 g_free(ramdisk);
260 return ret;
261 }
262
263 static bool get_signature_fingerprint_sha384(FILE *eif, uint64_t size,
264 uint8_t *sha384,
265 uint32_t *crc,
266 Error **errp)
267 {
268 size_t got;
269 g_autofree uint8_t *sig = NULL;
270 g_autofree uint8_t *cert = NULL;
271 cbor_item_t *item = NULL;
272 cbor_item_t *pcr0 = NULL;
273 size_t len;
274 size_t hash_len = QCRYPTO_HASH_DIGEST_LEN_SHA384;
275 struct cbor_pair *pair;
276 struct cbor_load_result result;
277 bool ret = false;
278
279 sig = g_try_malloc(size);
280 if (!sig) {
281 error_setg(errp, "Out of memory reading signature section");
282 goto cleanup;
283 }
284
285 got = fread(sig, 1, size, eif);
286 if ((uint64_t) got != size) {
287 error_setg(errp, "Failed to read EIF signature section data");
288 goto cleanup;
289 }
290
291 *crc = crc32(*crc, sig, size);
292
293 item = cbor_load(sig, size, &result);
294 if (!item || result.error.code != CBOR_ERR_NONE) {
295 error_setg(errp, "Failed to load signature section data as CBOR");
296 goto cleanup;
297 }
298 if (!cbor_isa_array(item) || cbor_array_size(item) < 1) {
299 error_setg(errp, "Invalid signature CBOR");
300 goto cleanup;
301 }
302 pcr0 = cbor_array_get(item, 0);
303 if (!pcr0) {
304 error_setg(errp, "Failed to get PCR0 signature");
305 goto cleanup;
306 }
307 if (!cbor_isa_map(pcr0) || cbor_map_size(pcr0) != 2) {
308 error_setg(errp, "Invalid signature CBOR");
309 goto cleanup;
310 }
311 pair = cbor_map_handle(pcr0);
312 if (!cbor_isa_string(pair->key) || cbor_string_length(pair->key) != 19 ||
313 memcmp(cbor_string_handle(pair->key), "signing_certificate", 19) != 0) {
314 error_setg(errp, "Invalid signautre CBOR");
315 goto cleanup;
316 }
317 if (!cbor_isa_array(pair->value)) {
318 error_setg(errp, "Invalid signature CBOR");
319 goto cleanup;
320 }
321 len = cbor_array_size(pair->value);
322 if (len == 0) {
323 error_setg(errp, "Invalid signature CBOR");
324 goto cleanup;
325 }
326 cert = g_try_malloc(len);
327 if (!cert) {
328 error_setg(errp, "Out of memory reading signature section");
329 goto cleanup;
330 }
331
332 for (int i = 0; i < len; ++i) {
333 cbor_item_t *tmp = cbor_array_get(pair->value, i);
334 if (!tmp) {
335 error_setg(errp, "Invalid signature CBOR");
336 goto cleanup;
337 }
338 if (!cbor_isa_uint(tmp) || cbor_int_get_width(tmp) != CBOR_INT_8) {
339 cbor_decref(&tmp);
340 error_setg(errp, "Invalid signature CBOR");
341 goto cleanup;
342 }
343 cert[i] = cbor_get_uint8(tmp);
344 cbor_decref(&tmp);
345 }
346
347 if (qcrypto_get_x509_cert_fingerprint(cert, len, QCRYPTO_HASH_ALGO_SHA384,
348 sha384, &hash_len, errp)) {
349 goto cleanup;
350 }
351
352 ret = true;
353
354 cleanup:
355 if (pcr0) {
356 cbor_decref(&pcr0);
357 }
358 if (item) {
359 cbor_decref(&item);
360 }
361 return ret;
362 }
363
364 /* Expects file to have offset 0 before this function is called */
365 static long get_file_size(FILE *f, Error **errp)
366 {
367 long size;
368
369 if (fseek(f, 0, SEEK_END) != 0) {
370 error_setg_errno(errp, errno, "Failed to seek to the end of file");
371 return -1;
372 }
373
374 size = ftell(f);
375 if (size == -1) {
376 error_setg_errno(errp, errno, "Failed to get offset");
377 return -1;
378 }
379
380 if (fseek(f, 0, SEEK_SET) != 0) {
381 error_setg_errno(errp, errno, "Failed to seek back to the start");
382 return -1;
383 }
384
385 return size;
386 }
387
388 static bool get_SHA384_hash(QCryptoHash *h, uint8_t *hash, Error **errp)
389 {
390 size_t hash_len = QCRYPTO_HASH_DIGEST_LEN_SHA384;
391 return qcrypto_hash_finalize_bytes(h, &hash, &hash_len, errp) == 0;
392 }
393
394 /*
395 * Upon success, the caller is reponsible for unlinking and freeing
396 * *kernel_path, *initrd_path and freeing *cmdline.
397 */
398 bool read_eif_file(const char *eif_path, const char *machine_initrd,
399 char **kernel_path, char **initrd_path, char **cmdline,
400 uint8_t *image_hash, uint8_t *bootstrap_hash,
401 uint8_t *app_hash, uint8_t *fingerprint_hash,
402 bool *signature_found, Error **errp)
403 {
404 FILE *f = NULL;
405 FILE *machine_initrd_f = NULL;
406 FILE *initrd_path_f = NULL;
407 long machine_initrd_size;
408 uint32_t crc = 0;
409 EifHeader eif_header;
410 bool seen_sections[EIF_SECTION_MAX] = {false};
411 /* kernel + ramdisks + cmdline SHA384 hash */
412 g_autoptr(QCryptoHash) hash0 = NULL;
413 /* kernel + boot ramdisk + cmdline SHA384 hash */
414 g_autoptr(QCryptoHash) hash1 = NULL;
415 /* application ramdisk(s) SHA384 hash */
416 g_autoptr(QCryptoHash) hash2 = NULL;
417
418 *signature_found = false;
419 *kernel_path = *initrd_path = *cmdline = NULL;
420
421 hash0 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp);
422 if (!hash0) {
423 goto cleanup;
424 }
425 hash1 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp);
426 if (!hash1) {
427 goto cleanup;
428 }
429 hash2 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp);
430 if (!hash2) {
431 goto cleanup;
432 }
433
434 f = fopen(eif_path, "rb");
435 if (f == NULL) {
436 error_setg_errno(errp, errno, "Failed to open %s", eif_path);
437 goto cleanup;
438 }
439
440 if (!read_eif_header(f, &eif_header, &crc, errp)) {
441 goto cleanup;
442 }
443
444 if (eif_header.version < 4) {
445 error_setg(errp, "Expected EIF version 4 or greater");
446 goto cleanup;
447 }
448
449 if (eif_header.flags != 0) {
450 error_setg(errp, "Expected EIF flags to be 0");
451 goto cleanup;
452 }
453
454 if (eif_header.section_cnt > MAX_SECTIONS) {
455 error_setg(errp, "EIF header section count must not be greater than "
456 "%d but found %d", MAX_SECTIONS, eif_header.section_cnt);
457 goto cleanup;
458 }
459
460 for (int i = 0; i < eif_header.section_cnt; ++i) {
461 EifSectionHeader hdr;
462 uint16_t section_type;
463
464 if (eif_header.section_offsets[i] > OFF_MAX) {
465 error_setg(errp, "Invalid EIF image. Section offset out of bounds");
466 goto cleanup;
467 }
468 if (fseek(f, eif_header.section_offsets[i], SEEK_SET) != 0) {
469 error_setg_errno(errp, errno, "Failed to offset to %" PRIu64 " in EIF file",
470 eif_header.section_offsets[i]);
471 goto cleanup;
472 }
473
474 if (!read_eif_section_header(f, &hdr, &crc, errp)) {
475 goto cleanup;
476 }
477
478 if (hdr.flags != 0) {
479 error_setg(errp, "Expected EIF section header flags to be 0");
480 goto cleanup;
481 }
482
483 if (eif_header.section_sizes[i] != hdr.section_size) {
484 error_setg(errp, "EIF section size mismatch between header and "
485 "section header: header %" PRIu64 ", section header %" PRIu64,
486 eif_header.section_sizes[i],
487 hdr.section_size);
488 goto cleanup;
489 }
490
491 section_type = hdr.section_type;
492
493 switch (section_type) {
494 case EIF_SECTION_KERNEL:
495 if (seen_sections[EIF_SECTION_KERNEL]) {
496 error_setg(errp, "Invalid EIF image. More than 1 kernel "
497 "section");
498 goto cleanup;
499 }
500
501 if (!read_eif_kernel(f, hdr.section_size, kernel_path, hash0,
502 hash1, &crc, errp)) {
503 goto cleanup;
504 }
505
506 break;
507 case EIF_SECTION_CMDLINE:
508 {
509 uint64_t size;
510 if (seen_sections[EIF_SECTION_CMDLINE]) {
511 error_setg(errp, "Invalid EIF image. More than 1 cmdline "
512 "section");
513 goto cleanup;
514 }
515 size = hdr.section_size;
516 *cmdline = g_try_malloc(size + 1);
517 if (!*cmdline) {
518 error_setg(errp, "Out of memory reading command line section");
519 goto cleanup;
520 }
521 if (!read_eif_cmdline(f, size, *cmdline, hash0, hash1, &crc,
522 errp)) {
523 goto cleanup;
524 }
525 (*cmdline)[size] = '\0';
526
527 break;
528 }
529 case EIF_SECTION_RAMDISK:
530 {
531 QCryptoHash *h = hash2;
532 if (!seen_sections[EIF_SECTION_RAMDISK]) {
533 /*
534 * If this is the first time we are seeing a ramdisk section,
535 * we need to:
536 * 1) hash it into bootstrap (hash1) instead of app (hash2)
537 * along with image (hash0)
538 * 2) create the initrd temporary file.
539 */
540 h = hash1;
541 if (!get_tmp_file("eif-initrd-XXXXXX", initrd_path, errp)) {
542 goto cleanup;
543 }
544 initrd_path_f = fopen(*initrd_path, "wb");
545 if (initrd_path_f == NULL) {
546 error_setg_errno(errp, errno, "Failed to open file %s",
547 *initrd_path);
548 goto cleanup;
549 }
550 }
551
552 if (!read_eif_ramdisk(f, initrd_path_f, hdr.section_size, hash0, h,
553 &crc, errp)) {
554 goto cleanup;
555 }
556
557 break;
558 }
559 case EIF_SECTION_SIGNATURE:
560 *signature_found = true;
561 if (!get_signature_fingerprint_sha384(f, hdr.section_size,
562 fingerprint_hash, &crc,
563 errp)) {
564 goto cleanup;
565 }
566 break;
567 default:
568 /* other sections including invalid or unknown sections */
569 {
570 uint8_t *buf;
571 size_t got;
572 uint64_t size = hdr.section_size;
573 buf = g_try_malloc(size);
574 if (!buf) {
575 error_setg(errp, "Out of memory reading unknown section");
576 goto cleanup;
577 }
578 got = fread(buf, 1, size, f);
579 if ((uint64_t) got != size) {
580 g_free(buf);
581 error_setg(errp, "Failed to read EIF %s section data",
582 section_type_to_string(section_type));
583 goto cleanup;
584 }
585 crc = crc32(crc, buf, size);
586 g_free(buf);
587 break;
588 }
589 }
590
591 if (section_type < EIF_SECTION_MAX) {
592 seen_sections[section_type] = true;
593 }
594 }
595
596 if (!seen_sections[EIF_SECTION_KERNEL]) {
597 error_setg(errp, "Invalid EIF image. No kernel section.");
598 goto cleanup;
599 }
600 if (!seen_sections[EIF_SECTION_CMDLINE]) {
601 error_setg(errp, "Invalid EIF image. No cmdline section.");
602 goto cleanup;
603 }
604 if (!seen_sections[EIF_SECTION_RAMDISK]) {
605 error_setg(errp, "Invalid EIF image. No ramdisk section.");
606 goto cleanup;
607 }
608
609 if (eif_header.eif_crc32 != crc) {
610 error_setg(errp, "CRC mismatch. Expected %u but header has %u.",
611 crc, eif_header.eif_crc32);
612 goto cleanup;
613 }
614
615 /*
616 * Let's append the initrd file from "-initrd" option if any. Although
617 * we pass the crc pointer to read_eif_ramdisk, it is not useful anymore.
618 * We have already done the crc mismatch check above this code.
619 */
620 if (machine_initrd) {
621 machine_initrd_f = fopen(machine_initrd, "rb");
622 if (machine_initrd_f == NULL) {
623 error_setg_errno(errp, errno, "Failed to open initrd file %s",
624 machine_initrd);
625 goto cleanup;
626 }
627
628 machine_initrd_size = get_file_size(machine_initrd_f, errp);
629 if (machine_initrd_size == -1) {
630 goto cleanup;
631 }
632
633 if (!read_eif_ramdisk(machine_initrd_f, initrd_path_f,
634 machine_initrd_size, hash0, hash2, &crc, errp)) {
635 goto cleanup;
636 }
637 }
638
639 if (!get_SHA384_hash(hash0, image_hash, errp)) {
640 goto cleanup;
641 }
642 if (!get_SHA384_hash(hash1, bootstrap_hash, errp)) {
643 goto cleanup;
644 }
645 if (!get_SHA384_hash(hash2, app_hash, errp)) {
646 goto cleanup;
647 }
648
649 fclose(f);
650 fclose(initrd_path_f);
651 safe_fclose(machine_initrd_f);
652 return true;
653
654 cleanup:
655 safe_fclose(f);
656 safe_fclose(initrd_path_f);
657 safe_fclose(machine_initrd_f);
658
659 safe_unlink(*kernel_path);
660 g_free(*kernel_path);
661 *kernel_path = NULL;
662
663 safe_unlink(*initrd_path);
664 g_free(*initrd_path);
665 *initrd_path = NULL;
666
667 g_free(*cmdline);
668 *cmdline = NULL;
669
670 return false;
671 }