/* * SPDX-License-Identifier: GPL-2.0-or-later * * uefi vars device - parse and generate efi signature databases */ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "system/dma.h" #include "hw/uefi/var-service.h" /* * Add x509 certificate to list (with duplicate check). */ static void uefi_vars_siglist_add_x509(uefi_vars_siglist *siglist, QemuUUID *owner, void *data, uint64_t size) { uefi_vars_cert *c; QTAILQ_FOREACH(c, &siglist->x509, next) { if (c->size != size) { continue; } if (memcmp(c->data, data, size) != 0) { continue; } return; } c = g_malloc(sizeof(*c) + size); c->owner = *owner; c->size = size; memcpy(c->data, data, size); QTAILQ_INSERT_TAIL(&siglist->x509, c, next); } /* * Add sha256 hash to list (with duplicate check). */ static void uefi_vars_siglist_add_sha256(uefi_vars_siglist *siglist, QemuUUID *owner, void *data) { uefi_vars_hash *h; QTAILQ_FOREACH(h, &siglist->sha256, next) { if (memcmp(h->data, data, 32) != 0) { continue; } return; } h = g_malloc(sizeof(*h) + 32); h->owner = *owner; memcpy(h->data, data, 32); QTAILQ_INSERT_TAIL(&siglist->sha256, h, next); } void uefi_vars_siglist_init(uefi_vars_siglist *siglist) { memset(siglist, 0, sizeof(*siglist)); QTAILQ_INIT(&siglist->x509); QTAILQ_INIT(&siglist->sha256); } void uefi_vars_siglist_free(uefi_vars_siglist *siglist) { uefi_vars_cert *c, *cs; uefi_vars_hash *h, *hs; QTAILQ_FOREACH_SAFE(c, &siglist->x509, next, cs) { QTAILQ_REMOVE(&siglist->x509, c, next); g_free(c); } QTAILQ_FOREACH_SAFE(h, &siglist->sha256, next, hs) { QTAILQ_REMOVE(&siglist->sha256, h, next); g_free(h); } } /* * Parse UEFI signature list. */ void uefi_vars_siglist_parse(uefi_vars_siglist *siglist, void *data, uint64_t size) { efi_siglist *efilist; uint64_t start; while (size) { if (size < sizeof(*efilist)) { break; } efilist = data; if (size < efilist->siglist_size) { break; } if (uadd64_overflow(sizeof(*efilist), efilist->header_size, &start)) { break; } if (efilist->sig_size <= sizeof(QemuUUID)) { break; } if (qemu_uuid_is_equal(&efilist->guid_type, &EfiCertX509Guid)) { if (start + efilist->sig_size != efilist->siglist_size) { break; } uefi_vars_siglist_add_x509(siglist, (QemuUUID *)(data + start), data + start + sizeof(QemuUUID), efilist->sig_size - sizeof(QemuUUID)); } else if (qemu_uuid_is_equal(&efilist->guid_type, &EfiCertSha256Guid)) { if (efilist->sig_size != sizeof(QemuUUID) + 32) { break; } if (start + efilist->sig_size > efilist->siglist_size) { break; } while (start <= efilist->siglist_size - efilist->sig_size) { uefi_vars_siglist_add_sha256(siglist, (QemuUUID *)(data + start), data + start + sizeof(QemuUUID)); start += efilist->sig_size; } } else { QemuUUID be = qemu_uuid_bswap(efilist->guid_type); char *str_uuid = qemu_uuid_unparse_strdup(&be); warn_report("%s: unknown type (%s)", __func__, str_uuid); g_free(str_uuid); } data += efilist->siglist_size; size -= efilist->siglist_size; } } uint64_t uefi_vars_siglist_blob_size(uefi_vars_siglist *siglist) { uefi_vars_cert *c; uefi_vars_hash *h; uint64_t size = 0; QTAILQ_FOREACH(c, &siglist->x509, next) { size += sizeof(efi_siglist) + sizeof(QemuUUID) + c->size; } if (!QTAILQ_EMPTY(&siglist->sha256)) { size += sizeof(efi_siglist); QTAILQ_FOREACH(h, &siglist->sha256, next) { size += sizeof(QemuUUID) + 32; } } return size; } /* * Generate UEFI signature list. */ void uefi_vars_siglist_blob_generate(uefi_vars_siglist *siglist, void *data, uint64_t size) { uefi_vars_cert *c; uefi_vars_hash *h; efi_siglist *efilist; uint64_t pos = 0, start; uint32_t i; QTAILQ_FOREACH(c, &siglist->x509, next) { efilist = data + pos; efilist->guid_type = EfiCertX509Guid; efilist->sig_size = sizeof(QemuUUID) + c->size; efilist->header_size = 0; start = pos + sizeof(efi_siglist); memcpy(data + start, &c->owner, sizeof(QemuUUID)); memcpy(data + start + sizeof(QemuUUID), c->data, c->size); efilist->siglist_size = sizeof(efi_siglist) + efilist->sig_size; pos += efilist->siglist_size; } if (!QTAILQ_EMPTY(&siglist->sha256)) { efilist = data + pos; efilist->guid_type = EfiCertSha256Guid; efilist->sig_size = sizeof(QemuUUID) + 32; efilist->header_size = 0; i = 0; start = pos + sizeof(efi_siglist); QTAILQ_FOREACH(h, &siglist->sha256, next) { memcpy(data + start + efilist->sig_size * i, &h->owner, sizeof(QemuUUID)); memcpy(data + start + efilist->sig_size * i + sizeof(QemuUUID), h->data, 32); i++; } efilist->siglist_size = sizeof(efi_siglist) + efilist->sig_size * i; pos += efilist->siglist_size; } assert(pos == size); }