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