Blob Blame History Raw
From 3d7c2262f2dcee5d0b1968e61cd78bc04a4a4cbb Mon Sep 17 00:00:00 2001
From: Matthew Garrett <mjg59@srcf.ucam.org>
Date: Fri, 9 Aug 2013 17:58:15 -0400
Subject: [PATCH] Add BSD-style securelevel support

Patch-mainline: Queued in subsystem maintainer repository
Git-repo: https://github.com/mjg59/linux
Git-commit: 3d7c2262f2dcee5d0b1968e61cd78bc04a4a4cbb
References: fate#320387

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>
Acked-by: Lee, Chun-Yi <jlee@suse.com>
---
 include/linux/security.h |    8 +++
 security/Kconfig         |    8 +++
 security/Makefile        |    1 
 security/securelevel.c   |  116 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 133 insertions(+)
 create mode 100644 security/securelevel.c

--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -1584,6 +1584,14 @@ static inline void security_audit_rule_f
 #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,
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -71,6 +71,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
--- a/security/Makefile
+++ b/security/Makefile
@@ -16,6 +16,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
--- /dev/null
+++ b/security/securelevel.c
@@ -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);