Jeremy Cline 5df4c55
From c2eb371cede78df9a47bf3a125aa9a45dd833da7 Mon Sep 17 00:00:00 2001
ead55fd
From: Kyle McMartin <kyle@redhat.com>
ead55fd
Date: Mon, 9 Apr 2018 09:52:45 +0100
Jeremy Cline 5df4c55
Subject: [PATCH] Add a SysRq option to lift kernel lockdown
ead55fd
ead55fd
Make an option to provide a sysrq key that will lift the kernel lockdown,
ead55fd
thereby allowing the running kernel image to be accessed and modified.
ead55fd
ead55fd
On x86 this is triggered with SysRq+x, but this key may not be available on
ead55fd
all arches, so it is set by setting LOCKDOWN_LIFT_KEY in asm/setup.h.
ead55fd
Since this macro must be defined in an arch to be able to use this facility
ead55fd
for that arch, the Kconfig option is restricted to arches that support it.
ead55fd
ead55fd
Signed-off-by: Kyle McMartin <kyle@redhat.com>
ead55fd
Signed-off-by: David Howells <dhowells@redhat.com>
ead55fd
cc: x86@kernel.org
Jeremy Cline 5df4c55
Signed-off-by: Jeremy Cline <jcline@redhat.com>
ead55fd
---
ead55fd
 arch/x86/include/asm/setup.h |  2 ++
ead55fd
 drivers/input/misc/uinput.c  |  1 +
Jeremy Cline 5df4c55
 drivers/tty/sysrq.c          | 27 +++++++++++++---------
Jeremy Cline 5df4c55
 include/linux/input.h        |  5 +++++
Jeremy Cline 5df4c55
 include/linux/sysrq.h        |  8 ++++++-
ead55fd
 kernel/debug/kdb/kdb_main.c  |  2 +-
Jeremy Cline 5df4c55
 security/lockdown/Kconfig    | 11 +++++++++
Jeremy Cline 5df4c55
 security/lockdown/lockdown.c | 43 ++++++++++++++++++++++++++++++++++++
Jeremy Cline 5df4c55
 8 files changed, 87 insertions(+), 12 deletions(-)
ead55fd
ead55fd
diff --git a/arch/x86/include/asm/setup.h b/arch/x86/include/asm/setup.h
ead55fd
index ed8ec011a9fd..8daf633a5347 100644
ead55fd
--- a/arch/x86/include/asm/setup.h
ead55fd
+++ b/arch/x86/include/asm/setup.h
ead55fd
@@ -9,6 +9,8 @@
ead55fd
 #include <linux/linkage.h>
ead55fd
 #include <asm/page_types.h>
ead55fd
 
ead55fd
+#define LOCKDOWN_LIFT_KEY 'x'
ead55fd
+
ead55fd
 #ifdef __i386__
ead55fd
 
ead55fd
 #include <linux/pfn.h>
ead55fd
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
4cbd7a3
index 84051f20b18a..583ab2bc1916 100644
ead55fd
--- a/drivers/input/misc/uinput.c
ead55fd
+++ b/drivers/input/misc/uinput.c
4cbd7a3
@@ -353,6 +353,7 @@ static int uinput_create_device(struct uinput_device *udev)
ead55fd
 		dev->flush = uinput_dev_flush;
ead55fd
 	}
ead55fd
 
ead55fd
+	dev->flags |= INPUTDEV_FLAGS_SYNTHETIC;
ead55fd
 	dev->event = uinput_dev_event;
ead55fd
 
ead55fd
 	input_set_drvdata(udev->dev, udev);
ead55fd
diff --git a/drivers/tty/sysrq.c b/drivers/tty/sysrq.c
Jeremy Cline 5df4c55
index 573b2055173c..99082faafc44 100644
ead55fd
--- a/drivers/tty/sysrq.c
ead55fd
+++ b/drivers/tty/sysrq.c
ead55fd
@@ -480,6 +480,7 @@ static struct sysrq_key_op *sysrq_key_table[36] = {
ead55fd
 	/* x: May be registered on mips for TLB dump */
ead55fd
 	/* x: May be registered on ppc/powerpc for xmon */
ead55fd
 	/* x: May be registered on sparc64 for global PMU dump */
ead55fd
+	/* x: May be registered on x86_64 for disabling secure boot */
ead55fd
 	NULL,				/* x */
ead55fd
 	/* y: May be registered on sparc64 for global register dump */
ead55fd
 	NULL,				/* y */
ead55fd
@@ -523,7 +524,7 @@ static void __sysrq_put_key_op(int key, struct sysrq_key_op *op_p)
ead55fd
                 sysrq_key_table[i] = op_p;
ead55fd
 }
ead55fd
 
ead55fd
-void __handle_sysrq(int key, bool check_mask)
ead55fd
+void __handle_sysrq(int key, unsigned int from)
ead55fd
 {
ead55fd
 	struct sysrq_key_op *op_p;
ead55fd
 	int orig_log_level;
4cbd7a3
@@ -546,11 +547,15 @@ void __handle_sysrq(int key, bool check_mask)
ead55fd
 
ead55fd
         op_p = __sysrq_get_key_op(key);
ead55fd
         if (op_p) {
Jeremy Cline 5df4c55
-		/*
Jeremy Cline 5df4c55
-		 * Should we check for enabled operations (/proc/sysrq-trigger
Jeremy Cline 5df4c55
-		 * should not) and is the invoked operation enabled?
Jeremy Cline 5df4c55
-		 */
Jeremy Cline 5df4c55
-		if (!check_mask || sysrq_on_mask(op_p->enable_mask)) {
ead55fd
+		/* Ban synthetic events from some sysrq functionality */
ead55fd
+		if ((from == SYSRQ_FROM_PROC || from == SYSRQ_FROM_SYNTHETIC) &&
Jeremy Cline 5df4c55
+		    op_p->enable_mask & SYSRQ_DISABLE_USERSPACE) {
ead55fd
+			printk("This sysrq operation is disabled from userspace.\n");
Jeremy Cline 5df4c55
+		} else if (from == SYSRQ_FROM_KERNEL || sysrq_on_mask(op_p->enable_mask)) {
Jeremy Cline 5df4c55
+			/*
Jeremy Cline 5df4c55
+			 * Should we check for enabled operations (/proc/sysrq-trigger
Jeremy Cline 5df4c55
+			 * should not) and is the invoked operation enabled?
Jeremy Cline 5df4c55
+			 */
ead55fd
 			pr_info("%s\n", op_p->action_msg);
ead55fd
 			console_loglevel = orig_log_level;
ead55fd
 			op_p->handler(key);
4cbd7a3
@@ -585,7 +590,7 @@ void __handle_sysrq(int key, bool check_mask)
ead55fd
 void handle_sysrq(int key)
ead55fd
 {
ead55fd
 	if (sysrq_on())
ead55fd
-		__handle_sysrq(key, true);
ead55fd
+		__handle_sysrq(key, SYSRQ_FROM_KERNEL);
ead55fd
 }
ead55fd
 EXPORT_SYMBOL(handle_sysrq);
ead55fd
 
4cbd7a3
@@ -665,7 +670,7 @@ static void sysrq_do_reset(struct timer_list *t)
ead55fd
 static void sysrq_handle_reset_request(struct sysrq_state *state)
ead55fd
 {
ead55fd
 	if (state->reset_requested)
ead55fd
-		__handle_sysrq(sysrq_xlate[KEY_B], false);
ead55fd
+		__handle_sysrq(sysrq_xlate[KEY_B], SYSRQ_FROM_KERNEL);
ead55fd
 
ead55fd
 	if (sysrq_reset_downtime_ms)
ead55fd
 		mod_timer(&state->keyreset_timer,
4cbd7a3
@@ -818,8 +823,10 @@ static bool sysrq_handle_keypress(struct sysrq_state *sysrq,
ead55fd
 
ead55fd
 	default:
ead55fd
 		if (sysrq->active && value && value != 2) {
ead55fd
+			int from = sysrq->handle.dev->flags & INPUTDEV_FLAGS_SYNTHETIC ?
ead55fd
+					SYSRQ_FROM_SYNTHETIC : 0;
ead55fd
 			sysrq->need_reinject = false;
ead55fd
-			__handle_sysrq(sysrq_xlate[code], true);
ead55fd
+			__handle_sysrq(sysrq_xlate[code], from);
ead55fd
 		}
ead55fd
 		break;
ead55fd
 	}
4cbd7a3
@@ -1102,7 +1109,7 @@ static ssize_t write_sysrq_trigger(struct file *file, const char __user *buf,
ead55fd
 
ead55fd
 		if (get_user(c, buf))
ead55fd
 			return -EFAULT;
ead55fd
-		__handle_sysrq(c, false);
ead55fd
+		__handle_sysrq(c, SYSRQ_FROM_PROC);
ead55fd
 	}
ead55fd
 
ead55fd
 	return count;
ead55fd
diff --git a/include/linux/input.h b/include/linux/input.h
Jeremy Cline 5df4c55
index 94f277cd806a..8539afa2c001 100644
ead55fd
--- a/include/linux/input.h
ead55fd
+++ b/include/linux/input.h
Jeremy Cline 5df4c55
@@ -48,6 +48,7 @@ enum input_clock_type {
ead55fd
  * @phys: physical path to the device in the system hierarchy
ead55fd
  * @uniq: unique identification code for the device (if device has it)
ead55fd
  * @id: id of the device (struct input_id)
ead55fd
+ * @flags: input device flags (SYNTHETIC, etc.)
ead55fd
  * @propbit: bitmap of device properties and quirks
ead55fd
  * @evbit: bitmap of types of events supported by the device (EV_KEY,
ead55fd
  *	EV_REL, etc.)
Jeremy Cline 5df4c55
@@ -134,6 +135,8 @@ struct input_dev {
ead55fd
 	const char *uniq;
ead55fd
 	struct input_id id;
ead55fd
 
ead55fd
+	unsigned int flags;
ead55fd
+
ead55fd
 	unsigned long propbit[BITS_TO_LONGS(INPUT_PROP_CNT)];
ead55fd
 
ead55fd
 	unsigned long evbit[BITS_TO_LONGS(EV_CNT)];
Jeremy Cline 5df4c55
@@ -204,6 +207,8 @@ struct input_dev {
ead55fd
 };
ead55fd
 #define to_input_dev(d) container_of(d, struct input_dev, dev)
ead55fd
 
ead55fd
+#define	INPUTDEV_FLAGS_SYNTHETIC	0x000000001
ead55fd
+
ead55fd
 /*
ead55fd
  * Verify that we are in sync with input_device_id mod_devicetable.h #defines
ead55fd
  */
ead55fd
diff --git a/include/linux/sysrq.h b/include/linux/sysrq.h
ead55fd
index 8c71874e8485..7de1f08b60a9 100644
ead55fd
--- a/include/linux/sysrq.h
ead55fd
+++ b/include/linux/sysrq.h
ead55fd
@@ -29,6 +29,8 @@
ead55fd
 #define SYSRQ_ENABLE_BOOT	0x0080
ead55fd
 #define SYSRQ_ENABLE_RTNICE	0x0100
ead55fd
 
ead55fd
+#define SYSRQ_DISABLE_USERSPACE	0x00010000
ead55fd
+
ead55fd
 struct sysrq_key_op {
ead55fd
 	void (*handler)(int);
ead55fd
 	char *help_msg;
ead55fd
@@ -43,8 +45,12 @@ struct sysrq_key_op {
ead55fd
  * are available -- else NULL's).
ead55fd
  */
ead55fd
 
ead55fd
+#define SYSRQ_FROM_KERNEL	0x0001
ead55fd
+#define SYSRQ_FROM_PROC		0x0002
ead55fd
+#define SYSRQ_FROM_SYNTHETIC	0x0004
ead55fd
+
ead55fd
 void handle_sysrq(int key);
ead55fd
-void __handle_sysrq(int key, bool check_mask);
ead55fd
+void __handle_sysrq(int key, unsigned int from);
ead55fd
 int register_sysrq_key(int key, struct sysrq_key_op *op);
ead55fd
 int unregister_sysrq_key(int key, struct sysrq_key_op *op);
ead55fd
 struct sysrq_key_op *__sysrq_get_key_op(int key);
ead55fd
diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c
Jeremy Cline 5df4c55
index 4567fe998c30..d05142ef44c4 100644
ead55fd
--- a/kernel/debug/kdb/kdb_main.c
ead55fd
+++ b/kernel/debug/kdb/kdb_main.c
ead55fd
@@ -1981,7 +1981,7 @@ static int kdb_sr(int argc, const char **argv)
ead55fd
 		return KDB_ARGCOUNT;
ead55fd
 
ead55fd
 	kdb_trap_printk++;
ead55fd
-	__handle_sysrq(*argv[1], check_mask);
ead55fd
+	__handle_sysrq(*argv[1], check_mask ? SYSRQ_FROM_KERNEL : 0);
ead55fd
 	kdb_trap_printk--;
ead55fd
 
ead55fd
 	return 0;
Jeremy Cline 5df4c55
diff --git a/security/lockdown/Kconfig b/security/lockdown/Kconfig
Jeremy Cline 5df4c55
index e84ddf484010..20e979178e1c 100644
Jeremy Cline 5df4c55
--- a/security/lockdown/Kconfig
Jeremy Cline 5df4c55
+++ b/security/lockdown/Kconfig
Jeremy Cline 5df4c55
@@ -45,3 +45,14 @@ config LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY
Jeremy Cline 5df4c55
 	 disabled.
4cbd7a3
 
Jeremy Cline 5df4c55
 endchoice
ead55fd
+
Jeremy Cline 5df4c55
+config ALLOW_LOCKDOWN_LIFT_BY_SYSRQ
Jeremy Cline 5df4c55
+	bool "Allow the kernel lockdown to be lifted by SysRq"
Jeremy Cline 5df4c55
+    depends on SECURITY_LOCKDOWN_LSM
Jeremy Cline 5df4c55
+    depends on !LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY
Jeremy Cline 5df4c55
+    depends on !LOCK_DOWN_KERNEL_FORCE_INTEGRITY
Jeremy Cline 5df4c55
+    depends on MAGIC_SYSRQ
Jeremy Cline 5df4c55
+    depends on X86
Jeremy Cline 5df4c55
+	help
Jeremy Cline 5df4c55
+      Allow setting the lockdown mode to "none" by pressing a SysRq key
Jeremy Cline 5df4c55
+      combination on a wired keyboard. On x86, this is SysRq+x
Jeremy Cline 5df4c55
diff --git a/security/lockdown/lockdown.c b/security/lockdown/lockdown.c
Jeremy Cline 5df4c55
index 8a10b43daf74..df4662257309 100644
Jeremy Cline 5df4c55
--- a/security/lockdown/lockdown.c
Jeremy Cline 5df4c55
+++ b/security/lockdown/lockdown.c
Jeremy Cline 5df4c55
@@ -13,6 +13,8 @@
ead55fd
 #include <linux/security.h>
ead55fd
 #include <linux/export.h>
Jeremy Cline 5df4c55
 #include <linux/lsm_hooks.h>
ead55fd
+#include <linux/sysrq.h>
ead55fd
+#include <asm/setup.h>
4cbd7a3
 
Jeremy Cline 5df4c55
 static enum lockdown_reason kernel_locked_down;
4cbd7a3
 
Jeremy Cline 5df4c55
@@ -179,6 +181,47 @@ static int __init lockdown_secfs_init(void)
Jeremy Cline 5df4c55
 	return PTR_ERR_OR_ZERO(dentry);
ead55fd
 }
Jeremy Cline 5df4c55
 
ead55fd
+#ifdef CONFIG_ALLOW_LOCKDOWN_LIFT_BY_SYSRQ
ead55fd
+
ead55fd
+/*
ead55fd
+ * Take the kernel out of lockdown mode.
ead55fd
+ */
ead55fd
+static void lift_kernel_lockdown(void)
ead55fd
+{
ead55fd
+	pr_notice("Lifting lockdown\n");
Jeremy Cline 5df4c55
+	kernel_locked_down = LOCKDOWN_NONE;
ead55fd
+}
ead55fd
+
ead55fd
+/*
ead55fd
+ * Allow lockdown to be lifted by pressing something like SysRq+x (and not by
ead55fd
+ * echoing the appropriate letter into the sysrq-trigger file).
ead55fd
+ */
ead55fd
+static void sysrq_handle_lockdown_lift(int key)
ead55fd
+{
ead55fd
+	if (kernel_locked_down)
ead55fd
+		lift_kernel_lockdown();
ead55fd
+}
ead55fd
+
ead55fd
+static struct sysrq_key_op lockdown_lift_sysrq_op = {
ead55fd
+	.handler	= sysrq_handle_lockdown_lift,
ead55fd
+	.help_msg	= "unSB(x)",
ead55fd
+	.action_msg	= "Disabling Secure Boot restrictions",
ead55fd
+	.enable_mask	= SYSRQ_DISABLE_USERSPACE,
ead55fd
+};
ead55fd
+
ead55fd
+static int __init lockdown_lift_sysrq(void)
ead55fd
+{
ead55fd
+	if (kernel_locked_down) {
ead55fd
+		lockdown_lift_sysrq_op.help_msg[5] = LOCKDOWN_LIFT_KEY;
ead55fd
+		register_sysrq_key(LOCKDOWN_LIFT_KEY, &lockdown_lift_sysrq_op);
ead55fd
+	}
ead55fd
+	return 0;
ead55fd
+}
ead55fd
+
ead55fd
+late_initcall(lockdown_lift_sysrq);
ead55fd
+
ead55fd
+#endif /* CONFIG_ALLOW_LOCKDOWN_LIFT_BY_SYSRQ */
Jeremy Cline 5df4c55
+
Jeremy Cline 5df4c55
 core_initcall(lockdown_secfs_init);
Jeremy Cline 5df4c55
 
Jeremy Cline 5df4c55
 #ifdef CONFIG_SECURITY_LOCKDOWN_LSM_EARLY
ead55fd
-- 
4cbd7a3
2.21.0
Jeremy Cline 5df4c55