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