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