mirror of
https://github.com/torvalds/linux.git
synced 2024-11-21 19:41:42 +00:00
Fix CVE-2020-26541
-----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEqG5UsNXhtOCrfGQP+7dXa6fLC2sFAmBKRxMACgkQ+7dXa6fL C2trYg/7Brf6d0JUAw/MbjCcPVL5SmTHRGJwmKq7+du/Z4yqz3VcL/flk2cyvMr3 lvGQK+KTWTZLidovQA42e54XIaUh3cqwUhz9H3+X61gY7kWJvioEhvg1tD007L7O DrMMkRhh9nnAV5GOhHj1nxIcgmxwrKNkzevf157RRKWnm9VBNmeZsu0kd2Ffx0i0 EqsejQU+sP6MgeKjTTKXKVpvH2GGB0NJRrpQCJSR4t9GrAt+rGlcNJFdqqmyxhpj cGtEhtNO7MiigGHxCbzpK0g6l6f31si+WIAywdxF65DGQOF3gcgxHQlPDcNiC/RH PLPEchUH2fOv4koDQWM8HJ4XDS5eRZmYSh6WPrSxJwuNH/NDyWxKSxrBXGhRWTfx RaMe2wQcQq9Rge+e6PwR+nJEbdSL2BHxdAaBDqBlxY9A0c6onTy+XzVSLTKYUJ5u /Y/fND3eHvMPZt4WMMZDQzHVnHscXFYPI4y1EMDLcAof9ltNG5zLAJZ6mHi6rqGl q+VhSPFi6equ7szdV2cZ5ltSROdAnwkbycs1LgeSzh8LWe83Tkq0eDEHSTjGpQFY VWGBs6JGl1QPdQdSc3uqki1LdTYUy5w0Pr3h0Ff6L3NS9fUrzCMtsN+/4aQNzS+C cP22WM2IRDtN17pRASNjI4/6sL7X7/rLQ8KNq/QpQeD4+ZkINaI= =fLQY -----END PGP SIGNATURE----- Merge tag 'keys-cve-2020-26541-v3' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs Pull x509 dbx/mokx UEFI support from David Howells: "Here's a set of patches from Eric Snowberg[1] that add support for EFI_CERT_X509_GUID entries in the dbx and mokx UEFI tables (such entries cause matching certificates to be rejected). These are currently ignored and only the hash entries are made use of. Additionally Eric included his patches to allow such certificates to be preloaded. These patches deal with CVE-2020-26541. To quote Eric: 'This is the fifth patch series for adding support for EFI_CERT_X509_GUID entries [2]. It has been expanded to not only include dbx entries but also entries in the mokx. Additionally my series to preload these certificate [3] has also been included'" Link: https://lore.kernel.org/r/20210122181054.32635-1-eric.snowberg@oracle.com [1] Link: https://patchwork.kernel.org/project/linux-security-module/patch/20200916004927.64276-1-eric.snowberg@oracle.com/ [2] Link: https://lore.kernel.org/patchwork/cover/1315485/ [3] * tag 'keys-cve-2020-26541-v3' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs: integrity: Load mokx variables into the blacklist keyring certs: Add ability to preload revocation certs certs: Move load_system_certificate_list to a common function certs: Add EFI_CERT_X509_GUID support for dbx entries
This commit is contained in:
commit
b0e22b47f6
@ -83,4 +83,21 @@ config SYSTEM_BLACKLIST_HASH_LIST
|
||||
wrapper to incorporate the list into the kernel. Each <hash> should
|
||||
be a string of hex digits.
|
||||
|
||||
config SYSTEM_REVOCATION_LIST
|
||||
bool "Provide system-wide ring of revocation certificates"
|
||||
depends on SYSTEM_BLACKLIST_KEYRING
|
||||
depends on PKCS7_MESSAGE_PARSER=y
|
||||
help
|
||||
If set, this allows revocation certificates to be stored in the
|
||||
blacklist keyring and implements a hook whereby a PKCS#7 message can
|
||||
be checked to see if it matches such a certificate.
|
||||
|
||||
config SYSTEM_REVOCATION_KEYS
|
||||
string "X.509 certificates to be preloaded into the system blacklist keyring"
|
||||
depends on SYSTEM_REVOCATION_LIST
|
||||
help
|
||||
If set, this option should be the filename of a PEM-formatted file
|
||||
containing X.509 certificates to be included in the default blacklist
|
||||
keyring.
|
||||
|
||||
endmenu
|
||||
|
@ -3,8 +3,9 @@
|
||||
# Makefile for the linux kernel signature checking certificates.
|
||||
#
|
||||
|
||||
obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o
|
||||
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist.o
|
||||
obj-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += system_keyring.o system_certificates.o common.o
|
||||
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist.o common.o
|
||||
obj-$(CONFIG_SYSTEM_REVOCATION_LIST) += revocation_certificates.o
|
||||
ifneq ($(CONFIG_SYSTEM_BLACKLIST_HASH_LIST),"")
|
||||
obj-$(CONFIG_SYSTEM_BLACKLIST_KEYRING) += blacklist_hashes.o
|
||||
else
|
||||
@ -29,7 +30,7 @@ $(obj)/x509_certificate_list: scripts/extract-cert $(SYSTEM_TRUSTED_KEYS_SRCPREF
|
||||
$(call if_changed,extract_certs,$(SYSTEM_TRUSTED_KEYS_SRCPREFIX)$(CONFIG_SYSTEM_TRUSTED_KEYS))
|
||||
endif # CONFIG_SYSTEM_TRUSTED_KEYRING
|
||||
|
||||
clean-files := x509_certificate_list .x509.list
|
||||
clean-files := x509_certificate_list .x509.list x509_revocation_list
|
||||
|
||||
ifeq ($(CONFIG_MODULE_SIG),y)
|
||||
###############################################################################
|
||||
@ -104,3 +105,17 @@ targets += signing_key.x509
|
||||
$(obj)/signing_key.x509: scripts/extract-cert $(X509_DEP) FORCE
|
||||
$(call if_changed,extract_certs,$(MODULE_SIG_KEY_SRCPREFIX)$(CONFIG_MODULE_SIG_KEY))
|
||||
endif # CONFIG_MODULE_SIG
|
||||
|
||||
ifeq ($(CONFIG_SYSTEM_REVOCATION_LIST),y)
|
||||
|
||||
$(eval $(call config_filename,SYSTEM_REVOCATION_KEYS))
|
||||
|
||||
$(obj)/revocation_certificates.o: $(obj)/x509_revocation_list
|
||||
|
||||
quiet_cmd_extract_certs = EXTRACT_CERTS $(patsubst "%",%,$(2))
|
||||
cmd_extract_certs = scripts/extract-cert $(2) $@
|
||||
|
||||
targets += x509_revocation_list
|
||||
$(obj)/x509_revocation_list: scripts/extract-cert $(SYSTEM_REVOCATION_KEYS_SRCPREFIX)$(SYSTEM_REVOCATION_KEYS_FILENAME) FORCE
|
||||
$(call if_changed,extract_certs,$(SYSTEM_REVOCATION_KEYS_SRCPREFIX)$(CONFIG_SYSTEM_REVOCATION_KEYS))
|
||||
endif
|
||||
|
@ -17,9 +17,15 @@
|
||||
#include <linux/uidgid.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include "blacklist.h"
|
||||
#include "common.h"
|
||||
|
||||
static struct key *blacklist_keyring;
|
||||
|
||||
#ifdef CONFIG_SYSTEM_REVOCATION_LIST
|
||||
extern __initconst const u8 revocation_certificate_list[];
|
||||
extern __initconst const unsigned long revocation_certificate_list_size;
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The description must be a type prefix, a colon and then an even number of
|
||||
* hex digits. The hash is kept in the description.
|
||||
@ -145,6 +151,49 @@ int is_binary_blacklisted(const u8 *hash, size_t hash_len)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(is_binary_blacklisted);
|
||||
|
||||
#ifdef CONFIG_SYSTEM_REVOCATION_LIST
|
||||
/**
|
||||
* add_key_to_revocation_list - Add a revocation certificate to the blacklist
|
||||
* @data: The data blob containing the certificate
|
||||
* @size: The size of data blob
|
||||
*/
|
||||
int add_key_to_revocation_list(const char *data, size_t size)
|
||||
{
|
||||
key_ref_t key;
|
||||
|
||||
key = key_create_or_update(make_key_ref(blacklist_keyring, true),
|
||||
"asymmetric",
|
||||
NULL,
|
||||
data,
|
||||
size,
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW),
|
||||
KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_BUILT_IN);
|
||||
|
||||
if (IS_ERR(key)) {
|
||||
pr_err("Problem with revocation key (%ld)\n", PTR_ERR(key));
|
||||
return PTR_ERR(key);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* is_key_on_revocation_list - Determine if the key for a PKCS#7 message is revoked
|
||||
* @pkcs7: The PKCS#7 message to check
|
||||
*/
|
||||
int is_key_on_revocation_list(struct pkcs7_message *pkcs7)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = pkcs7_validate_trust(pkcs7, blacklist_keyring);
|
||||
|
||||
if (ret == 0)
|
||||
return -EKEYREJECTED;
|
||||
|
||||
return -ENOKEY;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Initialise the blacklist
|
||||
*/
|
||||
@ -177,3 +226,18 @@ static int __init blacklist_init(void)
|
||||
* Must be initialised before we try and load the keys into the keyring.
|
||||
*/
|
||||
device_initcall(blacklist_init);
|
||||
|
||||
#ifdef CONFIG_SYSTEM_REVOCATION_LIST
|
||||
/*
|
||||
* Load the compiled-in list of revocation X.509 certificates.
|
||||
*/
|
||||
static __init int load_revocation_certificate_list(void)
|
||||
{
|
||||
if (revocation_certificate_list_size)
|
||||
pr_notice("Loading compiled-in revocation X.509 certificates\n");
|
||||
|
||||
return load_certificate_list(revocation_certificate_list, revocation_certificate_list_size,
|
||||
blacklist_keyring);
|
||||
}
|
||||
late_initcall(load_revocation_certificate_list);
|
||||
#endif
|
||||
|
@ -1,3 +1,5 @@
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
|
||||
extern const char __initconst *const blacklist_hashes[];
|
||||
|
57
certs/common.c
Normal file
57
certs/common.c
Normal file
@ -0,0 +1,57 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/key.h>
|
||||
#include "common.h"
|
||||
|
||||
int load_certificate_list(const u8 cert_list[],
|
||||
const unsigned long list_size,
|
||||
const struct key *keyring)
|
||||
{
|
||||
key_ref_t key;
|
||||
const u8 *p, *end;
|
||||
size_t plen;
|
||||
|
||||
p = cert_list;
|
||||
end = p + list_size;
|
||||
while (p < end) {
|
||||
/* Each cert begins with an ASN.1 SEQUENCE tag and must be more
|
||||
* than 256 bytes in size.
|
||||
*/
|
||||
if (end - p < 4)
|
||||
goto dodgy_cert;
|
||||
if (p[0] != 0x30 &&
|
||||
p[1] != 0x82)
|
||||
goto dodgy_cert;
|
||||
plen = (p[2] << 8) | p[3];
|
||||
plen += 4;
|
||||
if (plen > end - p)
|
||||
goto dodgy_cert;
|
||||
|
||||
key = key_create_or_update(make_key_ref(keyring, 1),
|
||||
"asymmetric",
|
||||
NULL,
|
||||
p,
|
||||
plen,
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ),
|
||||
KEY_ALLOC_NOT_IN_QUOTA |
|
||||
KEY_ALLOC_BUILT_IN |
|
||||
KEY_ALLOC_BYPASS_RESTRICTION);
|
||||
if (IS_ERR(key)) {
|
||||
pr_err("Problem loading in-kernel X.509 certificate (%ld)\n",
|
||||
PTR_ERR(key));
|
||||
} else {
|
||||
pr_notice("Loaded X.509 cert '%s'\n",
|
||||
key_ref_to_ptr(key)->description);
|
||||
key_ref_put(key);
|
||||
}
|
||||
p += plen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
dodgy_cert:
|
||||
pr_err("Problem parsing in-kernel X.509 certificate list\n");
|
||||
return 0;
|
||||
}
|
9
certs/common.h
Normal file
9
certs/common.h
Normal file
@ -0,0 +1,9 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#ifndef _CERT_COMMON_H
|
||||
#define _CERT_COMMON_H
|
||||
|
||||
int load_certificate_list(const u8 cert_list[], const unsigned long list_size,
|
||||
const struct key *keyring);
|
||||
|
||||
#endif
|
21
certs/revocation_certificates.S
Normal file
21
certs/revocation_certificates.S
Normal file
@ -0,0 +1,21 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#include <linux/export.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
__INITRODATA
|
||||
|
||||
.align 8
|
||||
.globl revocation_certificate_list
|
||||
revocation_certificate_list:
|
||||
__revocation_list_start:
|
||||
.incbin "certs/x509_revocation_list"
|
||||
__revocation_list_end:
|
||||
|
||||
.align 8
|
||||
.globl revocation_certificate_list_size
|
||||
revocation_certificate_list_size:
|
||||
#ifdef CONFIG_64BIT
|
||||
.quad __revocation_list_end - __revocation_list_start
|
||||
#else
|
||||
.long __revocation_list_end - __revocation_list_start
|
||||
#endif
|
@ -16,6 +16,7 @@
|
||||
#include <keys/asymmetric-type.h>
|
||||
#include <keys/system_keyring.h>
|
||||
#include <crypto/pkcs7.h>
|
||||
#include "common.h"
|
||||
|
||||
static struct key *builtin_trusted_keys;
|
||||
#ifdef CONFIG_SECONDARY_TRUSTED_KEYRING
|
||||
@ -137,54 +138,10 @@ device_initcall(system_trusted_keyring_init);
|
||||
*/
|
||||
static __init int load_system_certificate_list(void)
|
||||
{
|
||||
key_ref_t key;
|
||||
const u8 *p, *end;
|
||||
size_t plen;
|
||||
|
||||
pr_notice("Loading compiled-in X.509 certificates\n");
|
||||
|
||||
p = system_certificate_list;
|
||||
end = p + system_certificate_list_size;
|
||||
while (p < end) {
|
||||
/* Each cert begins with an ASN.1 SEQUENCE tag and must be more
|
||||
* than 256 bytes in size.
|
||||
*/
|
||||
if (end - p < 4)
|
||||
goto dodgy_cert;
|
||||
if (p[0] != 0x30 &&
|
||||
p[1] != 0x82)
|
||||
goto dodgy_cert;
|
||||
plen = (p[2] << 8) | p[3];
|
||||
plen += 4;
|
||||
if (plen > end - p)
|
||||
goto dodgy_cert;
|
||||
|
||||
key = key_create_or_update(make_key_ref(builtin_trusted_keys, 1),
|
||||
"asymmetric",
|
||||
NULL,
|
||||
p,
|
||||
plen,
|
||||
((KEY_POS_ALL & ~KEY_POS_SETATTR) |
|
||||
KEY_USR_VIEW | KEY_USR_READ),
|
||||
KEY_ALLOC_NOT_IN_QUOTA |
|
||||
KEY_ALLOC_BUILT_IN |
|
||||
KEY_ALLOC_BYPASS_RESTRICTION);
|
||||
if (IS_ERR(key)) {
|
||||
pr_err("Problem loading in-kernel X.509 certificate (%ld)\n",
|
||||
PTR_ERR(key));
|
||||
} else {
|
||||
pr_notice("Loaded X.509 cert '%s'\n",
|
||||
key_ref_to_ptr(key)->description);
|
||||
key_ref_put(key);
|
||||
}
|
||||
p += plen;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
dodgy_cert:
|
||||
pr_err("Problem parsing in-kernel X.509 certificate list\n");
|
||||
return 0;
|
||||
return load_certificate_list(system_certificate_list, system_certificate_list_size,
|
||||
builtin_trusted_keys);
|
||||
}
|
||||
late_initcall(load_system_certificate_list);
|
||||
|
||||
@ -242,6 +199,12 @@ int verify_pkcs7_message_sig(const void *data, size_t len,
|
||||
pr_devel("PKCS#7 platform keyring is not available\n");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ret = is_key_on_revocation_list(pkcs7);
|
||||
if (ret != -ENOKEY) {
|
||||
pr_devel("PKCS#7 platform key is on revocation list\n");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
ret = pkcs7_validate_trust(pkcs7, trusted_keys);
|
||||
if (ret < 0) {
|
||||
|
@ -31,6 +31,7 @@ extern int restrict_link_by_builtin_and_secondary_trusted(
|
||||
#define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted
|
||||
#endif
|
||||
|
||||
extern struct pkcs7_message *pkcs7;
|
||||
#ifdef CONFIG_SYSTEM_BLACKLIST_KEYRING
|
||||
extern int mark_hash_blacklisted(const char *hash);
|
||||
extern int is_hash_blacklisted(const u8 *hash, size_t hash_len,
|
||||
@ -49,6 +50,20 @@ static inline int is_binary_blacklisted(const u8 *hash, size_t hash_len)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYSTEM_REVOCATION_LIST
|
||||
extern int add_key_to_revocation_list(const char *data, size_t size);
|
||||
extern int is_key_on_revocation_list(struct pkcs7_message *pkcs7);
|
||||
#else
|
||||
static inline int add_key_to_revocation_list(const char *data, size_t size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
static inline int is_key_on_revocation_list(struct pkcs7_message *pkcs7)
|
||||
{
|
||||
return -ENOKEY;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_IMA_BLACKLIST_KEYRING
|
||||
extern struct key *ima_blacklist_keyring;
|
||||
|
||||
|
@ -14,6 +14,7 @@ hostprogs-always-$(CONFIG_ASN1) += asn1_compiler
|
||||
hostprogs-always-$(CONFIG_MODULE_SIG_FORMAT) += sign-file
|
||||
hostprogs-always-$(CONFIG_SYSTEM_TRUSTED_KEYRING) += extract-cert
|
||||
hostprogs-always-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert
|
||||
hostprogs-always-$(CONFIG_SYSTEM_REVOCATION_LIST) += extract-cert
|
||||
|
||||
HOSTCFLAGS_sorttable.o = -I$(srctree)/tools/include
|
||||
HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
|
||||
|
@ -55,6 +55,15 @@ static __init void uefi_blacklist_binary(const char *source,
|
||||
uefi_blacklist_hash(source, data, len, "bin:", 4);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add an X509 cert to the revocation list.
|
||||
*/
|
||||
static __init void uefi_revocation_list_x509(const char *source,
|
||||
const void *data, size_t len)
|
||||
{
|
||||
add_key_to_revocation_list(data, len);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the appropriate handler for particular signature list types found in
|
||||
* the UEFI db and MokListRT tables.
|
||||
@ -76,5 +85,7 @@ __init efi_element_handler_t get_handler_for_dbx(const efi_guid_t *sig_type)
|
||||
return uefi_blacklist_x509_tbs;
|
||||
if (efi_guidcmp(*sig_type, efi_cert_sha256_guid) == 0)
|
||||
return uefi_blacklist_binary;
|
||||
if (efi_guidcmp(*sig_type, efi_cert_x509_guid) == 0)
|
||||
return uefi_revocation_list_x509;
|
||||
return 0;
|
||||
}
|
||||
|
@ -132,8 +132,9 @@ static int __init load_moklist_certs(void)
|
||||
static int __init load_uefi_certs(void)
|
||||
{
|
||||
efi_guid_t secure_var = EFI_IMAGE_SECURITY_DATABASE_GUID;
|
||||
void *db = NULL, *dbx = NULL;
|
||||
unsigned long dbsize = 0, dbxsize = 0;
|
||||
efi_guid_t mok_var = EFI_SHIM_LOCK_GUID;
|
||||
void *db = NULL, *dbx = NULL, *mokx = NULL;
|
||||
unsigned long dbsize = 0, dbxsize = 0, mokxsize = 0;
|
||||
efi_status_t status;
|
||||
int rc = 0;
|
||||
|
||||
@ -175,6 +176,21 @@ static int __init load_uefi_certs(void)
|
||||
kfree(dbx);
|
||||
}
|
||||
|
||||
mokx = get_cert_list(L"MokListXRT", &mok_var, &mokxsize, &status);
|
||||
if (!mokx) {
|
||||
if (status == EFI_NOT_FOUND)
|
||||
pr_debug("mokx variable wasn't found\n");
|
||||
else
|
||||
pr_info("Couldn't get mokx list\n");
|
||||
} else {
|
||||
rc = parse_efi_signature_list("UEFI:MokListXRT",
|
||||
mokx, mokxsize,
|
||||
get_handler_for_dbx);
|
||||
if (rc)
|
||||
pr_err("Couldn't parse mokx signatures %d\n", rc);
|
||||
kfree(mokx);
|
||||
}
|
||||
|
||||
/* Load the MokListRT certs */
|
||||
rc = load_moklist_certs();
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user