mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-07-26 11:53:53 -06:00

We were storing the pointers to buffers in a GList due to lack of stateful crypto apis and instead doing the final hash computation at the end after we had all the necessary buffers. Now that we have the stateful qcrypto apis available, we can instead update the hashes inline in the read_eif_* functions which makes the code much simpler. Signed-off-by: Dorjoy Chowdhury <dorjoychy111@gmail.com> Reviewed-by: Alexander Graf <graf@amazon.com> Message-ID: <20241109123039.24180-1-dorjoychy111@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
709 lines
20 KiB
C
709 lines
20 KiB
C
/*
|
|
* 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;
|
|
}
|