From 0914ade209c452cff6a29b1c0ae6fff3167fa1d0 Mon Sep 17 00:00:00 2001 From: Nayna Jain Date: Tue, 9 Oct 2018 23:00:33 +0530 Subject: x86/ima: define arch_ima_get_secureboot Distros are concerned about totally disabling the kexec_load syscall. As a compromise, the kexec_load syscall will only be disabled when CONFIG_KEXEC_VERIFY_SIG is configured and the system is booted with secureboot enabled. This patch defines the new arch specific function called arch_ima_get_secureboot() to retrieve the secureboot state of the system. Signed-off-by: Nayna Jain Suggested-by: Seth Forshee Cc: David Howells Cc: Eric Biederman Cc: Peter Jones Cc: Vivek Goyal Cc: Dave Young Signed-off-by: Mimi Zohar --- include/linux/ima.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include') diff --git a/include/linux/ima.h b/include/linux/ima.h index 97914a2833d1..948135fb60f1 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -30,6 +30,15 @@ extern void ima_post_path_mknod(struct dentry *dentry); extern void ima_add_kexec_buffer(struct kimage *image); #endif +#ifdef CONFIG_X86 +extern bool arch_ima_get_secureboot(void); +#else +static inline bool arch_ima_get_secureboot(void) +{ + return false; +} +#endif + #else static inline int ima_bprm_check(struct linux_binprm *bprm) { -- cgit v1.2.3 From 6191706246de99ff2fac4b6f157f20205a0943cd Mon Sep 17 00:00:00 2001 From: Nayna Jain Date: Tue, 9 Oct 2018 23:00:36 +0530 Subject: ima: add support for arch specific policies Builtin IMA policies can be enabled on the boot command line, and replaced with a custom policy, normally during early boot in the initramfs. Build time IMA policy rules were recently added. These rules are automatically enabled on boot and persist after loading a custom policy. There is a need for yet another type of policy, an architecture specific policy, which is derived at runtime during kernel boot, based on the runtime secure boot flags. Like the build time policy rules, these rules persist after loading a custom policy. This patch adds support for loading an architecture specific IMA policy. Signed-off-by: Nayna Jain Co-Developed-by: Mimi Zohar Signed-off-by: Mimi Zohar --- include/linux/ima.h | 5 +++ security/integrity/ima/ima_policy.c | 72 +++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/ima.h b/include/linux/ima.h index 948135fb60f1..62c5241b0899 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -39,6 +39,11 @@ static inline bool arch_ima_get_secureboot(void) } #endif +static inline const char * const *arch_get_ima_policy(void) +{ + return NULL; +} + #else static inline int ima_bprm_check(struct linux_binprm *bprm) { diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 1e30d09a56db..b20770704b6c 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "ima.h" @@ -195,6 +196,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); @@ -501,6 +505,49 @@ static void add_rules(struct ima_rule_entry *entries, int count, } } +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. * @@ -509,7 +556,7 @@ static void add_rules(struct ima_rule_entry *entries, int count, */ void __init ima_init_policy(void) { - int build_appraise_entries; + int build_appraise_entries, arch_entries; /* if !ima_policy, we load NO default rules */ if (ima_policy) @@ -530,9 +577,22 @@ void __init ima_init_policy(void) 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. */ if (ima_use_secure_boot) add_rules(secure_boot_rules, ARRAY_SIZE(secure_boot_rules), @@ -591,6 +651,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(); } -- cgit v1.2.3 From d958083a8f6408e76850bc7394976050d7e43173 Mon Sep 17 00:00:00 2001 From: Eric Richter Date: Tue, 9 Oct 2018 23:00:37 +0530 Subject: x86/ima: define arch_get_ima_policy() for x86 On x86, there are two methods of verifying a kexec'ed kernel image signature being loaded via the kexec_file_load syscall - an architecture specific implementaton or a IMA KEXEC_KERNEL_CHECK appraisal rule. Neither of these methods verify the kexec'ed kernel image signature being loaded via the kexec_load syscall. Secure boot enabled systems require kexec images to be signed. Therefore, this patch loads an IMA KEXEC_KERNEL_CHECK policy rule on secure boot enabled systems not configured with CONFIG_KEXEC_VERIFY_SIG enabled. When IMA_APPRAISE_BOOTPARAM is configured, different IMA appraise modes (eg. fix, log) can be specified on the boot command line, allowing unsigned or invalidly signed kernel images to be kexec'ed. This patch permits enabling IMA_APPRAISE_BOOTPARAM or IMA_ARCH_POLICY, but not both. Signed-off-by: Eric Richter Signed-off-by: Nayna Jain Cc: David Howells Cc: Eric Biederman Cc: Peter Jones Cc: Vivek Goyal Cc: Dave Young Signed-off-by: Mimi Zohar --- arch/x86/kernel/ima_arch.c | 16 ++++++++++++++++ include/linux/ima.h | 3 ++- security/integrity/ima/Kconfig | 10 +++++++++- 3 files changed, 27 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/ima_arch.c b/arch/x86/kernel/ima_arch.c index bb5a88d2b271..6c248616ee57 100644 --- a/arch/x86/kernel/ima_arch.c +++ b/arch/x86/kernel/ima_arch.c @@ -15,3 +15,19 @@ bool arch_ima_get_secureboot(void) else return false; } + +/* secureboot arch rules */ +static const char * const sb_arch_rules[] = { +#if !IS_ENABLED(CONFIG_KEXEC_VERIFY_SIG) + "appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig", +#endif /* CONFIG_KEXEC_VERIFY_SIG */ + "measure func=KEXEC_KERNEL_CHECK", + NULL +}; + +const char * const *arch_get_ima_policy(void) +{ + if (IS_ENABLED(CONFIG_IMA_ARCH_POLICY) && arch_ima_get_secureboot()) + return sb_arch_rules; + return NULL; +} diff --git a/include/linux/ima.h b/include/linux/ima.h index 62c5241b0899..5ab9134d4fd7 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -32,17 +32,18 @@ extern void ima_add_kexec_buffer(struct kimage *image); #ifdef CONFIG_X86 extern bool arch_ima_get_secureboot(void); +extern const char * const *arch_get_ima_policy(void); #else static inline bool arch_ima_get_secureboot(void) { return false; } -#endif static inline const char * const *arch_get_ima_policy(void) { return NULL; } +#endif #else static inline int ima_bprm_check(struct linux_binprm *bprm) 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 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 -- cgit v1.2.3 From 399574c64eaf94e82b7cf056978d7e68748c0f1d Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Sun, 18 Nov 2018 04:08:12 -0500 Subject: x86/ima: retry detecting secure boot mode The secure boot mode may not be detected on boot for some reason (eg. buggy firmware). This patch attempts one more time to detect the secure boot mode. Signed-off-by: Mimi Zohar --- arch/x86/kernel/Makefile | 2 ++ arch/x86/kernel/ima_arch.c | 46 ++++++++++++++++++++++++++++++++++++++++++++-- include/linux/ima.h | 2 +- 3 files changed, 47 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index f0910a1e1db7..eb51b0e1189c 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -151,4 +151,6 @@ ifeq ($(CONFIG_X86_64),y) obj-y += vsmp_64.o endif +ifdef CONFIG_EFI obj-$(CONFIG_IMA) += ima_arch.o +endif diff --git a/arch/x86/kernel/ima_arch.c b/arch/x86/kernel/ima_arch.c index 6c248616ee57..e47cd9390ab4 100644 --- a/arch/x86/kernel/ima_arch.c +++ b/arch/x86/kernel/ima_arch.c @@ -7,10 +7,52 @@ extern struct boot_params boot_params; +static enum efi_secureboot_mode get_sb_mode(void) +{ + efi_char16_t efi_SecureBoot_name[] = L"SecureBoot"; + efi_guid_t efi_variable_guid = EFI_GLOBAL_VARIABLE_GUID; + efi_status_t status; + unsigned long size; + u8 secboot; + + size = sizeof(secboot); + + /* Get variable contents into buffer */ + status = efi.get_variable(efi_SecureBoot_name, &efi_variable_guid, + NULL, &size, &secboot); + if (status == EFI_NOT_FOUND) { + pr_info("ima: secureboot mode disabled\n"); + return efi_secureboot_mode_disabled; + } + + if (status != EFI_SUCCESS) { + pr_info("ima: secureboot mode unknown\n"); + return efi_secureboot_mode_unknown; + } + + if (secboot == 0) { + pr_info("ima: secureboot mode disabled\n"); + return efi_secureboot_mode_disabled; + } + + pr_info("ima: secureboot mode enabled\n"); + return efi_secureboot_mode_enabled; +} + bool arch_ima_get_secureboot(void) { - if (efi_enabled(EFI_BOOT) && - (boot_params.secure_boot == efi_secureboot_mode_enabled)) + static enum efi_secureboot_mode sb_mode; + static bool initialized; + + if (!initialized && efi_enabled(EFI_BOOT)) { + sb_mode = boot_params.secure_boot; + + if (sb_mode == efi_secureboot_mode_unset) + sb_mode = get_sb_mode(); + initialized = true; + } + + if (sb_mode == efi_secureboot_mode_enabled) return true; else return false; diff --git a/include/linux/ima.h b/include/linux/ima.h index 5ab9134d4fd7..b5e16b8c50b7 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -30,7 +30,7 @@ extern void ima_post_path_mknod(struct dentry *dentry); extern void ima_add_kexec_buffer(struct kimage *image); #endif -#ifdef CONFIG_X86 +#if defined(CONFIG_X86) && defined(CONFIG_EFI) extern bool arch_ima_get_secureboot(void); extern const char * const *arch_get_ima_policy(void); #else -- cgit v1.2.3 From 5c126ba22f894427cc770240faa1bf6b02ce7aca Mon Sep 17 00:00:00 2001 From: Dave Howells Date: Sun, 9 Dec 2018 01:57:01 +0530 Subject: efi: Add EFI signature data types Add the data types that are used for containing hashes, keys and certificates for cryptographic verification along with their corresponding type GUIDs. Signed-off-by: David Howells Acked-by: Nayna Jain Acked-by: Serge Hallyn Reviewed-by: James Morris Signed-off-by: Mimi Zohar --- include/linux/efi.h | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'include') diff --git a/include/linux/efi.h b/include/linux/efi.h index 845174e113ce..3d3de1673b15 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -663,6 +663,10 @@ void efi_native_runtime_setup(void); #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) +#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) +#define EFI_CERT_X509_SHA256_GUID EFI_GUID(0x3bd2a492, 0x96c0, 0x4079, 0xb4, 0x20, 0xfc, 0xf9, 0x8e, 0xf1, 0x03, 0xed) + /* * This GUID is used to pass to the kernel proper the struct screen_info * structure that was populated by the stub based on the GOP protocol instance @@ -934,6 +938,27 @@ typedef struct { efi_memory_desc_t entry[0]; } efi_memory_attributes_table_t; +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; + +typedef u8 efi_sha256_hash_t[32]; + +typedef struct { + efi_sha256_hash_t to_be_signed_hash; + efi_time_t time_of_revocation; +} efi_cert_x509_sha256_t; + /* * All runtime access to EFI goes through this structure: */ -- cgit v1.2.3 From 0bc9ae395b3f3b6557f0c5f0a0b0cd2fd5c00a04 Mon Sep 17 00:00:00 2001 From: Dave Howells Date: Sun, 9 Dec 2018 01:57:02 +0530 Subject: efi: Add an EFI signature blob parser Add a function to parse an EFI signature blob 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 function pointed to by the get_handler_for_guid argument 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. Signed-off-by: David Howells Signed-off-by: Nayna Jain Acked-by: Serge Hallyn Signed-off-by: Mimi Zohar --- include/linux/efi.h | 9 +++ security/integrity/Makefile | 3 +- security/integrity/platform_certs/efi_parser.c | 108 +++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 security/integrity/platform_certs/efi_parser.c (limited to 'include') diff --git a/include/linux/efi.h b/include/linux/efi.h index 3d3de1673b15..d916311f2a51 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -1141,6 +1141,15 @@ extern int efi_memattr_apply_permissions(struct mm_struct *mm, char * __init efi_md_typeattr_format(char *buf, size_t size, const efi_memory_desc_t *md); + +typedef void (*efi_element_handler_t)(const char *source, + const void *element_data, + size_t element_size); +extern 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_range_is_wc - check the WC bit on an address range * @start: starting kvirt address diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 046ffc1bb42d..6ee9058866cd 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -9,7 +9,8 @@ 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 +integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyring.o \ + platform_certs/efi_parser.o subdir-$(CONFIG_IMA) += ima obj-$(CONFIG_IMA) += ima/ 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 +#include +#include +#include + +/** + * 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; +} -- cgit v1.2.3