diff mbox series

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

Message ID 20190918012643.25192-8-takahiro.akashi@linaro.org
State RFC
Delegated to: Heinrich Schuchardt
Headers show
Series efi_loader: add secure boot support | expand

Commit Message

AKASHI Takahiro Sept. 18, 2019, 1:26 a.m. UTC
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 mbox series

Patch

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 */