diff options
author | James Morris <james.morris@microsoft.com> | 2018-12-17 11:26:46 -0800 |
---|---|---|
committer | James Morris <james.morris@microsoft.com> | 2018-12-17 11:26:46 -0800 |
commit | 5580b4a1a8ca85c53bd5b94c5d302e47dca3e5cb (patch) | |
tree | 69597129504be73e8826be9984e05d073a5c47b5 /security/integrity | |
parent | 8bd8ea195f6d135a8d85201116314eb5237ad7e7 (diff) | |
parent | eed9de3b4f47114f440980203ca27c5fab70f529 (diff) |
Merge branch 'next-integrity' of git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity into next-integrity
From Mimi:
In Linux 4.19, a new LSM hook named security_kernel_load_data was
upstreamed, allowing LSMs and IMA to prevent the kexec_load
syscall. Different signature verification methods exist for verifying
the kexec'ed kernel image. This pull request adds additional support
in IMA to prevent loading unsigned kernel images via the kexec_load
syscall, independently of the IMA policy rules, based on the runtime
"secure boot" flag. An initial IMA kselftest is included.
In addition, this pull request defines a new, separate keyring named
".platform" for storing the preboot/firmware keys needed for verifying
the kexec'ed kernel image's signature and includes the associated IMA
kexec usage of the ".platform" keyring.
(David Howell's and Josh Boyer's patches for reading the
preboot/firmware keys, which were previously posted for a different
use case scenario, are included here.)
Diffstat (limited to 'security/integrity')
-rw-r--r-- | security/integrity/Kconfig | 11 | ||||
-rw-r--r-- | security/integrity/Makefile | 5 | ||||
-rw-r--r-- | security/integrity/digsig.c | 110 | ||||
-rw-r--r-- | security/integrity/ima/Kconfig | 10 | ||||
-rw-r--r-- | security/integrity/ima/ima_appraise.c | 14 | ||||
-rw-r--r-- | security/integrity/ima/ima_main.c | 21 | ||||
-rw-r--r-- | security/integrity/ima/ima_policy.c | 171 | ||||
-rw-r--r-- | security/integrity/integrity.h | 23 | ||||
-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 |
11 files changed, 634 insertions, 91 deletions
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig index da9565891738..4b4d2aeef539 100644 --- a/security/integrity/Kconfig +++ b/security/integrity/Kconfig @@ -51,6 +51,17 @@ config INTEGRITY_TRUSTED_KEYRING .evm keyrings be signed by a key on the system trusted keyring. +config INTEGRITY_PLATFORM_KEYRING + bool "Provide keyring for platform/firmware trusted keys" + depends on INTEGRITY_ASYMMETRIC_KEYS + depends on SYSTEM_BLACKLIST_KEYRING + depends on EFI + help + Provide a separate, distinct keyring for platform trusted keys, which + the kernel automatically populates during initialization from values + provided by the platform for verifying the kexec'ed kerned image + and, possibly, the initramfs signature. + config INTEGRITY_AUDIT bool "Enables integrity auditing support " depends on AUDIT diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 04d6e462b079..86df9aba8c0f 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -9,6 +9,11 @@ integrity-y := iint.o integrity-$(CONFIG_INTEGRITY_AUDIT) += integrity_audit.o integrity-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o +integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyring.o \ + platform_certs/efi_parser.o \ + platform_certs/load_uefi.o +obj-$(CONFIG_LOAD_UEFI_KEYS) += platform_certs/load_uefi.o +$(obj)/load_uefi.o: KBUILD_CFLAGS += -fshort-wchar subdir-$(CONFIG_IMA) += ima obj-$(CONFIG_IMA) += ima/ diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 5eacba858e4b..71c3200521d6 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -35,6 +35,7 @@ static const char * const keyring_name[INTEGRITY_KEYRING_MAX] = { ".ima", #endif "_module", + ".platform", }; #ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY @@ -73,12 +74,38 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, return -EOPNOTSUPP; } -int __init integrity_init_keyring(const unsigned int id) +static int __integrity_init_keyring(const unsigned int id, key_perm_t perm, + struct key_restriction *restriction) { const struct cred *cred = current_cred(); - struct key_restriction *restriction; int err = 0; + keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0), + KGIDT_INIT(0), cred, perm, + KEY_ALLOC_NOT_IN_QUOTA, restriction, NULL); + if (IS_ERR(keyring[id])) { + err = PTR_ERR(keyring[id]); + pr_info("Can't allocate %s keyring (%d)\n", + keyring_name[id], err); + keyring[id] = NULL; + } + + return err; +} + +int __init integrity_init_keyring(const unsigned int id) +{ + struct key_restriction *restriction; + key_perm_t perm; + + perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW + | KEY_USR_READ | KEY_USR_SEARCH; + + if (id == INTEGRITY_KEYRING_PLATFORM) { + restriction = NULL; + goto out; + } + if (!IS_ENABLED(CONFIG_INTEGRITY_TRUSTED_KEYRING)) return 0; @@ -87,32 +114,43 @@ int __init integrity_init_keyring(const unsigned int id) return -ENOMEM; restriction->check = restrict_link_to_ima; + perm |= KEY_USR_WRITE; - keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0), - KGIDT_INIT(0), cred, - ((KEY_POS_ALL & ~KEY_POS_SETATTR) | - KEY_USR_VIEW | KEY_USR_READ | - KEY_USR_WRITE | KEY_USR_SEARCH), - KEY_ALLOC_NOT_IN_QUOTA, - restriction, NULL); - if (IS_ERR(keyring[id])) { - err = PTR_ERR(keyring[id]); - pr_info("Can't allocate %s keyring (%d)\n", - keyring_name[id], err); - keyring[id] = NULL; +out: + return __integrity_init_keyring(id, perm, restriction); +} + +int __init integrity_add_key(const unsigned int id, const void *data, + off_t size, key_perm_t perm) +{ + key_ref_t key; + int rc = 0; + + if (!keyring[id]) + return -EINVAL; + + key = key_create_or_update(make_key_ref(keyring[id], 1), "asymmetric", + NULL, data, size, perm, + KEY_ALLOC_NOT_IN_QUOTA); + if (IS_ERR(key)) { + rc = PTR_ERR(key); + pr_err("Problem loading X.509 certificate %d\n", rc); + } else { + pr_notice("Loaded X.509 cert '%s'\n", + key_ref_to_ptr(key)->description); + key_ref_put(key); } - return err; + + return rc; + } int __init integrity_load_x509(const unsigned int id, const char *path) { - key_ref_t key; void *data; loff_t size; int rc; - - if (!keyring[id]) - return -EINVAL; + key_perm_t perm; rc = kernel_read_file_from_path(path, &data, &size, 0, READING_X509_CERTIFICATE); @@ -121,23 +159,21 @@ int __init integrity_load_x509(const unsigned int id, const char *path) return rc; } - key = key_create_or_update(make_key_ref(keyring[id], 1), - "asymmetric", - NULL, - data, - size, - ((KEY_POS_ALL & ~KEY_POS_SETATTR) | - KEY_USR_VIEW | KEY_USR_READ), - KEY_ALLOC_NOT_IN_QUOTA); - if (IS_ERR(key)) { - rc = PTR_ERR(key); - pr_err("Problem loading X.509 certificate (%d): %s\n", - rc, path); - } else { - pr_notice("Loaded X.509 cert '%s': %s\n", - key_ref_to_ptr(key)->description, path); - key_ref_put(key); - } + perm = (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ; + + pr_info("Loading X.509 certificate: %s\n", path); + rc = integrity_add_key(id, (const void *)data, size, perm); + vfree(data); - return 0; + return rc; +} + +int __init integrity_load_cert(const unsigned int id, const char *source, + const void *data, size_t len, key_perm_t perm) +{ + if (!data) + return -EINVAL; + + pr_info("Loading X.509 certificate: %s\n", source); + return integrity_add_key(id, data, len, perm); } diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 13b446328dda..a18f8c6d13b5 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -157,6 +157,14 @@ config IMA_APPRAISE <http://linux-ima.sourceforge.net> If unsure, say N. +config IMA_ARCH_POLICY + bool "Enable loading an IMA architecture specific policy" + depends on KEXEC_VERIFY_SIG || IMA_APPRAISE && INTEGRITY_ASYMMETRIC_KEYS + default n + help + This option enables loading an IMA architecture specific policy + based on run time secure boot flags. + config IMA_APPRAISE_BUILD_POLICY bool "IMA build time configured policy rules" depends on IMA_APPRAISE && INTEGRITY_ASYMMETRIC_KEYS @@ -217,7 +225,7 @@ config IMA_APPRAISE_REQUIRE_POLICY_SIGS config IMA_APPRAISE_BOOTPARAM bool "ima_appraise boot parameter" - depends on IMA_APPRAISE + depends on IMA_APPRAISE && !IMA_ARCH_POLICY default y help This option enables the different "ima_appraise=" modes diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 2e11e750a067..a2baa85ea2f5 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -289,12 +289,22 @@ int ima_appraise_measurement(enum ima_hooks func, case EVM_IMA_XATTR_DIGSIG: set_bit(IMA_DIGSIG, &iint->atomic_flags); rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, - (const char *)xattr_value, rc, + (const char *)xattr_value, + xattr_len, iint->ima_hash->digest, iint->ima_hash->length); if (rc == -EOPNOTSUPP) { status = INTEGRITY_UNKNOWN; - } else if (rc) { + break; + } + if (IS_ENABLED(CONFIG_INTEGRITY_PLATFORM_KEYRING) && rc && + func == KEXEC_KERNEL_CHECK) + rc = integrity_digsig_verify(INTEGRITY_KEYRING_PLATFORM, + (const char *)xattr_value, + xattr_len, + iint->ima_hash->digest, + iint->ima_hash->length); + if (rc) { cause = "invalid-signature"; status = INTEGRITY_FAIL; } else { diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index adaf96932237..043a09189df1 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -105,7 +105,7 @@ static void ima_rdwr_violation_check(struct file *file, } else { if (must_measure) set_bit(IMA_MUST_MEASURE, &iint->atomic_flags); - if ((atomic_read(&inode->i_writecount) > 0) && must_measure) + if (inode_is_open_for_write(inode) && must_measure) send_writers = true; } @@ -507,20 +507,26 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size, */ int ima_load_data(enum kernel_load_data_id id) { - bool sig_enforce; + bool ima_enforce, sig_enforce; - if ((ima_appraise & IMA_APPRAISE_ENFORCE) != IMA_APPRAISE_ENFORCE) - return 0; + ima_enforce = + (ima_appraise & IMA_APPRAISE_ENFORCE) == IMA_APPRAISE_ENFORCE; switch (id) { case LOADING_KEXEC_IMAGE: - if (ima_appraise & IMA_APPRAISE_KEXEC) { + if (IS_ENABLED(CONFIG_KEXEC_VERIFY_SIG) + && arch_ima_get_secureboot()) { + pr_err("impossible to appraise a kernel image without a file descriptor; try using kexec_file_load syscall.\n"); + return -EACCES; + } + + if (ima_enforce && (ima_appraise & IMA_APPRAISE_KEXEC)) { pr_err("impossible to appraise a kernel image without a file descriptor; try using kexec_file_load syscall.\n"); return -EACCES; /* INTEGRITY_UNKNOWN */ } break; case LOADING_FIRMWARE: - if (ima_appraise & IMA_APPRAISE_FIRMWARE) { + if (ima_enforce && (ima_appraise & IMA_APPRAISE_FIRMWARE)) { pr_err("Prevent firmware sysfs fallback loading.\n"); return -EACCES; /* INTEGRITY_UNKNOWN */ } @@ -528,7 +534,8 @@ int ima_load_data(enum kernel_load_data_id id) case LOADING_MODULE: sig_enforce = is_module_sig_enforced(); - if (!sig_enforce && (ima_appraise & IMA_APPRAISE_MODULES)) { + if (ima_enforce && (!sig_enforce + && (ima_appraise & IMA_APPRAISE_MODULES))) { pr_err("impossible to appraise a module without a file descriptor. sig_enforce kernel parameter might help\n"); return -EACCES; /* INTEGRITY_UNKNOWN */ } diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 3778dc396193..fccd7b925498 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -20,6 +20,7 @@ #include <linux/rculist.h> #include <linux/genhd.h> #include <linux/seq_file.h> +#include <linux/ima.h> #include "ima.h" @@ -58,6 +59,8 @@ enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, enum policy_types { ORIGINAL_TCB = 1, DEFAULT_TCB }; +enum policy_rule_list { IMA_DEFAULT_POLICY = 1, IMA_CUSTOM_POLICY }; + struct ima_rule_entry { struct list_head list; int action; @@ -104,7 +107,8 @@ static struct ima_rule_entry dont_measure_rules[] __ro_after_init = { .flags = IMA_FSMAGIC}, {.action = DONT_MEASURE, .fsmagic = CGROUP2_SUPER_MAGIC, .flags = IMA_FSMAGIC}, - {.action = DONT_MEASURE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC} + {.action = DONT_MEASURE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC}, + {.action = DONT_MEASURE, .fsmagic = EFIVARFS_MAGIC, .flags = IMA_FSMAGIC} }; static struct ima_rule_entry original_measurement_rules[] __ro_after_init = { @@ -147,6 +151,7 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { {.action = DONT_APPRAISE, .fsmagic = SELINUX_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = SMACK_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC}, + {.action = DONT_APPRAISE, .fsmagic = EFIVARFS_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = CGROUP_SUPER_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = CGROUP2_SUPER_MAGIC, .flags = IMA_FSMAGIC}, #ifdef CONFIG_IMA_WRITE_POLICY @@ -193,6 +198,9 @@ static struct ima_rule_entry secure_boot_rules[] __ro_after_init = { .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, }; +/* An array of architecture specific rules */ +struct ima_rule_entry *arch_policy_entry __ro_after_init; + static LIST_HEAD(ima_default_rules); static LIST_HEAD(ima_policy_rules); static LIST_HEAD(ima_temp_rules); @@ -473,6 +481,75 @@ static int ima_appraise_flag(enum ima_hooks func) return 0; } +static void add_rules(struct ima_rule_entry *entries, int count, + enum policy_rule_list policy_rule) +{ + int i = 0; + + for (i = 0; i < count; i++) { + struct ima_rule_entry *entry; + + if (policy_rule & IMA_DEFAULT_POLICY) + list_add_tail(&entries[i].list, &ima_default_rules); + + if (policy_rule & IMA_CUSTOM_POLICY) { + entry = kmemdup(&entries[i], sizeof(*entry), + GFP_KERNEL); + if (!entry) + continue; + + list_add_tail(&entry->list, &ima_policy_rules); + } + if (entries[i].action == APPRAISE) + temp_ima_appraise |= ima_appraise_flag(entries[i].func); + if (entries[i].func == POLICY_CHECK) + temp_ima_appraise |= IMA_APPRAISE_POLICY; + } +} + +static int ima_parse_rule(char *rule, struct ima_rule_entry *entry); + +static int __init ima_init_arch_policy(void) +{ + const char * const *arch_rules; + const char * const *rules; + int arch_entries = 0; + int i = 0; + + arch_rules = arch_get_ima_policy(); + if (!arch_rules) + return arch_entries; + + /* Get number of rules */ + for (rules = arch_rules; *rules != NULL; rules++) + arch_entries++; + + arch_policy_entry = kcalloc(arch_entries + 1, + sizeof(*arch_policy_entry), GFP_KERNEL); + if (!arch_policy_entry) + return 0; + + /* Convert each policy string rules to struct ima_rule_entry format */ + for (rules = arch_rules, i = 0; *rules != NULL; rules++) { + char rule[255]; + int result; + + result = strlcpy(rule, *rules, sizeof(rule)); + + INIT_LIST_HEAD(&arch_policy_entry[i].list); + result = ima_parse_rule(rule, &arch_policy_entry[i]); + if (result) { + pr_warn("Skipping unknown architecture policy rule: %s\n", + rule); + memset(&arch_policy_entry[i], 0, + sizeof(*arch_policy_entry)); + continue; + } + i++; + } + return i; +} + /** * ima_init_policy - initialize the default measure rules. * @@ -481,68 +558,68 @@ static int ima_appraise_flag(enum ima_hooks func) */ void __init ima_init_policy(void) { - int i, measure_entries, appraise_entries, secure_boot_entries; + int build_appraise_entries, arch_entries; - /* if !ima_policy set entries = 0 so we load NO default rules */ - measure_entries = ima_policy ? ARRAY_SIZE(dont_measure_rules) : 0; - appraise_entries = ima_use_appraise_tcb ? - ARRAY_SIZE(default_appraise_rules) : 0; - secure_boot_entries = ima_use_secure_boot ? - ARRAY_SIZE(secure_boot_rules) : 0; - - for (i = 0; i < measure_entries; i++) - list_add_tail(&dont_measure_rules[i].list, &ima_default_rules); + /* if !ima_policy, we load NO default rules */ + if (ima_policy) + add_rules(dont_measure_rules, ARRAY_SIZE(dont_measure_rules), + IMA_DEFAULT_POLICY); switch (ima_policy) { case ORIGINAL_TCB: - for (i = 0; i < ARRAY_SIZE(original_measurement_rules); i++) - list_add_tail(&original_measurement_rules[i].list, - &ima_default_rules); + add_rules(original_measurement_rules, + ARRAY_SIZE(original_measurement_rules), + IMA_DEFAULT_POLICY); break; case DEFAULT_TCB: - for (i = 0; i < ARRAY_SIZE(default_measurement_rules); i++) - list_add_tail(&default_measurement_rules[i].list, - &ima_default_rules); + add_rules(default_measurement_rules, + ARRAY_SIZE(default_measurement_rules), + IMA_DEFAULT_POLICY); default: break; } /* + * Based on runtime secure boot flags, insert arch specific measurement + * and appraise rules requiring file signatures for both the initial + * and custom policies, prior to other appraise rules. + * (Highest priority) + */ + arch_entries = ima_init_arch_policy(); + if (!arch_entries) + pr_info("No architecture policies found\n"); + else + add_rules(arch_policy_entry, arch_entries, + IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY); + + /* * Insert the builtin "secure_boot" policy rules requiring file - * signatures, prior to any other appraise rules. + * signatures, prior to other appraise rules. */ - for (i = 0; i < secure_boot_entries; i++) { - list_add_tail(&secure_boot_rules[i].list, &ima_default_rules); - temp_ima_appraise |= - ima_appraise_flag(secure_boot_rules[i].func); - } + if (ima_use_secure_boot) + add_rules(secure_boot_rules, ARRAY_SIZE(secure_boot_rules), + IMA_DEFAULT_POLICY); /* * Insert the build time appraise rules requiring file signatures * for both the initial and custom policies, prior to other appraise - * rules. + * rules. As the secure boot rules includes all of the build time + * rules, include either one or the other set of rules, but not both. */ - for (i = 0; i < ARRAY_SIZE(build_appraise_rules); i++) { - struct ima_rule_entry *entry; - - if (!secure_boot_entries) - list_add_tail(&build_appraise_rules[i].list, - &ima_default_rules); - - entry = kmemdup(&build_appraise_rules[i], sizeof(*entry), - GFP_KERNEL); - if (entry) - list_add_tail(&entry->list, &ima_policy_rules); - build_ima_appraise |= - ima_appraise_flag(build_appraise_rules[i].func); + build_appraise_entries = ARRAY_SIZE(build_appraise_rules); + if (build_appraise_entries) { + if (ima_use_secure_boot) + add_rules(build_appraise_rules, build_appraise_entries, + IMA_CUSTOM_POLICY); + else + add_rules(build_appraise_rules, build_appraise_entries, + IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY); } - for (i = 0; i < appraise_entries; i++) { - list_add_tail(&default_appraise_rules[i].list, - &ima_default_rules); - if (default_appraise_rules[i].func == POLICY_CHECK) - temp_ima_appraise |= IMA_APPRAISE_POLICY; - } + if (ima_use_appraise_tcb) + add_rules(default_appraise_rules, + ARRAY_SIZE(default_appraise_rules), + IMA_DEFAULT_POLICY); ima_rules = &ima_default_rules; ima_update_policy_flag(); @@ -576,6 +653,14 @@ void ima_update_policy(void) if (ima_rules != policy) { ima_policy_flag = 0; ima_rules = policy; + + /* + * IMA architecture specific policy rules are specified + * as strings and converted to an array of ima_entry_rules + * on boot. After loading a custom policy, free the + * architecture specific rules stored as an array. + */ + kfree(arch_policy_entry); } ima_update_policy_flag(); } diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index e60473b13a8d..3517d2852a07 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -142,7 +142,8 @@ int integrity_kernel_read(struct file *file, loff_t offset, #define INTEGRITY_KEYRING_EVM 0 #define INTEGRITY_KEYRING_IMA 1 #define INTEGRITY_KEYRING_MODULE 2 -#define INTEGRITY_KEYRING_MAX 3 +#define INTEGRITY_KEYRING_PLATFORM 3 +#define INTEGRITY_KEYRING_MAX 4 extern struct dentry *integrity_dir; @@ -153,6 +154,8 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, int __init integrity_init_keyring(const unsigned int id); int __init integrity_load_x509(const unsigned int id, const char *path); +int __init integrity_load_cert(const unsigned int id, const char *source, + const void *data, size_t len, key_perm_t perm); #else static inline int integrity_digsig_verify(const unsigned int id, @@ -166,6 +169,14 @@ static inline int integrity_init_keyring(const unsigned int id) { return 0; } + +static inline int __init integrity_load_cert(const unsigned int id, + const char *source, + const void *data, size_t len, + key_perm_t perm) +{ + return 0; +} #endif /* CONFIG_INTEGRITY_SIGNATURE */ #ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS @@ -222,3 +233,13 @@ integrity_audit_log_start(struct audit_context *ctx, gfp_t gfp_mask, int type) } #endif + +#ifdef CONFIG_INTEGRITY_PLATFORM_KEYRING +void __init add_to_platform_keyring(const char *source, const void *data, + size_t len); +#else +static inline void __init add_to_platform_keyring(const char *source, + const void *data, size_t len) +{ +} +#endif 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); |