diff options
Diffstat (limited to 'security/integrity/platform_certs')
-rw-r--r-- | security/integrity/platform_certs/efi_parser.c | 108 | ||||
-rw-r--r-- | security/integrity/platform_certs/load_uefi.c | 194 | ||||
-rw-r--r-- | security/integrity/platform_certs/platform_keyring.c | 58 |
3 files changed, 360 insertions, 0 deletions
diff --git a/security/integrity/platform_certs/efi_parser.c b/security/integrity/platform_certs/efi_parser.c new file mode 100644 index 000000000000..18f01f36fe6a --- /dev/null +++ b/security/integrity/platform_certs/efi_parser.c @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* EFI signature/key/certificate list parser + * + * Copyright (C) 2012, 2016 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + */ + +#define pr_fmt(fmt) "EFI: "fmt +#include <linux/module.h> +#include <linux/printk.h> +#include <linux/err.h> +#include <linux/efi.h> + +/** + * parse_efi_signature_list - Parse an EFI signature list for certificates + * @source: The source of the key + * @data: The data blob to parse + * @size: The size of the data blob + * @get_handler_for_guid: Get the handler func for the sig type (or NULL) + * + * Parse an EFI signature list looking for elements of interest. A list is + * made up of a series of sublists, where all the elements in a sublist are of + * the same type, but sublists can be of different types. + * + * For each sublist encountered, the @get_handler_for_guid function is called + * with the type specifier GUID and returns either a pointer to a function to + * handle elements of that type or NULL if the type is not of interest. + * + * If the sublist is of interest, each element is passed to the handler + * function in turn. + * + * Error EBADMSG is returned if the list doesn't parse correctly and 0 is + * returned if the list was parsed correctly. No error can be returned from + * the @get_handler_for_guid function or the element handler function it + * returns. + */ +int __init parse_efi_signature_list( + const char *source, + const void *data, size_t size, + efi_element_handler_t (*get_handler_for_guid)(const efi_guid_t *)) +{ + efi_element_handler_t handler; + unsigned int offs = 0; + + pr_devel("-->%s(,%zu)\n", __func__, size); + + while (size > 0) { + const efi_signature_data_t *elem; + efi_signature_list_t list; + size_t lsize, esize, hsize, elsize; + + 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; + } + + handler = get_handler_for_guid(&list.signature_type); + if (!handler) { + 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); + handler(source, + &elem->signature_data, + esize - sizeof(*elem)); + + data += esize; + size -= esize; + offs += esize; + } + } + + return 0; +} diff --git a/security/integrity/platform_certs/load_uefi.c b/security/integrity/platform_certs/load_uefi.c new file mode 100644 index 000000000000..81b19c52832b --- /dev/null +++ b/security/integrity/platform_certs/load_uefi.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/cred.h> +#include <linux/err.h> +#include <linux/efi.h> +#include <linux/slab.h> +#include <keys/asymmetric-type.h> +#include <keys/system_keyring.h> +#include "../integrity.h" + +static efi_guid_t efi_cert_x509_guid __initdata = EFI_CERT_X509_GUID; +static efi_guid_t efi_cert_x509_sha256_guid __initdata = + EFI_CERT_X509_SHA256_GUID; +static efi_guid_t efi_cert_sha256_guid __initdata = EFI_CERT_SHA256_GUID; + +/* + * Look to see if a UEFI variable called MokIgnoreDB exists and return true if + * it does. + * + * This UEFI variable is set by the shim if a user tells the shim to not use + * the certs/hashes in the UEFI db variable for verification purposes. If it + * is set, we should ignore the db variable also and the true return indicates + * this. + */ +static __init bool uefi_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; + + status = efi.get_variable(L"MokIgnoreDB", &guid, NULL, &size, &db); + return status == EFI_SUCCESS; +} + +/* + * Get a certificate list blob from the named EFI variable. + */ +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; + + 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) + return NULL; + + status = efi.get_variable(name, guid, NULL, &lsize, db); + if (status != EFI_SUCCESS) { + kfree(db); + pr_err("Error reading db var: 0x%lx\n", status); + return NULL; + } + + *size = lsize; + return db; +} + +/* + * Blacklist a hash. + */ +static __init void uefi_blacklist_hash(const char *source, const void *data, + size_t len, const char *type, + size_t type_len) +{ + char *hash, *p; + + hash = kmalloc(type_len + len * 2 + 1, GFP_KERNEL); + if (!hash) + return; + p = memcpy(hash, type, type_len); + p += type_len; + bin2hex(p, data, len); + p += len * 2; + *p = 0; + + mark_hash_blacklisted(hash); + kfree(hash); +} + +/* + * Blacklist an X509 TBS hash. + */ +static __init void uefi_blacklist_x509_tbs(const char *source, + const void *data, size_t len) +{ + uefi_blacklist_hash(source, data, len, "tbs:", 4); +} + +/* + * Blacklist the hash of an executable. + */ +static __init void uefi_blacklist_binary(const char *source, + const void *data, size_t len) +{ + uefi_blacklist_hash(source, data, len, "bin:", 4); +} + +/* + * Return the appropriate handler for particular signature list types found in + * the UEFI db and MokListRT tables. + */ +static __init efi_element_handler_t get_handler_for_db(const efi_guid_t * + sig_type) +{ + if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0) + return add_to_platform_keyring; + return 0; +} + +/* + * Return the appropriate handler for particular signature list types found in + * the UEFI dbx and MokListXRT tables. + */ +static __init efi_element_handler_t get_handler_for_dbx(const efi_guid_t * + sig_type) +{ + if (efi_guidcmp(*sig_type, efi_cert_x509_sha256_guid) == 0) + return uefi_blacklist_x509_tbs; + if (efi_guidcmp(*sig_type, efi_cert_sha256_guid) == 0) + return uefi_blacklist_binary; + return 0; +} + +/* + * Load the certs contained in the UEFI databases into the platform trusted + * keyring and the UEFI blacklisted X.509 cert SHA256 hashes into the blacklist + * keyring. + */ +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; + + if (!efi.get_variable) + return false; + + /* Get db, MokListRT, and dbx. They might not exist, so it isn't + * an error if we can't get them. + */ + if (!uefi_check_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("UEFI:db", + db, dbsize, get_handler_for_db); + 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("Couldn't get UEFI MokListRT\n"); + } else { + rc = parse_efi_signature_list("UEFI:MokListRT", + mok, moksize, get_handler_for_db); + 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("Couldn't get UEFI dbx list\n"); + } else { + rc = parse_efi_signature_list("UEFI:dbx", + dbx, dbxsize, + get_handler_for_dbx); + if (rc) + pr_err("Couldn't parse dbx signatures: %d\n", rc); + kfree(dbx); + } + + return rc; +} +late_initcall(load_uefi_certs); diff --git a/security/integrity/platform_certs/platform_keyring.c b/security/integrity/platform_certs/platform_keyring.c new file mode 100644 index 000000000000..bcafd7387729 --- /dev/null +++ b/security/integrity/platform_certs/platform_keyring.c @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Platform keyring for firmware/platform keys + * + * Copyright IBM Corporation, 2018 + * Author(s): Nayna Jain <nayna@linux.ibm.com> + */ + +#include <linux/export.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/cred.h> +#include <linux/err.h> +#include <linux/slab.h> +#include "../integrity.h" + +/** + * add_to_platform_keyring - Add to platform keyring without validation. + * @source: Source of key + * @data: The blob holding the key + * @len: The length of the data blob + * + * Add a key to the platform keyring without checking its trust chain. This + * is available only during kernel initialisation. + */ +void __init add_to_platform_keyring(const char *source, const void *data, + size_t len) +{ + key_perm_t perm; + int rc; + + perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW; + + rc = integrity_load_cert(INTEGRITY_KEYRING_PLATFORM, source, data, len, + perm); + if (rc) + pr_info("Error adding keys to platform keyring %s\n", source); +} + +/* + * Create the trusted keyrings. + */ +static __init int platform_keyring_init(void) +{ + int rc; + + rc = integrity_init_keyring(INTEGRITY_KEYRING_PLATFORM); + if (rc) + return rc; + + pr_notice("Platform Keyring initialized\n"); + return 0; +} + +/* + * Must be initialised before we try and load the keys into the keyring. + */ +device_initcall(platform_keyring_init); |