From 2e10f053682b2614c8689ab7cd792030adb37c3d Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 9 Aug 2013 17:58:15 -0400 Subject: [PATCH 01/21] Add secure_modules() call Provide a single call to allow kernel code to determine whether the system has been configured to either disable module loading entirely or to load only modules signed with a trusted key. Bugzilla: N/A Upstream-status: Fedora mustard. Replaced by securelevels, but that was nak'd Signed-off-by: Matthew Garrett --- include/linux/module.h | 6 ++++++ kernel/module.c | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/include/linux/module.h b/include/linux/module.h index 3a19c79918e02d..db386349cd0144 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -635,6 +635,8 @@ static inline bool module_requested_async_probing(struct module *module) return module && module->async_probe_requested; } +extern bool secure_modules(void); + #else /* !CONFIG_MODULES... */ /* Given an address, look for it in the exception tables. */ @@ -751,6 +753,10 @@ static inline bool module_requested_async_probing(struct module *module) return false; } +static inline bool secure_modules(void) +{ + return false; +} #endif /* CONFIG_MODULES */ #ifdef CONFIG_SYSFS diff --git a/kernel/module.c b/kernel/module.c index 38c7bd5583fff0..a8f8c64689273e 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -4097,3 +4097,13 @@ void module_layout(struct module *mod, } EXPORT_SYMBOL(module_layout); #endif + +bool secure_modules(void) +{ +#ifdef CONFIG_MODULE_SIG + return (sig_enforce || modules_disabled); +#else + return modules_disabled; +#endif +} +EXPORT_SYMBOL(secure_modules); From 8161285fced6623edd4c66f9c2d3ece69014a392 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 8 Mar 2012 10:10:38 -0500 Subject: [PATCH 02/21] PCI: Lock down BAR access when module security is enabled Any hardware that can potentially generate DMA has to be locked down from userspace in order to avoid it being possible for an attacker to modify kernel code, allowing them to circumvent disabled module loading or module signing. Default to paranoid - in future we can potentially relax this for sufficiently IOMMU-isolated devices. Signed-off-by: Matthew Garrett --- drivers/pci/pci-sysfs.c | 10 ++++++++++ drivers/pci/proc.c | 8 +++++++- drivers/pci/syscall.c | 3 ++- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index eead54cd01b2bf..bb59ecd2d778ba 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "pci.h" static int sysfs_initialized; /* = 0 */ @@ -713,6 +714,9 @@ static ssize_t pci_write_config(struct file *filp, struct kobject *kobj, loff_t init_off = off; u8 *data = (u8 *) buf; + if (secure_modules()) + return -EPERM; + if (off > dev->cfg_size) return 0; if (off + count > dev->cfg_size) { @@ -1007,6 +1011,9 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, resource_size_t start, end; int i; + if (secure_modules()) + return -EPERM; + for (i = 0; i < PCI_ROM_RESOURCE; i++) if (res == &pdev->resource[i]) break; @@ -1108,6 +1115,9 @@ static ssize_t pci_write_resource_io(struct file *filp, struct kobject *kobj, struct bin_attribute *attr, char *buf, loff_t off, size_t count) { + if (secure_modules()) + return -EPERM; + return pci_resource_io(filp, kobj, attr, buf, off, count, true); } diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 3f155e78513fd4..4265ea07e3b066 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -116,6 +116,9 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf, int size = dev->cfg_size; int cnt; + if (secure_modules()) + return -EPERM; + if (pos >= size) return 0; if (nbytes >= size) @@ -195,6 +198,9 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, #endif /* HAVE_PCI_MMAP */ int ret = 0; + if (secure_modules()) + return -EPERM; + switch (cmd) { case PCIIOC_CONTROLLER: ret = pci_domain_nr(dev->bus); @@ -233,7 +239,7 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) struct pci_filp_private *fpriv = file->private_data; int i, ret; - if (!capable(CAP_SYS_RAWIO)) + if (!capable(CAP_SYS_RAWIO) || secure_modules()) return -EPERM; /* Make sure the caller is mapping a real resource for this device */ diff --git a/drivers/pci/syscall.c b/drivers/pci/syscall.c index b91c4da6836574..98f5637304d1ba 100644 --- a/drivers/pci/syscall.c +++ b/drivers/pci/syscall.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "pci.h" @@ -92,7 +93,7 @@ SYSCALL_DEFINE5(pciconfig_write, unsigned long, bus, unsigned long, dfn, u32 dword; int err = 0; - if (!capable(CAP_SYS_ADMIN)) + if (!capable(CAP_SYS_ADMIN) || secure_modules()) return -EPERM; dev = pci_get_bus_and_slot(bus, dfn); From f55838325eadbb393aaf61a61a177fd7ad2f0280 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 8 Mar 2012 10:35:59 -0500 Subject: [PATCH 03/21] x86: Lock down IO port access when module security is enabled IO port access would permit users to gain access to PCI configuration registers, which in turn (on a lot of hardware) give access to MMIO register space. This would potentially permit root to trigger arbitrary DMA, so lock it down by default. Signed-off-by: Matthew Garrett --- arch/x86/kernel/ioport.c | 5 +++-- drivers/char/mem.c | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/ioport.c b/arch/x86/kernel/ioport.c index 37dae792dbbed0..1ecc03ca3c1503 100644 --- a/arch/x86/kernel/ioport.c +++ b/arch/x86/kernel/ioport.c @@ -15,6 +15,7 @@ #include #include #include +#include #include /* @@ -28,7 +29,7 @@ asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int turn_on) if ((from + num <= from) || (from + num > IO_BITMAP_BITS)) return -EINVAL; - if (turn_on && !capable(CAP_SYS_RAWIO)) + if (turn_on && (!capable(CAP_SYS_RAWIO) || secure_modules())) return -EPERM; /* @@ -103,7 +104,7 @@ SYSCALL_DEFINE1(iopl, unsigned int, level) return -EINVAL; /* Trying to gain more privileges? */ if (level > old) { - if (!capable(CAP_SYS_RAWIO)) + if (!capable(CAP_SYS_RAWIO) || secure_modules()) return -EPERM; } regs->flags = (regs->flags & ~X86_EFLAGS_IOPL) | (level << 12); diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 6b1721f978c294..53fe675f9bd764 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -27,6 +27,7 @@ #include #include #include +#include #include @@ -577,6 +578,9 @@ static ssize_t write_port(struct file *file, const char __user *buf, unsigned long i = *ppos; const char __user *tmp = buf; + if (secure_modules()) + return -EPERM; + if (!access_ok(VERIFY_READ, buf, count)) return -EFAULT; while (count-- > 0 && i < 65536) { From 957b35947b86b16d1baadce8ec63db80bfb6466a Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 9 Mar 2012 08:39:37 -0500 Subject: [PATCH 04/21] ACPI: Limit access to custom_method custom_method effectively allows arbitrary access to system memory, making it possible for an attacker to circumvent restrictions on module loading. Disable it if any such restrictions have been enabled. Signed-off-by: Matthew Garrett --- drivers/acpi/custom_method.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/acpi/custom_method.c b/drivers/acpi/custom_method.c index c68e72414a67a9..4277938af700d1 100644 --- a/drivers/acpi/custom_method.c +++ b/drivers/acpi/custom_method.c @@ -29,6 +29,9 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf, struct acpi_table_header table; acpi_status status; + if (secure_modules()) + return -EPERM; + if (!(*ppos)) { /* parse the table header to get the table length */ if (count <= sizeof(struct acpi_table_header)) From 86c4a0683e7310bad411a1834ce2b949d5bd4534 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 9 Mar 2012 08:46:50 -0500 Subject: [PATCH 05/21] asus-wmi: Restrict debugfs interface when module loading is restricted We have no way of validating what all of the Asus WMI methods do on a given machine, and there's a risk that some will allow hardware state to be manipulated in such a way that arbitrary code can be executed in the kernel, circumventing module loading restrictions. Prevent that if any of these features are enabled. Signed-off-by: Matthew Garrett --- drivers/platform/x86/asus-wmi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index f96f7b86526792..01af903be5928b 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1870,6 +1870,9 @@ static int show_dsts(struct seq_file *m, void *data) int err; u32 retval = -1; + if (secure_modules()) + return -EPERM; + err = asus_wmi_get_devstate(asus, asus->debug.dev_id, &retval); if (err < 0) @@ -1886,6 +1889,9 @@ static int show_devs(struct seq_file *m, void *data) int err; u32 retval = -1; + if (secure_modules()) + return -EPERM; + err = asus_wmi_set_devstate(asus->debug.dev_id, asus->debug.ctrl_param, &retval); @@ -1910,6 +1916,9 @@ static int show_call(struct seq_file *m, void *data) union acpi_object *obj; acpi_status status; + if (secure_modules()) + return -EPERM; + status = wmi_evaluate_method(ASUS_WMI_MGMT_GUID, 1, asus->debug.method_id, &input, &output); From 03bc662b54a1a5978a2c840eba182b28e65f0c81 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 9 Mar 2012 09:28:15 -0500 Subject: [PATCH 06/21] Restrict /dev/mem and /dev/kmem when module loading is restricted Allowing users to write to address space makes it possible for the kernel to be subverted, avoiding module loading restrictions. Prevent this when any restrictions have been imposed on loading modules. Signed-off-by: Matthew Garrett --- drivers/char/mem.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 53fe675f9bd764..b52c8886053228 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -167,6 +167,9 @@ static ssize_t write_mem(struct file *file, const char __user *buf, if (p != *ppos) return -EFBIG; + if (secure_modules()) + return -EPERM; + if (!valid_phys_addr_range(p, count)) return -EFAULT; @@ -513,6 +516,9 @@ static ssize_t write_kmem(struct file *file, const char __user *buf, char *kbuf; /* k-addr because vwrite() takes vmlist_lock rwlock */ int err = 0; + if (secure_modules()) + return -EPERM; + if (p < (unsigned long) high_memory) { unsigned long to_write = min_t(unsigned long, count, (unsigned long)high_memory - p); From 16d485311fc3079de4f5b986f2fc2f7d70274f8d Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Mon, 25 Jun 2012 19:57:30 -0400 Subject: [PATCH 07/21] acpi: Ignore acpi_rsdp kernel parameter when module loading is restricted This option allows userspace to pass the RSDP address to the kernel, which makes it possible for a user to circumvent any restrictions imposed on loading modules. Disable it in that case. Signed-off-by: Josh Boyer --- drivers/acpi/osl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 32d684af0ec7c8..f8570a0f6d75e4 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include @@ -252,7 +253,7 @@ early_param("acpi_rsdp", setup_acpi_rsdp); acpi_physical_address __init acpi_os_get_root_pointer(void) { #ifdef CONFIG_KEXEC - if (acpi_rsdp) + if (acpi_rsdp && !secure_modules()) return acpi_rsdp; #endif From 7d0d3cb705bb1ae5a739d0087e62844d3bec5e6f Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 19 Nov 2015 18:55:53 -0800 Subject: [PATCH 08/21] kexec: Disable at runtime if the kernel enforces module loading restrictions kexec permits the loading and execution of arbitrary code in ring 0, which is something that module signing enforcement is meant to prevent. It makes sense to disable kexec in this situation. Signed-off-by: Matthew Garrett --- kernel/kexec.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/kexec.c b/kernel/kexec.c index d873b64fbddcdd..3d096423b81fd7 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c @@ -17,6 +17,7 @@ #include #include #include +#include #include "kexec_internal.h" @@ -131,7 +132,7 @@ SYSCALL_DEFINE4(kexec_load, unsigned long, entry, unsigned long, nr_segments, int result; /* We only trust the superuser with rebooting the system. */ - if (!capable(CAP_SYS_BOOT) || kexec_load_disabled) + if (!capable(CAP_SYS_BOOT) || kexec_load_disabled || secure_modules()) return -EPERM; /* From c682c72e808feb7c4dcb42ecaae7016c13ce5610 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 8 Feb 2013 11:12:13 -0800 Subject: [PATCH 09/21] x86: Restrict MSR access when module loading is restricted Writing to MSRs should not be allowed if module loading is restricted, since it could lead to execution of arbitrary code in kernel mode. Based on a patch by Kees Cook. Cc: Kees Cook Signed-off-by: Matthew Garrett --- arch/x86/kernel/msr.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/x86/kernel/msr.c b/arch/x86/kernel/msr.c index 113e70784854fb..26c2f83fc47056 100644 --- a/arch/x86/kernel/msr.c +++ b/arch/x86/kernel/msr.c @@ -105,6 +105,9 @@ static ssize_t msr_write(struct file *file, const char __user *buf, int err = 0; ssize_t bytes = 0; + if (secure_modules()) + return -EPERM; + if (count % 8) return -EINVAL; /* Invalid chunk size */ @@ -152,6 +155,10 @@ static long msr_ioctl(struct file *file, unsigned int ioc, unsigned long arg) err = -EBADF; break; } + if (secure_modules()) { + err = -EPERM; + break; + } if (copy_from_user(®s, uregs, sizeof regs)) { err = -EFAULT; break; From abac45cbcaa27170eef195cb48c33a1b37071f2a Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Fri, 9 Aug 2013 18:36:30 -0400 Subject: [PATCH 10/21] Add option to automatically enforce module signatures when in Secure Boot mode UEFI Secure Boot provides a mechanism for ensuring that the firmware will only load signed bootloaders and kernels. Certain use cases may also require that all kernel modules also be signed. Add a configuration option that enforces this automatically when enabled. Signed-off-by: Matthew Garrett --- Documentation/x86/zero-page.txt | 2 ++ arch/x86/Kconfig | 10 ++++++++ arch/x86/boot/compressed/eboot.c | 36 +++++++++++++++++++++++++++ arch/x86/include/uapi/asm/bootparam.h | 3 ++- arch/x86/kernel/setup.c | 6 +++++ include/linux/module.h | 6 +++++ kernel/module.c | 7 ++++++ 7 files changed, 69 insertions(+), 1 deletion(-) diff --git a/Documentation/x86/zero-page.txt b/Documentation/x86/zero-page.txt index 95a4d34af3fdd7..b8527c6b76461c 100644 --- a/Documentation/x86/zero-page.txt +++ b/Documentation/x86/zero-page.txt @@ -31,6 +31,8 @@ Offset Proto Name Meaning 1E9/001 ALL eddbuf_entries Number of entries in eddbuf (below) 1EA/001 ALL edd_mbr_sig_buf_entries Number of entries in edd_mbr_sig_buffer (below) +1EB/001 ALL kbd_status Numlock is enabled +1EC/001 ALL secure_boot Secure boot is enabled in the firmware 1EF/001 ALL sentinel Used to detect broken bootloaders 290/040 ALL edd_mbr_sig_buffer EDD MBR signatures 2D0/A00 ALL e820_map E820 memory map table diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index db3622f22b6183..5578b6e64a500d 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1720,6 +1720,16 @@ config EFI_MIXED If unsure, say N. +config EFI_SECURE_BOOT_SIG_ENFORCE + def_bool n + prompt "Force module signing when UEFI Secure Boot is enabled" + ---help--- + UEFI Secure Boot provides a mechanism for ensuring that the + firmware will only load signed bootloaders and kernels. Certain + use cases may also require that all kernel modules also be signed. + Say Y here to automatically enable module signature enforcement + when a system boots with UEFI Secure Boot enabled. + config SECCOMP def_bool y prompt "Enable seccomp to safely compute untrusted bytecode" diff --git a/arch/x86/boot/compressed/eboot.c b/arch/x86/boot/compressed/eboot.c index 583d539a41977a..ca120ac1d78380 100644 --- a/arch/x86/boot/compressed/eboot.c +++ b/arch/x86/boot/compressed/eboot.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "../string.h" #include "eboot.h" @@ -847,6 +848,37 @@ setup_gop64(struct screen_info *si, efi_guid_t *proto, return status; } +static int get_secure_boot(void) +{ + u8 sb, setup; + unsigned long datasize = sizeof(sb); + efi_guid_t var_guid = EFI_GLOBAL_VARIABLE_GUID; + efi_status_t status; + + status = efi_early->call((unsigned long)sys_table->runtime->get_variable, + L"SecureBoot", &var_guid, NULL, &datasize, &sb); + + if (status != EFI_SUCCESS) + return 0; + + if (sb == 0) + return 0; + + + status = efi_early->call((unsigned long)sys_table->runtime->get_variable, + L"SetupMode", &var_guid, NULL, &datasize, + &setup); + + if (status != EFI_SUCCESS) + return 0; + + if (setup == 1) + return 0; + + return 1; +} + + /* * See if we have Graphics Output Protocol */ @@ -1432,6 +1464,10 @@ struct boot_params *efi_main(struct efi_config *c, else setup_boot_services32(efi_early); + sanitize_boot_params(boot_params); + + boot_params->secure_boot = get_secure_boot(); + setup_graphics(boot_params); setup_efi_pci(boot_params); diff --git a/arch/x86/include/uapi/asm/bootparam.h b/arch/x86/include/uapi/asm/bootparam.h index 329254373479ad..b61f8533c0fdaf 100644 --- a/arch/x86/include/uapi/asm/bootparam.h +++ b/arch/x86/include/uapi/asm/bootparam.h @@ -134,7 +134,8 @@ struct boot_params { __u8 eddbuf_entries; /* 0x1e9 */ __u8 edd_mbr_sig_buf_entries; /* 0x1ea */ __u8 kbd_status; /* 0x1eb */ - __u8 _pad5[3]; /* 0x1ec */ + __u8 secure_boot; /* 0x1ec */ + __u8 _pad5[2]; /* 0x1ed */ /* * The sentinel is set to a nonzero value (0xff) in header.S. * diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index d2bbe343fda74a..a35c42f01d7771 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1143,6 +1143,12 @@ void __init setup_arch(char **cmdline_p) io_delay_init(); +#ifdef CONFIG_EFI_SECURE_BOOT_SIG_ENFORCE + if (boot_params.secure_boot) { + enforce_signed_modules(); + } +#endif + /* * Parse the ACPI tables for possible boot-time SMP configuration. */ diff --git a/include/linux/module.h b/include/linux/module.h index db386349cd0144..4b8df91f03cda6 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -273,6 +273,12 @@ const struct exception_table_entry *search_exception_tables(unsigned long add); struct notifier_block; +#ifdef CONFIG_MODULE_SIG +extern void enforce_signed_modules(void); +#else +static inline void enforce_signed_modules(void) {}; +#endif + #ifdef CONFIG_MODULES extern int modules_disabled; /* for sysctl */ diff --git a/kernel/module.c b/kernel/module.c index a8f8c64689273e..3eb8c7434973dc 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -4098,6 +4098,13 @@ void module_layout(struct module *mod, EXPORT_SYMBOL(module_layout); #endif +#ifdef CONFIG_MODULE_SIG +void enforce_signed_modules(void) +{ + sig_enforce = true; +} +#endif + bool secure_modules(void) { #ifdef CONFIG_MODULE_SIG From 76ba8b2fee84c6489316547f19d03a0485f59dc3 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Tue, 27 Aug 2013 13:28:43 -0400 Subject: [PATCH 11/21] efi: Make EFI_SECURE_BOOT_SIG_ENFORCE depend on EFI The functionality of the config option is dependent upon the platform being UEFI based. Reflect this in the config deps. Signed-off-by: Josh Boyer --- arch/x86/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 5578b6e64a500d..da9ae8adb944b3 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1721,7 +1721,8 @@ config EFI_MIXED If unsure, say N. config EFI_SECURE_BOOT_SIG_ENFORCE - def_bool n + def_bool n + depends on EFI prompt "Force module signing when UEFI Secure Boot is enabled" ---help--- UEFI Secure Boot provides a mechanism for ensuring that the From 8d2a8d8ce61706a3a778ae9fd79cb5bab91a2817 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Tue, 27 Aug 2013 13:33:03 -0400 Subject: [PATCH 12/21] efi: Add EFI_SECURE_BOOT bit UEFI machines can be booted in Secure Boot mode. Add a EFI_SECURE_BOOT bit for use with efi_enabled. Signed-off-by: Josh Boyer --- arch/x86/kernel/setup.c | 2 ++ include/linux/efi.h | 1 + 2 files changed, 3 insertions(+) diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index a35c42f01d7771..e96398f9aee0b7 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -1145,7 +1145,9 @@ void __init setup_arch(char **cmdline_p) #ifdef CONFIG_EFI_SECURE_BOOT_SIG_ENFORCE if (boot_params.secure_boot) { + set_bit(EFI_SECURE_BOOT, &efi.flags); enforce_signed_modules(); + pr_info("Secure boot enabled\n"); } #endif diff --git a/include/linux/efi.h b/include/linux/efi.h index 569b5a866bb1e6..4dc970e48b4341 100644 --- a/include/linux/efi.h +++ b/include/linux/efi.h @@ -980,6 +980,7 @@ extern int __init efi_setup_pcdp_console(char *); #define EFI_ARCH_1 7 /* First arch-specific bit */ #define EFI_DBG 8 /* Print additional debug info at runtime */ #define EFI_NX_PE_DATA 9 /* Can runtime data regions be mapped non-executable? */ +#define EFI_SECURE_BOOT 10 /* Are we in Secure Boot mode? */ #ifdef CONFIG_EFI /* From b671df07aed28fcbc9e470b52b8c1822f78303c0 Mon Sep 17 00:00:00 2001 From: Josh Boyer Date: Fri, 20 Jun 2014 08:53:24 -0400 Subject: [PATCH 13/21] hibernate: Disable in a signed modules environment There is currently no way to verify the resume image when returning from hibernate. This might compromise the signed modules trust model, so until we can work with signed hibernate images we disable it in a secure modules environment. Signed-off-by: Josh Boyer --- kernel/power/hibernate.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index b7342a24f55921..8a6b21875381fd 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include "power.h" @@ -66,7 +67,7 @@ static const struct platform_hibernation_ops *hibernation_ops; bool hibernation_available(void) { - return (nohibernate == 0); + return ((nohibernate == 0) && !secure_modules()); } /** From 9cb22840851be7a7f842229e6603a6b4b25e824d Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Jun 2015 14:14:31 +0100 Subject: [PATCH 14/21] Security: Provide copy-up security hooks for unioned files Provide two new security hooks for use with security files that are used when a file is copied up between layers: (1) security_inode_copy_up(). This is called so that the security label on the destination file can be set appropriately. (2) security_inode_copy_up_xattr(). This is called so that each xattr being copied up can be vetted - including modification and discard. Signed-off-by: David Howells --- include/linux/lsm_hooks.h | 23 +++++++++++++++++++++++ include/linux/security.h | 14 ++++++++++++++ security/security.c | 17 +++++++++++++++++ 3 files changed, 54 insertions(+) diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index ec3a6bab29de3a..8c0c524dd232dc 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -401,6 +401,24 @@ * @inode contains a pointer to the inode. * @secid contains a pointer to the location where result will be saved. * In case of failure, @secid will be set to zero. + * @inode_copy_up: + * Appropriately label the destination inode when a unioned file is copied + * up from a lower layer to the union/overlay layer. + * @src indicates the file that is being copied up. + * @dst indicates the file that has being created by the copy up. + * Returns 0 on success or a negative error code on error. + * @inode_copy_up_xattr: + * Filter/modify the xattrs being copied up when a unioned file is copied + * up from a lower layer to the union/overlay layer. + * @src indicates the file that is being copied up. + * @dst indicates the file that has being created by the copy up. + * @name indicates the name of the xattr. + * @value, *@size indicate the payload of the xattr. + * Returns 0 to accept the xattr, 1 to discard the xattr or a negative + * error code to abort the copy up. The xattr buffer must be at least + * XATTR_SIZE_MAX in capacity and the contents may be modified and *@size + * changed appropriately. Note that the caller is responsible for reading + * and writing the xattrs as this hook is merely a filter. * * Security hooks for file operations * @@ -1421,6 +1439,9 @@ union security_list_options { int (*inode_listsecurity)(struct inode *inode, char *buffer, size_t buffer_size); void (*inode_getsecid)(const struct inode *inode, u32 *secid); + int (*inode_copy_up) (struct dentry *src, struct dentry *dst); + int (*inode_copy_up_xattr) (struct dentry *src, struct dentry *dst, + const char *name, void *value, size_t *size); int (*file_permission)(struct file *file, int mask); int (*file_alloc_security)(struct file *file); @@ -1689,6 +1710,8 @@ struct security_hook_heads { struct list_head inode_setsecurity; struct list_head inode_listsecurity; struct list_head inode_getsecid; + struct list_head inode_copy_up; + struct list_head inode_copy_up_xattr; struct list_head file_permission; struct list_head file_alloc_security; struct list_head file_free_security; diff --git a/include/linux/security.h b/include/linux/security.h index 2f4c1f7aa7db70..ec21144d88074b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -274,6 +274,10 @@ int security_inode_getsecurity(const struct inode *inode, const char *name, void int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags); int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size); void security_inode_getsecid(const struct inode *inode, u32 *secid); +int security_inode_copy_up(struct dentry *src, struct dentry *dst); +int security_inode_copy_up_xattr(struct dentry *src, struct dentry *dst, + const char *name, void *value, size_t *size); + int security_file_permission(struct file *file, int mask); int security_file_alloc(struct file *file); void security_file_free(struct file *file); @@ -739,6 +743,16 @@ static inline void security_inode_getsecid(const struct inode *inode, u32 *secid *secid = 0; } +static inline int security_inode_copy_up(struct dentry *src, struct dentry *dst) +{ + return 0; +} +static inline int security_inode_copy_up_xattr(struct dentry *src, struct dentry *dst, + const char *name, const void *value, size_t *size) +{ + return 0; +} + static inline int security_file_permission(struct file *file, int mask) { return 0; diff --git a/security/security.c b/security/security.c index 46f405ce6b0fba..e33c5d5bdc6b59 100644 --- a/security/security.c +++ b/security/security.c @@ -726,6 +726,19 @@ void security_inode_getsecid(const struct inode *inode, u32 *secid) call_void_hook(inode_getsecid, inode, secid); } +int security_inode_copy_up(struct dentry *src, struct dentry *dst) +{ + return call_int_hook(inode_copy_up, 0, src, dst); +} +EXPORT_SYMBOL(security_inode_copy_up); + +int security_inode_copy_up_xattr(struct dentry *src, struct dentry *dst, + const char *name, void *value, size_t *size) +{ + return call_int_hook(inode_copy_up_xattr, 0, src, dst, name, value, size); +} +EXPORT_SYMBOL(security_inode_copy_up_xattr); + int security_file_permission(struct file *file, int mask) { int ret; @@ -1654,6 +1667,10 @@ struct security_hook_heads security_hook_heads = { LIST_HEAD_INIT(security_hook_heads.inode_listsecurity), .inode_getsecid = LIST_HEAD_INIT(security_hook_heads.inode_getsecid), + .inode_copy_up = + LIST_HEAD_INIT(security_hook_heads.inode_copy_up), + .inode_copy_up_xattr = + LIST_HEAD_INIT(security_hook_heads.inode_copy_up_xattr), .file_permission = LIST_HEAD_INIT(security_hook_heads.file_permission), .file_alloc_security = From 64ef0efdd90f5aae4fae7c76783b09af53d29dfe Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Jun 2015 14:14:31 +0100 Subject: [PATCH 15/21] Overlayfs: Use copy-up security hooks Use the copy-up security hooks previously provided to allow an LSM to adjust the security on a newly created copy and to filter the xattrs copied to that file copy. Signed-off-by: David Howells --- fs/overlayfs/copy_up.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index 0a8983492d917b..f59e1d873c28ce 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -58,6 +58,14 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new) error = size; goto out_free_value; } + error = security_inode_copy_up_xattr(old, new, + name, value, &size); + if (error < 0) + goto out_free_value; + if (error == 1) { + error = 0; + continue; /* Discard */ + } error = vfs_setxattr(new, name, value, size, 0); if (error) goto out_free_value; @@ -222,6 +230,10 @@ static int ovl_copy_up_locked(struct dentry *workdir, struct dentry *upperdir, if (err) goto out2; + err = security_inode_copy_up(lowerpath->dentry, newdentry); + if (err < 0) + goto out_cleanup; + if (S_ISREG(stat->mode)) { struct path upperpath; ovl_path_upper(dentry, &upperpath); From 38d19edb9bae02a9e78b26a7b2c4f0980ee13ee3 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Jun 2015 14:14:32 +0100 Subject: [PATCH 16/21] SELinux: Stub in copy-up handling Provide stubs for union/overlay copy-up handling. The xattr copy up stub discards lower SELinux xattrs rather than letting them be copied up so that the security label on the copy doesn't get corrupted. Signed-off-by: David Howells --- security/selinux/hooks.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d0cfaa9f19d080..d0622099b302d6 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3188,6 +3188,24 @@ static void selinux_inode_getsecid(const struct inode *inode, u32 *secid) *secid = isec->sid; } +static int selinux_inode_copy_up(struct dentry *src, struct dentry *dst) +{ + return 0; +} + +static int selinux_inode_copy_up_xattr(struct dentry *src, struct dentry *dst, + const char *name, void *value, + size_t *size) +{ + /* The copy_up hook above sets the initial context on an inode, but we + * don't then want to overwrite it by blindly copying all the lower + * xattrs up. Instead, we have to filter out SELinux-related xattrs. + */ + if (strcmp(name, XATTR_NAME_SELINUX) == 0) + return 1; /* Discard */ + return 0; +} + /* file security operations */ static int selinux_revalidate_file_permission(struct file *file, int mask) @@ -5919,6 +5937,8 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(inode_setsecurity, selinux_inode_setsecurity), LSM_HOOK_INIT(inode_listsecurity, selinux_inode_listsecurity), LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid), + LSM_HOOK_INIT(inode_copy_up, selinux_inode_copy_up), + LSM_HOOK_INIT(inode_copy_up_xattr, selinux_inode_copy_up_xattr), LSM_HOOK_INIT(file_permission, selinux_file_permission), LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security), From 3e6ccc54dd0383a8c57287f9e63f392595e28cb1 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Jun 2015 14:14:32 +0100 Subject: [PATCH 17/21] SELinux: Handle opening of a unioned file Handle the opening of a unioned file by trying to derive the label that would be attached to the union-layer inode if it doesn't exist. If the union-layer inode does exist (as it necessarily does in overlayfs, but not in unionmount), we assume that it has the right label and use that. Otherwise we try to get it from the superblock. If the superblock has a globally-applied label, we use that, otherwise we try to transition to an appropriate label. This union label is then stored in the file_security_struct. We then perform an additional check to make sure that the calling task is granted permission by the union-layer inode label to open the file in addition to a check to make sure that the task is granted permission to open the lower file with the lower inode label. Signed-off-by: David Howells --- security/selinux/hooks.c | 69 +++++++++++++++++++++++++++++++ security/selinux/include/objsec.h | 1 + 2 files changed, 70 insertions(+) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d0622099b302d6..5f0a11f434d94d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3518,10 +3518,72 @@ static int selinux_file_receive(struct file *file) return file_has_perm(cred, file, file_to_av(file)); } +/* + * We have a file opened on a unioned file system that falls through to a file + * on a lower layer. If there is a union inode, we try to get the label from + * that, otherwise we need to get it from the superblock. + * + * file->f_path points to the union layer and file->f_inode points to the lower + * layer. + */ +static int selinux_file_open_union(struct file *file, + struct file_security_struct *fsec, + const struct cred *cred) +{ + const struct superblock_security_struct *sbsec; + const struct inode_security_struct *isec, *dsec, *fisec; + const struct task_security_struct *tsec = current_security(); + struct common_audit_data ad; + struct dentry *union_dentry = file->f_path.dentry; + const struct inode *union_inode = d_inode(union_dentry); + const struct inode *lower_inode = file_inode(file); + struct dentry *dir; + int rc; + + sbsec = union_dentry->d_sb->s_security; + + if (union_inode) { + isec = union_inode->i_security; + fsec->union_isid = isec->sid; + } else if ((sbsec->flags & SE_SBINITIALIZED) && + (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) { + fsec->union_isid = sbsec->mntpoint_sid; + } else { + dir = dget_parent(union_dentry); + dsec = d_inode(dir)->i_security; + + rc = security_transition_sid( + tsec->sid, dsec->sid, + inode_mode_to_security_class(lower_inode->i_mode), + &union_dentry->d_name, + &fsec->union_isid); + dput(dir); + if (rc) { + pr_warn("%s: security_transition_sid failed, rc=%d (name=%pD)\n", + __func__, -rc, file); + return rc; + } + } + + /* We need to check that the union file is allowed to be opened as well + * as checking that the lower file is allowed to be opened. + */ + if (unlikely(IS_PRIVATE(lower_inode))) + return 0; + + ad.type = LSM_AUDIT_DATA_PATH; + ad.u.path = file->f_path; + + fisec = lower_inode->i_security; + return avc_has_perm(cred_sid(cred), fsec->union_isid, fisec->sclass, + open_file_to_av(file), &ad); +} + static int selinux_file_open(struct file *file, const struct cred *cred) { struct file_security_struct *fsec; struct inode_security_struct *isec; + int rc; fsec = file->f_security; isec = file_inode(file)->i_security; @@ -3542,6 +3604,13 @@ static int selinux_file_open(struct file *file, const struct cred *cred) * new inode label or new policy. * This check is not redundant - do not remove. */ + + if (d_inode(file->f_path.dentry) != file->f_inode) { + rc = selinux_file_open_union(file, fsec, cred); + if (rc < 0) + return rc; + } + return file_path_has_perm(cred, file, open_file_to_av(file)); } diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 81fa718d5cb388..f088c080aa9e8a 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -54,6 +54,7 @@ struct file_security_struct { u32 sid; /* SID of open file description */ u32 fown_sid; /* SID of file owner (for SIGIO) */ u32 isid; /* SID of inode at the time of file open */ + u32 union_isid; /* SID of would-be inodes in union top (or 0) */ u32 pseqno; /* Policy seqno at the time of file open */ }; From 7b0a1257f4b4a35f087db9120b684d3a9c8181e5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 16 Jun 2015 14:14:32 +0100 Subject: [PATCH 18/21] SELinux: Check against union label for file operations File operations (eg. read, write) issued against a file that is attached to the lower layer of a union file needs to be checked against the union-layer label not the lower layer label. The union label is stored in the file_security_struct rather than being retrieved from one of the inodes. Signed-off-by: David Howells --- security/selinux/hooks.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5f0a11f434d94d..e33019ebc2bedb 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1682,6 +1682,7 @@ static int file_has_perm(const struct cred *cred, struct file *file, u32 av) { + struct inode_security_struct *isec; struct file_security_struct *fsec = file->f_security; struct inode *inode = file_inode(file); struct common_audit_data ad; @@ -1702,8 +1703,15 @@ static int file_has_perm(const struct cred *cred, /* av is zero if only checking access to the descriptor. */ rc = 0; - if (av) - rc = inode_has_perm(cred, inode, av, &ad); + if (av && likely(!IS_PRIVATE(inode))) { + if (fsec->union_isid) { + isec = inode->i_security; + rc = avc_has_perm(sid, fsec->union_isid, isec->sclass, + av, &ad); + } + if (!rc) + rc = inode_has_perm(cred, inode, av, &ad); + } out: return rc; From 7505098adc7a76c3d001831af40f39c86d624a67 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Mon, 19 Oct 2015 17:53:12 -0700 Subject: [PATCH 19/21] overlayfs: use a minimal buffer in ovl_copy_xattr Rather than always allocating the high-order XATTR_SIZE_MAX buffer which is costly and prone to failure, only allocate what is needed and realloc if necessary. Fixes https://github.com/coreos/bugs/issues/489 --- fs/overlayfs/copy_up.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index f59e1d873c28ce..fff40c4a7b74dd 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -22,8 +22,8 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new) { - ssize_t list_size, size; - char *buf, *name, *value; + ssize_t list_size, size, value_size = 0; + char *buf, *name, *value = NULL; int error; if (!old->d_inode->i_op->getxattr || @@ -41,23 +41,36 @@ int ovl_copy_xattr(struct dentry *old, struct dentry *new) if (!buf) return -ENOMEM; - error = -ENOMEM; - value = kmalloc(XATTR_SIZE_MAX, GFP_KERNEL); - if (!value) - goto out; - list_size = vfs_listxattr(old, buf, list_size); if (list_size <= 0) { error = list_size; - goto out_free_value; + goto out; } for (name = buf; name < (buf + list_size); name += strlen(name) + 1) { - size = vfs_getxattr(old, name, value, XATTR_SIZE_MAX); +retry: + size = vfs_getxattr(old, name, value, value_size); + if (size == -ERANGE) { + size = vfs_getxattr(old, name, NULL, 0); + } + if (size <= 0) { error = size; goto out_free_value; } + + if (size > value_size) { + void *new; + new = krealloc(value, size, GFP_KERNEL); + if (!new) { + error = -ENOMEM; + goto out_free_value; + } + value = new; + value_size = size; + goto retry; + } + error = security_inode_copy_up_xattr(old, new, name, value, &size); if (error < 0) From b0a4a60266e116f35e31a2054d9769f23dc88a95 Mon Sep 17 00:00:00 2001 From: Vito Caputo Date: Wed, 25 Nov 2015 02:59:45 -0800 Subject: [PATCH 20/21] kbuild: derive relative path for KBUILD_SRC from CURDIR This enables relocating source and build trees to different roots, provided they stay reachable relative to one another. Useful for builds done within a sandbox where the eventual root is prefixed by some undesirable path component. --- Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index c6a265b52c93c7..8125380ecaa7a7 100644 --- a/Makefile +++ b/Makefile @@ -143,7 +143,8 @@ $(filter-out _all sub-make $(CURDIR)/Makefile, $(MAKECMDGOALS)) _all: sub-make @: sub-make: FORCE - $(Q)$(MAKE) -C $(KBUILD_OUTPUT) KBUILD_SRC=$(CURDIR) \ + $(Q)$(MAKE) -C $(KBUILD_OUTPUT) \ + KBUILD_SRC=$(shell realpath --relative-to=$(KBUILD_OUTPUT) $(CURDIR)) \ -f $(CURDIR)/Makefile $(filter-out _all sub-make,$(MAKECMDGOALS)) # Leave processing to above invocation of make From 196c562e9a0ef9a1580f35c014ee7f4669cfb5d7 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 22 Dec 2015 07:43:52 +0000 Subject: [PATCH 21/21] Don't verify write permissions on lower inodes on overlayfs If a user opens a file r/w on overlayfs, and if the underlying inode is currently still on the lower fs, right now we're verifying whether selinux policy permits writes to the selinux context on the underlying inode. This is suboptimal, since we don't want confined processes to be able to write to these files if they're able to escape from a container and so don't want to permit this in policy. Have overlayfs pass down an additional flag when verifying the permission on lower inodes, and mask off the write bits in the selinux permissions check if that flag is set. --- fs/overlayfs/inode.c | 3 +++ include/linux/fs.h | 1 + security/selinux/hooks.c | 9 +++++++++ 3 files changed, 13 insertions(+) diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 4060ffde87225c..b6f02f2bdf6b15 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -125,6 +125,9 @@ int ovl_permission(struct inode *inode, int mask) goto out_dput; } + if (!is_upper) + mask |= MAY_OPEN_LOWER; + err = __inode_permission(realinode, mask); out_dput: dput(alias); diff --git a/include/linux/fs.h b/include/linux/fs.h index 3aa51425416148..57120135dada3b 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -82,6 +82,7 @@ typedef void (dax_iodone_t)(struct buffer_head *bh_map, int uptodate); #define MAY_CHDIR 0x00000040 /* called from RCU mode, don't block */ #define MAY_NOT_BLOCK 0x00000080 +#define MAY_OPEN_LOWER 0x00000100 /* * flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e33019ebc2bedb..48746ee42f1406 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2904,6 +2904,15 @@ static int selinux_inode_permission(struct inode *inode, int mask) u32 audited, denied; from_access = mask & MAY_ACCESS; + + /* + * If we're trying to open the lower layer of an overlay mount, don't + * worry about write or append permissions - these will be verified + * against the upper context + */ + if (mask & MAY_OPEN_LOWER) + mask &= ~(MAY_WRITE|MAY_APPEND); + mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); /* No permission to check. Existence test. */