From 725d7e7fb9ca1065102e640cf8af606169e63075 Mon Sep 17 00:00:00 2001 From: Dave Howells Date: Tue, 23 Oct 2012 09:30:54 -0400 Subject: [PATCH 1/4] 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 eed2202..1da1b3c 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -389,6 +389,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; @@ -524,6 +530,20 @@ typedef struct { #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.3.1 From f204253a34b477c5d68df711326a694b58e51c76 Mon Sep 17 00:00:00 2001 From: Dave Howells Date: Tue, 23 Oct 2012 09:36:28 -0400 Subject: [PATCH 2/4] 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 6d2c2ea..ace9c30 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -35,4 +35,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 1da1b3c..42a1d25 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -619,6 +619,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.3.1 From cebc8870a8f0b97b2585a07d9957ead0f21cdc8a Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Fri, 26 Oct 2012 12:36:24 -0400 Subject: [PATCH 3/4] 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 9f3cfdc..547f617 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1676,6 +1676,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" help diff --git a/kernel/module_signing.c b/kernel/module_signing.c index 0b6b870..0a29b40 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 4ca7072..b19cc6e 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 __initdata const u8 system_certificate_list[]; extern __initdata const u8 system_certificate_list_end[]; @@ -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.3.1 From aa4f39ac51f8b9f589c13766e5e32a5b7794ed68 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Fri, 26 Oct 2012 12:42:16 -0400 Subject: [PATCH 4/4] 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 42a1d25..d3e6036 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -395,6 +395,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 547f617..8e943bf 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -1794,6 +1794,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 1ff5e3b..f9b9998 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -56,6 +56,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 @@ -115,6 +116,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.3.1