Add BSD-style securelevel support
authorMatthew Garrett <mjg59@srcf.ucam.org>
Fri, 9 Aug 2013 21:58:15 +0000 (17:58 -0400)
committerYves-Alexis Perez <corsac@debian.org>
Wed, 21 Feb 2018 15:29:03 +0000 (15:29 +0000)
Provide a coarse-grained runtime configuration option for restricting
userspace's ability to modify the running kernel.

Signed-off-by: Matthew Garrett <mjg59@srcf.ucam.org>
Gbp-Pq: Topic features/all/securelevel
Gbp-Pq: Name add-bsd-style-securelevel-support.patch

Documentation/security/securelevel.txt [new file with mode: 0644]
include/linux/security.h
security/Kconfig
security/Makefile
security/securelevel.c [new file with mode: 0644]

diff --git a/Documentation/security/securelevel.txt b/Documentation/security/securelevel.txt
new file mode 100644 (file)
index 0000000..a1355a0
--- /dev/null
@@ -0,0 +1,23 @@
+Linux securelevel interface
+---------------------------
+
+The Linux securelevel interface (inspired by the BSD securelevel interface)
+is a runtime mechanism for configuring coarse-grained kernel-level security
+restrictions. It provides a runtime configuration variable at
+/sys/kernel/security/securelevel which can be written to by root. The
+following values are supported:
+
+-1: Permanently insecure mode. This level is equivalent to level 0, but once
+    set cannot be changed.
+
+0:  Insecure mode (default). This level imposes no additional kernel
+    restrictions.
+
+1:  Secure mode. If set, userspace will be unable to perform direct access
+    to PCI devices, port IO access, access system memory directly via
+    /dev/mem and /dev/kmem, perform kexec_load(), use the userspace
+    software suspend mechanism, insert new ACPI code at runtime via the
+    custom_method interface or modify CPU MSRs (on x86). Certain drivers
+    may also limit additional interfaces.
+
+Once the securelevel value is increased, it may not be decreased.
index c2125e9093e8e51b51662ba8ce4a5faa767710b6..fd227b471dea9f0063ceb472eec717a22ac8812b 100644 (file)
@@ -1632,6 +1632,14 @@ static inline void security_audit_rule_free(void *lsmrule)
 #endif /* CONFIG_SECURITY */
 #endif /* CONFIG_AUDIT */
 
+#ifdef CONFIG_SECURITY_SECURELEVEL
+extern int get_securelevel(void);
+extern int set_securelevel(int new_securelevel);
+#else
+static inline int get_securelevel(void) { return 0; }
+static inline int set_securelevel(int new_securelevel) { return 0; }
+#endif /* CONFIG_SECURELEVEL */
+
 #ifdef CONFIG_SECURITYFS
 
 extern struct dentry *securityfs_create_file(const char *name, umode_t mode,
index 80a2934d3110881cfc11fd4fd74336f9a2104a50..73da6f9ad151cff99385be9a45a946e87c5521f7 100644 (file)
@@ -90,6 +90,14 @@ config SECURITY_PATH
          implement pathname based access controls.
          If you are unsure how to answer this question, answer N.
 
+config SECURITY_SECURELEVEL
+        bool "Securelevel kernel restriction interface"
+       depends on SECURITY
+       help
+         This enables support for adding a set of additional kernel security
+         restrictions at runtime. See Documentation/security/securelevel.txt
+         for further information.
+
 config INTEL_TXT
        bool "Enable Intel(R) Trusted Execution Technology (Intel(R) TXT)"
        depends on HAVE_INTEL_TXT
index f2d71cdb8e19b16968b5185296dd91ea9120cd39..8631f0c8f6abd27d62f08ae476e9c73b9b5c843d 100644 (file)
@@ -17,6 +17,7 @@ obj-$(CONFIG_MMU)                     += min_addr.o
 # Object file lists
 obj-$(CONFIG_SECURITY)                 += security.o
 obj-$(CONFIG_SECURITYFS)               += inode.o
+obj-$(CONFIG_SECURITY_SECURELEVEL)     += securelevel.o
 obj-$(CONFIG_SECURITY_SELINUX)         += selinux/
 obj-$(CONFIG_SECURITY_SMACK)           += smack/
 obj-$(CONFIG_AUDIT)                    += lsm_audit.o
diff --git a/security/securelevel.c b/security/securelevel.c
new file mode 100644 (file)
index 0000000..75ea4f7
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ *  securelevel.c - support for generic kernel lockdown
+ *
+ *  Copyright Nebula, Inc <mjg59@srcf.ucam.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License version 2 as
+ *  published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/security.h>
+#include <linux/uaccess.h>
+
+static int securelevel;
+
+static DEFINE_SPINLOCK(securelevel_lock);
+
+#define MAX_SECURELEVEL 1
+
+int get_securelevel(void)
+{
+       return securelevel;
+}
+EXPORT_SYMBOL(get_securelevel);
+
+int set_securelevel(int new_securelevel)
+{
+       int ret = 0;
+
+       spin_lock(&securelevel_lock);
+
+       if ((securelevel == -1) || (new_securelevel < securelevel) ||
+           (new_securelevel > MAX_SECURELEVEL)) {
+               ret = -EINVAL;
+               goto out;
+       }
+
+       securelevel = new_securelevel;
+out:
+       spin_unlock(&securelevel_lock);
+       return ret;
+}
+EXPORT_SYMBOL(set_securelevel);
+
+static ssize_t securelevel_read(struct file *filp, char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       char tmpbuf[12];
+       ssize_t length;
+
+       length = scnprintf(tmpbuf, sizeof(tmpbuf), "%d", securelevel);
+       return simple_read_from_buffer(buf, count, ppos, tmpbuf, length);
+}
+
+static ssize_t securelevel_write(struct file *file, const char __user *buf,
+                                size_t count, loff_t *ppos)
+{
+       char *page = NULL;
+       ssize_t length;
+       int new_securelevel;
+
+       length = -ENOMEM;
+       if (count >= PAGE_SIZE)
+               goto out;
+
+       length = -EINVAL;
+       if (*ppos != 0)
+               goto out;
+
+       length = -ENOMEM;
+       page = (char *)get_zeroed_page(GFP_KERNEL);
+       if (!page)
+               goto out;
+
+       length = -EFAULT;
+       if (copy_from_user(page, buf, count))
+               goto out;
+
+       length = -EINVAL;
+       if (sscanf(page, "%d", &new_securelevel) != 1)
+               goto out;
+
+       length = set_securelevel(new_securelevel);
+       if (length)
+               goto out;
+
+       length = count;
+out:
+       free_page((unsigned long) page);
+       return length;
+}
+
+static const struct file_operations securelevel_fops = {
+       .read   = securelevel_read,
+       .write  = securelevel_write,
+       .llseek = generic_file_llseek,
+};
+
+static __init int setup_securelevel(void)
+{
+       struct dentry *securelevel_file;
+
+       securelevel_file = securityfs_create_file("securelevel",
+                                                 S_IWUSR | S_IRUGO,
+                                                 NULL, NULL,
+                                                 &securelevel_fops);
+
+       if (IS_ERR(securelevel_file))
+               return PTR_ERR(securelevel_file);
+
+       return 0;
+}
+late_initcall(setup_securelevel);