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 } 123 124 header->unused = be32_to_cpu(header->unused); 125 header->eif_crc32 = be32_to_cpu(header->eif_crc32); 126 return true; 127 } 128 129 static bool read_eif_section_header(FILE *f, EifSectionHeader *section_header, 130 uint32_t *crc, Error **errp) 131 { 132 size_t got; 133 size_t section_header_size = sizeof(*section_header); 134 135 got = fread(section_header, 1, section_header_size, f); 136 if (got != section_header_size) { 137 error_setg(errp, "Failed to read EIF section header"); 138 return false; 139 } 140 141 *crc = crc32(*crc, (uint8_t *)section_header, section_header_size); 142 143 section_header->section_type = be16_to_cpu(section_header->section_type); 144 section_header->flags = be16_to_cpu(section_header->flags); 145 section_header->section_size = be64_to_cpu(section_header->section_size); 146 return true; 147 } 148 149 /* 150 * Upon success, the caller is responsible for unlinking and freeing *tmp_path. 151 */ 152 static bool get_tmp_file(const char *template, char **tmp_path, Error **errp) 153 { 154 int tmp_fd; 155 156 *tmp_path = NULL; 157 tmp_fd = g_file_open_tmp(template, tmp_path, NULL); 158 if (tmp_fd < 0 || *tmp_path == NULL) { 159 error_setg(errp, "Failed to create temporary file for template %s", 160 template); 161 return false; 162 } 163 164 close(tmp_fd); 165 return true; 166 } 167 168 static void safe_fclose(FILE *f) 169 { 170 if (f) { 171 fclose(f); 172 } 173 } 174 175 static void safe_unlink(char *f) 176 { 177 if (f) { 178 unlink(f); 179 } 180 } 181 182 /* 183 * Upon success, the caller is reponsible for unlinking and freeing *kernel_path 184 */ 185 static bool read_eif_kernel(FILE *f, uint64_t size, char **kernel_path, 186 uint8_t *kernel, uint32_t *crc, Error **errp) 187 { 188 size_t got; 189 FILE *tmp_file = NULL; 190 191 *kernel_path = NULL; 192 if (!get_tmp_file("eif-kernel-XXXXXX", kernel_path, errp)) { 193 goto cleanup; 194 } 195 196 tmp_file = fopen(*kernel_path, "wb"); 197 if (tmp_file == NULL) { 198 error_setg_errno(errp, errno, "Failed to open temporary file %s", 199 *kernel_path); 200 goto cleanup; 201 } 202 203 got = fread(kernel, 1, size, f); 204 if ((uint64_t) got != size) { 205 error_setg(errp, "Failed to read EIF kernel section data"); 206 goto cleanup; 207 } 208 209 got = fwrite(kernel, 1, size, tmp_file); 210 if ((uint64_t) got != size) { 211 error_setg(errp, "Failed to write EIF kernel section data to temporary" 212 " file"); 213 goto cleanup; 214 } 215 216 *crc = crc32(*crc, kernel, size); 217 fclose(tmp_file); 218 219 return true; 220 221 cleanup: 222 safe_fclose(tmp_file); 223 224 safe_unlink(*kernel_path); 225 g_free(*kernel_path); 226 *kernel_path = NULL; 227 228 return false; 229 } 230 231 static bool read_eif_cmdline(FILE *f, uint64_t size, char *cmdline, 232 uint32_t *crc, Error **errp) 233 { 234 size_t got = fread(cmdline, 1, size, f); 235 if ((uint64_t) got != size) { 236 error_setg(errp, "Failed to read EIF cmdline section data"); 237 return false; 238 } 239 240 *crc = crc32(*crc, (uint8_t *)cmdline, size); 241 return true; 242 } 243 244 static bool read_eif_ramdisk(FILE *eif, FILE *initrd, uint64_t size, 245 uint8_t *ramdisk, uint32_t *crc, Error **errp) 246 { 247 size_t got; 248 249 got = fread(ramdisk, 1, size, eif); 250 if ((uint64_t) got != size) { 251 error_setg(errp, "Failed to read EIF ramdisk section data"); 252 return false; 253 } 254 255 got = fwrite(ramdisk, 1, size, initrd); 256 if ((uint64_t) got != size) { 257 error_setg(errp, "Failed to write EIF ramdisk data to temporary file"); 258 return false; 259 } 260 261 *crc = crc32(*crc, ramdisk, size); 262 return true; 263 } 264 265 static bool get_signature_fingerprint_sha384(FILE *eif, uint64_t size, 266 uint8_t *sha384, 267 uint32_t *crc, 268 Error **errp) 269 { 270 size_t got; 271 g_autofree uint8_t *sig = NULL; 272 g_autofree uint8_t *cert = NULL; 273 cbor_item_t *item = NULL; 274 cbor_item_t *pcr0 = NULL; 275 size_t len; 276 size_t hash_len = QCRYPTO_HASH_DIGEST_LEN_SHA384; 277 struct cbor_pair *pair; 278 struct cbor_load_result result; 279 bool ret = false; 280 281 sig = g_malloc(size); 282 got = fread(sig, 1, size, eif); 283 if ((uint64_t) got != size) { 284 error_setg(errp, "Failed to read EIF signature section data"); 285 goto cleanup; 286 } 287 288 *crc = crc32(*crc, sig, size); 289 290 item = cbor_load(sig, size, &result); 291 if (!item || result.error.code != CBOR_ERR_NONE) { 292 error_setg(errp, "Failed to load signature section data as CBOR"); 293 goto cleanup; 294 } 295 if (!cbor_isa_array(item) || cbor_array_size(item) < 1) { 296 error_setg(errp, "Invalid signature CBOR"); 297 goto cleanup; 298 } 299 pcr0 = cbor_array_get(item, 0); 300 if (!pcr0) { 301 error_setg(errp, "Failed to get PCR0 signature"); 302 goto cleanup; 303 } 304 if (!cbor_isa_map(pcr0) || cbor_map_size(pcr0) != 2) { 305 error_setg(errp, "Invalid signature CBOR"); 306 goto cleanup; 307 } 308 pair = cbor_map_handle(pcr0); 309 if (!cbor_isa_string(pair->key) || cbor_string_length(pair->key) != 19 || 310 memcmp(cbor_string_handle(pair->key), "signing_certificate", 19) != 0) { 311 error_setg(errp, "Invalid signautre CBOR"); 312 goto cleanup; 313 } 314 if (!cbor_isa_array(pair->value)) { 315 error_setg(errp, "Invalid signature CBOR"); 316 goto cleanup; 317 } 318 len = cbor_array_size(pair->value); 319 if (len == 0) { 320 error_setg(errp, "Invalid signature CBOR"); 321 goto cleanup; 322 } 323 cert = g_malloc(len); 324 for (int i = 0; i < len; ++i) { 325 cbor_item_t *tmp = cbor_array_get(pair->value, i); 326 if (!tmp) { 327 error_setg(errp, "Invalid signature CBOR"); 328 goto cleanup; 329 } 330 if (!cbor_isa_uint(tmp) || cbor_int_get_width(tmp) != CBOR_INT_8) { 331 cbor_decref(&tmp); 332 error_setg(errp, "Invalid signature CBOR"); 333 goto cleanup; 334 } 335 cert[i] = cbor_get_uint8(tmp); 336 cbor_decref(&tmp); 337 } 338 339 if (qcrypto_get_x509_cert_fingerprint(cert, len, QCRYPTO_HASH_ALGO_SHA384, 340 sha384, &hash_len, errp)) { 341 goto cleanup; 342 } 343 344 ret = true; 345 346 cleanup: 347 if (pcr0) { 348 cbor_decref(&pcr0); 349 } 350 if (item) { 351 cbor_decref(&item); 352 } 353 return ret; 354 } 355 356 /* Expects file to have offset 0 before this function is called */ 357 static long get_file_size(FILE *f, Error **errp) 358 { 359 long size; 360 361 if (fseek(f, 0, SEEK_END) != 0) { 362 error_setg_errno(errp, errno, "Failed to seek to the end of file"); 363 return -1; 364 } 365 366 size = ftell(f); 367 if (size == -1) { 368 error_setg_errno(errp, errno, "Failed to get offset"); 369 return -1; 370 } 371 372 if (fseek(f, 0, SEEK_SET) != 0) { 373 error_setg_errno(errp, errno, "Failed to seek back to the start"); 374 return -1; 375 } 376 377 return size; 378 } 379 380 static bool get_SHA384_digest(GList *list, uint8_t *digest, Error **errp) 381 { 382 size_t digest_len = QCRYPTO_HASH_DIGEST_LEN_SHA384; 383 size_t list_len = g_list_length(list); 384 struct iovec *iovec_list = g_new0(struct iovec, list_len); 385 bool ret = true; 386 GList *l; 387 int i; 388 389 for (i = 0, l = list; l != NULL; l = l->next, i++) { 390 iovec_list[i] = *(struct iovec *) l->data; 391 } 392 393 if (qcrypto_hash_bytesv(QCRYPTO_HASH_ALGO_SHA384, iovec_list, list_len, 394 &digest, &digest_len, errp) < 0) { 395 ret = false; 396 } 397 398 g_free(iovec_list); 399 return ret; 400 } 401 402 static void free_iovec(struct iovec *iov) 403 { 404 if (iov) { 405 g_free(iov->iov_base); 406 g_free(iov); 407 } 408 } 409 410 /* 411 * Upon success, the caller is reponsible for unlinking and freeing 412 * *kernel_path, *initrd_path and freeing *cmdline. 413 */ 414 bool read_eif_file(const char *eif_path, const char *machine_initrd, 415 char **kernel_path, char **initrd_path, char **cmdline, 416 uint8_t *image_sha384, uint8_t *bootstrap_sha384, 417 uint8_t *app_sha384, uint8_t *fingerprint_sha384, 418 bool *signature_found, Error **errp) 419 { 420 FILE *f = NULL; 421 FILE *machine_initrd_f = NULL; 422 FILE *initrd_path_f = NULL; 423 long machine_initrd_size; 424 uint32_t crc = 0; 425 EifHeader eif_header; 426 bool seen_sections[EIF_SECTION_MAX] = {false}; 427 /* kernel + ramdisks + cmdline sha384 hash */ 428 GList *iov_PCR0 = NULL; 429 /* kernel + boot ramdisk + cmdline sha384 hash */ 430 GList *iov_PCR1 = NULL; 431 /* application ramdisk(s) hash */ 432 GList *iov_PCR2 = NULL; 433 uint8_t *ptr = NULL; 434 struct iovec *iov_ptr = NULL; 435 436 *signature_found = false; 437 *kernel_path = *initrd_path = *cmdline = NULL; 438 439 f = fopen(eif_path, "rb"); 440 if (f == NULL) { 441 error_setg_errno(errp, errno, "Failed to open %s", eif_path); 442 goto cleanup; 443 } 444 445 if (!read_eif_header(f, &eif_header, &crc, errp)) { 446 goto cleanup; 447 } 448 449 if (eif_header.version < 4) { 450 error_setg(errp, "Expected EIF version 4 or greater"); 451 goto cleanup; 452 } 453 454 if (eif_header.flags != 0) { 455 error_setg(errp, "Expected EIF flags to be 0"); 456 goto cleanup; 457 } 458 459 if (eif_header.section_cnt > MAX_SECTIONS) { 460 error_setg(errp, "EIF header section count must not be greater than " 461 "%d but found %d", MAX_SECTIONS, eif_header.section_cnt); 462 goto cleanup; 463 } 464 465 for (int i = 0; i < eif_header.section_cnt; ++i) { 466 EifSectionHeader hdr; 467 uint16_t section_type; 468 469 if (fseek(f, eif_header.section_offsets[i], SEEK_SET) != 0) { 470 error_setg_errno(errp, errno, "Failed to offset to %" PRIu64 " in EIF file", 471 eif_header.section_offsets[i]); 472 goto cleanup; 473 } 474 475 if (!read_eif_section_header(f, &hdr, &crc, errp)) { 476 goto cleanup; 477 } 478 479 if (hdr.flags != 0) { 480 error_setg(errp, "Expected EIF section header flags to be 0"); 481 goto cleanup; 482 } 483 484 if (eif_header.section_sizes[i] != hdr.section_size) { 485 error_setg(errp, "EIF section size mismatch between header and " 486 "section header: header %" PRIu64 ", section header %" PRIu64, 487 eif_header.section_sizes[i], 488 hdr.section_size); 489 goto cleanup; 490 } 491 492 section_type = hdr.section_type; 493 494 switch (section_type) { 495 case EIF_SECTION_KERNEL: 496 if (seen_sections[EIF_SECTION_KERNEL]) { 497 error_setg(errp, "Invalid EIF image. More than 1 kernel " 498 "section"); 499 goto cleanup; 500 } 501 502 ptr = g_malloc(hdr.section_size); 503 504 iov_ptr = g_malloc(sizeof(struct iovec)); 505 iov_ptr->iov_base = ptr; 506 iov_ptr->iov_len = hdr.section_size; 507 508 iov_PCR0 = g_list_append(iov_PCR0, iov_ptr); 509 iov_PCR1 = g_list_append(iov_PCR1, iov_ptr); 510 511 if (!read_eif_kernel(f, hdr.section_size, kernel_path, ptr, &crc, 512 errp)) { 513 goto cleanup; 514 } 515 516 break; 517 case EIF_SECTION_CMDLINE: 518 { 519 uint64_t size; 520 uint8_t *cmdline_copy; 521 if (seen_sections[EIF_SECTION_CMDLINE]) { 522 error_setg(errp, "Invalid EIF image. More than 1 cmdline " 523 "section"); 524 goto cleanup; 525 } 526 size = hdr.section_size; 527 *cmdline = g_malloc(size + 1); 528 if (!read_eif_cmdline(f, size, *cmdline, &crc, errp)) { 529 goto cleanup; 530 } 531 (*cmdline)[size] = '\0'; 532 533 /* 534 * We make a copy of '*cmdline' for putting it in iovecs so that 535 * we can easily free all the iovec entries later as we cannot 536 * free '*cmdline' which is used by the caller. 537 */ 538 cmdline_copy = g_memdup2(*cmdline, size); 539 540 iov_ptr = g_malloc(sizeof(struct iovec)); 541 iov_ptr->iov_base = cmdline_copy; 542 iov_ptr->iov_len = size; 543 544 iov_PCR0 = g_list_append(iov_PCR0, iov_ptr); 545 iov_PCR1 = g_list_append(iov_PCR1, iov_ptr); 546 break; 547 } 548 case EIF_SECTION_RAMDISK: 549 { 550 if (!seen_sections[EIF_SECTION_RAMDISK]) { 551 /* 552 * If this is the first time we are seeing a ramdisk section, 553 * we need to create the initrd temporary file. 554 */ 555 if (!get_tmp_file("eif-initrd-XXXXXX", initrd_path, errp)) { 556 goto cleanup; 557 } 558 initrd_path_f = fopen(*initrd_path, "wb"); 559 if (initrd_path_f == NULL) { 560 error_setg_errno(errp, errno, "Failed to open file %s", 561 *initrd_path); 562 goto cleanup; 563 } 564 } 565 566 ptr = g_malloc(hdr.section_size); 567 568 iov_ptr = g_malloc(sizeof(struct iovec)); 569 iov_ptr->iov_base = ptr; 570 iov_ptr->iov_len = hdr.section_size; 571 572 iov_PCR0 = g_list_append(iov_PCR0, iov_ptr); 573 /* 574 * If it's the first ramdisk, we need to hash it into bootstrap 575 * i.e., iov_PCR1, otherwise we need to hash it into app i.e., 576 * iov_PCR2. 577 */ 578 if (!seen_sections[EIF_SECTION_RAMDISK]) { 579 iov_PCR1 = g_list_append(iov_PCR1, iov_ptr); 580 } else { 581 iov_PCR2 = g_list_append(iov_PCR2, iov_ptr); 582 } 583 584 if (!read_eif_ramdisk(f, initrd_path_f, hdr.section_size, ptr, 585 &crc, errp)) { 586 goto cleanup; 587 } 588 589 break; 590 } 591 case EIF_SECTION_SIGNATURE: 592 *signature_found = true; 593 if (!get_signature_fingerprint_sha384(f, hdr.section_size, 594 fingerprint_sha384, &crc, 595 errp)) { 596 goto cleanup; 597 } 598 break; 599 default: 600 /* other sections including invalid or unknown sections */ 601 { 602 uint8_t *buf; 603 size_t got; 604 uint64_t size = hdr.section_size; 605 buf = g_malloc(size); 606 got = fread(buf, 1, size, f); 607 if ((uint64_t) got != size) { 608 g_free(buf); 609 error_setg(errp, "Failed to read EIF %s section data", 610 section_type_to_string(section_type)); 611 goto cleanup; 612 } 613 crc = crc32(crc, buf, size); 614 g_free(buf); 615 break; 616 } 617 } 618 619 if (section_type < EIF_SECTION_MAX) { 620 seen_sections[section_type] = true; 621 } 622 } 623 624 if (!seen_sections[EIF_SECTION_KERNEL]) { 625 error_setg(errp, "Invalid EIF image. No kernel section."); 626 goto cleanup; 627 } 628 if (!seen_sections[EIF_SECTION_CMDLINE]) { 629 error_setg(errp, "Invalid EIF image. No cmdline section."); 630 goto cleanup; 631 } 632 if (!seen_sections[EIF_SECTION_RAMDISK]) { 633 error_setg(errp, "Invalid EIF image. No ramdisk section."); 634 goto cleanup; 635 } 636 637 if (eif_header.eif_crc32 != crc) { 638 error_setg(errp, "CRC mismatch. Expected %u but header has %u.", 639 crc, eif_header.eif_crc32); 640 goto cleanup; 641 } 642 643 /* 644 * Let's append the initrd file from "-initrd" option if any. Although 645 * we pass the crc pointer to read_eif_ramdisk, it is not useful anymore. 646 * We have already done the crc mismatch check above this code. 647 */ 648 if (machine_initrd) { 649 machine_initrd_f = fopen(machine_initrd, "rb"); 650 if (machine_initrd_f == NULL) { 651 error_setg_errno(errp, errno, "Failed to open initrd file %s", 652 machine_initrd); 653 goto cleanup; 654 } 655 656 machine_initrd_size = get_file_size(machine_initrd_f, errp); 657 if (machine_initrd_size == -1) { 658 goto cleanup; 659 } 660 661 ptr = g_malloc(machine_initrd_size); 662 663 iov_ptr = g_malloc(sizeof(struct iovec)); 664 iov_ptr->iov_base = ptr; 665 iov_ptr->iov_len = machine_initrd_size; 666 667 iov_PCR0 = g_list_append(iov_PCR0, iov_ptr); 668 iov_PCR2 = g_list_append(iov_PCR2, iov_ptr); 669 670 if (!read_eif_ramdisk(machine_initrd_f, initrd_path_f, 671 machine_initrd_size, ptr, &crc, errp)) { 672 goto cleanup; 673 } 674 } 675 676 if (!get_SHA384_digest(iov_PCR0, image_sha384, errp)) { 677 goto cleanup; 678 } 679 if (!get_SHA384_digest(iov_PCR1, bootstrap_sha384, errp)) { 680 goto cleanup; 681 } 682 if (!get_SHA384_digest(iov_PCR2, app_sha384, errp)) { 683 goto cleanup; 684 } 685 686 /* 687 * We only need to free iov_PCR0 entries because iov_PCR1 and 688 * iov_PCR2 iovec entries are subsets of iov_PCR0 iovec entries. 689 */ 690 g_list_free_full(iov_PCR0, (GDestroyNotify) free_iovec); 691 g_list_free(iov_PCR1); 692 g_list_free(iov_PCR2); 693 fclose(f); 694 fclose(initrd_path_f); 695 safe_fclose(machine_initrd_f); 696 return true; 697 698 cleanup: 699 g_list_free_full(iov_PCR0, (GDestroyNotify) free_iovec); 700 g_list_free(iov_PCR1); 701 g_list_free(iov_PCR2); 702 703 safe_fclose(f); 704 safe_fclose(initrd_path_f); 705 safe_fclose(machine_initrd_f); 706 707 safe_unlink(*kernel_path); 708 g_free(*kernel_path); 709 *kernel_path = NULL; 710 711 safe_unlink(*initrd_path); 712 g_free(*initrd_path); 713 *initrd_path = NULL; 714 715 g_free(*cmdline); 716 *cmdline = NULL; 717 718 return false; 719 } 720