Merge branch 'next-lockdown' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris...
authorLinus Torvalds <torvalds@linux-foundation.org>
Sat, 28 Sep 2019 15:14:15 +0000 (08:14 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 28 Sep 2019 15:14:15 +0000 (08:14 -0700)
Pull kernel lockdown mode from James Morris:
 "This is the latest iteration of the kernel lockdown patchset, from
  Matthew Garrett, David Howells and others.

  From the original description:

    This patchset introduces an optional kernel lockdown feature,
    intended to strengthen the boundary between UID 0 and the kernel.
    When enabled, various pieces of kernel functionality are restricted.
    Applications that rely on low-level access to either hardware or the
    kernel may cease working as a result - therefore this should not be
    enabled without appropriate evaluation beforehand.

    The majority of mainstream distributions have been carrying variants
    of this patchset for many years now, so there's value in providing a
    doesn't meet every distribution requirement, but gets us much closer
    to not requiring external patches.

  There are two major changes since this was last proposed for mainline:

   - Separating lockdown from EFI secure boot. Background discussion is
     covered here: https://lwn.net/Articles/751061/

   -  Implementation as an LSM, with a default stackable lockdown LSM
      module. This allows the lockdown feature to be policy-driven,
      rather than encoding an implicit policy within the mechanism.

  The new locked_down LSM hook is provided to allow LSMs to make a
  policy decision around whether kernel functionality that would allow
  tampering with or examining the runtime state of the kernel should be
  permitted.

  The included lockdown LSM provides an implementation with a simple
  policy intended for general purpose use. This policy provides a coarse
  level of granularity, controllable via the kernel command line:

    lockdown={integrity|confidentiality}

  Enable the kernel lockdown feature. If set to integrity, kernel features
  that allow userland to modify the running kernel are disabled. If set to
  confidentiality, kernel features that allow userland to extract
  confidential information from the kernel are also disabled.

  This may also be controlled via /sys/kernel/security/lockdown and
  overriden by kernel configuration.

  New or existing LSMs may implement finer-grained controls of the
  lockdown features. Refer to the lockdown_reason documentation in
  include/linux/security.h for details.

  The lockdown feature has had signficant design feedback and review
  across many subsystems. This code has been in linux-next for some
  weeks, with a few fixes applied along the way.

  Stephen Rothwell noted that commit 9d1f8be5cf42 ("bpf: Restrict bpf
  when kernel lockdown is in confidentiality mode") is missing a
  Signed-off-by from its author. Matthew responded that he is providing
  this under category (c) of the DCO"

* 'next-lockdown' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (31 commits)
  kexec: Fix file verification on S390
  security: constify some arrays in lockdown LSM
  lockdown: Print current->comm in restriction messages
  efi: Restrict efivar_ssdt_load when the kernel is locked down
  tracefs: Restrict tracefs when the kernel is locked down
  debugfs: Restrict debugfs when the kernel is locked down
  kexec: Allow kexec_file() with appropriate IMA policy when locked down
  lockdown: Lock down perf when in confidentiality mode
  bpf: Restrict bpf when kernel lockdown is in confidentiality mode
  lockdown: Lock down tracing and perf kprobes when in confidentiality mode
  lockdown: Lock down /proc/kcore
  x86/mmiotrace: Lock down the testmmiotrace module
  lockdown: Lock down module params that specify hardware parameters (eg. ioport)
  lockdown: Lock down TIOCSSERIAL
  lockdown: Prohibit PCMCIA CIS storage when the kernel is locked down
  acpi: Disable ACPI table override if the kernel is locked down
  acpi: Ignore acpi_rsdp kernel param when the kernel has been locked down
  ACPI: Limit access to custom_method when the kernel is locked down
  x86/msr: Restrict MSR access when the kernel is locked down
  x86: Lock down IO port access when the kernel is locked down
  ...

58 files changed:
Documentation/admin-guide/kernel-parameters.txt
arch/arm64/Kconfig
arch/s390/Kconfig
arch/s390/kernel/kexec_elf.c
arch/s390/kernel/kexec_image.c
arch/s390/kernel/machine_kexec_file.c
arch/x86/Kconfig
arch/x86/boot/compressed/acpi.c
arch/x86/include/asm/acpi.h
arch/x86/include/asm/x86_init.h
arch/x86/kernel/acpi/boot.c
arch/x86/kernel/ima_arch.c
arch/x86/kernel/ioport.c
arch/x86/kernel/kexec-bzimage64.c
arch/x86/kernel/msr.c
arch/x86/kernel/x86_init.c
arch/x86/mm/testmmiotrace.c
crypto/asymmetric_keys/verify_pefile.c
drivers/acpi/custom_method.c
drivers/acpi/osl.c
drivers/acpi/tables.c
drivers/char/mem.c
drivers/firmware/efi/efi.c
drivers/pci/pci-sysfs.c
drivers/pci/proc.c
drivers/pci/syscall.c
drivers/pcmcia/cistpl.c
drivers/tty/serial/serial_core.c
fs/debugfs/file.c
fs/debugfs/inode.c
fs/proc/kcore.c
fs/tracefs/inode.c
include/asm-generic/vmlinux.lds.h
include/linux/acpi.h
include/linux/ima.h
include/linux/kexec.h
include/linux/lsm_hooks.h
include/linux/security.h
init/Kconfig
init/main.c
kernel/events/core.c
kernel/kexec.c
kernel/kexec_file.c
kernel/module.c
kernel/params.c
kernel/power/hibernate.c
kernel/trace/bpf_trace.c
kernel/trace/trace_kprobe.c
security/Kconfig
security/Makefile
security/integrity/ima/Kconfig
security/integrity/ima/ima.h
security/integrity/ima/ima_main.c
security/integrity/ima/ima_policy.c
security/lockdown/Kconfig [new file with mode: 0644]
security/lockdown/Makefile [new file with mode: 0644]
security/lockdown/lockdown.c [new file with mode: 0644]
security/security.c

index 944e03e29f65c9d31075d59bb06f8732d9fc4245..c7ac2f3ac99fd29bef9db55d03f01c20c9912ad9 100644 (file)
        lockd.nlm_udpport=M     [NFS] Assign UDP port.
                        Format: <integer>
 
+       lockdown=       [SECURITY]
+                       { integrity | confidentiality }
+                       Enable the kernel lockdown feature. If set to
+                       integrity, kernel features that allow userland to
+                       modify the running kernel are disabled. If set to
+                       confidentiality, kernel features that allow userland
+                       to extract confidential information from the kernel
+                       are also disabled.
+
        locktorture.nreaders_stress= [KNL]
                        Set the number of locking read-acquisition kthreads.
                        Defaults to being automatically set based on the
index 866e05882799069f0a54ccac0cc45b1a2775163c..41a9b4257b727710bcd4be1e9bcf47e6739a908d 100644 (file)
@@ -982,7 +982,7 @@ config KEXEC_FILE
          for kernel and initramfs as opposed to list of segments as
          accepted by previous system call.
 
-config KEXEC_VERIFY_SIG
+config KEXEC_SIG
        bool "Verify kernel signature during kexec_file_load() syscall"
        depends on KEXEC_FILE
        help
@@ -997,13 +997,13 @@ config KEXEC_VERIFY_SIG
 config KEXEC_IMAGE_VERIFY_SIG
        bool "Enable Image signature verification support"
        default y
-       depends on KEXEC_VERIFY_SIG
+       depends on KEXEC_SIG
        depends on EFI && SIGNED_PE_FILE_VERIFICATION
        help
          Enable Image signature verification support.
 
 comment "Support for PE file signature verification disabled"
-       depends on KEXEC_VERIFY_SIG
+       depends on KEXEC_SIG
        depends on !EFI || !SIGNED_PE_FILE_VERIFICATION
 
 config CRASH_DUMP
index 68f7c2b16ff71f7bb5cacff692111e04c2cb9581..43a81d0ad5074dc8fd22adba001ce61d428b6f3e 100644 (file)
@@ -554,7 +554,7 @@ config ARCH_HAS_KEXEC_PURGATORY
        def_bool y
        depends on KEXEC_FILE
 
-config KEXEC_VERIFY_SIG
+config KEXEC_SIG
        bool "Verify kernel signature during kexec_file_load() syscall"
        depends on KEXEC_FILE && MODULE_SIG_FORMAT
        help
index 6d0635ceddd0475de217b73cc05e1f0c3187e312..9da6fa30c44749fd7f4b044f5d211f7cd25d2b33 100644 (file)
@@ -130,7 +130,7 @@ static int s390_elf_probe(const char *buf, unsigned long len)
 const struct kexec_file_ops s390_kexec_elf_ops = {
        .probe = s390_elf_probe,
        .load = s390_elf_load,
-#ifdef CONFIG_KEXEC_VERIFY_SIG
+#ifdef CONFIG_KEXEC_SIG
        .verify_sig = s390_verify_sig,
-#endif /* CONFIG_KEXEC_VERIFY_SIG */
+#endif /* CONFIG_KEXEC_SIG */
 };
index 58318bf89fd905beb8fab9171be4f06e3c118a8a..af23eff5774dba339beeb69c34565f92edd87568 100644 (file)
@@ -59,7 +59,7 @@ static int s390_image_probe(const char *buf, unsigned long len)
 const struct kexec_file_ops s390_kexec_image_ops = {
        .probe = s390_image_probe,
        .load = s390_image_load,
-#ifdef CONFIG_KEXEC_VERIFY_SIG
+#ifdef CONFIG_KEXEC_SIG
        .verify_sig = s390_verify_sig,
-#endif /* CONFIG_KEXEC_VERIFY_SIG */
+#endif /* CONFIG_KEXEC_SIG */
 };
index 1ac9fbc6e01ec2f2d313860764d54232a7d5f5e5..8415ae7d2a23f5173e1628839ef3a68703b31890 100644 (file)
@@ -22,7 +22,7 @@ const struct kexec_file_ops * const kexec_file_loaders[] = {
        NULL,
 };
 
-#ifdef CONFIG_KEXEC_VERIFY_SIG
+#ifdef CONFIG_KEXEC_SIG
 int s390_verify_sig(const char *kernel, unsigned long kernel_len)
 {
        const unsigned long marker_len = sizeof(MODULE_SIG_STRING) - 1;
@@ -68,7 +68,7 @@ int s390_verify_sig(const char *kernel, unsigned long kernel_len)
                                      VERIFYING_MODULE_SIGNATURE,
                                      NULL, NULL);
 }
-#endif /* CONFIG_KEXEC_VERIFY_SIG */
+#endif /* CONFIG_KEXEC_SIG */
 
 static int kexec_file_update_purgatory(struct kimage *image,
                                       struct s390_load_data *data)
index 37ed5f5910d55cd6502c645e051be500accabb27..d6e1faa28c58ebaea297e8e450e9edf2319b3a1a 100644 (file)
@@ -2031,20 +2031,30 @@ config KEXEC_FILE
 config ARCH_HAS_KEXEC_PURGATORY
        def_bool KEXEC_FILE
 
-config KEXEC_VERIFY_SIG
+config KEXEC_SIG
        bool "Verify kernel signature during kexec_file_load() syscall"
        depends on KEXEC_FILE
        ---help---
-         This option makes kernel signature verification mandatory for
-         the kexec_file_load() syscall.
 
-         In addition to that option, you need to enable signature
+         This option makes the kexec_file_load() syscall check for a valid
+         signature of the kernel image.  The image can still be loaded without
+         a valid signature unless you also enable KEXEC_SIG_FORCE, though if
+         there's a signature that we can check, then it must be valid.
+
+         In addition to this option, you need to enable signature
          verification for the corresponding kernel image type being
          loaded in order for this to work.
 
+config KEXEC_SIG_FORCE
+       bool "Require a valid signature in kexec_file_load() syscall"
+       depends on KEXEC_SIG
+       ---help---
+         This option makes kernel signature verification mandatory for
+         the kexec_file_load() syscall.
+
 config KEXEC_BZIMAGE_VERIFY_SIG
        bool "Enable bzImage signature verification support"
-       depends on KEXEC_VERIFY_SIG
+       depends on KEXEC_SIG
        depends on SIGNED_PE_FILE_VERIFICATION
        select SYSTEM_TRUSTED_KEYRING
        ---help---
index 15255f388a85b8e19830d27ec329ffa924533525..149795c369f275d241ffaa7c28106bd184c99b1c 100644 (file)
@@ -26,7 +26,7 @@ struct mem_vector immovable_mem[MAX_NUMNODES*2];
  */
 #define MAX_ADDR_LEN 19
 
-static acpi_physical_address get_acpi_rsdp(void)
+static acpi_physical_address get_cmdline_acpi_rsdp(void)
 {
        acpi_physical_address addr = 0;
 
@@ -278,10 +278,7 @@ acpi_physical_address get_rsdp_addr(void)
 {
        acpi_physical_address pa;
 
-       pa = get_acpi_rsdp();
-
-       if (!pa)
-               pa = boot_params->acpi_rsdp_addr;
+       pa = boot_params->acpi_rsdp_addr;
 
        /*
         * Try to get EFI data from setup_data. This can happen when we're a
@@ -311,7 +308,17 @@ static unsigned long get_acpi_srat_table(void)
        char arg[10];
        u8 *entry;
 
-       rsdp = (struct acpi_table_rsdp *)(long)boot_params->acpi_rsdp_addr;
+       /*
+        * Check whether we were given an RSDP on the command line. We don't
+        * stash this in boot params because the kernel itself may have
+        * different ideas about whether to trust a command-line parameter.
+        */
+       rsdp = (struct acpi_table_rsdp *)get_cmdline_acpi_rsdp();
+
+       if (!rsdp)
+               rsdp = (struct acpi_table_rsdp *)(long)
+                       boot_params->acpi_rsdp_addr;
+
        if (!rsdp)
                return 0;
 
index aac686e1e00589b92192cfa6cb2ff9b92424cd8d..bc9693c9107e8163cb67d95e680c755601344a84 100644 (file)
@@ -117,6 +117,12 @@ static inline bool acpi_has_cpu_in_madt(void)
        return !!acpi_lapic;
 }
 
+#define ACPI_HAVE_ARCH_SET_ROOT_POINTER
+static inline void acpi_arch_set_root_pointer(u64 addr)
+{
+       x86_init.acpi.set_root_pointer(addr);
+}
+
 #define ACPI_HAVE_ARCH_GET_ROOT_POINTER
 static inline u64 acpi_arch_get_root_pointer(void)
 {
@@ -125,6 +131,7 @@ static inline u64 acpi_arch_get_root_pointer(void)
 
 void acpi_generic_reduced_hw_init(void);
 
+void x86_default_set_root_pointer(u64 addr);
 u64 x86_default_get_root_pointer(void);
 
 #else /* !CONFIG_ACPI */
@@ -138,6 +145,8 @@ static inline void disable_acpi(void) { }
 
 static inline void acpi_generic_reduced_hw_init(void) { }
 
+static inline void x86_default_set_root_pointer(u64 addr) { }
+
 static inline u64 x86_default_get_root_pointer(void)
 {
        return 0;
index ac093418901736d548498882a2c61ac100c43c10..19435858df5f147ad950cf03fe2811a09d20ab73 100644 (file)
@@ -134,10 +134,12 @@ struct x86_hyper_init {
 
 /**
  * struct x86_init_acpi - x86 ACPI init functions
+ * @set_root_poitner:          set RSDP address
  * @get_root_pointer:          get RSDP address
  * @reduced_hw_early_init:     hardware reduced platform early init
  */
 struct x86_init_acpi {
+       void (*set_root_pointer)(u64 addr);
        u64 (*get_root_pointer)(void);
        void (*reduced_hw_early_init)(void);
 };
index 17b33ef604f3ff2aae6c065986ac0de54d369968..04205ce127a12a6f32ddebd2bee99f6682cfd1c6 100644 (file)
@@ -1760,6 +1760,11 @@ void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
        e820__update_table_print();
 }
 
+void x86_default_set_root_pointer(u64 addr)
+{
+       boot_params.acpi_rsdp_addr = addr;
+}
+
 u64 x86_default_get_root_pointer(void)
 {
        return boot_params.acpi_rsdp_addr;
index 4c407833facab7b7d8866de965e6c465313a0520..4d4f5d9faac314ad5fe5c21a05e72c873ec42d65 100644 (file)
@@ -74,9 +74,9 @@ bool arch_ima_get_secureboot(void)
 
 /* secureboot arch rules */
 static const char * const sb_arch_rules[] = {
-#if !IS_ENABLED(CONFIG_KEXEC_VERIFY_SIG)
+#if !IS_ENABLED(CONFIG_KEXEC_SIG)
        "appraise func=KEXEC_KERNEL_CHECK appraise_type=imasig",
-#endif /* CONFIG_KEXEC_VERIFY_SIG */
+#endif /* CONFIG_KEXEC_SIG */
        "measure func=KEXEC_KERNEL_CHECK",
 #if !IS_ENABLED(CONFIG_MODULE_SIG)
        "appraise func=MODULE_CHECK appraise_type=imasig",
index 0fe1c8782208753082f645129f25f1476f7d63ac..61a89d3c0382b947713fd9d779f1680e28a651ee 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/errno.h>
 #include <linux/types.h>
 #include <linux/ioport.h>
+#include <linux/security.h>
 #include <linux/smp.h>
 #include <linux/stddef.h>
 #include <linux/slab.h>
@@ -31,7 +32,8 @@ long ksys_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) ||
+                       security_locked_down(LOCKDOWN_IOPORT)))
                return -EPERM;
 
        /*
@@ -126,7 +128,8 @@ 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) ||
+                   security_locked_down(LOCKDOWN_IOPORT))
                        return -EPERM;
        }
        regs->flags = (regs->flags & ~X86_EFLAGS_IOPL) |
index 5ebcd02cbca712110b8baa999060f625eb5b0a2e..d2f4e706a428c61090973b43bdaa986f3d4b7558 100644 (file)
@@ -180,6 +180,7 @@ setup_efi_state(struct boot_params *params, unsigned long params_load_addr,
        if (efi_enabled(EFI_OLD_MEMMAP))
                return 0;
 
+       params->secure_boot = boot_params.secure_boot;
        ei->efi_loader_signature = current_ei->efi_loader_signature;
        ei->efi_systab = current_ei->efi_systab;
        ei->efi_systab_hi = current_ei->efi_systab_hi;
index 3db2252b958d19fe7cf4de4e8e91f257da4e4409..1547be359d7f5a539ab470381654b6b02c4bf102 100644 (file)
@@ -34,6 +34,7 @@
 #include <linux/notifier.h>
 #include <linux/uaccess.h>
 #include <linux/gfp.h>
+#include <linux/security.h>
 
 #include <asm/cpufeature.h>
 #include <asm/msr.h>
@@ -79,6 +80,10 @@ static ssize_t msr_write(struct file *file, const char __user *buf,
        int err = 0;
        ssize_t bytes = 0;
 
+       err = security_locked_down(LOCKDOWN_MSR);
+       if (err)
+               return err;
+
        if (count % 8)
                return -EINVAL; /* Invalid chunk size */
 
@@ -130,6 +135,9 @@ static long msr_ioctl(struct file *file, unsigned int ioc, unsigned long arg)
                        err = -EFAULT;
                        break;
                }
+               err = security_locked_down(LOCKDOWN_MSR);
+               if (err)
+                       break;
                err = wrmsr_safe_regs_on_cpu(cpu, regs);
                if (err)
                        break;
index 1bef687faf2278dad2042e42004df305a6c6ad0e..18a799c8fa28b0eac90b3505fae50559d70250df 100644 (file)
@@ -95,6 +95,7 @@ struct x86_init_ops x86_init __initdata = {
        },
 
        .acpi = {
+               .set_root_pointer       = x86_default_set_root_pointer,
                .get_root_pointer       = x86_default_get_root_pointer,
                .reduced_hw_early_init  = acpi_generic_reduced_hw_init,
        },
index 0881e1ff1e5835d2b057833503ea9fdf131e8cb1..a8bd952e136d57a661e745075159d71fc55b14f0 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/module.h>
 #include <linux/io.h>
 #include <linux/mmiotrace.h>
+#include <linux/security.h>
 
 static unsigned long mmio_address;
 module_param_hw(mmio_address, ulong, iomem, 0);
@@ -115,6 +116,10 @@ static void do_test_bulk_ioremapping(void)
 static int __init init(void)
 {
        unsigned long size = (read_far) ? (8 << 20) : (16 << 10);
+       int ret = security_locked_down(LOCKDOWN_MMIOTRACE);
+
+       if (ret)
+               return ret;
 
        if (mmio_address == 0) {
                pr_err("you have to use the module argument mmio_address.\n");
index 3b303fe2f061c3e58ba4944197aef36c7c5fe8a9..cc9dbcecaacaa9ee886be4240d94e77e6bb793b8 100644 (file)
@@ -96,7 +96,7 @@ static int pefile_parse_binary(const void *pebuf, unsigned int pelen,
 
        if (!ddir->certs.virtual_address || !ddir->certs.size) {
                pr_debug("Unsigned PE binary\n");
-               return -EKEYREJECTED;
+               return -ENODATA;
        }
 
        chkaddr(ctx->header_size, ddir->certs.virtual_address,
@@ -403,6 +403,8 @@ error_no_desc:
  *  (*) 0 if at least one signature chain intersects with the keys in the trust
  *     keyring, or:
  *
+ *  (*) -ENODATA if there is no signature present.
+ *
  *  (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a
  *     chain.
  *
index fd66a736621cff9627b7c6bec47e2079f48de684..b097ef209313fe2761850ff58c4cbeacd8c079ca 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/uaccess.h>
 #include <linux/debugfs.h>
 #include <linux/acpi.h>
+#include <linux/security.h>
 
 #include "internal.h"
 
@@ -29,6 +30,11 @@ static ssize_t cm_write(struct file *file, const char __user * user_buf,
 
        struct acpi_table_header table;
        acpi_status status;
+       int ret;
+
+       ret = security_locked_down(LOCKDOWN_ACPI_TABLES);
+       if (ret)
+               return ret;
 
        if (!(*ppos)) {
                /* parse the table header to get the table length */
index 2f9d0d20b83661af54500daf5d2771e55d0db284..a2e844a8e9edb0565abab6357997e2f5cc0e14bf 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/list.h>
 #include <linux/jiffies.h>
 #include <linux/semaphore.h>
+#include <linux/security.h>
 
 #include <asm/io.h>
 #include <linux/uaccess.h>
@@ -182,8 +183,19 @@ acpi_physical_address __init acpi_os_get_root_pointer(void)
        acpi_physical_address pa;
 
 #ifdef CONFIG_KEXEC
-       if (acpi_rsdp)
+       /*
+        * We may have been provided with an RSDP on the command line,
+        * but if a malicious user has done so they may be pointing us
+        * at modified ACPI tables that could alter kernel behaviour -
+        * so, we check the lockdown status before making use of
+        * it. If we trust it then also stash it in an architecture
+        * specific location (if appropriate) so it can be carried
+        * over further kexec()s.
+        */
+       if (acpi_rsdp && !security_locked_down(LOCKDOWN_ACPI_TABLES)) {
+               acpi_arch_set_root_pointer(acpi_rsdp);
                return acpi_rsdp;
+       }
 #endif
        pa = acpi_arch_get_root_pointer();
        if (pa)
index b323277593809269aaf4af8e7c8da3850be08011..180ac432976316f1193f65ac071d557fae911487 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/memblock.h>
 #include <linux/earlycpio.h>
 #include <linux/initrd.h>
+#include <linux/security.h>
 #include "internal.h"
 
 #ifdef CONFIG_ACPI_CUSTOM_DSDT
@@ -578,6 +579,11 @@ void __init acpi_table_upgrade(void)
        if (table_nr == 0)
                return;
 
+       if (security_locked_down(LOCKDOWN_ACPI_TABLES)) {
+               pr_notice("kernel is locked down, ignoring table override\n");
+               return;
+       }
+
        acpi_tables_addr =
                memblock_find_in_range(0, ACPI_TABLE_UPGRADE_MAX_PHYS,
                                       all_tables_size, PAGE_SIZE);
index 9eb564c002f66e221646c5225e69ff71b38f913e..43dd0891ca1ed072115943ecdf64c2c9a4d1b7f3 100644 (file)
@@ -29,8 +29,8 @@
 #include <linux/export.h>
 #include <linux/io.h>
 #include <linux/uio.h>
-
 #include <linux/uaccess.h>
+#include <linux/security.h>
 
 #ifdef CONFIG_IA64
 # include <linux/efi.h>
@@ -807,7 +807,10 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig)
 
 static int open_port(struct inode *inode, struct file *filp)
 {
-       return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
+       if (!capable(CAP_SYS_RAWIO))
+               return -EPERM;
+
+       return security_locked_down(LOCKDOWN_DEV_MEM);
 }
 
 #define zero_lseek     null_lseek
index 8f1ab04f6743ae9c402ac813195fd1a19d8f5b63..8d3e778e988b3d139f04e8728c2a0adf1bfa072f 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/acpi.h>
 #include <linux/ucs2_string.h>
 #include <linux/memblock.h>
+#include <linux/security.h>
 
 #include <asm/early_ioremap.h>
 
@@ -221,6 +222,11 @@ static void generic_ops_unregister(void)
 static char efivar_ssdt[EFIVAR_SSDT_NAME_MAX] __initdata;
 static int __init efivar_ssdt_setup(char *str)
 {
+       int ret = security_locked_down(LOCKDOWN_ACPI_TABLES);
+
+       if (ret)
+               return ret;
+
        if (strlen(str) < sizeof(efivar_ssdt))
                memcpy(efivar_ssdt, str, strlen(str));
        else
index 868e351092844f8bf63f3c730cf50226833efea7..7934129545290480b573f03976e8d5e5e220ef01 100644 (file)
@@ -755,6 +755,11 @@ static ssize_t pci_write_config(struct file *filp, struct kobject *kobj,
        unsigned int size = count;
        loff_t init_off = off;
        u8 *data = (u8 *) buf;
+       int ret;
+
+       ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
+       if (ret)
+               return ret;
 
        if (off > dev->cfg_size)
                return 0;
@@ -1016,6 +1021,11 @@ static int pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
        int bar = (unsigned long)attr->private;
        enum pci_mmap_state mmap_type;
        struct resource *res = &pdev->resource[bar];
+       int ret;
+
+       ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
+       if (ret)
+               return ret;
 
        if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(res->start))
                return -EINVAL;
@@ -1092,6 +1102,12 @@ 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)
 {
+       int ret;
+
+       ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
+       if (ret)
+               return ret;
+
        return pci_resource_io(filp, kobj, attr, buf, off, count, true);
 }
 
index fe7fe678965be08c439f65074f3c74e306b2615e..5495537c60c2441a2b9922526833f4d81e393753 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/seq_file.h>
 #include <linux/capability.h>
 #include <linux/uaccess.h>
+#include <linux/security.h>
 #include <asm/byteorder.h>
 #include "pci.h"
 
@@ -115,7 +116,11 @@ static ssize_t proc_bus_pci_write(struct file *file, const char __user *buf,
        struct pci_dev *dev = PDE_DATA(ino);
        int pos = *ppos;
        int size = dev->cfg_size;
-       int cnt;
+       int cnt, ret;
+
+       ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
+       if (ret)
+               return ret;
 
        if (pos >= size)
                return 0;
@@ -196,6 +201,10 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd,
 #endif /* HAVE_PCI_MMAP */
        int ret = 0;
 
+       ret = security_locked_down(LOCKDOWN_PCI_ACCESS);
+       if (ret)
+               return ret;
+
        switch (cmd) {
        case PCIIOC_CONTROLLER:
                ret = pci_domain_nr(dev->bus);
@@ -238,7 +247,8 @@ 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, write_combine = 0, res_bit = IORESOURCE_MEM;
 
-       if (!capable(CAP_SYS_RAWIO))
+       if (!capable(CAP_SYS_RAWIO) ||
+           security_locked_down(LOCKDOWN_PCI_ACCESS))
                return -EPERM;
 
        if (fpriv->mmap_state == pci_mmap_io) {
index d96626c614f5674b4de24b1c11bcb13a456a6ca5..31e39558d49d81e98b48a62d57f4563e5ae9a213 100644 (file)
@@ -7,6 +7,7 @@
 
 #include <linux/errno.h>
 #include <linux/pci.h>
+#include <linux/security.h>
 #include <linux/syscalls.h>
 #include <linux/uaccess.h>
 #include "pci.h"
@@ -90,7 +91,8 @@ 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) ||
+           security_locked_down(LOCKDOWN_PCI_ACCESS))
                return -EPERM;
 
        dev = pci_get_domain_bus_and_slot(0, bus, dfn);
index abd029945cc8cf7fd1866d8ea4570e903bab1e06..629359fe35131110dcfef9444c362a0d51edf67f 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/pci.h>
 #include <linux/ioport.h>
 #include <linux/io.h>
+#include <linux/security.h>
 #include <asm/byteorder.h>
 #include <asm/unaligned.h>
 
@@ -1575,6 +1576,10 @@ static ssize_t pccard_store_cis(struct file *filp, struct kobject *kobj,
        struct pcmcia_socket *s;
        int error;
 
+       error = security_locked_down(LOCKDOWN_PCMCIA_CIS);
+       if (error)
+               return error;
+
        s = to_socket(container_of(kobj, struct device, kobj));
 
        if (off)
index 4223cb4967646696afab42943715d964e77da3f0..6e713be1d4e9ebd94d1cd8c84f13f6f9edd892ae 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/serial_core.h>
 #include <linux/delay.h>
 #include <linux/mutex.h>
+#include <linux/security.h>
 
 #include <linux/irq.h>
 #include <linux/uaccess.h>
@@ -862,6 +863,10 @@ static int uart_set_info(struct tty_struct *tty, struct tty_port *port,
                goto check_and_exit;
        }
 
+       retval = security_locked_down(LOCKDOWN_TIOCSSERIAL);
+       if (retval && (change_irq || change_port))
+               goto exit;
+
        /*
         * Ask the low level driver to verify the settings.
         */
index 93e4ca6b2ad7a9185cefc973d2240dccd3441313..87846aad594b70ec06d6437b9c9bd3e56b6b43fd 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/atomic.h>
 #include <linux/device.h>
 #include <linux/poll.h>
+#include <linux/security.h>
 
 #include "internal.h"
 
@@ -136,6 +137,25 @@ void debugfs_file_put(struct dentry *dentry)
 }
 EXPORT_SYMBOL_GPL(debugfs_file_put);
 
+/*
+ * Only permit access to world-readable files when the kernel is locked down.
+ * We also need to exclude any file that has ways to write or alter it as root
+ * can bypass the permissions check.
+ */
+static bool debugfs_is_locked_down(struct inode *inode,
+                                  struct file *filp,
+                                  const struct file_operations *real_fops)
+{
+       if ((inode->i_mode & 07777) == 0444 &&
+           !(filp->f_mode & FMODE_WRITE) &&
+           !real_fops->unlocked_ioctl &&
+           !real_fops->compat_ioctl &&
+           !real_fops->mmap)
+               return false;
+
+       return security_locked_down(LOCKDOWN_DEBUGFS);
+}
+
 static int open_proxy_open(struct inode *inode, struct file *filp)
 {
        struct dentry *dentry = F_DENTRY(filp);
@@ -147,6 +167,11 @@ static int open_proxy_open(struct inode *inode, struct file *filp)
                return r == -EIO ? -ENOENT : r;
 
        real_fops = debugfs_real_fops(filp);
+
+       r = debugfs_is_locked_down(inode, filp, real_fops);
+       if (r)
+               goto out;
+
        real_fops = fops_get(real_fops);
        if (!real_fops) {
                /* Huh? Module did not clean up after itself at exit? */
@@ -272,6 +297,11 @@ static int full_proxy_open(struct inode *inode, struct file *filp)
                return r == -EIO ? -ENOENT : r;
 
        real_fops = debugfs_real_fops(filp);
+
+       r = debugfs_is_locked_down(inode, filp, real_fops);
+       if (r)
+               goto out;
+
        real_fops = fops_get(real_fops);
        if (!real_fops) {
                /* Huh? Module did not cleanup after itself at exit? */
index 042b688ed124a98052cf61ade8c996b4e42eb118..7b975dbb2bb49cc7e80272aae59cabd5cffec7df 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/parser.h>
 #include <linux/magic.h>
 #include <linux/slab.h>
+#include <linux/security.h>
 
 #include "internal.h"
 
@@ -35,6 +36,32 @@ static struct vfsmount *debugfs_mount;
 static int debugfs_mount_count;
 static bool debugfs_registered;
 
+/*
+ * Don't allow access attributes to be changed whilst the kernel is locked down
+ * so that we can use the file mode as part of a heuristic to determine whether
+ * to lock down individual files.
+ */
+static int debugfs_setattr(struct dentry *dentry, struct iattr *ia)
+{
+       int ret = security_locked_down(LOCKDOWN_DEBUGFS);
+
+       if (ret && (ia->ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID)))
+               return ret;
+       return simple_setattr(dentry, ia);
+}
+
+static const struct inode_operations debugfs_file_inode_operations = {
+       .setattr        = debugfs_setattr,
+};
+static const struct inode_operations debugfs_dir_inode_operations = {
+       .lookup         = simple_lookup,
+       .setattr        = debugfs_setattr,
+};
+static const struct inode_operations debugfs_symlink_inode_operations = {
+       .get_link       = simple_get_link,
+       .setattr        = debugfs_setattr,
+};
+
 static struct inode *debugfs_get_inode(struct super_block *sb)
 {
        struct inode *inode = new_inode(sb);
@@ -369,6 +396,7 @@ static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
        inode->i_mode = mode;
        inode->i_private = data;
 
+       inode->i_op = &debugfs_file_inode_operations;
        inode->i_fop = proxy_fops;
        dentry->d_fsdata = (void *)((unsigned long)real_fops |
                                DEBUGFS_FSDATA_IS_REAL_FOPS_BIT);
@@ -532,7 +560,7 @@ struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
        }
 
        inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
-       inode->i_op = &simple_dir_inode_operations;
+       inode->i_op = &debugfs_dir_inode_operations;
        inode->i_fop = &simple_dir_operations;
 
        /* directory inodes start off with i_nlink == 2 (for "." entry) */
@@ -632,7 +660,7 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
                return failed_creating(dentry);
        }
        inode->i_mode = S_IFLNK | S_IRWXUGO;
-       inode->i_op = &simple_symlink_inode_operations;
+       inode->i_op = &debugfs_symlink_inode_operations;
        inode->i_link = link;
        d_instantiate(dentry, inode);
        return end_creating(dentry);
index f5834488b67d564d91b5534c210fc7fcd8e95969..e2ed8e08cc7adc2b6bb7739e245002ca53f5c873 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/ioport.h>
 #include <linux/memory.h>
 #include <linux/sched/task.h>
+#include <linux/security.h>
 #include <asm/sections.h>
 #include "internal.h"
 
@@ -545,9 +546,14 @@ out:
 
 static int open_kcore(struct inode *inode, struct file *filp)
 {
+       int ret = security_locked_down(LOCKDOWN_KCORE);
+
        if (!capable(CAP_SYS_RAWIO))
                return -EPERM;
 
+       if (ret)
+               return ret;
+
        filp->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL);
        if (!filp->private_data)
                return -ENOMEM;
index eeeae0475da9d0cc15b5b6fe363fd77404e481b9..9fc14e38927f9d5adc32f41bb67c92074f26cd6f 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/parser.h>
 #include <linux/magic.h>
 #include <linux/slab.h>
+#include <linux/security.h>
 
 #define TRACEFS_DEFAULT_MODE   0700
 
@@ -27,6 +28,25 @@ static struct vfsmount *tracefs_mount;
 static int tracefs_mount_count;
 static bool tracefs_registered;
 
+static int default_open_file(struct inode *inode, struct file *filp)
+{
+       struct dentry *dentry = filp->f_path.dentry;
+       struct file_operations *real_fops;
+       int ret;
+
+       if (!dentry)
+               return -EINVAL;
+
+       ret = security_locked_down(LOCKDOWN_TRACEFS);
+       if (ret)
+               return ret;
+
+       real_fops = dentry->d_fsdata;
+       if (!real_fops->open)
+               return 0;
+       return real_fops->open(inode, filp);
+}
+
 static ssize_t default_read_file(struct file *file, char __user *buf,
                                 size_t count, loff_t *ppos)
 {
@@ -221,6 +241,12 @@ static int tracefs_apply_options(struct super_block *sb)
        return 0;
 }
 
+static void tracefs_destroy_inode(struct inode *inode)
+{
+       if (S_ISREG(inode->i_mode))
+               kfree(inode->i_fop);
+}
+
 static int tracefs_remount(struct super_block *sb, int *flags, char *data)
 {
        int err;
@@ -257,6 +283,7 @@ static int tracefs_show_options(struct seq_file *m, struct dentry *root)
 static const struct super_operations tracefs_super_operations = {
        .statfs         = simple_statfs,
        .remount_fs     = tracefs_remount,
+       .destroy_inode  = tracefs_destroy_inode,
        .show_options   = tracefs_show_options,
 };
 
@@ -387,6 +414,7 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode,
                                   struct dentry *parent, void *data,
                                   const struct file_operations *fops)
 {
+       struct file_operations *proxy_fops;
        struct dentry *dentry;
        struct inode *inode;
 
@@ -402,8 +430,20 @@ struct dentry *tracefs_create_file(const char *name, umode_t mode,
        if (unlikely(!inode))
                return failed_creating(dentry);
 
+       proxy_fops = kzalloc(sizeof(struct file_operations), GFP_KERNEL);
+       if (unlikely(!proxy_fops)) {
+               iput(inode);
+               return failed_creating(dentry);
+       }
+
+       if (!fops)
+               fops = &tracefs_file_operations;
+
+       dentry->d_fsdata = (void *)fops;
+       memcpy(proxy_fops, fops, sizeof(*proxy_fops));
+       proxy_fops->open = default_open_file;
        inode->i_mode = mode;
-       inode->i_fop = fops ? fops : &tracefs_file_operations;
+       inode->i_fop = proxy_fops;
        inode->i_private = data;
        d_instantiate(dentry, inode);
        fsnotify_create(dentry->d_parent->d_inode, dentry);
index cd28f63bfbc76f5180c62f92350dd0f4ea11ea35..dae64600ccbffe9d006164de3243bbe284e638a5 100644 (file)
                        __start_lsm_info = .;                           \
                        KEEP(*(.lsm_info.init))                         \
                        __end_lsm_info = .;
+#define EARLY_LSM_TABLE()      . = ALIGN(8);                           \
+                       __start_early_lsm_info = .;                     \
+                       KEEP(*(.early_lsm_info.init))                   \
+                       __end_early_lsm_info = .;
 #else
 #define LSM_TABLE()
+#define EARLY_LSM_TABLE()
 #endif
 
 #define ___OF_TABLE(cfg, name) _OF_TABLE_##cfg(name)
        ACPI_PROBE_TABLE(timer)                                         \
        THERMAL_TABLE(governor)                                         \
        EARLYCON_TABLE()                                                \
-       LSM_TABLE()
+       LSM_TABLE()                                                     \
+       EARLY_LSM_TABLE()
 
 #define INIT_TEXT                                                      \
        *(.init.text .init.text.*)                                      \
index 978cc239f23b97802a787d297e954465c969ef5f..8b4e516bac0095debf1cba96318950d87ef0e16b 100644 (file)
@@ -643,6 +643,12 @@ bool acpi_gtdt_c3stop(int type);
 int acpi_arch_timer_mem_init(struct arch_timer_mem *timer_mem, int *timer_count);
 #endif
 
+#ifndef ACPI_HAVE_ARCH_SET_ROOT_POINTER
+static inline void acpi_arch_set_root_pointer(u64 addr)
+{
+}
+#endif
+
 #ifndef ACPI_HAVE_ARCH_GET_ROOT_POINTER
 static inline u64 acpi_arch_get_root_pointer(void)
 {
index a20ad398d260189e6eb9238abc5a1c65a85d81bf..1c37f17f72035bb0437670168bfa2bfc01bbe72d 100644 (file)
@@ -131,4 +131,13 @@ static inline int ima_inode_removexattr(struct dentry *dentry,
        return 0;
 }
 #endif /* CONFIG_IMA_APPRAISE */
+
+#if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
+extern bool ima_appraise_signature(enum kernel_read_file_id func);
+#else
+static inline bool ima_appraise_signature(enum kernel_read_file_id func)
+{
+       return false;
+}
+#endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
 #endif /* _LINUX_IMA_H */
index cc162f3e6461504535ba7ca98b73461e76cc7736..1776eb2e43a444c3b83ffa08806b6361d4444f17 100644 (file)
@@ -125,7 +125,7 @@ typedef void *(kexec_load_t)(struct kimage *image, char *kernel_buf,
                             unsigned long cmdline_len);
 typedef int (kexec_cleanup_t)(void *loader_data);
 
-#ifdef CONFIG_KEXEC_VERIFY_SIG
+#ifdef CONFIG_KEXEC_SIG
 typedef int (kexec_verify_sig_t)(const char *kernel_buf,
                                 unsigned long kernel_len);
 #endif
@@ -134,7 +134,7 @@ struct kexec_file_ops {
        kexec_probe_t *probe;
        kexec_load_t *load;
        kexec_cleanup_t *cleanup;
-#ifdef CONFIG_KEXEC_VERIFY_SIG
+#ifdef CONFIG_KEXEC_SIG
        kexec_verify_sig_t *verify_sig;
 #endif
 };
index 3fced5824aee829f4758cf04255a47a1832deec0..a3763247547c3f9b656a690433229feeecc2219b 100644 (file)
  * @bpf_prog_free_security:
  *     Clean up the security information stored inside bpf prog.
  *
+ * @locked_down
+ *     Determine whether a kernel feature that potentially enables arbitrary
+ *     code execution in kernel space should be permitted.
+ *
+ *     @what: kernel feature being accessed
  */
 union security_list_options {
        int (*binder_set_context_mgr)(struct task_struct *mgr);
@@ -1812,6 +1817,7 @@ union security_list_options {
        int (*bpf_prog_alloc_security)(struct bpf_prog_aux *aux);
        void (*bpf_prog_free_security)(struct bpf_prog_aux *aux);
 #endif /* CONFIG_BPF_SYSCALL */
+       int (*locked_down)(enum lockdown_reason what);
 };
 
 struct security_hook_heads {
@@ -2053,6 +2059,7 @@ struct security_hook_heads {
        struct hlist_head bpf_prog_alloc_security;
        struct hlist_head bpf_prog_free_security;
 #endif /* CONFIG_BPF_SYSCALL */
+       struct hlist_head locked_down;
 } __randomize_layout;
 
 /*
@@ -2111,12 +2118,18 @@ struct lsm_info {
 };
 
 extern struct lsm_info __start_lsm_info[], __end_lsm_info[];
+extern struct lsm_info __start_early_lsm_info[], __end_early_lsm_info[];
 
 #define DEFINE_LSM(lsm)                                                        \
        static struct lsm_info __lsm_##lsm                              \
                __used __section(.lsm_info.init)                        \
                __aligned(sizeof(unsigned long))
 
+#define DEFINE_EARLY_LSM(lsm)                                          \
+       static struct lsm_info __early_lsm_##lsm                        \
+               __used __section(.early_lsm_info.init)                  \
+               __aligned(sizeof(unsigned long))
+
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 /*
  * Assuring the safety of deleting a security module is up to
index ace6fdb604f90fea5d4a891f6605f7ffa12ab4a6..a8d59d612d274bdb5df8cbd13245b35fc2e08908 100644 (file)
@@ -77,6 +77,54 @@ enum lsm_event {
        LSM_POLICY_CHANGE,
 };
 
+/*
+ * These are reasons that can be passed to the security_locked_down()
+ * LSM hook. Lockdown reasons that protect kernel integrity (ie, the
+ * ability for userland to modify kernel code) are placed before
+ * LOCKDOWN_INTEGRITY_MAX.  Lockdown reasons that protect kernel
+ * confidentiality (ie, the ability for userland to extract
+ * information from the running kernel that would otherwise be
+ * restricted) are placed before LOCKDOWN_CONFIDENTIALITY_MAX.
+ *
+ * LSM authors should note that the semantics of any given lockdown
+ * reason are not guaranteed to be stable - the same reason may block
+ * one set of features in one kernel release, and a slightly different
+ * set of features in a later kernel release. LSMs that seek to expose
+ * lockdown policy at any level of granularity other than "none",
+ * "integrity" or "confidentiality" are responsible for either
+ * ensuring that they expose a consistent level of functionality to
+ * userland, or ensuring that userland is aware that this is
+ * potentially a moving target. It is easy to misuse this information
+ * in a way that could break userspace. Please be careful not to do
+ * so.
+ *
+ * If you add to this, remember to extend lockdown_reasons in
+ * security/lockdown/lockdown.c.
+ */
+enum lockdown_reason {
+       LOCKDOWN_NONE,
+       LOCKDOWN_MODULE_SIGNATURE,
+       LOCKDOWN_DEV_MEM,
+       LOCKDOWN_KEXEC,
+       LOCKDOWN_HIBERNATION,
+       LOCKDOWN_PCI_ACCESS,
+       LOCKDOWN_IOPORT,
+       LOCKDOWN_MSR,
+       LOCKDOWN_ACPI_TABLES,
+       LOCKDOWN_PCMCIA_CIS,
+       LOCKDOWN_TIOCSSERIAL,
+       LOCKDOWN_MODULE_PARAMETERS,
+       LOCKDOWN_MMIOTRACE,
+       LOCKDOWN_DEBUGFS,
+       LOCKDOWN_INTEGRITY_MAX,
+       LOCKDOWN_KCORE,
+       LOCKDOWN_KPROBES,
+       LOCKDOWN_BPF_READ,
+       LOCKDOWN_PERF,
+       LOCKDOWN_TRACEFS,
+       LOCKDOWN_CONFIDENTIALITY_MAX,
+};
+
 /* These functions are in security/commoncap.c */
 extern int cap_capable(const struct cred *cred, struct user_namespace *ns,
                       int cap, unsigned int opts);
@@ -195,6 +243,7 @@ int unregister_blocking_lsm_notifier(struct notifier_block *nb);
 
 /* prototypes */
 extern int security_init(void);
+extern int early_security_init(void);
 
 /* Security operations */
 int security_binder_set_context_mgr(struct task_struct *mgr);
@@ -392,6 +441,7 @@ void security_inode_invalidate_secctx(struct inode *inode);
 int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
 int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
 int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
+int security_locked_down(enum lockdown_reason what);
 #else /* CONFIG_SECURITY */
 
 static inline int call_blocking_lsm_notifier(enum lsm_event event, void *data)
@@ -423,6 +473,11 @@ static inline int security_init(void)
        return 0;
 }
 
+static inline int early_security_init(void)
+{
+       return 0;
+}
+
 static inline int security_binder_set_context_mgr(struct task_struct *mgr)
 {
        return 0;
@@ -1210,6 +1265,10 @@ static inline int security_inode_getsecctx(struct inode *inode, void **ctx, u32
 {
        return -EOPNOTSUPP;
 }
+static inline int security_locked_down(enum lockdown_reason what)
+{
+       return 0;
+}
 #endif /* CONFIG_SECURITY */
 
 #ifdef CONFIG_SECURITY_NETWORK
index 92c867e25a294c304a9593983ad0e98abe4b67ad..b4daad2bac233cfc32744bfb7b78a6d0ab466744 100644 (file)
@@ -2061,6 +2061,11 @@ config MODULE_SIG
          kernel build dependency so that the signing tool can use its crypto
          library.
 
+         You should enable this option if you wish to use either
+         CONFIG_SECURITY_LOCKDOWN_LSM or lockdown functionality imposed via
+         another LSM - otherwise unsigned modules will be loadable regardless
+         of the lockdown policy.
+
          !!!WARNING!!!  If you enable this option, you MUST make sure that the
          module DOES NOT get stripped after being signed.  This includes the
          debuginfo strip done by some packagers (such as rpmbuild) and
index 208b8fa1808e029db9cf74270ba06007f340e111..91f6ebb30ef041a8667c18b949ccf42e976174e1 100644 (file)
@@ -593,6 +593,7 @@ asmlinkage __visible void __init start_kernel(void)
        boot_cpu_init();
        page_address_init();
        pr_notice("%s", linux_banner);
+       early_security_init();
        setup_arch(&command_line);
        setup_command_line(command_line);
        setup_nr_cpu_ids();
index 275eae05af20e93732b2a5df8c486501ab517a73..4655adbbae10d09eabfe155744b4e8539c507494 100644 (file)
@@ -10917,6 +10917,13 @@ SYSCALL_DEFINE5(perf_event_open,
            perf_paranoid_kernel() && !capable(CAP_SYS_ADMIN))
                return -EACCES;
 
+       err = security_locked_down(LOCKDOWN_PERF);
+       if (err && (attr.sample_type & PERF_SAMPLE_REGS_INTR))
+               /* REGS_INTR can leak data, lockdown must prevent this */
+               return err;
+
+       err = 0;
+
        /*
         * In cgroup mode, the pid argument is used to pass the fd
         * opened to the cgroup directory in cgroupfs. The cpu argument
index 1b018f1a6e0d85647e4cc5e6646cacc15e5a2bee..bc933c0db9bf019aefa7c5bc7061bc2541fb2a00 100644 (file)
@@ -205,6 +205,14 @@ static inline int kexec_load_check(unsigned long nr_segments,
        if (result < 0)
                return result;
 
+       /*
+        * kexec can be used to circumvent module loading restrictions, so
+        * prevent loading in that case
+        */
+       result = security_locked_down(LOCKDOWN_KEXEC);
+       if (result)
+               return result;
+
        /*
         * Verify we have a legal set of flags
         * This leaves us room for future extensions.
index b8cc032d5620115547d38428a767dd2d40c6523b..79f252af7dee3b98391fcb1058f3e4463973e941 100644 (file)
@@ -88,7 +88,7 @@ int __weak arch_kimage_file_post_load_cleanup(struct kimage *image)
        return kexec_image_post_load_cleanup_default(image);
 }
 
-#ifdef CONFIG_KEXEC_VERIFY_SIG
+#ifdef CONFIG_KEXEC_SIG
 static int kexec_image_verify_sig_default(struct kimage *image, void *buf,
                                          unsigned long buf_len)
 {
@@ -177,6 +177,59 @@ void kimage_file_post_load_cleanup(struct kimage *image)
        image->image_loader_data = NULL;
 }
 
+#ifdef CONFIG_KEXEC_SIG
+static int
+kimage_validate_signature(struct kimage *image)
+{
+       const char *reason;
+       int ret;
+
+       ret = arch_kexec_kernel_verify_sig(image, image->kernel_buf,
+                                          image->kernel_buf_len);
+       switch (ret) {
+       case 0:
+               break;
+
+               /* Certain verification errors are non-fatal if we're not
+                * checking errors, provided we aren't mandating that there
+                * must be a valid signature.
+                */
+       case -ENODATA:
+               reason = "kexec of unsigned image";
+               goto decide;
+       case -ENOPKG:
+               reason = "kexec of image with unsupported crypto";
+               goto decide;
+       case -ENOKEY:
+               reason = "kexec of image with unavailable key";
+       decide:
+               if (IS_ENABLED(CONFIG_KEXEC_SIG_FORCE)) {
+                       pr_notice("%s rejected\n", reason);
+                       return ret;
+               }
+
+               /* If IMA is guaranteed to appraise a signature on the kexec
+                * image, permit it even if the kernel is otherwise locked
+                * down.
+                */
+               if (!ima_appraise_signature(READING_KEXEC_IMAGE) &&
+                   security_locked_down(LOCKDOWN_KEXEC))
+                       return -EPERM;
+
+               return 0;
+
+               /* All other errors are fatal, including nomem, unparseable
+                * signatures and signature check failures - even if signatures
+                * aren't required.
+                */
+       default:
+               pr_notice("kernel signature verification failed (%d).\n", ret);
+       }
+
+       return ret;
+}
+#endif
+
 /*
  * In file mode list of segments is prepared by kernel. Copy relevant
  * data from user space, do error checking, prepare segment list
@@ -186,7 +239,7 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
                             const char __user *cmdline_ptr,
                             unsigned long cmdline_len, unsigned flags)
 {
-       int ret = 0;
+       int ret;
        void *ldata;
        loff_t size;
 
@@ -202,14 +255,11 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
        if (ret)
                goto out;
 
-#ifdef CONFIG_KEXEC_VERIFY_SIG
-       ret = arch_kexec_kernel_verify_sig(image, image->kernel_buf,
-                                          image->kernel_buf_len);
-       if (ret) {
-               pr_debug("kernel signature verification failed.\n");
+#ifdef CONFIG_KEXEC_SIG
+       ret = kimage_validate_signature(image);
+
+       if (ret)
                goto out;
-       }
-       pr_debug("kernel signature verification successful.\n");
 #endif
        /* It is possible that there no initramfs is being loaded */
        if (!(flags & KEXEC_FILE_NO_INITRAMFS)) {
index edbe42755a27ed8508c53e348deedc50479807e3..ff2d7359a41896ad58deb18a679eb967f3523f38 100644 (file)
@@ -2839,8 +2839,9 @@ static inline void kmemleak_load_module(const struct module *mod,
 #ifdef CONFIG_MODULE_SIG
 static int module_sig_check(struct load_info *info, int flags)
 {
-       int err = -ENOKEY;
+       int err = -ENODATA;
        const unsigned long markerlen = sizeof(MODULE_SIG_STRING) - 1;
+       const char *reason;
        const void *mod = info->hdr;
 
        /*
@@ -2855,16 +2856,38 @@ static int module_sig_check(struct load_info *info, int flags)
                err = mod_verify_sig(mod, info);
        }
 
-       if (!err) {
+       switch (err) {
+       case 0:
                info->sig_ok = true;
                return 0;
-       }
 
-       /* Not having a signature is only an error if we're strict. */
-       if (err == -ENOKEY && !is_module_sig_enforced())
-               err = 0;
+               /* We don't permit modules to be loaded into trusted kernels
+                * without a valid signature on them, but if we're not
+                * enforcing, certain errors are non-fatal.
+                */
+       case -ENODATA:
+               reason = "Loading of unsigned module";
+               goto decide;
+       case -ENOPKG:
+               reason = "Loading of module with unsupported crypto";
+               goto decide;
+       case -ENOKEY:
+               reason = "Loading of module with unavailable key";
+       decide:
+               if (is_module_sig_enforced()) {
+                       pr_notice("%s is rejected\n", reason);
+                       return -EKEYREJECTED;
+               }
 
-       return err;
+               return security_locked_down(LOCKDOWN_MODULE_SIGNATURE);
+
+               /* All other errors are fatal, including nomem, unparseable
+                * signatures and signature check failures - even if signatures
+                * aren't required.
+                */
+       default:
+               return err;
+       }
 }
 #else /* !CONFIG_MODULE_SIG */
 static int module_sig_check(struct load_info *info, int flags)
index cf448785d0586d72b34366de83362342de4d9838..8e56f8b12d8f7e87db7388287facf6c978b2d2b3 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/ctype.h>
+#include <linux/security.h>
 
 #ifdef CONFIG_SYSFS
 /* Protects all built-in parameters, modules use their own param_lock */
@@ -96,13 +97,19 @@ bool parameq(const char *a, const char *b)
        return parameqn(a, b, strlen(a)+1);
 }
 
-static void param_check_unsafe(const struct kernel_param *kp)
+static bool param_check_unsafe(const struct kernel_param *kp)
 {
+       if (kp->flags & KERNEL_PARAM_FL_HWPARAM &&
+           security_locked_down(LOCKDOWN_MODULE_PARAMETERS))
+               return false;
+
        if (kp->flags & KERNEL_PARAM_FL_UNSAFE) {
                pr_notice("Setting dangerous option %s - tainting kernel\n",
                          kp->name);
                add_taint(TAINT_USER, LOCKDEP_STILL_OK);
        }
+
+       return true;
 }
 
 static int parse_one(char *param,
@@ -132,8 +139,10 @@ static int parse_one(char *param,
                        pr_debug("handling %s with %p\n", param,
                                params[i].ops->set);
                        kernel_param_lock(params[i].mod);
-                       param_check_unsafe(&params[i]);
-                       err = params[i].ops->set(val, &params[i]);
+                       if (param_check_unsafe(&params[i]))
+                               err = params[i].ops->set(val, &params[i]);
+                       else
+                               err = -EPERM;
                        kernel_param_unlock(params[i].mod);
                        return err;
                }
@@ -553,8 +562,10 @@ static ssize_t param_attr_store(struct module_attribute *mattr,
                return -EPERM;
 
        kernel_param_lock(mk->mod);
-       param_check_unsafe(attribute->param);
-       err = attribute->param->ops->set(buf, attribute->param);
+       if (param_check_unsafe(attribute->param))
+               err = attribute->param->ops->set(buf, attribute->param);
+       else
+               err = -EPERM;
        kernel_param_unlock(mk->mod);
        if (!err)
                return len;
index cd7434e6000d90eb5be5a179ec6815e77b21201a..3c0a5a8170b02634df7c289be46d49124718023d 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/ctype.h>
 #include <linux/genhd.h>
 #include <linux/ktime.h>
+#include <linux/security.h>
 #include <trace/events/power.h>
 
 #include "power.h"
@@ -68,7 +69,7 @@ static const struct platform_hibernation_ops *hibernation_ops;
 
 bool hibernation_available(void)
 {
-       return (nohibernate == 0);
+       return nohibernate == 0 && !security_locked_down(LOCKDOWN_HIBERNATION);
 }
 
 /**
index ca1255d145766af019c19d4e58d22e894b52152e..492a8bfaae98c584bdf94c3d53b656e889f02099 100644 (file)
@@ -142,8 +142,13 @@ BPF_CALL_3(bpf_probe_read, void *, dst, u32, size, const void *, unsafe_ptr)
 {
        int ret;
 
+       ret = security_locked_down(LOCKDOWN_BPF_READ);
+       if (ret < 0)
+               goto out;
+
        ret = probe_kernel_read(dst, unsafe_ptr, size);
        if (unlikely(ret < 0))
+out:
                memset(dst, 0, size);
 
        return ret;
@@ -569,6 +574,10 @@ BPF_CALL_3(bpf_probe_read_str, void *, dst, u32, size,
 {
        int ret;
 
+       ret = security_locked_down(LOCKDOWN_BPF_READ);
+       if (ret < 0)
+               goto out;
+
        /*
         * The strncpy_from_unsafe() call will likely not fill the entire
         * buffer, but that's okay in this circumstance as we're probing
@@ -580,6 +589,7 @@ BPF_CALL_3(bpf_probe_read_str, void *, dst, u32, size,
         */
        ret = strncpy_from_unsafe(dst, unsafe_ptr, size);
        if (unlikely(ret < 0))
+out:
                memset(dst, 0, size);
 
        return ret;
index 402dc3ce88d3ea2917954271f02dbcfde38b8f6f..324ffbea35567d5167511bac40aefbfa51b1268c 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/uaccess.h>
 #include <linux/rculist.h>
 #include <linux/error-injection.h>
+#include <linux/security.h>
 
 #include <asm/setup.h>  /* for COMMAND_LINE_SIZE */
 
@@ -460,6 +461,10 @@ static int __register_trace_kprobe(struct trace_kprobe *tk)
 {
        int i, ret;
 
+       ret = security_locked_down(LOCKDOWN_KPROBES);
+       if (ret)
+               return ret;
+
        if (trace_kprobe_is_registered(tk))
                return -EINVAL;
 
index 0d65594b5196849425d6c738101f629593a989e7..2a1a2d3962281da328421d37b035e2906e45b32b 100644 (file)
@@ -237,6 +237,7 @@ source "security/apparmor/Kconfig"
 source "security/loadpin/Kconfig"
 source "security/yama/Kconfig"
 source "security/safesetid/Kconfig"
+source "security/lockdown/Kconfig"
 
 source "security/integrity/Kconfig"
 
@@ -276,11 +277,11 @@ endchoice
 
 config LSM
        string "Ordered list of enabled LSMs"
-       default "yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor" if DEFAULT_SECURITY_SMACK
-       default "yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo" if DEFAULT_SECURITY_APPARMOR
-       default "yama,loadpin,safesetid,integrity,tomoyo" if DEFAULT_SECURITY_TOMOYO
-       default "yama,loadpin,safesetid,integrity" if DEFAULT_SECURITY_DAC
-       default "yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor"
+       default "lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor" if DEFAULT_SECURITY_SMACK
+       default "lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo" if DEFAULT_SECURITY_APPARMOR
+       default "lockdown,yama,loadpin,safesetid,integrity,tomoyo" if DEFAULT_SECURITY_TOMOYO
+       default "lockdown,yama,loadpin,safesetid,integrity" if DEFAULT_SECURITY_DAC
+       default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor"
        help
          A comma-separated list of LSMs, in initialization order.
          Any LSMs left off this list will be ignored. This can be
index c598b904938f90cd24fe170e9d21ed16e6caaf46..be1dd9d2cb2fb90d3969902e456144b6ecc3ea89 100644 (file)
@@ -11,6 +11,7 @@ subdir-$(CONFIG_SECURITY_APPARMOR)    += apparmor
 subdir-$(CONFIG_SECURITY_YAMA)         += yama
 subdir-$(CONFIG_SECURITY_LOADPIN)      += loadpin
 subdir-$(CONFIG_SECURITY_SAFESETID)    += safesetid
+subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown
 
 # always enable default capabilities
 obj-y                                  += commoncap.o
@@ -27,6 +28,7 @@ obj-$(CONFIG_SECURITY_APPARMOR)               += apparmor/
 obj-$(CONFIG_SECURITY_YAMA)            += yama/
 obj-$(CONFIG_SECURITY_LOADPIN)         += loadpin/
 obj-$(CONFIG_SECURITY_SAFESETID)       += safesetid/
+obj-$(CONFIG_SECURITY_LOCKDOWN_LSM)    += lockdown/
 obj-$(CONFIG_CGROUP_DEVICE)            += device_cgroup.o
 
 # Object integrity file lists
index 897bafc59a339e794b6aa8728997b6558288fd25..838476d780e55c855c601a58d30752d9caf04ea3 100644 (file)
@@ -160,7 +160,7 @@ config IMA_APPRAISE
 
 config IMA_ARCH_POLICY
         bool "Enable loading an IMA architecture specific policy"
-        depends on (KEXEC_VERIFY_SIG && IMA) || IMA_APPRAISE \
+        depends on (KEXEC_SIG && IMA) || IMA_APPRAISE \
                   && INTEGRITY_ASYMMETRIC_KEYS
         default n
         help
index 19769bf5f6ab82c5c2fc7bf8feebfb4fda0bb5f9..3689081aaf38048f4e840c5f0c4366d62113872e 100644 (file)
@@ -114,6 +114,8 @@ struct ima_kexec_hdr {
        u64 count;
 };
 
+extern const int read_idmap[];
+
 #ifdef CONFIG_HAVE_IMA_KEXEC
 void ima_load_kexec_buffer(void);
 #else
index 79c01516211b8ba9523aafedab83172d9a297e83..60027c643ecdf79ba00ea664153f103b2960e54e 100644 (file)
@@ -518,7 +518,7 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id)
        return 0;
 }
 
-static const int read_idmap[READING_MAX_ID] = {
+const int read_idmap[READING_MAX_ID] = {
        [READING_FIRMWARE] = FIRMWARE_CHECK,
        [READING_FIRMWARE_PREALLOC_BUFFER] = FIRMWARE_CHECK,
        [READING_MODULE] = MODULE_CHECK,
@@ -590,7 +590,7 @@ int ima_load_data(enum kernel_load_data_id id)
 
        switch (id) {
        case LOADING_KEXEC_IMAGE:
-               if (IS_ENABLED(CONFIG_KEXEC_VERIFY_SIG)
+               if (IS_ENABLED(CONFIG_KEXEC_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;
index 4badc4fcda9850b3695e758e1489ec6121c6d993..5380aca2b35181b1c1788bd09f7065539b98a995 100644 (file)
@@ -1507,3 +1507,53 @@ int ima_policy_show(struct seq_file *m, void *v)
        return 0;
 }
 #endif /* CONFIG_IMA_READ_POLICY */
+
+#if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
+/*
+ * ima_appraise_signature: whether IMA will appraise a given function using
+ * an IMA digital signature. This is restricted to cases where the kernel
+ * has a set of built-in trusted keys in order to avoid an attacker simply
+ * loading additional keys.
+ */
+bool ima_appraise_signature(enum kernel_read_file_id id)
+{
+       struct ima_rule_entry *entry;
+       bool found = false;
+       enum ima_hooks func;
+
+       if (id >= READING_MAX_ID)
+               return false;
+
+       func = read_idmap[id] ?: FILE_CHECK;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(entry, ima_rules, list) {
+               if (entry->action != APPRAISE)
+                       continue;
+
+               /*
+                * A generic entry will match, but otherwise require that it
+                * match the func we're looking for
+                */
+               if (entry->func && entry->func != func)
+                       continue;
+
+               /*
+                * We require this to be a digital signature, not a raw IMA
+                * hash.
+                */
+               if (entry->flags & IMA_DIGSIG_REQUIRED)
+                       found = true;
+
+               /*
+                * We've found a rule that matches, so break now even if it
+                * didn't require a digital signature - a later rule that does
+                * won't override it, so would be a false positive.
+                */
+               break;
+       }
+
+       rcu_read_unlock();
+       return found;
+}
+#endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
diff --git a/security/lockdown/Kconfig b/security/lockdown/Kconfig
new file mode 100644 (file)
index 0000000..e84ddf4
--- /dev/null
@@ -0,0 +1,47 @@
+config SECURITY_LOCKDOWN_LSM
+       bool "Basic module for enforcing kernel lockdown"
+       depends on SECURITY
+       select MODULE_SIG if MODULES
+       help
+         Build support for an LSM that enforces a coarse kernel lockdown
+         behaviour.
+
+config SECURITY_LOCKDOWN_LSM_EARLY
+       bool "Enable lockdown LSM early in init"
+       depends on SECURITY_LOCKDOWN_LSM
+       help
+         Enable the lockdown LSM early in boot. This is necessary in order
+         to ensure that lockdown enforcement can be carried out on kernel
+         boot parameters that are otherwise parsed before the security
+         subsystem is fully initialised. If enabled, lockdown will
+         unconditionally be called before any other LSMs.
+
+choice
+       prompt "Kernel default lockdown mode"
+       default LOCK_DOWN_KERNEL_FORCE_NONE
+       depends on SECURITY_LOCKDOWN_LSM
+       help
+         The kernel can be configured to default to differing levels of
+         lockdown.
+
+config LOCK_DOWN_KERNEL_FORCE_NONE
+       bool "None"
+       help
+         No lockdown functionality is enabled by default. Lockdown may be
+         enabled via the kernel commandline or /sys/kernel/security/lockdown.
+
+config LOCK_DOWN_KERNEL_FORCE_INTEGRITY
+       bool "Integrity"
+       help
+        The kernel runs in integrity mode by default. Features that allow
+        the kernel to be modified at runtime are disabled.
+
+config LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY
+       bool "Confidentiality"
+       help
+        The kernel runs in confidentiality mode by default. Features that
+        allow the kernel to be modified at runtime or that permit userland
+        code to read confidential material held inside the kernel are
+        disabled.
+
+endchoice
diff --git a/security/lockdown/Makefile b/security/lockdown/Makefile
new file mode 100644 (file)
index 0000000..e3634b9
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown.o
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
new file mode 100644 (file)
index 0000000..8a10b43
--- /dev/null
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Lock down the kernel
+ *
+ * Copyright (C) 2016 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/security.h>
+#include <linux/export.h>
+#include <linux/lsm_hooks.h>
+
+static enum lockdown_reason kernel_locked_down;
+
+static const char *const lockdown_reasons[LOCKDOWN_CONFIDENTIALITY_MAX+1] = {
+       [LOCKDOWN_NONE] = "none",
+       [LOCKDOWN_MODULE_SIGNATURE] = "unsigned module loading",
+       [LOCKDOWN_DEV_MEM] = "/dev/mem,kmem,port",
+       [LOCKDOWN_KEXEC] = "kexec of unsigned images",
+       [LOCKDOWN_HIBERNATION] = "hibernation",
+       [LOCKDOWN_PCI_ACCESS] = "direct PCI access",
+       [LOCKDOWN_IOPORT] = "raw io port access",
+       [LOCKDOWN_MSR] = "raw MSR access",
+       [LOCKDOWN_ACPI_TABLES] = "modifying ACPI tables",
+       [LOCKDOWN_PCMCIA_CIS] = "direct PCMCIA CIS storage",
+       [LOCKDOWN_TIOCSSERIAL] = "reconfiguration of serial port IO",
+       [LOCKDOWN_MODULE_PARAMETERS] = "unsafe module parameters",
+       [LOCKDOWN_MMIOTRACE] = "unsafe mmio",
+       [LOCKDOWN_DEBUGFS] = "debugfs access",
+       [LOCKDOWN_INTEGRITY_MAX] = "integrity",
+       [LOCKDOWN_KCORE] = "/proc/kcore access",
+       [LOCKDOWN_KPROBES] = "use of kprobes",
+       [LOCKDOWN_BPF_READ] = "use of bpf to read kernel RAM",
+       [LOCKDOWN_PERF] = "unsafe use of perf",
+       [LOCKDOWN_TRACEFS] = "use of tracefs",
+       [LOCKDOWN_CONFIDENTIALITY_MAX] = "confidentiality",
+};
+
+static const enum lockdown_reason lockdown_levels[] = {LOCKDOWN_NONE,
+                                                LOCKDOWN_INTEGRITY_MAX,
+                                                LOCKDOWN_CONFIDENTIALITY_MAX};
+
+/*
+ * Put the kernel into lock-down mode.
+ */
+static int lock_kernel_down(const char *where, enum lockdown_reason level)
+{
+       if (kernel_locked_down >= level)
+               return -EPERM;
+
+       kernel_locked_down = level;
+       pr_notice("Kernel is locked down from %s; see man kernel_lockdown.7\n",
+                 where);
+       return 0;
+}
+
+static int __init lockdown_param(char *level)
+{
+       if (!level)
+               return -EINVAL;
+
+       if (strcmp(level, "integrity") == 0)
+               lock_kernel_down("command line", LOCKDOWN_INTEGRITY_MAX);
+       else if (strcmp(level, "confidentiality") == 0)
+               lock_kernel_down("command line", LOCKDOWN_CONFIDENTIALITY_MAX);
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+early_param("lockdown", lockdown_param);
+
+/**
+ * lockdown_is_locked_down - Find out if the kernel is locked down
+ * @what: Tag to use in notice generated if lockdown is in effect
+ */
+static int lockdown_is_locked_down(enum lockdown_reason what)
+{
+       if (WARN(what >= LOCKDOWN_CONFIDENTIALITY_MAX,
+                "Invalid lockdown reason"))
+               return -EPERM;
+
+       if (kernel_locked_down >= what) {
+               if (lockdown_reasons[what])
+                       pr_notice("Lockdown: %s: %s is restricted; see man kernel_lockdown.7\n",
+                                 current->comm, lockdown_reasons[what]);
+               return -EPERM;
+       }
+
+       return 0;
+}
+
+static struct security_hook_list lockdown_hooks[] __lsm_ro_after_init = {
+       LSM_HOOK_INIT(locked_down, lockdown_is_locked_down),
+};
+
+static int __init lockdown_lsm_init(void)
+{
+#if defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY)
+       lock_kernel_down("Kernel configuration", LOCKDOWN_INTEGRITY_MAX);
+#elif defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY)
+       lock_kernel_down("Kernel configuration", LOCKDOWN_CONFIDENTIALITY_MAX);
+#endif
+       security_add_hooks(lockdown_hooks, ARRAY_SIZE(lockdown_hooks),
+                          "lockdown");
+       return 0;
+}
+
+static ssize_t lockdown_read(struct file *filp, char __user *buf, size_t count,
+                            loff_t *ppos)
+{
+       char temp[80];
+       int i, offset = 0;
+
+       for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
+               enum lockdown_reason level = lockdown_levels[i];
+
+               if (lockdown_reasons[level]) {
+                       const char *label = lockdown_reasons[level];
+
+                       if (kernel_locked_down == level)
+                               offset += sprintf(temp+offset, "[%s] ", label);
+                       else
+                               offset += sprintf(temp+offset, "%s ", label);
+               }
+       }
+
+       /* Convert the last space to a newline if needed. */
+       if (offset > 0)
+               temp[offset-1] = '\n';
+
+       return simple_read_from_buffer(buf, count, ppos, temp, strlen(temp));
+}
+
+static ssize_t lockdown_write(struct file *file, const char __user *buf,
+                             size_t n, loff_t *ppos)
+{
+       char *state;
+       int i, len, err = -EINVAL;
+
+       state = memdup_user_nul(buf, n);
+       if (IS_ERR(state))
+               return PTR_ERR(state);
+
+       len = strlen(state);
+       if (len && state[len-1] == '\n') {
+               state[len-1] = '\0';
+               len--;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(lockdown_levels); i++) {
+               enum lockdown_reason level = lockdown_levels[i];
+               const char *label = lockdown_reasons[level];
+
+               if (label && !strcmp(state, label))
+                       err = lock_kernel_down("securityfs", level);
+       }
+
+       kfree(state);
+       return err ? err : n;
+}
+
+static const struct file_operations lockdown_ops = {
+       .read  = lockdown_read,
+       .write = lockdown_write,
+};
+
+static int __init lockdown_secfs_init(void)
+{
+       struct dentry *dentry;
+
+       dentry = securityfs_create_file("lockdown", 0600, NULL, NULL,
+                                       &lockdown_ops);
+       return PTR_ERR_OR_ZERO(dentry);
+}
+
+core_initcall(lockdown_secfs_init);
+
+#ifdef CONFIG_SECURITY_LOCKDOWN_LSM_EARLY
+DEFINE_EARLY_LSM(lockdown) = {
+#else
+DEFINE_LSM(lockdown) = {
+#endif
+       .name = "lockdown",
+       .init = lockdown_lsm_init,
+};
index 25ee5c75551f6e62428a9b035349afd4ebf840d4..1bc000f834e2ddfce587b116604c9c4ad1435739 100644 (file)
@@ -33,6 +33,7 @@
 
 /* How many LSMs were built into the kernel? */
 #define LSM_COUNT (__end_lsm_info - __start_lsm_info)
+#define EARLY_LSM_COUNT (__end_early_lsm_info - __start_early_lsm_info)
 
 struct security_hook_heads security_hook_heads __lsm_ro_after_init;
 static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
@@ -277,6 +278,8 @@ static void __init ordered_lsm_parse(const char *order, const char *origin)
 static void __init lsm_early_cred(struct cred *cred);
 static void __init lsm_early_task(struct task_struct *task);
 
+static int lsm_append(const char *new, char **result);
+
 static void __init ordered_lsm_init(void)
 {
        struct lsm_info **lsm;
@@ -323,6 +326,26 @@ static void __init ordered_lsm_init(void)
        kfree(ordered_lsms);
 }
 
+int __init early_security_init(void)
+{
+       int i;
+       struct hlist_head *list = (struct hlist_head *) &security_hook_heads;
+       struct lsm_info *lsm;
+
+       for (i = 0; i < sizeof(security_hook_heads) / sizeof(struct hlist_head);
+            i++)
+               INIT_HLIST_HEAD(&list[i]);
+
+       for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
+               if (!lsm->enabled)
+                       lsm->enabled = &lsm_enabled_true;
+               prepare_lsm(lsm);
+               initialize_lsm(lsm);
+       }
+
+       return 0;
+}
+
 /**
  * security_init - initializes the security framework
  *
@@ -330,14 +353,18 @@ static void __init ordered_lsm_init(void)
  */
 int __init security_init(void)
 {
-       int i;
-       struct hlist_head *list = (struct hlist_head *) &security_hook_heads;
+       struct lsm_info *lsm;
 
        pr_info("Security Framework initializing\n");
 
-       for (i = 0; i < sizeof(security_hook_heads) / sizeof(struct hlist_head);
-            i++)
-               INIT_HLIST_HEAD(&list[i]);
+       /*
+        * Append the names of the early LSM modules now that kmalloc() is
+        * available
+        */
+       for (lsm = __start_early_lsm_info; lsm < __end_early_lsm_info; lsm++) {
+               if (lsm->enabled)
+                       lsm_append(lsm->name, &lsm_names);
+       }
 
        /* Load LSMs in specified order. */
        ordered_lsm_init();
@@ -384,7 +411,7 @@ static bool match_last_lsm(const char *list, const char *lsm)
        return !strcmp(last, lsm);
 }
 
-static int lsm_append(char *new, char **result)
+static int lsm_append(const char *new, char **result)
 {
        char *cp;
 
@@ -422,8 +449,15 @@ void __init security_add_hooks(struct security_hook_list *hooks, int count,
                hooks[i].lsm = lsm;
                hlist_add_tail_rcu(&hooks[i].list, hooks[i].head);
        }
-       if (lsm_append(lsm, &lsm_names) < 0)
-               panic("%s - Cannot get early memory.\n", __func__);
+
+       /*
+        * Don't try to append during early_security_init(), we'll come back
+        * and fix this up afterwards.
+        */
+       if (slab_is_available()) {
+               if (lsm_append(lsm, &lsm_names) < 0)
+                       panic("%s - Cannot get early memory.\n", __func__);
+       }
 }
 
 int call_blocking_lsm_notifier(enum lsm_event event, void *data)
@@ -2364,3 +2398,9 @@ void security_bpf_prog_free(struct bpf_prog_aux *aux)
        call_void_hook(bpf_prog_free_security, aux);
 }
 #endif /* CONFIG_BPF_SYSCALL */
+
+int security_locked_down(enum lockdown_reason what)
+{
+       return call_int_hook(locked_down, 0, what);
+}
+EXPORT_SYMBOL(security_locked_down);