|  | /* | 
|  | * EIF (Enclave Image Format) related helpers | 
|  | * | 
|  | * Copyright (c) 2024 Dorjoy Chowdhury <dorjoychy111@gmail.com> | 
|  | * | 
|  | * This work is licensed under the terms of the GNU GPL, version 2 or | 
|  | * (at your option) any later version.  See the COPYING file in the | 
|  | * top-level directory. | 
|  | */ | 
|  |  | 
|  | #include "qemu/osdep.h" | 
|  | #include "qemu/bswap.h" | 
|  | #include "qapi/error.h" | 
|  | #include "crypto/hash.h" | 
|  | #include "crypto/x509-utils.h" | 
|  | #include <zlib.h> /* for crc32 */ | 
|  | #include <cbor.h> | 
|  |  | 
|  | #include "hw/core/eif.h" | 
|  |  | 
|  | #define MAX_SECTIONS 32 | 
|  |  | 
|  | /* members are ordered according to field order in .eif file */ | 
|  | typedef struct EifHeader { | 
|  | uint8_t  magic[4]; /* must be .eif in ascii i.e., [46, 101, 105, 102] */ | 
|  | uint16_t version; | 
|  | uint16_t flags; | 
|  | uint64_t default_memory; | 
|  | uint64_t default_cpus; | 
|  | uint16_t reserved; | 
|  | uint16_t section_cnt; | 
|  | uint64_t section_offsets[MAX_SECTIONS]; | 
|  | uint64_t section_sizes[MAX_SECTIONS]; | 
|  | uint32_t unused; | 
|  | uint32_t eif_crc32; | 
|  | } QEMU_PACKED EifHeader; | 
|  |  | 
|  | /* members are ordered according to field order in .eif file */ | 
|  | typedef struct EifSectionHeader { | 
|  | /* | 
|  | * 0 = invalid, 1 = kernel, 2 = cmdline, 3 = ramdisk, 4 = signature, | 
|  | * 5 = metadata | 
|  | */ | 
|  | uint16_t section_type; | 
|  | uint16_t flags; | 
|  | uint64_t section_size; | 
|  | } QEMU_PACKED EifSectionHeader; | 
|  |  | 
|  | enum EifSectionTypes { | 
|  | EIF_SECTION_INVALID = 0, | 
|  | EIF_SECTION_KERNEL = 1, | 
|  | EIF_SECTION_CMDLINE = 2, | 
|  | EIF_SECTION_RAMDISK = 3, | 
|  | EIF_SECTION_SIGNATURE = 4, | 
|  | EIF_SECTION_METADATA = 5, | 
|  | EIF_SECTION_MAX = 6, | 
|  | }; | 
|  |  | 
|  | static const char *section_type_to_string(uint16_t type) | 
|  | { | 
|  | const char *str; | 
|  | switch (type) { | 
|  | case EIF_SECTION_INVALID: | 
|  | str = "invalid"; | 
|  | break; | 
|  | case EIF_SECTION_KERNEL: | 
|  | str = "kernel"; | 
|  | break; | 
|  | case EIF_SECTION_CMDLINE: | 
|  | str = "cmdline"; | 
|  | break; | 
|  | case EIF_SECTION_RAMDISK: | 
|  | str = "ramdisk"; | 
|  | break; | 
|  | case EIF_SECTION_SIGNATURE: | 
|  | str = "signature"; | 
|  | break; | 
|  | case EIF_SECTION_METADATA: | 
|  | str = "metadata"; | 
|  | break; | 
|  | default: | 
|  | str = "unknown"; | 
|  | break; | 
|  | } | 
|  |  | 
|  | return str; | 
|  | } | 
|  |  | 
|  | static bool read_eif_header(FILE *f, EifHeader *header, uint32_t *crc, | 
|  | Error **errp) | 
|  | { | 
|  | size_t got; | 
|  | size_t header_size = sizeof(*header); | 
|  |  | 
|  | got = fread(header, 1, header_size, f); | 
|  | if (got != header_size) { | 
|  | error_setg(errp, "Failed to read EIF header"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (memcmp(header->magic, ".eif", 4) != 0) { | 
|  | error_setg(errp, "Invalid EIF image. Magic mismatch."); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* Exclude header->eif_crc32 field from CRC calculation */ | 
|  | *crc = crc32(*crc, (uint8_t *)header, header_size - 4); | 
|  |  | 
|  | header->version = be16_to_cpu(header->version); | 
|  | header->flags = be16_to_cpu(header->flags); | 
|  | header->default_memory = be64_to_cpu(header->default_memory); | 
|  | header->default_cpus = be64_to_cpu(header->default_cpus); | 
|  | header->reserved = be16_to_cpu(header->reserved); | 
|  | header->section_cnt = be16_to_cpu(header->section_cnt); | 
|  |  | 
|  | for (int i = 0; i < MAX_SECTIONS; ++i) { | 
|  | header->section_offsets[i] = be64_to_cpu(header->section_offsets[i]); | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < MAX_SECTIONS; ++i) { | 
|  | header->section_sizes[i] = be64_to_cpu(header->section_sizes[i]); | 
|  | if (header->section_sizes[i] > SSIZE_MAX) { | 
|  | error_setg(errp, "Invalid EIF image. Section size out of bounds"); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | header->unused = be32_to_cpu(header->unused); | 
|  | header->eif_crc32 = be32_to_cpu(header->eif_crc32); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool read_eif_section_header(FILE *f, EifSectionHeader *section_header, | 
|  | uint32_t *crc, Error **errp) | 
|  | { | 
|  | size_t got; | 
|  | size_t section_header_size = sizeof(*section_header); | 
|  |  | 
|  | got = fread(section_header, 1, section_header_size, f); | 
|  | if (got != section_header_size) { | 
|  | error_setg(errp, "Failed to read EIF section header"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *crc = crc32(*crc, (uint8_t *)section_header, section_header_size); | 
|  |  | 
|  | section_header->section_type = be16_to_cpu(section_header->section_type); | 
|  | section_header->flags = be16_to_cpu(section_header->flags); | 
|  | section_header->section_size = be64_to_cpu(section_header->section_size); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Upon success, the caller is responsible for unlinking and freeing *tmp_path. | 
|  | */ | 
|  | static bool get_tmp_file(const char *template, char **tmp_path, Error **errp) | 
|  | { | 
|  | int tmp_fd; | 
|  |  | 
|  | *tmp_path = NULL; | 
|  | tmp_fd = g_file_open_tmp(template, tmp_path, NULL); | 
|  | if (tmp_fd < 0 || *tmp_path == NULL) { | 
|  | error_setg(errp, "Failed to create temporary file for template %s", | 
|  | template); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | close(tmp_fd); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static void safe_fclose(FILE *f) | 
|  | { | 
|  | if (f) { | 
|  | fclose(f); | 
|  | } | 
|  | } | 
|  |  | 
|  | static void safe_unlink(char *f) | 
|  | { | 
|  | if (f) { | 
|  | unlink(f); | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Upon success, the caller is reponsible for unlinking and freeing *kernel_path | 
|  | */ | 
|  | static bool read_eif_kernel(FILE *f, uint64_t size, char **kernel_path, | 
|  | QCryptoHash *hash0, QCryptoHash *hash1, | 
|  | uint32_t *crc, Error **errp) | 
|  | { | 
|  | size_t got; | 
|  | FILE *tmp_file = NULL; | 
|  | uint8_t *kernel = g_try_malloc(size); | 
|  | if (!kernel) { | 
|  | error_setg(errp, "Out of memory reading kernel section"); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | *kernel_path = NULL; | 
|  | if (!get_tmp_file("eif-kernel-XXXXXX", kernel_path, errp)) { | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | tmp_file = fopen(*kernel_path, "wb"); | 
|  | if (tmp_file == NULL) { | 
|  | error_setg_errno(errp, errno, "Failed to open temporary file %s", | 
|  | *kernel_path); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | got = fread(kernel, 1, size, f); | 
|  | if ((uint64_t) got != size) { | 
|  | error_setg(errp, "Failed to read EIF kernel section data"); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | got = fwrite(kernel, 1, size, tmp_file); | 
|  | if ((uint64_t) got != size) { | 
|  | error_setg(errp, "Failed to write EIF kernel section data to temporary" | 
|  | " file"); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | *crc = crc32(*crc, kernel, size); | 
|  | if (qcrypto_hash_update(hash0, (char *)kernel, size, errp) != 0 || | 
|  | qcrypto_hash_update(hash1, (char *)kernel, size, errp) != 0) { | 
|  | goto cleanup; | 
|  | } | 
|  | g_free(kernel); | 
|  | fclose(tmp_file); | 
|  |  | 
|  | return true; | 
|  |  | 
|  | cleanup: | 
|  | safe_fclose(tmp_file); | 
|  |  | 
|  | safe_unlink(*kernel_path); | 
|  | g_free(*kernel_path); | 
|  | *kernel_path = NULL; | 
|  |  | 
|  | g_free(kernel); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | static bool read_eif_cmdline(FILE *f, uint64_t size, char *cmdline, | 
|  | QCryptoHash *hash0, QCryptoHash *hash1, | 
|  | uint32_t *crc, Error **errp) | 
|  | { | 
|  | size_t got = fread(cmdline, 1, size, f); | 
|  | if ((uint64_t) got != size) { | 
|  | error_setg(errp, "Failed to read EIF cmdline section data"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *crc = crc32(*crc, (uint8_t *)cmdline, size); | 
|  | if (qcrypto_hash_update(hash0, cmdline, size, errp) != 0 || | 
|  | qcrypto_hash_update(hash1, cmdline, size, errp) != 0) { | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | static bool read_eif_ramdisk(FILE *eif, FILE *initrd, uint64_t size, | 
|  | QCryptoHash *hash0, QCryptoHash *h, uint32_t *crc, | 
|  | Error **errp) | 
|  | { | 
|  | size_t got; | 
|  | bool ret = false; | 
|  | uint8_t *ramdisk = g_try_malloc(size); | 
|  | if (!ramdisk) { | 
|  | error_setg(errp, "Out of memory reading initrd section"); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | got = fread(ramdisk, 1, size, eif); | 
|  | if ((uint64_t) got != size) { | 
|  | error_setg(errp, "Failed to read EIF ramdisk section data"); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | got = fwrite(ramdisk, 1, size, initrd); | 
|  | if ((uint64_t) got != size) { | 
|  | error_setg(errp, "Failed to write EIF ramdisk data to temporary file"); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | *crc = crc32(*crc, ramdisk, size); | 
|  | if (qcrypto_hash_update(hash0, (char *)ramdisk, size, errp) != 0 || | 
|  | qcrypto_hash_update(h, (char *)ramdisk, size, errp) != 0) { | 
|  | goto cleanup; | 
|  | } | 
|  | ret = true; | 
|  |  | 
|  | cleanup: | 
|  | g_free(ramdisk); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static bool get_signature_fingerprint_sha384(FILE *eif, uint64_t size, | 
|  | uint8_t *sha384, | 
|  | uint32_t *crc, | 
|  | Error **errp) | 
|  | { | 
|  | size_t got; | 
|  | g_autofree uint8_t *sig = NULL; | 
|  | g_autofree uint8_t *cert = NULL; | 
|  | cbor_item_t *item = NULL; | 
|  | cbor_item_t *pcr0 = NULL; | 
|  | size_t len; | 
|  | size_t hash_len = QCRYPTO_HASH_DIGEST_LEN_SHA384; | 
|  | struct cbor_pair *pair; | 
|  | struct cbor_load_result result; | 
|  | bool ret = false; | 
|  |  | 
|  | sig = g_try_malloc(size); | 
|  | if (!sig) { | 
|  | error_setg(errp, "Out of memory reading signature section"); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | got = fread(sig, 1, size, eif); | 
|  | if ((uint64_t) got != size) { | 
|  | error_setg(errp, "Failed to read EIF signature section data"); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | *crc = crc32(*crc, sig, size); | 
|  |  | 
|  | item = cbor_load(sig, size, &result); | 
|  | if (!item || result.error.code != CBOR_ERR_NONE) { | 
|  | error_setg(errp, "Failed to load signature section data as CBOR"); | 
|  | goto cleanup; | 
|  | } | 
|  | if (!cbor_isa_array(item) || cbor_array_size(item) < 1) { | 
|  | error_setg(errp, "Invalid signature CBOR"); | 
|  | goto cleanup; | 
|  | } | 
|  | pcr0 = cbor_array_get(item, 0); | 
|  | if (!pcr0) { | 
|  | error_setg(errp, "Failed to get PCR0 signature"); | 
|  | goto cleanup; | 
|  | } | 
|  | if (!cbor_isa_map(pcr0) || cbor_map_size(pcr0) != 2) { | 
|  | error_setg(errp, "Invalid signature CBOR"); | 
|  | goto cleanup; | 
|  | } | 
|  | pair = cbor_map_handle(pcr0); | 
|  | if (!cbor_isa_string(pair->key) || cbor_string_length(pair->key) != 19 || | 
|  | memcmp(cbor_string_handle(pair->key), "signing_certificate", 19) != 0) { | 
|  | error_setg(errp, "Invalid signautre CBOR"); | 
|  | goto cleanup; | 
|  | } | 
|  | if (!cbor_isa_array(pair->value)) { | 
|  | error_setg(errp, "Invalid signature CBOR"); | 
|  | goto cleanup; | 
|  | } | 
|  | len = cbor_array_size(pair->value); | 
|  | if (len == 0) { | 
|  | error_setg(errp, "Invalid signature CBOR"); | 
|  | goto cleanup; | 
|  | } | 
|  | cert = g_try_malloc(len); | 
|  | if (!cert) { | 
|  | error_setg(errp, "Out of memory reading signature section"); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < len; ++i) { | 
|  | cbor_item_t *tmp = cbor_array_get(pair->value, i); | 
|  | if (!tmp) { | 
|  | error_setg(errp, "Invalid signature CBOR"); | 
|  | goto cleanup; | 
|  | } | 
|  | if (!cbor_isa_uint(tmp) || cbor_int_get_width(tmp) != CBOR_INT_8) { | 
|  | cbor_decref(&tmp); | 
|  | error_setg(errp, "Invalid signature CBOR"); | 
|  | goto cleanup; | 
|  | } | 
|  | cert[i] = cbor_get_uint8(tmp); | 
|  | cbor_decref(&tmp); | 
|  | } | 
|  |  | 
|  | if (qcrypto_get_x509_cert_fingerprint(cert, len, QCRYPTO_HASH_ALGO_SHA384, | 
|  | sha384, &hash_len, errp)) { | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | ret = true; | 
|  |  | 
|  | cleanup: | 
|  | if (pcr0) { | 
|  | cbor_decref(&pcr0); | 
|  | } | 
|  | if (item) { | 
|  | cbor_decref(&item); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Expects file to have offset 0 before this function is called */ | 
|  | static long get_file_size(FILE *f, Error **errp) | 
|  | { | 
|  | long size; | 
|  |  | 
|  | if (fseek(f, 0, SEEK_END) != 0) { | 
|  | error_setg_errno(errp, errno, "Failed to seek to the end of file"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | size = ftell(f); | 
|  | if (size == -1) { | 
|  | error_setg_errno(errp, errno, "Failed to get offset"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | if (fseek(f, 0, SEEK_SET) != 0) { | 
|  | error_setg_errno(errp, errno, "Failed to seek back to the start"); | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | return size; | 
|  | } | 
|  |  | 
|  | static bool get_SHA384_hash(QCryptoHash *h, uint8_t *hash, Error **errp) | 
|  | { | 
|  | size_t hash_len = QCRYPTO_HASH_DIGEST_LEN_SHA384; | 
|  | return qcrypto_hash_finalize_bytes(h, &hash, &hash_len, errp) == 0; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Upon success, the caller is reponsible for unlinking and freeing | 
|  | * *kernel_path, *initrd_path and freeing *cmdline. | 
|  | */ | 
|  | bool read_eif_file(const char *eif_path, const char *machine_initrd, | 
|  | char **kernel_path, char **initrd_path, char **cmdline, | 
|  | uint8_t *image_hash, uint8_t *bootstrap_hash, | 
|  | uint8_t *app_hash, uint8_t *fingerprint_hash, | 
|  | bool *signature_found, Error **errp) | 
|  | { | 
|  | FILE *f = NULL; | 
|  | FILE *machine_initrd_f = NULL; | 
|  | FILE *initrd_path_f = NULL; | 
|  | long machine_initrd_size; | 
|  | uint32_t crc = 0; | 
|  | EifHeader eif_header; | 
|  | bool seen_sections[EIF_SECTION_MAX] = {false}; | 
|  | /* kernel + ramdisks + cmdline SHA384 hash */ | 
|  | g_autoptr(QCryptoHash) hash0 = NULL; | 
|  | /* kernel + boot ramdisk + cmdline SHA384 hash */ | 
|  | g_autoptr(QCryptoHash) hash1 = NULL; | 
|  | /* application ramdisk(s) SHA384 hash */ | 
|  | g_autoptr(QCryptoHash) hash2 = NULL; | 
|  |  | 
|  | *signature_found = false; | 
|  | *kernel_path = *initrd_path = *cmdline = NULL; | 
|  |  | 
|  | hash0 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp); | 
|  | if (!hash0) { | 
|  | goto cleanup; | 
|  | } | 
|  | hash1 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp); | 
|  | if (!hash1) { | 
|  | goto cleanup; | 
|  | } | 
|  | hash2 = qcrypto_hash_new(QCRYPTO_HASH_ALGO_SHA384, errp); | 
|  | if (!hash2) { | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | f = fopen(eif_path, "rb"); | 
|  | if (f == NULL) { | 
|  | error_setg_errno(errp, errno, "Failed to open %s", eif_path); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | if (!read_eif_header(f, &eif_header, &crc, errp)) { | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | if (eif_header.version < 4) { | 
|  | error_setg(errp, "Expected EIF version 4 or greater"); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | if (eif_header.flags != 0) { | 
|  | error_setg(errp, "Expected EIF flags to be 0"); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | if (eif_header.section_cnt > MAX_SECTIONS) { | 
|  | error_setg(errp, "EIF header section count must not be greater than " | 
|  | "%d but found %d", MAX_SECTIONS, eif_header.section_cnt); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | for (int i = 0; i < eif_header.section_cnt; ++i) { | 
|  | EifSectionHeader hdr; | 
|  | uint16_t section_type; | 
|  |  | 
|  | if (eif_header.section_offsets[i] > OFF_MAX) { | 
|  | error_setg(errp, "Invalid EIF image. Section offset out of bounds"); | 
|  | goto cleanup; | 
|  | } | 
|  | if (fseek(f, eif_header.section_offsets[i], SEEK_SET) != 0) { | 
|  | error_setg_errno(errp, errno, "Failed to offset to %" PRIu64 " in EIF file", | 
|  | eif_header.section_offsets[i]); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | if (!read_eif_section_header(f, &hdr, &crc, errp)) { | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | if (hdr.flags != 0) { | 
|  | error_setg(errp, "Expected EIF section header flags to be 0"); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | if (eif_header.section_sizes[i] != hdr.section_size) { | 
|  | error_setg(errp, "EIF section size mismatch between header and " | 
|  | "section header: header %" PRIu64 ", section header %" PRIu64, | 
|  | eif_header.section_sizes[i], | 
|  | hdr.section_size); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | section_type = hdr.section_type; | 
|  |  | 
|  | switch (section_type) { | 
|  | case EIF_SECTION_KERNEL: | 
|  | if (seen_sections[EIF_SECTION_KERNEL]) { | 
|  | error_setg(errp, "Invalid EIF image. More than 1 kernel " | 
|  | "section"); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | if (!read_eif_kernel(f, hdr.section_size, kernel_path, hash0, | 
|  | hash1, &crc, errp)) { | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | break; | 
|  | case EIF_SECTION_CMDLINE: | 
|  | { | 
|  | uint64_t size; | 
|  | if (seen_sections[EIF_SECTION_CMDLINE]) { | 
|  | error_setg(errp, "Invalid EIF image. More than 1 cmdline " | 
|  | "section"); | 
|  | goto cleanup; | 
|  | } | 
|  | size = hdr.section_size; | 
|  | *cmdline = g_try_malloc(size + 1); | 
|  | if (!*cmdline) { | 
|  | error_setg(errp, "Out of memory reading command line section"); | 
|  | goto cleanup; | 
|  | } | 
|  | if (!read_eif_cmdline(f, size, *cmdline, hash0, hash1, &crc, | 
|  | errp)) { | 
|  | goto cleanup; | 
|  | } | 
|  | (*cmdline)[size] = '\0'; | 
|  |  | 
|  | break; | 
|  | } | 
|  | case EIF_SECTION_RAMDISK: | 
|  | { | 
|  | QCryptoHash *h = hash2; | 
|  | if (!seen_sections[EIF_SECTION_RAMDISK]) { | 
|  | /* | 
|  | * If this is the first time we are seeing a ramdisk section, | 
|  | * we need to: | 
|  | * 1) hash it into bootstrap (hash1) instead of app (hash2) | 
|  | *    along with image (hash0) | 
|  | * 2) create the initrd temporary file. | 
|  | */ | 
|  | h = hash1; | 
|  | if (!get_tmp_file("eif-initrd-XXXXXX", initrd_path, errp)) { | 
|  | goto cleanup; | 
|  | } | 
|  | initrd_path_f = fopen(*initrd_path, "wb"); | 
|  | if (initrd_path_f == NULL) { | 
|  | error_setg_errno(errp, errno, "Failed to open file %s", | 
|  | *initrd_path); | 
|  | goto cleanup; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!read_eif_ramdisk(f, initrd_path_f, hdr.section_size, hash0, h, | 
|  | &crc, errp)) { | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | break; | 
|  | } | 
|  | case EIF_SECTION_SIGNATURE: | 
|  | *signature_found = true; | 
|  | if (!get_signature_fingerprint_sha384(f, hdr.section_size, | 
|  | fingerprint_hash, &crc, | 
|  | errp)) { | 
|  | goto cleanup; | 
|  | } | 
|  | break; | 
|  | default: | 
|  | /* other sections including invalid or unknown sections */ | 
|  | { | 
|  | uint8_t *buf; | 
|  | size_t got; | 
|  | uint64_t size = hdr.section_size; | 
|  | buf = g_try_malloc(size); | 
|  | if (!buf) { | 
|  | error_setg(errp, "Out of memory reading unknown section"); | 
|  | goto cleanup; | 
|  | } | 
|  | got = fread(buf, 1, size, f); | 
|  | if ((uint64_t) got != size) { | 
|  | g_free(buf); | 
|  | error_setg(errp, "Failed to read EIF %s section data", | 
|  | section_type_to_string(section_type)); | 
|  | goto cleanup; | 
|  | } | 
|  | crc = crc32(crc, buf, size); | 
|  | g_free(buf); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (section_type < EIF_SECTION_MAX) { | 
|  | seen_sections[section_type] = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!seen_sections[EIF_SECTION_KERNEL]) { | 
|  | error_setg(errp, "Invalid EIF image. No kernel section."); | 
|  | goto cleanup; | 
|  | } | 
|  | if (!seen_sections[EIF_SECTION_CMDLINE]) { | 
|  | error_setg(errp, "Invalid EIF image. No cmdline section."); | 
|  | goto cleanup; | 
|  | } | 
|  | if (!seen_sections[EIF_SECTION_RAMDISK]) { | 
|  | error_setg(errp, "Invalid EIF image. No ramdisk section."); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | if (eif_header.eif_crc32 != crc) { | 
|  | error_setg(errp, "CRC mismatch. Expected %u but header has %u.", | 
|  | crc, eif_header.eif_crc32); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Let's append the initrd file from "-initrd" option if any. Although | 
|  | * we pass the crc pointer to read_eif_ramdisk, it is not useful anymore. | 
|  | * We have already done the crc mismatch check above this code. | 
|  | */ | 
|  | if (machine_initrd) { | 
|  | machine_initrd_f = fopen(machine_initrd, "rb"); | 
|  | if (machine_initrd_f == NULL) { | 
|  | error_setg_errno(errp, errno, "Failed to open initrd file %s", | 
|  | machine_initrd); | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | machine_initrd_size = get_file_size(machine_initrd_f, errp); | 
|  | if (machine_initrd_size == -1) { | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | if (!read_eif_ramdisk(machine_initrd_f, initrd_path_f, | 
|  | machine_initrd_size, hash0, hash2, &crc, errp)) { | 
|  | goto cleanup; | 
|  | } | 
|  | } | 
|  |  | 
|  | if (!get_SHA384_hash(hash0, image_hash, errp)) { | 
|  | goto cleanup; | 
|  | } | 
|  | if (!get_SHA384_hash(hash1, bootstrap_hash, errp)) { | 
|  | goto cleanup; | 
|  | } | 
|  | if (!get_SHA384_hash(hash2, app_hash, errp)) { | 
|  | goto cleanup; | 
|  | } | 
|  |  | 
|  | fclose(f); | 
|  | fclose(initrd_path_f); | 
|  | safe_fclose(machine_initrd_f); | 
|  | return true; | 
|  |  | 
|  | cleanup: | 
|  | safe_fclose(f); | 
|  | safe_fclose(initrd_path_f); | 
|  | safe_fclose(machine_initrd_f); | 
|  |  | 
|  | safe_unlink(*kernel_path); | 
|  | g_free(*kernel_path); | 
|  | *kernel_path = NULL; | 
|  |  | 
|  | safe_unlink(*initrd_path); | 
|  | g_free(*initrd_path); | 
|  | *initrd_path = NULL; | 
|  |  | 
|  | g_free(*cmdline); | 
|  | *cmdline = NULL; | 
|  |  | 
|  | return false; | 
|  | } |