f2c60e
From 90dc66270b02981b19a085c6a9184e3452b7b3e8 Mon Sep 17 00:00:00 2001
f2c60e
From: Josh Boyer <jwboyer@fedoraproject.org>
f2c60e
Date: Fri, 5 May 2017 08:21:59 +0100
f2c60e
Subject: [PATCH 3/4] MODSIGN: Import certificates from UEFI Secure Boot
f2c60e
f2c60e
Secure Boot stores a list of allowed certificates in the 'db' variable.
f2c60e
This imports those certificates into the system trusted keyring.  This
f2c60e
allows for a third party signing certificate to be used in conjunction
f2c60e
with signed modules.  By importing the public certificate into the 'db'
f2c60e
variable, a user can allow a module signed with that certificate to
f2c60e
load.  The shim UEFI bootloader has a similar certificate list stored
f2c60e
in the 'MokListRT' variable.  We import those as well.
f2c60e
f2c60e
Secure Boot also maintains a list of disallowed certificates in the 'dbx'
f2c60e
variable.  We load those certificates into the newly introduced system
f2c60e
blacklist keyring and forbid any module signed with those from loading and
f2c60e
forbid the use within the kernel of any key with a matching hash.
f2c60e
f2c60e
This facility is enabled by setting CONFIG_LOAD_UEFI_KEYS.
f2c60e
f2c60e
Signed-off-by: Josh Boyer <jwboyer@fedoraproject.org>
f2c60e
Signed-off-by: David Howells <dhowells@redhat.com>
f2c60e
---
f2c60e
 certs/Kconfig     |  16 ++++++
f2c60e
 certs/Makefile    |   4 ++
f2c60e
 certs/load_uefi.c | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
f2c60e
 3 files changed, 188 insertions(+)
f2c60e
 create mode 100644 certs/load_uefi.c
f2c60e
f2c60e
diff --git a/certs/Kconfig b/certs/Kconfig
f2c60e
index 630ae09..edf9f75 100644
f2c60e
--- a/certs/Kconfig
f2c60e
+++ b/certs/Kconfig
f2c60e
@@ -90,4 +90,20 @@ config EFI_SIGNATURE_LIST_PARSER
f2c60e
 	  This option provides support for parsing EFI signature lists for
f2c60e
 	  X.509 certificates and turning them into keys.
f2c60e
f2c60e
+config LOAD_UEFI_KEYS
f2c60e
+	bool "Load certs and blacklist from UEFI db for module checking"
f2c60e
+	depends on SYSTEM_BLACKLIST_KEYRING
f2c60e
+	depends on SECONDARY_TRUSTED_KEYRING
f2c60e
+	depends on EFI
f2c60e
+	depends on EFI_SIGNATURE_LIST_PARSER
f2c60e
+	help
f2c60e
+	  If the kernel is booted in secure boot mode, this option will cause
f2c60e
+	  the kernel to load the certificates from the UEFI db and MokListRT
f2c60e
+	  into the secondary trusted keyring.  It will also load any X.509
f2c60e
+	  SHA256 hashes in the dbx list into the blacklist.
f2c60e
+
f2c60e
+	  The effect of this is that, if the kernel is booted in secure boot
f2c60e
+	  mode, modules signed with UEFI-stored keys will be permitted to be
f2c60e
+	  loaded and keys that match the blacklist will be rejected.
f2c60e
+
f2c60e
 endmenu
f2c60e
diff --git a/certs/Makefile b/certs/Makefile
f2c60e
index 738151a..a5e057a 100644
f2c60e
--- a/certs/Makefile
f2c60e
+++ b/certs/Makefile
f2c60e
@@ -11,6 +11,10 @@ obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_nohashes.o
f2c60e
 endif
f2c60e
 obj-$(CONFIG_EFI_SIGNATURE_LIST_PARSER) += efi_parser.o
f2c60e
f2c60e
+obj-$(CONFIG_LOAD_UEFI_KEYS) += load_uefi.o
f2c60e
+$(obj)/load_uefi.o: KBUILD_CFLAGS += -fshort-wchar
f2c60e
+
f2c60e
+
f2c60e
 ifeq ($(CONFIG_SYSTEM_TRUSTED_KEYRING),y)
f2c60e
f2c60e
 $(eval $(call config_filename,SYSTEM_TRUSTED_KEYS))
f2c60e
diff --git a/certs/load_uefi.c b/certs/load_uefi.c
f2c60e
new file mode 100644
f2c60e
index 0000000..b44e464
f2c60e
--- /dev/null
f2c60e
+++ b/certs/load_uefi.c
f2c60e
@@ -0,0 +1,168 @@
f2c60e
+#include <linux/kernel.h>
f2c60e
+#include <linux/sched.h>
f2c60e
+#include <linux/cred.h>
f2c60e
+#include <linux/err.h>
f2c60e
+#include <linux/efi.h>
f2c60e
+#include <linux/slab.h>
f2c60e
+#include <keys/asymmetric-type.h>
f2c60e
+#include <keys/system_keyring.h>
f2c60e
+#include "internal.h"
f2c60e
+
f2c60e
+static __initdata efi_guid_t efi_cert_x509_guid = EFI_CERT_X509_GUID;
f2c60e
+static __initdata efi_guid_t efi_cert_x509_sha256_guid = EFI_CERT_X509_SHA256_GUID;
f2c60e
+static __initdata efi_guid_t efi_cert_sha256_guid = EFI_CERT_SHA256_GUID;
f2c60e
+
f2c60e
+/*
f2c60e
+ * Get a certificate list blob from the named EFI variable.
f2c60e
+ */
f2c60e
+static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid,
f2c60e
+				  unsigned long *size)
f2c60e
+{
f2c60e
+	efi_status_t status;
f2c60e
+	unsigned long lsize = 4;
f2c60e
+	unsigned long tmpdb[4];
f2c60e
+	void *db;
f2c60e
+
f2c60e
+	status = efi.get_variable(name, guid, NULL, &lsize, &tmpdb);
f2c60e
+	if (status != EFI_BUFFER_TOO_SMALL) {
f2c60e
+		pr_err("Couldn't get size: 0x%lx\n", status);
f2c60e
+		return NULL;
f2c60e
+	}
f2c60e
+
f2c60e
+	db = kmalloc(lsize, GFP_KERNEL);
f2c60e
+	if (!db) {
f2c60e
+		pr_err("Couldn't allocate memory for uefi cert list\n");
f2c60e
+		return NULL;
f2c60e
+	}
f2c60e
+
f2c60e
+	status = efi.get_variable(name, guid, NULL, &lsize, db);
f2c60e
+	if (status != EFI_SUCCESS) {
f2c60e
+		kfree(db);
f2c60e
+		pr_err("Error reading db var: 0x%lx\n", status);
f2c60e
+		return NULL;
f2c60e
+	}
f2c60e
+
f2c60e
+	*size = lsize;
f2c60e
+	return db;
f2c60e
+}
f2c60e
+
f2c60e
+/*
f2c60e
+ * Blacklist an X509 TBS hash.
f2c60e
+ */
f2c60e
+static __init void uefi_blacklist_x509_tbs(const char *source,
f2c60e
+					   const void *data, size_t len)
f2c60e
+{
f2c60e
+	char *hash, *p;
f2c60e
+
f2c60e
+	hash = kmalloc(4 + len * 2 + 1, GFP_KERNEL);
f2c60e
+	if (!hash)
f2c60e
+		return;
f2c60e
+	p = memcpy(hash, "tbs:", 4);
f2c60e
+	p += 4;
f2c60e
+	bin2hex(p, data, len);
f2c60e
+	p += len * 2;
f2c60e
+	*p = 0;
f2c60e
+
f2c60e
+	mark_hash_blacklisted(hash);
f2c60e
+	kfree(hash);
f2c60e
+}
f2c60e
+
f2c60e
+/*
f2c60e
+ * Blacklist the hash of an executable.
f2c60e
+ */
f2c60e
+static __init void uefi_blacklist_binary(const char *source,
f2c60e
+					 const void *data, size_t len)
f2c60e
+{
f2c60e
+	char *hash, *p;
f2c60e
+
f2c60e
+	hash = kmalloc(4 + len * 2 + 1, GFP_KERNEL);
f2c60e
+	if (!hash)
f2c60e
+		return;
f2c60e
+	p = memcpy(hash, "bin:", 4);
f2c60e
+	p += 4;
f2c60e
+	bin2hex(p, data, len);
f2c60e
+	p += len * 2;
f2c60e
+	*p = 0;
f2c60e
+
f2c60e
+	mark_hash_blacklisted(hash);
f2c60e
+	kfree(hash);
f2c60e
+}
f2c60e
+
f2c60e
+/*
f2c60e
+ * Return the appropriate handler for particular signature list types found in
f2c60e
+ * the UEFI db and MokListRT tables.
f2c60e
+ */
f2c60e
+static __init efi_element_handler_t get_handler_for_db(const efi_guid_t *sig_type)
f2c60e
+{
f2c60e
+	if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
f2c60e
+		return add_trusted_secondary_key;
f2c60e
+	return 0;
f2c60e
+}
f2c60e
+
f2c60e
+/*
f2c60e
+ * Return the appropriate handler for particular signature list types found in
f2c60e
+ * the UEFI dbx and MokListXRT tables.
f2c60e
+ */
f2c60e
+static __init efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type)
f2c60e
+{
f2c60e
+	if (efi_guidcmp(*sig_type, efi_cert_x509_sha256_guid) == 0)
f2c60e
+		return uefi_blacklist_x509_tbs;
f2c60e
+	if (efi_guidcmp(*sig_type, efi_cert_sha256_guid) == 0)
f2c60e
+		return uefi_blacklist_binary;
f2c60e
+	return 0;
f2c60e
+}
f2c60e
+
f2c60e
+/*
f2c60e
+ * Load the certs contained in the UEFI databases
f2c60e
+ */
f2c60e
+static int __init load_uefi_certs(void)
f2c60e
+{
f2c60e
+	efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
f2c60e
+	efi_guid_t mok_var = EFI_SHIM_LOCK_GUID;
f2c60e
+	void *db = NULL, *dbx = NULL, *mok = NULL;
f2c60e
+	unsigned long dbsize = 0, dbxsize = 0, moksize = 0;
f2c60e
+	int rc = 0;
f2c60e
+
f2c60e
+	if (!efi.get_variable)
f2c60e
+		return false;
f2c60e
+
f2c60e
+	/* Get db, MokListRT, and dbx.  They might not exist, so it isn't
f2c60e
+	 * an error if we can't get them.
f2c60e
+	 */
f2c60e
+	db = get_cert_list(L"db", &secure_var, &dbsize);
f2c60e
+	if (!db) {
f2c60e
+		pr_err("MODSIGN: Couldn't get UEFI db list\n");
f2c60e
+	} else {
f2c60e
+		rc = parse_efi_signature_list("UEFI:db",
f2c60e
+					      db, dbsize, get_handler_for_db);
f2c60e
+		if (rc)
f2c60e
+			pr_err("Couldn't parse db signatures: %d\n", rc);
f2c60e
+		kfree(db);
f2c60e
+	}
f2c60e
+
f2c60e
+	mok = get_cert_list(L"MokListRT", &mok_var, &moksize);
f2c60e
+	if (!mok) {
f2c60e
+		pr_info("MODSIGN: Couldn't get UEFI MokListRT\n");
f2c60e
+	} else {
f2c60e
+		rc = parse_efi_signature_list("UEFI:MokListRT",
f2c60e
+					      mok, moksize, get_handler_for_db);
f2c60e
+		if (rc)
f2c60e
+			pr_err("Couldn't parse MokListRT signatures: %d\n", rc);
f2c60e
+		kfree(mok);
f2c60e
+	}
f2c60e
+
f2c60e
+	dbx = get_cert_list(L"dbx", &secure_var, &dbxsize);
f2c60e
+	if (!dbx) {
f2c60e
+		pr_info("MODSIGN: Couldn't get UEFI dbx list\n");
f2c60e
+	} else {
f2c60e
+		rc = parse_efi_signature_list("UEFI:dbx",
f2c60e
+					      dbx, dbxsize,
f2c60e
+					      get_handler_for_dbx);
f2c60e
+		if (rc)
f2c60e
+			pr_err("Couldn't parse dbx signatures: %d\n", rc);
f2c60e
+		kfree(dbx);
f2c60e
+	}
f2c60e
+
f2c60e
+	return rc;
f2c60e
+}
f2c60e
+late_initcall(load_uefi_certs);
f2c60e
-- 
f2c60e
2.9.3
f2c60e