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