[U-Boot] [RFC 07/15] efi_loader: add signature verification functions

AKASHI Takahiro
2019-09-18T01:26:35Z

In this commit, implemented are efi_signature_verify_with_db(),
efi_signature_parse_sigdb() and a couple of helper functions which will be
used for variable authentication as well as image authentication in UEFI
secure boot.

efi_signature_verify_with_db() - authenticate an image with its hash value
for unsigned image, and with its embedded pkcs7 signature with a given
signature store if signed. This function will also be used to validate
authentication data in authenticated variables.

efi_signature_parse_sigdb() - parse signature database variable and
retrieve signature lists, which may consist of x509 certificates or message
digests (SHA256 only for now).

Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org>
---
include/efi_api.h | 47 +++
include/efi_loader.h | 47 +++
lib/efi_loader/Makefile | 1 +
lib/efi_loader/efi_signature.c | 602 +++++++++++++++++++++++++++++++++
4 files changed, 697 insertions(+)
create mode 100644 lib/efi_loader/efi_signature.c

diff --git a/include/efi_api.h b/include/efi_api.h
index 9f49a4575e07..72999f762515 100644
--- a/include/efi_api.h
+++ b/include/efi_api.h
@@ -18,6 +18,7 @@

#include <efi.h>
#include <charset.h>
+#include <pe.h>

#ifdef CONFIG_EFI_LOADER
#include <asm/setjmp.h>
@@ -307,6 +308,10 @@ struct efi_runtime_services {
EFI_GUID(0x8be4df61, 0x93ca, 0x11d2, 0xaa, 0x0d, \
0x00, 0xe0, 0x98, 0x03, 0x2b, 0x8c)

+#define EFI_IMAGE_SECURITY_DATABASE_GUID \
+ EFI_GUID(0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, \
+ 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f)
+
#define EFI_FDT_GUID \
EFI_GUID(0xb1b621d5, 0xf19c, 0x41a5, \
0x83, 0x0b, 0xd9, 0x15, 0x2c, 0x69, 0xaa, 0xe0)
@@ -1597,4 +1602,46 @@ struct efi_unicode_collation_protocol {
#define LOAD_OPTION_CATEGORY_BOOT 0x00000000
#define LOAD_OPTION_CATEGORY_APP 0x00000100

+/* Secure boot */
+#define EFI_CERT_SHA256_GUID \
+ EFI_GUID(0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, \
+ 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28)
+#define EFI_CERT_RSA2048_GUID \
+ EFI_GUID(0x3c5766e8, 0x269c, 0x4e34, 0xaa, 0x14, \
+ 0xed, 0x77, 0x6e, 0x85, 0xb3, 0xb6)
+#define EFI_CERT_X509_GUID \
+ EFI_GUID(0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, \
+ 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72)
+#define EFI_CERT_X509_SHA256_GUID \
+ EFI_GUID(0x3bd2a492, 0x96c0, 0x4079, 0xb4, 0x20, \
+ 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed)
+#define EFI_CERT_TYPE_PKCS7_GUID \
+ EFI_GUID(0x4aafd29d, 0x68df, 0x49ee, 0x8a, 0xa9, \
+ 0x34, 0x7d, 0x37, 0x56, 0x65, 0xa7)
+
+struct win_certificate_uefi_guid {
+ WIN_CERTIFICATE hdr;
+ efi_guid_t cert_type;
+ u8 cert_data[];
+} __attribute__((__packed__));
+
+struct efi_variable_authentication_2 {
+ struct efi_time time_stamp;
+ struct win_certificate_uefi_guid auth_info;
+} __attribute__((__packed__));
+
+struct efi_signature_data {
+ efi_guid_t signature_owner;
+ u8 signature_data[];
+} __attribute__((__packed__));
+
+struct efi_signature_list {
+ efi_guid_t signature_type;
+ u32 signature_list_size;
+ u32 signature_header_size;
+ u32 signature_size;
+/* u8 signature_header[signature_header_size]; */
+/* struct efi_signature_data signatures[...][signature_size]; */
+} __attribute__((__packed__));
+
#endif
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 5298ea7997f7..c75ee5fcb6ba 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -16,6 +16,7 @@
#if CONFIG_IS_ENABLED(EFI_LOADER)

#include <linux/list.h>
+#include <linux/oid_registry.h>

/* Maximum number of configuration tables */
#define EFI_MAX_CONFIGURATION_TABLES 16
@@ -156,6 +157,11 @@ extern const efi_guid_t efi_guid_hii_config_routing_protocol;
extern const efi_guid_t efi_guid_hii_config_access_protocol;
extern const efi_guid_t efi_guid_hii_database_protocol;
extern const efi_guid_t efi_guid_hii_string_protocol;
+/* GUID for authentication */
+extern const efi_guid_t efi_guid_image_security_database;
+extern const efi_guid_t efi_guid_sha256;
+extern const efi_guid_t efi_guid_cert_x509;
+extern const efi_guid_t efi_guid_cert_x509_sha256;

extern unsigned int __efi_runtime_start, __efi_runtime_stop;
extern unsigned int __efi_runtime_rel_start, __efi_runtime_rel_stop;
@@ -654,6 +660,47 @@ void efi_deserialize_load_option(struct efi_load_option *lo, u8 *data);
unsigned long efi_serialize_load_option(struct efi_load_option *lo, u8 **data);
efi_status_t efi_bootmgr_load(efi_handle_t *handle);

+#ifdef CONFIG_EFI_SECURE_BOOT
+#include <image.h>
+
+#define EFI_REGS_MAX 16 /* currently good enough */
+
+typedef struct {
+ int num;
+ struct image_region reg[EFI_REGS_MAX];
+} efi_image_regions;
+
+struct efi_sig_data {
+ struct efi_sig_data *next;
+ efi_guid_t owner;
+ void *data;
+ size_t size;
+};
+
+typedef struct efi_signature_store {
+ struct efi_signature_store *next;
+ efi_guid_t sig_type;
+ struct efi_sig_data *sig_data_list;
+} efi_signature_store;
+
+struct pkcs7_message;
+
+bool efi_signature_verify_with_db(efi_image_regions *regs,
+ struct pkcs7_message *msg,
+ efi_signature_store *trusted);
+bool efi_signature_revoke(efi_image_regions *regs,
+ struct pkcs7_message *msg,
+ efi_signature_store *untrusted,
+ efi_signature_store *tsa);
+
+efi_status_t efi_image_region_add(efi_image_regions *ctx,
+ const void *start, const void *end,
+ int nocheck);
+
+void efi_sigstore_free(efi_signature_store *ctx);
+efi_signature_store *efi_sigstore_parse_sigdb(u16 *name);
+#endif /* CONFIG_EFI_SECURE_BOOT */
+
#else /* CONFIG_IS_ENABLED(EFI_LOADER) */

/* Without CONFIG_EFI_LOADER we don't have a runtime section, stub it out */
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 01769ea58ba6..49c996c89052 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -39,3 +39,4 @@ obj-$(CONFIG_PARTITIONS) += efi_disk.o
obj-$(CONFIG_NET) += efi_net.o
obj-$(CONFIG_GENERATE_ACPI_TABLE) += efi_acpi.o
obj-$(CONFIG_GENERATE_SMBIOS_TABLE) += efi_smbios.o
+obj-y += efi_signature.o
diff --git a/lib/efi_loader/efi_signature.c b/lib/efi_loader/efi_signature.c
new file mode 100644
index 000000000000..55a335cc44ae
--- /dev/null
+++ b/lib/efi_loader/efi_signature.c
@@ -0,0 +1,602 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2018 Patrick Wildt <patrick@blueri.se>
+ * Copyright (c) 2019 Linaro Limited, Author: AKASHI Takahiro
+ */
+
+#include <charset.h>
+#include <efi_loader.h>
+#include <image.h>
+#include <hexdump.h>
+#include <malloc.h>
+#include <pe.h>
+#include <linux/compat.h>
+#include <linux/oid_registry.h>
+#include <u-boot/rsa.h>
+#include <u-boot/sha256.h>
+/*
+ * avoid duplicated inclusion:
+ * #include "../lib/crypto/x509_parser.h"
+ */
+#include "../lib/crypto/pkcs7_parser.h"
+
+const efi_guid_t efi_guid_image_security_database =
+ EFI_IMAGE_SECURITY_DATABASE_GUID;
+const efi_guid_t efi_guid_sha256 = EFI_CERT_SHA256_GUID;
+const efi_guid_t efi_guid_cert_rsa2048 = EFI_CERT_RSA2048_GUID;
+const efi_guid_t efi_guid_cert_x509 = EFI_CERT_X509_GUID;
+const efi_guid_t efi_guid_cert_x509_sha256 = EFI_CERT_X509_SHA256_GUID;
+
+#ifdef CONFIG_EFI_SECURE_BOOT
+/* TODO: generalized for other hash algos */
+static const unsigned char WinIndirectSha256[] = {
+ 0x30, 0x33, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02,
+ 0x01, 0x0f, 0x30, 0x25, 0x03, 0x01, 0x00, 0xa0, 0x20, 0xa2, 0x1e, 0x80,
+ 0x1c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x4f, 0x00, 0x62, 0x00,
+ 0x73, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x74, 0x00, 0x65, 0x00,
+ 0x3e, 0x00, 0x3e, 0x00, 0x3e, 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60,
+ 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
+};
+
+static bool efi_hash_regions(efi_image_regions *regs, void **hash, size_t *size)
+{
+ *size = 0;
+ *hash = calloc(1, SHA256_SUM_LEN);
+ if (!*hash) {
+ debug("Out of memory\n");
+ return false;
+ }
+ *size = SHA256_SUM_LEN;
+
+ hash_calculate("sha256", regs->reg, regs->num, *hash);
+#ifdef DEBUG
+ debug("hash calculated:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
+ *hash, SHA256_SUM_LEN, false);
+#endif
+
+ return true;
+}
+
+static bool efi_hash_regions_in_der(efi_image_regions *regs, void **hash,
+ size_t *size)
+{
+ void *msg;
+ size_t msg_size;
+ struct image_region regtmp[2];
+
+ if (!efi_hash_regions(regs, &msg, &msg_size)) {
+ debug("Hash calculation failed\n");
+ return false;
+ ;
+ }
+
+ *size = 0;
+ *hash = calloc(1, SHA256_SUM_LEN);
+ if (!*hash) {
+ debug("Out of memory\n");
+ free(msg);
+ return false;
+ }
+ *size = SHA256_SUM_LEN;
+
+ /* File image hash is digested with some DER wrapper. */
+ regtmp[0].data = WinIndirectSha256;
+ regtmp[0].size = sizeof(WinIndirectSha256);
+ regtmp[1].data = msg;
+ regtmp[1].size = msg_size;
+
+ hash_calculate("sha256", regtmp, 2, *hash);
+#ifdef DEBUG
+ debug("hash calculated in der:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
+ *hash, SHA256_SUM_LEN, false);
+#endif
+
+ free(msg);
+
+ return true;
+}
+
+static bool efi_signature_verify(efi_image_regions *regs,
+ struct pkcs7_signed_info *ps_info,
+ struct x509_certificate *cert)
+{
+ struct image_sign_info info;
+ struct image_region regtmp[2];
+ void *hash;
+ size_t size;
+ char c;
+ bool verified;
+
+ debug("%s: Enter, %p, %p, %p(issuer: %s, subject: %s)\n", __func__,
+ regs, ps_info, cert, cert->issuer, cert->subject);
+
+ verified = false;
+
+ memset(&info, '\0', sizeof(info));
+ info.padding = image_get_padding_algo("pkcs-1.5");
+ /*
+ * Note: image_get_[checksum|crypto]_algo takes an string
+ * argument like "<checksum>,<crypto>"
+ */
+ if (!strcmp(ps_info->sig->hash_algo, "sha1")) {
+ info.checksum = image_get_checksum_algo("sha1,rsa2048");
+ info.name = "sha1,rsa2048";
+ } else if (!strcmp(ps_info->sig->hash_algo, "sha256")) {
+ info.checksum = image_get_checksum_algo("sha256,rsa2048");
+ info.name = "sha256,rsa2048";
+ } else {
+ debug("unknown msg digest algo: %s\n", ps_info->sig->hash_algo);
+ goto out;
+ }
+ info.crypto = image_get_crypto_algo(info.name);
+
+ info.key = cert->pub->key;
+ info.keylen = cert->pub->keylen;
+
+ /* verify signature */
+ debug("%s: crypto: %s, signature len:%x\n", __func__,
+ info.name, ps_info->sig->s_size);
+ if (ps_info->authattrs_len) {
+ debug("%s: RSA verify authentication attribute\n", __func__);
+ /*
+ * NOTE: This path will be executed only for
+ * PE image authentication
+ */
+
+ /* check if hash matches digest first */
+ debug("checking msg digest first, len:0x%x\n",
+ ps_info->msgdigest_len);
+
+ if (efi_hash_regions_in_der(regs, &hash, &size)) {
+ if (ps_info->msgdigest_len != size ||
+ memcmp(hash, ps_info->msgdigest, size)) {
+ debug("Digest doesn't match\n");
+ free(hash);
+ goto out;
+ }
+
+ free(hash);
+ } else {
+ debug("Digesting image failed\n");
+ goto out;
+ }
+
+ /* against digest */
+ c = 0x31;
+ regtmp[0].data = &c;
+ regtmp[0].size = 1;
+ regtmp[1].data = ps_info->authattrs;
+ regtmp[1].size = ps_info->authattrs_len;
+
+ if (!rsa_verify(&info, regtmp, 2,
+ ps_info->sig->s, ps_info->sig->s_size))
+ verified = true;
+ } else {
+ debug("%s: RSA verify content data\n", __func__);
+ /* against all data */
+ if (!rsa_verify(&info, regs->reg, regs->num,
+ ps_info->sig->s, ps_info->sig->s_size))
+ verified = true;
+ }
+
+out:
+ debug("%s: Exit, verified: %d\n", __func__, verified);
+ return verified;
+}
+
+static
+bool efi_signature_verify_with_list(efi_image_regions *regs,
+ struct pkcs7_signed_info *signed_info,
+ efi_signature_store *siglist)
+{
+ struct x509_certificate *cert;
+ struct efi_sig_data *sig_data;
+ bool verified = false;
+
+ debug("%s: Enter, %p, %p, %p\n", __func__, regs, signed_info, siglist);
+
+ if (!signed_info) {
+ void *hash;
+ size_t size;
+
+ debug("%s: unsigned image\n", __func__);
+ /* verify based on calculated hash value */
+ if (guidcmp(&siglist->sig_type, &efi_guid_sha256)) {
+ debug("Digest algorithm is not supported: %pUl\n",
+ &siglist->sig_type);
+ goto out;
+ }
+
+ /* TODO: other than CERT_SHA256 */
+ if (!efi_hash_regions(regs, &hash, &size)) {
+ debug("Digesting unsigned image failed\n");
+ goto out;
+ }
+
+ /* go through the list */
+ for (sig_data = siglist->sig_data_list; sig_data;
+ sig_data = sig_data->next) {
+#ifdef DEBUG
+ debug("Msg digest in database:\n");
+ print_hex_dump(" ", DUMP_PREFIX_OFFSET, 16, 1,
+ sig_data->data, sig_data->size, false);
+#endif
+ if ((sig_data->size == size) &&
+ !memcmp(sig_data->data, hash, size)) {
+ verified = true;
+ free(hash);
+ goto out;
+ }
+ }
+ free(hash);
+ goto out;
+ }
+
+ debug("%s: signed image\n", __func__);
+ if (guidcmp(&siglist->sig_type, &efi_guid_cert_x509)) {
+ debug("Signature type is not supported: %pUl\n",
+ &siglist->sig_type);
+ goto out;
+ }
+
+ /* go through the list */
+ for (sig_data = siglist->sig_data_list; sig_data;
+ sig_data = sig_data->next) {
+ /* TODO: owner check by policy? */
+
+ cert = x509_cert_parse(sig_data->data, sig_data->size);
+ if (IS_ERR(cert)) {
+ debug("Parsing x509 certificate failed\n");
+ goto out;
+ }
+
+ verified = efi_signature_verify(regs, signed_info, cert);
+ x509_free_certificate(cert);
+
+ if (verified)
+ break;
+ }
+
+out:
+ debug("%s: Exit, verified: %d\n", __func__, verified);
+ return verified;
+}
+
+bool efi_signature_verify_with_db(efi_image_regions *regs,
+ struct pkcs7_message *msg,
+ efi_signature_store *trusted)
+{
+ struct pkcs7_signed_info *info;
+ efi_signature_store *siglist;
+ bool verified = false;
+
+ if (!trusted)
+ goto out;
+
+ if (!trusted->sig_data_list)
+ goto out;
+
+ /* for unsigned image */
+ if (!msg) {
+ for (siglist = trusted; siglist; siglist = siglist->next)
+ if (efi_signature_verify_with_list(regs, NULL,
+ siglist)) {
+ verified = true;
+ goto out;
+ }
+
+ goto out;
+ }
+
+ /* signed image or variable */
+ for (info = msg->signed_infos; info; info = info->next) {
+ debug("Signed Info: digest algo: %s, pkey algo: %s\n",
+ info->sig->hash_algo, info->sig->pkey_algo);
+
+ for (siglist = trusted; siglist; siglist = siglist->next) {
+ if (efi_signature_verify_with_list(regs, info,
+ siglist)) {
+ verified = true;
+ goto out;
+ }
+ }
+ }
+
+out:
+ return verified;
+}
+
+/* TODO: TSA support */
+bool efi_signature_revoke(efi_image_regions *regs,
+ struct pkcs7_message *msg,
+ efi_signature_store *untrusted,
+ efi_signature_store *tsa)
+{
+ struct pkcs7_signed_info *info;
+ efi_signature_store *siglist;
+ bool rejected = false;
+
+ if (!untrusted)
+ goto out;
+
+ if (!untrusted->sig_data_list)
+ goto out;
+
+ for (info = msg->signed_infos; info; info = info->next) {
+ debug("Signed Info: digest algo: %s, pkey algo: %s\n",
+ info->sig->hash_algo, info->sig->pkey_algo);
+
+ for (siglist = untrusted; siglist; siglist = siglist->next) {
+ if (efi_signature_verify_with_list(regs, info,
+ siglist)) {
+ rejected = true;
+ goto out;
+ }
+ }
+ }
+
+out:
+ return rejected;
+}
+
+/*
+ * Image region helper. With this it's easier to record what parts
+ * of an image should be checksummed and then do the checksumming
+ * later depending on the hash.
+ */
+efi_status_t efi_image_region_add(efi_image_regions *ctx,
+ const void *start, const void *end,
+ int nocheck)
+{
+ struct image_region *reg;
+ int i, j;
+
+ if (ctx->num >= EFI_REGS_MAX) {
+ debug("%s: no more room for regions\n", __func__);
+ return EFI_OUT_OF_RESOURCES;
+ }
+
+ if (end < start)
+ return EFI_INVALID_PARAMETER;
+
+ for (i = 0; i < ctx->num; i++) {
+ reg = &ctx->reg[i];
+ if (nocheck)
+ continue;
+
+ if (start > reg->data + reg->size)
+ continue;
+
+ if ((start >= reg->data && start < reg->data + reg->size) ||
+ (end > reg->data && end < reg->data + reg->size)) {
+ debug("%s: new region already part of another\n",
+ __func__);
+ return EFI_INVALID_PARAMETER;
+ }
+
+ if (start < reg->data && end < reg->data + reg->size) {
+ for (j = ctx->num - 1; j >= i; j--)
+ memcpy(&ctx->reg[j], &ctx->reg[j + 1],
+ sizeof(*reg));
+ break;
+ }
+ }
+
+ reg = &ctx->reg[i];
+ reg->data = start;
+ reg->size = end - start;
+ ctx->num++;
+
+ return EFI_SUCCESS;
+}
+
+void efi_sigstore_free(efi_signature_store *sigstore)
+{
+ efi_signature_store *sigstore_next;
+ struct efi_sig_data *sig_data, *sig_data_next;
+
+ while (sigstore) {
+ sigstore_next = sigstore->next;
+
+ /* TODO: more structured data? */
+ sig_data = sigstore->sig_data_list;
+ while (sig_data) {
+ if (sig_data)
+ sig_data_next = sig_data->next;
+ free(sig_data->data);
+ free(sig_data);
+ sig_data = sig_data_next;
+ }
+
+ free(sigstore);
+ sigstore = sigstore_next;
+ }
+}
+
+static
+efi_signature_store *efi_sigstore_parse_siglist(struct efi_signature_list *esl)
+{
+ efi_signature_store *sigstore = NULL;
+ struct efi_sig_data *sig_data, *sig_data_next;
+ struct efi_signature_data *esd;
+ size_t left;
+
+ /*
+ * UEFI specification defines certificate types:
+ * for non-signed images,
+ * EFI_CERT_SHA256_GUID
+ * EFI_CERT_RSA2048_GUID
+ * EFI_CERT_RSA2048_SHA256_GUID
+ * EFI_CERT_SHA1_GUID
+ * EFI_CERT_RSA2048_SHA_GUID
+ * EFI_CERT_SHA224_GUID
+ * EFI_CERT_SHA384_GUID
+ * EFI_CERT_SHA512_GUID
+ *
+ * for signed images,
+ * EFI_CERT_X509_GUID
+ * NOTE: Each certificate will normally be in a separate
+ * EFI_SIGNATURE_LIST as the size may vary depending on
+ * its algo's.
+ *
+ * for timestamp revocation of certificate,
+ * EFI_CERT_X509_SHA512_GUID
+ * EFI_CERT_X509_SHA256_GUID
+ * EFI_CERT_X509_SHA384_GUID
+ */
+
+ if (esl->signature_list_size
+ <= (sizeof(*esl) + esl->signature_header_size)) {
+ debug("Siglist in wrong format\n");
+ return NULL;
+ }
+
+ /* Create a head */
+ sigstore = calloc(sizeof(*sigstore), 1);
+ if (!sigstore) {
+ debug("Out of memory\n");
+ goto err;
+ }
+ memcpy(&sigstore->sig_type, &esl->signature_type, sizeof(efi_guid_t));
+
+ /* Go through the list */
+ sig_data_next = NULL;
+ left = esl->signature_list_size
+ - (sizeof(*esl) + esl->signature_header_size);
+ esd = (struct efi_signature_data *)
+ ((u8 *)esl + sizeof(*esl) + esl->signature_header_size);
+
+ while ((left > 0) && left >= esl->signature_size) {
+ /* Signature must exist if there is remaining data. */
+ if (left < esl->signature_size) {
+ debug("Certificate is too small\n");
+ goto err;
+ }
+
+ sig_data = calloc(esl->signature_size
+ - sizeof(esd->signature_owner), 1);
+ if (!sig_data) {
+ debug("Out of memory\n");
+ goto err;
+ }
+
+ /* Append signature data */
+ memcpy(&sig_data->owner, &esd->signature_owner,
+ sizeof(efi_guid_t));
+ sig_data->size = esl->signature_size
+ - sizeof(esd->signature_owner);
+ sig_data->data = malloc(sig_data->size);
+ if (!sig_data->data) {
+ debug("Out of memory\n");
+ goto err;
+ }
+ memcpy(sig_data->data, esd->signature_data, sig_data->size);
+
+ sig_data->next = sig_data_next;
+ sig_data_next = sig_data;
+
+ /* Next */
+ esd = (struct efi_signature_data *)
+ ((u8 *)esd + esl->signature_size);
+ left -= esl->signature_size;
+ }
+ sigstore->sig_data_list = sig_data_next;
+
+ return sigstore;
+
+err:
+ efi_sigstore_free(sigstore);
+
+ return NULL;
+}
+
+efi_signature_store *efi_sigstore_parse_sigdb(u16 *name)
+{
+ efi_signature_store *sigstore = NULL, *sigstore_list;
+ struct efi_signature_list *esl;
+ const efi_guid_t *vendor;
+ void *db;
+ efi_uintn_t db_size;
+ efi_status_t ret;
+
+ if (!u16_strcmp(name, L"PK") || !u16_strcmp(name, L"KEK")) {
+ vendor = &efi_global_variable_guid;
+ } else if (!u16_strcmp(name, L"db") || !u16_strcmp(name, L"dbx")) {
+ vendor = &efi_guid_image_security_database;
+ } else {
+ debug("unknown signature database, %ls\n", name);
+ return NULL;
+ }
+
+ /* retrieve variable data */
+ db_size = 0;
+ ret = EFI_CALL(efi_get_variable(name, vendor, NULL, &db_size, NULL));
+ if (ret == EFI_NOT_FOUND) {
+ debug("variable, %ls, not found\n", name);
+ /*
+ * TODO:
+ * how should this condition be notified of to caller?
+ * Returning empty sigstore won't harm anything.
+ */
+ sigstore = calloc(sizeof(*sigstore), 1);
+
+ return sigstore;
+ } else if (ret != EFI_BUFFER_TOO_SMALL) {
+ debug("Getting variable, %ls, failed\n", name);
+ return NULL;
+ }
+
+ db = malloc(db_size);
+ if (!db) {
+ debug("Out of memory\n");
+ return NULL;
+ }
+
+ ret = EFI_CALL(efi_get_variable(name, vendor, NULL, &db_size, db));
+ if (ret != EFI_SUCCESS) {
+ debug("Getting variable, %ls, failed\n", name);
+ goto err;
+ }
+
+ /* Parse siglist list */
+ esl = db;
+ while (db_size > 0) {
+ /* List must exist if there is remaining data. */
+ if (db_size < sizeof(*esl)) {
+ debug("variable, %ls, in wrong format\n", name);
+ goto err;
+ }
+
+ if (db_size < esl->signature_list_size) {
+ debug("variable, %ls, in wrong format\n", name);
+ goto err;
+ }
+
+ /* Parse a single siglist. */
+ sigstore_list = efi_sigstore_parse_siglist(esl);
+ if (!sigstore_list) {
+ debug("Parsing signature list of %ls failed\n", name);
+ goto err;
+ }
+
+ /* Append siglist */
+ sigstore_list->next = sigstore;
+ sigstore = sigstore_list;
+
+ /* Next */
+ db_size -= esl->signature_list_size;
+ esl = (void *)esl + esl->signature_list_size;
+ }
+ free(db);
+
+ return sigstore;
+
+err:
+ efi_sigstore_free(sigstore);
+ free(db);
+
+ return NULL;
+}
+#endif /* CONFIG_EFI_SECURE_BOOT */
--
2.21.0