6a9155
From: Josh Boyer <jwboyer@fedoraproject.org>
6a9155
Date: Fri, 26 Oct 2012 12:42:16 -0400
6a9155
Subject: [PATCH] MODSIGN: Import certificates from UEFI Secure Boot
6a9155
6a9155
Secure Boot stores a list of allowed certificates in the 'db' variable.
6a9155
This imports those certificates into the system trusted keyring.  This
6a9155
allows for a third party signing certificate to be used in conjunction
6a9155
with signed modules.  By importing the public certificate into the 'db'
6a9155
variable, a user can allow a module signed with that certificate to
6a9155
load.  The shim UEFI bootloader has a similar certificate list stored
6a9155
in the 'MokListRT' variable.  We import those as well.
6a9155
6a9155
In the opposite case, Secure Boot maintains a list of disallowed
6a9155
certificates in the 'dbx' variable.  We load those certificates into
6a9155
the newly introduced system blacklist keyring and forbid any module
6a9155
signed with those from loading.
6a9155
6a9155
Signed-off-by: Josh Boyer <jwboyer@fedoraproject.org>
6a9155
---
6a9155
 include/linux/efi.h   |  6 ++++
6a9155
 init/Kconfig          |  9 +++++
6a9155
 kernel/Makefile       |  3 ++
6a9155
 kernel/modsign_uefi.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++
6a9155
 4 files changed, 110 insertions(+)
6a9155
 create mode 100644 kernel/modsign_uefi.c
6a9155
6a9155
diff --git a/include/linux/efi.h b/include/linux/efi.h
6a50f3
index 414c3c3d988d..d920a6be6c8b 100644
6a9155
--- a/include/linux/efi.h
6a9155
+++ b/include/linux/efi.h
6a50f3
@@ -601,6 +601,12 @@ void efi_native_runtime_setup(void);
6a9155
 #define EFI_CERT_X509_GUID \
6a9155
     EFI_GUID(  0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72 )
6a9155
 
6a9155
+#define EFI_IMAGE_SECURITY_DATABASE_GUID \
6a9155
+    EFI_GUID(  0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f )
6a9155
+
6a9155
+#define EFI_SHIM_LOCK_GUID \
6a9155
+    EFI_GUID(  0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 )
6a9155
+
6a9155
 typedef struct {
6a9155
 	efi_guid_t guid;
6a9155
 	u64 table;
6a9155
diff --git a/init/Kconfig b/init/Kconfig
088a50
index 62f6fd191e4f..648bb79d6b73 100644
6a9155
--- a/init/Kconfig
6a9155
+++ b/init/Kconfig
088a50
@@ -1906,6 +1906,15 @@ config MODULE_SIG_ALL
6a9155
 comment "Do not forget to sign required modules with scripts/sign-file"
6a9155
 	depends on MODULE_SIG_FORCE && !MODULE_SIG_ALL
6a9155
 
6a9155
+config MODULE_SIG_UEFI
6a9155
+	bool "Allow modules signed with certs stored in UEFI"
6a9155
+	depends on MODULE_SIG && SYSTEM_BLACKLIST_KEYRING && EFI
6a9155
+	select EFI_SIGNATURE_LIST_PARSER
6a9155
+	help
6a9155
+	  This will import certificates stored in UEFI and allow modules
6a9155
+	  signed with those to be loaded.  It will also disallow loading
6a9155
+	  of modules stored in the UEFI dbx variable.
6a9155
+
6a9155
 choice
6a9155
 	prompt "Which hash algorithm should modules be signed with?"
6a9155
 	depends on MODULE_SIG
6a9155
diff --git a/kernel/Makefile b/kernel/Makefile
088a50
index 43c4c920f30a..3193574387ac 100644
6a9155
--- a/kernel/Makefile
6a9155
+++ b/kernel/Makefile
84326c
@@ -48,6 +48,7 @@ obj-$(CONFIG_UID16) += uid16.o
6a9155
 obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o
6a9155
 obj-$(CONFIG_MODULES) += module.o
6a9155
 obj-$(CONFIG_MODULE_SIG) += module_signing.o
6a9155
+obj-$(CONFIG_MODULE_SIG_UEFI) += modsign_uefi.o
6a9155
 obj-$(CONFIG_KALLSYMS) += kallsyms.o
6a9155
 obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o
6a9155
 obj-$(CONFIG_KEXEC) += kexec.o
84326c
@@ -101,6 +102,8 @@ obj-$(CONFIG_TORTURE_TEST) += torture.o
6a9155
 
6a9155
 $(obj)/configs.o: $(obj)/config_data.h
6a9155
 
6a9155
+$(obj)/modsign_uefi.o: KBUILD_CFLAGS += -fshort-wchar
6a9155
+
6a9155
 # config_data.h contains the same information as ikconfig.h but gzipped.
6a9155
 # Info from config_data can be extracted from /proc/config*
6a9155
 targets += config_data.gz
6a9155
diff --git a/kernel/modsign_uefi.c b/kernel/modsign_uefi.c
6a9155
new file mode 100644
6a9155
index 000000000000..94b0eb38a284
6a9155
--- /dev/null
6a9155
+++ b/kernel/modsign_uefi.c
6a9155
@@ -0,0 +1,92 @@
6a9155
+#include <linux/kernel.h>
6a9155
+#include <linux/sched.h>
6a9155
+#include <linux/cred.h>
6a9155
+#include <linux/err.h>
6a9155
+#include <linux/efi.h>
6a9155
+#include <linux/slab.h>
6a9155
+#include <keys/asymmetric-type.h>
6a9155
+#include <keys/system_keyring.h>
6a9155
+#include "module-internal.h"
6a9155
+
6a9155
+static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, unsigned long *size)
6a9155
+{
6a9155
+	efi_status_t status;
6a9155
+	unsigned long lsize = 4;
6a9155
+	unsigned long tmpdb[4];
6a9155
+	void *db = NULL;
6a9155
+
6a9155
+	status = efi.get_variable(name, guid, NULL, &lsize, &tmpdb);
6a9155
+	if (status != EFI_BUFFER_TOO_SMALL) {
6a9155
+		pr_err("Couldn't get size: 0x%lx\n", status);
6a9155
+		return NULL;
6a9155
+	}
6a9155
+
6a9155
+	db = kmalloc(lsize, GFP_KERNEL);
6a9155
+	if (!db) {
6a9155
+		pr_err("Couldn't allocate memory for uefi cert list\n");
6a9155
+		goto out;
6a9155
+	}
6a9155
+
6a9155
+	status = efi.get_variable(name, guid, NULL, &lsize, db);
6a9155
+	if (status != EFI_SUCCESS) {
6a9155
+		kfree(db);
6a9155
+		db = NULL;
6a9155
+		pr_err("Error reading db var: 0x%lx\n", status);
6a9155
+	}
6a9155
+out:
6a9155
+	*size = lsize;
6a9155
+	return db;
6a9155
+}
6a9155
+
6a9155
+/*
6a9155
+ *  * Load the certs contained in the UEFI databases
6a9155
+ *   */
6a9155
+static int __init load_uefi_certs(void)
6a9155
+{
6a9155
+	efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
6a9155
+	efi_guid_t mok_var = EFI_SHIM_LOCK_GUID;
6a9155
+	void *db = NULL, *dbx = NULL, *mok = NULL;
6a9155
+	unsigned long dbsize = 0, dbxsize = 0, moksize = 0;
6a9155
+	int rc = 0;
6a9155
+
6a9155
+	/* Check if SB is enabled and just return if not */
6a9155
+	if (!efi_enabled(EFI_SECURE_BOOT))
6a9155
+		return 0;
6a9155
+
6a9155
+	/* Get db, MokListRT, and dbx.  They might not exist, so it isn't
6a9155
+	 * an error if we can't get them.
6a9155
+	 */
6a9155
+	db = get_cert_list(L"db", &secure_var, &dbsize);
6a9155
+	if (!db) {
6a9155
+		pr_err("MODSIGN: Couldn't get UEFI db list\n");
6a9155
+	} else {
6a9155
+		rc = parse_efi_signature_list(db, dbsize, system_trusted_keyring);
6a9155
+		if (rc)
6a9155
+			pr_err("Couldn't parse db signatures: %d\n", rc);
6a9155
+		kfree(db);
6a9155
+	}
6a9155
+
6a9155
+	mok = get_cert_list(L"MokListRT", &mok_var, &moksize);
6a9155
+	if (!mok) {
6a9155
+		pr_info("MODSIGN: Couldn't get UEFI MokListRT\n");
6a9155
+	} else {
6a9155
+		rc = parse_efi_signature_list(mok, moksize, system_trusted_keyring);
6a9155
+		if (rc)
6a9155
+			pr_err("Couldn't parse MokListRT signatures: %d\n", rc);
6a9155
+		kfree(mok);
6a9155
+	}
6a9155
+
6a9155
+	dbx = get_cert_list(L"dbx", &secure_var, &dbxsize);
6a9155
+	if (!dbx) {
6a9155
+		pr_info("MODSIGN: Couldn't get UEFI dbx list\n");
6a9155
+	} else {
6a9155
+		rc = parse_efi_signature_list(dbx, dbxsize,
6a9155
+			system_blacklist_keyring);
6a9155
+		if (rc)
6a9155
+			pr_err("Couldn't parse dbx signatures: %d\n", rc);
6a9155
+		kfree(dbx);
6a9155
+	}
6a9155
+
6a9155
+	return rc;
6a9155
+}
6a9155
+late_initcall(load_uefi_certs);