Bugzilla: N/A Upstream-status: Fedora mustard for now From 2b668e069365b608e855cf1f5edcf8caed0aaa4d Mon Sep 17 00:00:00 2001 From: Dave Howells Date: Tue, 23 Oct 2012 09:30:54 -0400 Subject: [PATCH 1/5] Add EFI signature data types Add the data types that are used for containing hashes, keys and certificates for cryptographic verification. Signed-off-by: David Howells --- include/linux/efi.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/linux/efi.h b/include/linux/efi.h index 0c1d367..de1faea 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -394,6 +394,12 @@ typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long si #define EFI_FILE_SYSTEM_GUID \ EFI_GUID( 0x964e5b22, 0x6459, 0x11d2, 0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b ) +#define EFI_CERT_SHA256_GUID \ + EFI_GUID( 0xc1c41626, 0x504c, 0x4092, 0xac, 0xa9, 0x41, 0xf9, 0x36, 0x93, 0x43, 0x28 ) + +#define EFI_CERT_X509_GUID \ + EFI_GUID( 0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72 ) + typedef struct { efi_guid_t guid; u64 table; @@ -541,6 +547,20 @@ typedef struct _efi_file_io_interface { #define EFI_INVALID_TABLE_ADDR (~0UL) +typedef struct { + efi_guid_t signature_owner; + u8 signature_data[]; +} efi_signature_data_t; + +typedef struct { + efi_guid_t signature_type; + u32 signature_list_size; + u32 signature_header_size; + u32 signature_size; + u8 signature_header[]; + /* efi_signature_data_t signatures[][] */ +} efi_signature_list_t; + /* * All runtime access to EFI goes through this structure: */ -- 1.8.4.2 From 42d75e3e3fe134cc274f765525031b764540a587 Mon Sep 17 00:00:00 2001 From: Dave Howells Date: Tue, 23 Oct 2012 09:36:28 -0400 Subject: [PATCH 2/5] Add an EFI signature blob parser and key loader. X.509 certificates are loaded into the specified keyring as asymmetric type keys. Signed-off-by: David Howells --- crypto/asymmetric_keys/Kconfig | 8 +++ crypto/asymmetric_keys/Makefile | 1 + crypto/asymmetric_keys/efi_parser.c | 109 ++++++++++++++++++++++++++++++++++++ include/linux/efi.h | 4 ++ 4 files changed, 122 insertions(+) create mode 100644 crypto/asymmetric_keys/efi_parser.c diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 03a6eb9..6306ffc 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -37,4 +37,12 @@ config X509_CERTIFICATE_PARSER data and provides the ability to instantiate a crypto key from a public key packet found inside the certificate. +config EFI_SIGNATURE_LIST_PARSER + bool "EFI signature list parser" + depends on EFI + select X509_CERTIFICATE_PARSER + help + This option provides support for parsing EFI signature lists for + X.509 certificates and turning them into keys. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 0727204..cd8388e 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -8,6 +8,7 @@ asymmetric_keys-y := asymmetric_type.o signature.o obj-$(CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE) += public_key.o obj-$(CONFIG_PUBLIC_KEY_ALGO_RSA) += rsa.o +obj-$(CONFIG_EFI_SIGNATURE_LIST_PARSER) += efi_parser.o # # X.509 Certificate handling diff --git a/crypto/asymmetric_keys/efi_parser.c b/crypto/asymmetric_keys/efi_parser.c new file mode 100644 index 0000000..424896a --- /dev/null +++ b/crypto/asymmetric_keys/efi_parser.c @@ -0,0 +1,109 @@ +/* EFI signature/key/certificate list parser + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "EFI: "fmt +#include +#include +#include +#include +#include + +static __initdata efi_guid_t efi_cert_x509_guid = EFI_CERT_X509_GUID; + +/** + * parse_efi_signature_list - Parse an EFI signature list for certificates + * @data: The data blob to parse + * @size: The size of the data blob + * @keyring: The keyring to add extracted keys to + */ +int __init parse_efi_signature_list(const void *data, size_t size, struct key *keyring) +{ + unsigned offs = 0; + size_t lsize, esize, hsize, elsize; + + pr_devel("-->%s(,%zu)\n", __func__, size); + + while (size > 0) { + efi_signature_list_t list; + const efi_signature_data_t *elem; + key_ref_t key; + + if (size < sizeof(list)) + return -EBADMSG; + + memcpy(&list, data, sizeof(list)); + pr_devel("LIST[%04x] guid=%pUl ls=%x hs=%x ss=%x\n", + offs, + list.signature_type.b, list.signature_list_size, + list.signature_header_size, list.signature_size); + + lsize = list.signature_list_size; + hsize = list.signature_header_size; + esize = list.signature_size; + elsize = lsize - sizeof(list) - hsize; + + if (lsize > size) { + pr_devel("<--%s() = -EBADMSG [overrun @%x]\n", + __func__, offs); + return -EBADMSG; + } + if (lsize < sizeof(list) || + lsize - sizeof(list) < hsize || + esize < sizeof(*elem) || + elsize < esize || + elsize % esize != 0) { + pr_devel("- bad size combo @%x\n", offs); + return -EBADMSG; + } + + if (efi_guidcmp(list.signature_type, efi_cert_x509_guid) != 0) { + data += lsize; + size -= lsize; + offs += lsize; + continue; + } + + data += sizeof(list) + hsize; + size -= sizeof(list) + hsize; + offs += sizeof(list) + hsize; + + for (; elsize > 0; elsize -= esize) { + elem = data; + + pr_devel("ELEM[%04x]\n", offs); + + key = key_create_or_update( + make_key_ref(keyring, 1), + "asymmetric", + NULL, + &elem->signature_data, + esize - sizeof(*elem), + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW, + KEY_ALLOC_NOT_IN_QUOTA | + KEY_ALLOC_TRUSTED); + + if (IS_ERR(key)) + pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", + PTR_ERR(key)); + else + pr_notice("Loaded cert '%s' linked to '%s'\n", + key_ref_to_ptr(key)->description, + keyring->description); + + data += esize; + size -= esize; + offs += esize; + } + } + + return 0; +} diff --git a/include/linux/efi.h b/include/linux/efi.h index de1faea..13e1425 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -641,6 +641,10 @@ extern int efi_set_rtc_mmss(const struct timespec *now); extern void efi_reserve_boot_services(void); extern struct efi_memory_map memmap; +struct key; +extern int __init parse_efi_signature_list(const void *data, size_t size, + struct key *keyring); + /** * efi_range_is_wc - check the WC bit on an address range * @start: starting kvirt address -- 1.8.4.2 From d750dbcdcb3a712a2ea4ec57b9c9729c6a26b41d Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Fri, 26 Oct 2012 12:36:24 -0400 Subject: [PATCH 3/5] KEYS: Add a system blacklist keyring This adds an additional keyring that is used to store certificates that are blacklisted. This keyring is searched first when loading signed modules and if the module's certificate is found, it will refuse to load. This is useful in cases where third party certificates are used for module signing. Signed-off-by: Josh Boyer --- include/keys/system_keyring.h | 4 ++++ init/Kconfig | 9 +++++++++ kernel/module_signing.c | 12 ++++++++++++ kernel/system_keyring.c | 17 +++++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 8dabc39..e466de1 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -18,6 +18,10 @@ extern struct key *system_trusted_keyring; +#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING +extern struct key *system_blacklist_keyring; +#endif + #endif #endif /* _KEYS_SYSTEM_KEYRING_H */ diff --git a/init/Kconfig b/init/Kconfig index 5236dc5..f59e6fe 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1673,6 +1673,15 @@ config SYSTEM_TRUSTED_KEYRING Keys in this keyring are used by module signature checking. +config SYSTEM_BLACKLIST_KEYRING + bool "Provide system-wide ring of blacklisted keys" + depends on KEYS + help + Provide a system keyring to which blacklisted keys can be added. Keys + in the keyring are considered entirely untrusted. Keys in this keyring + are used by the module signature checking to reject loading of modules + signed with a blacklisted key. + menuconfig MODULES bool "Enable loadable module support" option modules diff --git a/kernel/module_signing.c b/kernel/module_signing.c index be5b8fa..fed815f 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c @@ -158,6 +158,18 @@ static struct key *request_asymmetric_key(const char *signer, size_t signer_len, pr_debug("Look up: \"%s\"\n", id); +#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING + key = keyring_search(make_key_ref(system_blacklist_keyring, 1), + &key_type_asymmetric, id); + if (!IS_ERR(key)) { + /* module is signed with a cert in the blacklist. reject */ + pr_err("Module key '%s' is in blacklist\n", id); + key_ref_put(key); + kfree(id); + return ERR_PTR(-EKEYREJECTED); + } +#endif + key = keyring_search(make_key_ref(system_trusted_keyring, 1), &key_type_asymmetric, id); if (IS_ERR(key)) diff --git a/kernel/system_keyring.c b/kernel/system_keyring.c index 52ebc70..478c4f8 100644 --- a/kernel/system_keyring.c +++ b/kernel/system_keyring.c @@ -20,6 +20,9 @@ struct key *system_trusted_keyring; EXPORT_SYMBOL_GPL(system_trusted_keyring); +#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING +struct key *system_blacklist_keyring; +#endif extern __initconst const u8 system_certificate_list[]; extern __initconst const unsigned long system_certificate_list_size; @@ -41,6 +44,20 @@ static __init int system_trusted_keyring_init(void) panic("Can't allocate system trusted keyring\n"); set_bit(KEY_FLAG_TRUSTED_ONLY, &system_trusted_keyring->flags); + +#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING + system_blacklist_keyring = keyring_alloc(".system_blacklist_keyring", + KUIDT_INIT(0), KGIDT_INIT(0), + current_cred(), + (KEY_POS_ALL & ~KEY_POS_SETATTR) | + KEY_USR_VIEW | KEY_USR_READ, + KEY_ALLOC_NOT_IN_QUOTA, NULL); + if (IS_ERR(system_blacklist_keyring)) + panic("Can't allocate system blacklist keyring\n"); + + set_bit(KEY_FLAG_TRUSTED_ONLY, &system_blacklist_keyring->flags); +#endif + return 0; } -- 1.8.4.2 From c32beadd0d75fddcd75b700e4a75884d7a82e9bb Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Fri, 26 Oct 2012 12:42:16 -0400 Subject: [PATCH 4/5] MODSIGN: Import certificates from UEFI Secure Boot Secure Boot stores a list of allowed certificates in the 'db' variable. This imports those certificates into the system trusted keyring. This allows for a third party signing certificate to be used in conjunction with signed modules. By importing the public certificate into the 'db' variable, a user can allow a module signed with that certificate to load. The shim UEFI bootloader has a similar certificate list stored in the 'MokListRT' variable. We import those as well. In the opposite case, Secure Boot maintains a list of disallowed certificates in the 'dbx' variable. We load those certificates into the newly introduced system blacklist keyring and forbid any module signed with those from loading. Signed-off-by: Josh Boyer --- include/linux/efi.h | 6 ++++ init/Kconfig | 9 +++++ kernel/Makefile | 3 ++ kernel/modsign_uefi.c | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 110 insertions(+) create mode 100644 kernel/modsign_uefi.c diff --git a/include/linux/efi.h b/include/linux/efi.h index 13e1425..a7175eb 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -400,6 +400,12 @@ typedef efi_status_t efi_query_variable_store_t(u32 attributes, unsigned long si #define EFI_CERT_X509_GUID \ EFI_GUID( 0xa5c059a1, 0x94e4, 0x4aa7, 0x87, 0xb5, 0xab, 0x15, 0x5c, 0x2b, 0xf0, 0x72 ) +#define EFI_IMAGE_SECURITY_DATABASE_GUID \ + EFI_GUID( 0xd719b2cb, 0x3d3a, 0x4596, 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f ) + +#define EFI_SHIM_LOCK_GUID \ + EFI_GUID( 0x605dab50, 0xe046, 0x4300, 0xab, 0xb6, 0x3d, 0xd8, 0x10, 0xdd, 0x8b, 0x23 ) + typedef struct { efi_guid_t guid; u64 table; diff --git a/init/Kconfig b/init/Kconfig index f59e6fe..90fa75f 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1792,6 +1792,15 @@ config MODULE_SIG_ALL comment "Do not forget to sign required modules with scripts/sign-file" depends on MODULE_SIG_FORCE && !MODULE_SIG_ALL +config MODULE_SIG_UEFI + bool "Allow modules signed with certs stored in UEFI" + depends on MODULE_SIG && SYSTEM_BLACKLIST_KEYRING && EFI + select EFI_SIGNATURE_LIST_PARSER + help + This will import certificates stored in UEFI and allow modules + signed with those to be loaded. It will also disallow loading + of modules stored in the UEFI dbx variable. + choice prompt "Which hash algorithm should modules be signed with?" depends on MODULE_SIG diff --git a/kernel/Makefile b/kernel/Makefile index bc010ee..bee938f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -44,6 +44,7 @@ obj-$(CONFIG_UID16) += uid16.o obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o obj-$(CONFIG_MODULES) += module.o obj-$(CONFIG_MODULE_SIG) += module_signing.o +obj-$(CONFIG_MODULE_SIG_UEFI) += modsign_uefi.o obj-$(CONFIG_KALLSYMS) += kallsyms.o obj-$(CONFIG_BSD_PROCESS_ACCT) += acct.o obj-$(CONFIG_KEXEC) += kexec.o @@ -96,6 +97,8 @@ obj-$(CONFIG_CONTEXT_TRACKING) += context_tracking.o $(obj)/configs.o: $(obj)/config_data.h +$(obj)/modsign_uefi.o: KBUILD_CFLAGS += -fshort-wchar + # config_data.h contains the same information as ikconfig.h but gzipped. # Info from config_data can be extracted from /proc/config* targets += config_data.gz diff --git a/kernel/modsign_uefi.c b/kernel/modsign_uefi.c new file mode 100644 index 0000000..94b0eb3 --- /dev/null +++ b/kernel/modsign_uefi.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "module-internal.h" + +static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, unsigned long *size) +{ + efi_status_t status; + unsigned long lsize = 4; + unsigned long tmpdb[4]; + void *db = NULL; + + status = efi.get_variable(name, guid, NULL, &lsize, &tmpdb); + if (status != EFI_BUFFER_TOO_SMALL) { + pr_err("Couldn't get size: 0x%lx\n", status); + return NULL; + } + + db = kmalloc(lsize, GFP_KERNEL); + if (!db) { + pr_err("Couldn't allocate memory for uefi cert list\n"); + goto out; + } + + status = efi.get_variable(name, guid, NULL, &lsize, db); + if (status != EFI_SUCCESS) { + kfree(db); + db = NULL; + pr_err("Error reading db var: 0x%lx\n", status); + } +out: + *size = lsize; + return db; +} + +/* + * * Load the certs contained in the UEFI databases + * */ +static int __init load_uefi_certs(void) +{ + efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID; + efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; + void *db = NULL, *dbx = NULL, *mok = NULL; + unsigned long dbsize = 0, dbxsize = 0, moksize = 0; + int rc = 0; + + /* Check if SB is enabled and just return if not */ + if (!efi_enabled(EFI_SECURE_BOOT)) + return 0; + + /* Get db, MokListRT, and dbx. They might not exist, so it isn't + * an error if we can't get them. + */ + db = get_cert_list(L"db", &secure_var, &dbsize); + if (!db) { + pr_err("MODSIGN: Couldn't get UEFI db list\n"); + } else { + rc = parse_efi_signature_list(db, dbsize, system_trusted_keyring); + if (rc) + pr_err("Couldn't parse db signatures: %d\n", rc); + kfree(db); + } + + mok = get_cert_list(L"MokListRT", &mok_var, &moksize); + if (!mok) { + pr_info("MODSIGN: Couldn't get UEFI MokListRT\n"); + } else { + rc = parse_efi_signature_list(mok, moksize, system_trusted_keyring); + if (rc) + pr_err("Couldn't parse MokListRT signatures: %d\n", rc); + kfree(mok); + } + + dbx = get_cert_list(L"dbx", &secure_var, &dbxsize); + if (!dbx) { + pr_info("MODSIGN: Couldn't get UEFI dbx list\n"); + } else { + rc = parse_efi_signature_list(dbx, dbxsize, + system_blacklist_keyring); + if (rc) + pr_err("Couldn't parse dbx signatures: %d\n", rc); + kfree(dbx); + } + + return rc; +} +late_initcall(load_uefi_certs); -- 1.8.4.2 From 5c86fc6c7e4d51286d75ee6d8ceedf983ae434fb Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Thu, 3 Oct 2013 10:14:23 -0400 Subject: [PATCH 5/5] MODSIGN: Support not importing certs from db If a user tells shim to not use the certs/hashes in the UEFI db variable for verification purposes, shim will set a UEFI variable called MokIgnoreDB. Have the uefi import code look for this and not import things from the db variable. Signed-off-by: Josh Boyer --- kernel/modsign_uefi.c | 40 +++++++++++++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/kernel/modsign_uefi.c b/kernel/modsign_uefi.c index 94b0eb3..ae28b97 100644 --- a/kernel/modsign_uefi.c +++ b/kernel/modsign_uefi.c @@ -8,6 +8,23 @@ #include #include "module-internal.h" +static __init int check_ignore_db(void) +{ + efi_status_t status; + unsigned int db = 0; + unsigned long size = sizeof(db); + efi_guid_t guid = EFI_SHIM_LOCK_GUID; + + /* Check and see if the MokIgnoreDB variable exists. If that fails + * then we don't ignore DB. If it succeeds, we do. + */ + status = efi.get_variable(L"MokIgnoreDB", &guid, NULL, &size, &db); + if (status != EFI_SUCCESS) + return 0; + + return 1; +} + static __init void *get_cert_list(efi_char16_t *name, efi_guid_t *guid, unsigned long *size) { efi_status_t status; @@ -47,23 +64,28 @@ static int __init load_uefi_certs(void) efi_guid_t mok_var = EFI_SHIM_LOCK_GUID; void *db = NULL, *dbx = NULL, *mok = NULL; unsigned long dbsize = 0, dbxsize = 0, moksize = 0; - int rc = 0; + int ignore_db, rc = 0; /* Check if SB is enabled and just return if not */ if (!efi_enabled(EFI_SECURE_BOOT)) return 0; + /* See if the user has setup Ignore DB mode */ + ignore_db = check_ignore_db(); + /* Get db, MokListRT, and dbx. They might not exist, so it isn't * an error if we can't get them. */ - db = get_cert_list(L"db", &secure_var, &dbsize); - if (!db) { - pr_err("MODSIGN: Couldn't get UEFI db list\n"); - } else { - rc = parse_efi_signature_list(db, dbsize, system_trusted_keyring); - if (rc) - pr_err("Couldn't parse db signatures: %d\n", rc); - kfree(db); + if (!ignore_db) { + db = get_cert_list(L"db", &secure_var, &dbsize); + if (!db) { + pr_err("MODSIGN: Couldn't get UEFI db list\n"); + } else { + rc = parse_efi_signature_list(db, dbsize, system_trusted_keyring); + if (rc) + pr_err("Couldn't parse db signatures: %d\n", rc); + kfree(db); + } } mok = get_cert_list(L"MokListRT", &mok_var, &moksize); -- 1.8.4.2