LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [RFC] Turn lockdown into an LSM
@ 2019-05-21 22:40 Matthew Garrett
  2019-05-21 22:40 ` [RFC 1/2] security: Support early LSMs Matthew Garrett
                   ` (2 more replies)
  0 siblings, 3 replies; 12+ messages in thread
From: Matthew Garrett @ 2019-05-21 22:40 UTC (permalink / raw)
  To: jmorris; +Cc: linux-security-module, linux-kernel

Hi James,

This is a quick attempt to integrate lockdown into the existing LSM
framework. It adds a new lockdown security hook and an LSM that defines
the existing coarse-grained policy, and also adds a new
DEFINE_EARLY_LSM() definition in order to permit lockdown (and
potentially other modules) to be initialised at the top of kernel init
in order to allow policy to be imposed on stuff that happens in
setup_arch(). The goal here is to allow policy to be devolved to other
LSMs on systems that have a secure mechanism for loading LSM policy
early in boot, allowing creation of arbitrarily complicated policies
without interfering with the common-case coarse-grained approach.

This should probably be extended so a uapi-exposed constant is passed to
the hook in order to make it easier to write policy in other LSMs, but
does this broadly look like you were imagining?



^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC 1/2] security: Support early LSMs
  2019-05-21 22:40 [RFC] Turn lockdown into an LSM Matthew Garrett
@ 2019-05-21 22:40 ` Matthew Garrett
  2019-05-21 22:40 ` [RFC 2/2] Add the ability to lock down access to the running kernel image Matthew Garrett
  2019-05-22  2:40 ` [RFC] Turn lockdown into an LSM James Morris
  2 siblings, 0 replies; 12+ messages in thread
From: Matthew Garrett @ 2019-05-21 22:40 UTC (permalink / raw)
  To: jmorris
  Cc: linux-security-module, linux-kernel, Matthew Garrett, Matthew Garrett

The lockdown module is intended to allow for kernels to be locked down
early in boot - sufficiently early that we don't have the ability to
kmalloc() yet. Add support for early initialisation of some LSMs, and
then add them to the list of names when we do full initialisation later.

Signed-off-by: Matthew Garrett <mjg59@google.com>
---
 include/asm-generic/vmlinux.lds.h |  8 +++++-
 include/linux/lsm_hooks.h         |  6 ++++
 include/linux/security.h          |  1 +
 init/main.c                       |  1 +
 security/security.c               | 48 +++++++++++++++++++++++++------
 5 files changed, 54 insertions(+), 10 deletions(-)

diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 3d7a6a9c2370..e9761ed1f5b3 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -208,8 +208,13 @@
 			__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)
@@ -610,7 +615,8 @@
 	ACPI_PROBE_TABLE(irqchip)					\
 	ACPI_PROBE_TABLE(timer)						\
 	EARLYCON_TABLE()						\
-	LSM_TABLE()
+	LSM_TABLE()							\
+	EARLY_LSM_TABLE()
 
 #define INIT_TEXT							\
 	*(.init.text .init.text.*)					\
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 22fc786d723a..1a841d134e05 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -2074,12 +2074,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
diff --git a/include/linux/security.h b/include/linux/security.h
index 13537a49ae97..f4363a1339a8 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -191,6 +191,7 @@ int unregister_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);
diff --git a/init/main.c b/init/main.c
index e2e80ca3165a..6bdceaaeddbd 100644
--- a/init/main.c
+++ b/init/main.c
@@ -555,6 +555,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);
 	/*
 	 * Set up the the initial canary and entropy after arch
diff --git a/security/security.c b/security/security.c
index ed9b8cbf21cf..e3a7d4e96541 100644
--- a/security/security.c
+++ b/security/security.c
@@ -37,6 +37,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 ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
@@ -281,6 +282,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;
@@ -327,15 +330,11 @@ static void __init ordered_lsm_init(void)
 	kfree(ordered_lsms);
 }
 
-/**
- * security_init - initializes the security framework
- *
- * This should be called early in the kernel initialization sequence.
- */
-int __init security_init(void)
+int __init early_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");
 
@@ -343,6 +342,30 @@ int __init security_init(void)
 	     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;
+		initialize_lsm(lsm);
+	}
+
+	return 0;
+}
+
+/**
+ * security_init - initializes the security framework
+ *
+ * This should be called early in the kernel initialization sequence.
+ */
+int __init security_init(void)
+{
+	struct lsm_info *lsm;
+
+	/* Append the names of the early LSM modules now */
+	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();
 
@@ -388,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;
 
@@ -426,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_lsm_notifier(enum lsm_event event, void *data)
-- 
2.21.0.1020.gf2820cf01a-goog


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [RFC 2/2] Add the ability to lock down access to the running kernel image
  2019-05-21 22:40 [RFC] Turn lockdown into an LSM Matthew Garrett
  2019-05-21 22:40 ` [RFC 1/2] security: Support early LSMs Matthew Garrett
@ 2019-05-21 22:40 ` Matthew Garrett
  2019-05-22  2:48   ` James Morris
  2019-05-22  2:40 ` [RFC] Turn lockdown into an LSM James Morris
  2 siblings, 1 reply; 12+ messages in thread
From: Matthew Garrett @ 2019-05-21 22:40 UTC (permalink / raw)
  To: jmorris
  Cc: linux-security-module, linux-kernel, David Howells, Matthew Garrett

From: David Howells <dhowells@redhat.com>

Provide a single call to allow kernel code to determine whether the system
should be locked down, thereby disallowing various accesses that might
allow the running kernel image to be changed including the loading of
modules that aren't validly signed with a key we recognise, fiddling with
MSR registers and disallowing hibernation.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Matthew Garrett <matthewgarrett@google.com>
---
 Documentation/ABI/testing/lockdown            |  19 +++
 .../admin-guide/kernel-parameters.txt         |   9 +
 Documentation/admin-guide/lockdown.rst        |  60 +++++++
 include/linux/kernel.h                        |   7 +
 include/linux/lsm_hooks.h                     |   2 +
 include/linux/security.h                      |   6 +-
 security/Kconfig                              |   3 +-
 security/Makefile                             |   2 +
 security/lockdown/Kconfig                     |  46 +++++
 security/lockdown/Makefile                    |   1 +
 security/lockdown/lockdown.c                  | 157 ++++++++++++++++++
 security/security.c                           |   6 +
 12 files changed, 316 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/ABI/testing/lockdown
 create mode 100644 Documentation/admin-guide/lockdown.rst
 create mode 100644 security/lockdown/Kconfig
 create mode 100644 security/lockdown/Makefile
 create mode 100644 security/lockdown/lockdown.c

diff --git a/Documentation/ABI/testing/lockdown b/Documentation/ABI/testing/lockdown
new file mode 100644
index 000000000000..5bd51e20917a
--- /dev/null
+++ b/Documentation/ABI/testing/lockdown
@@ -0,0 +1,19 @@
+What:		security/lockdown
+Date:		March 2019
+Contact:	Matthew Garrett <mjg59@google.com>
+Description:
+		If CONFIG_LOCK_DOWN_KERNEL is enabled, the kernel can be
+		moved to a more locked down state at runtime by writing to
+		this attribute. Valid values are:
+
+		integrity:
+			The kernel will disable functionality that allows
+			userland to modify the running kernel image, other
+			than through the loading or execution of appropriately
+			signed objects.
+
+		confidentiality:
+			The kernel will disable all functionality disabled by
+			the integrity mode, but additionally will disable
+			features that potentially permit userland to obtain
+			confidential information stored within the kernel.
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index 91c0251fdb86..594d268d92ba 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -2213,6 +2213,15 @@
 	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
diff --git a/Documentation/admin-guide/lockdown.rst b/Documentation/admin-guide/lockdown.rst
new file mode 100644
index 000000000000..d05dcedd20d1
--- /dev/null
+++ b/Documentation/admin-guide/lockdown.rst
@@ -0,0 +1,60 @@
+Kernel lockdown functionality
+-----------------------------
+
+.. CONTENTS
+..
+.. - Overview.
+.. - Enabling Lockdown.
+
+========
+Overview
+========
+
+Traditionally Linux systems have been run with the presumption that a
+process running with full capabilities is effectively equivalent in
+privilege to the kernel itself. The lockdown feature attempts to draw
+a stronger boundary between privileged processes and the kernel,
+increasing the level of trust that can be placed in the kernel even in
+the face of hostile processes.
+
+Lockdown can be run in two modes - integrity and confidentiality. In
+integrity mode, kernel features that allow arbitrary modification of
+the running kernel image are disabled. Confidentiality mode behaves in
+the same way as integrity mode, but also blocks features that
+potentially allow a hostile userland process to extract secret
+information from the kernel.
+
+Note that lockdown depends upon the correct behaviour of the
+kernel. Exploitable vulnerabilities in the kernel may still permit
+arbitrary modification of the kernel or make it possible to disable
+lockdown features.
+
+=================
+Enabling Lockdown
+=================
+
+Lockdown can be enabled in multiple ways.
+
+Kernel configuration
+====================
+
+The kernel can be statically configured by setting either
+CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY or
+CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY. A kernel configured
+with CONFIG_LOCK_DOWN_KERNEL_FORCE_INTEGRITY may be booted into
+confidentiality mode using one of the other mechanisms, but otherwise
+the kernel will always boot into the configured mode.
+
+Kernel command line
+===================
+
+Passing lockdown=integrity or lockdown=confidentiality on the kernel
+command line will configure lockdown into the appropriate mode.
+
+Runtime configuration
+=====================
+
+/sys/kernel/security/lockdown will indicate the current lockdown
+state. The system state may be made stricter by writing either
+"integrity" or "confidentiality" into this file, but any attempts to
+make it less strict will fail.
diff --git a/include/linux/kernel.h b/include/linux/kernel.h
index 8f0e68e250a7..7ce52151b76c 100644
--- a/include/linux/kernel.h
+++ b/include/linux/kernel.h
@@ -340,6 +340,13 @@ static inline void refcount_error_report(struct pt_regs *regs, const char *err)
 { }
 #endif
 
+enum lockdown_level {
+	LOCKDOWN_NONE,
+	LOCKDOWN_INTEGRITY,
+	LOCKDOWN_CONFIDENTIALITY,
+	LOCKDOWN_MAX,
+};
+
 /* Internal, do not use. */
 int __must_check _kstrtoul(const char *s, unsigned int base, unsigned long *res);
 int __must_check _kstrtol(const char *s, unsigned int base, long *res);
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 1a841d134e05..0377d8ee6454 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1781,6 +1781,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)(const char *where, enum lockdown_level level);
 };
 
 struct security_hook_heads {
@@ -2016,6 +2017,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;
 
 /*
diff --git a/include/linux/security.h b/include/linux/security.h
index f4363a1339a8..87102694b033 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -384,6 +384,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_is_locked_down(const char *where, enum lockdown_level level);
 #else /* CONFIG_SECURITY */
 
 static inline int call_lsm_notifier(enum lsm_event event, void *data)
@@ -1173,6 +1174,10 @@ static inline int security_inode_getsecctx(struct inode *inode, void **ctx, u32
 {
 	return -EOPNOTSUPP;
 }
+static inline int security_is_locked_down(const char *where, enum lockdown_level level)
+{
+	return 0;
+}
 #endif	/* CONFIG_SECURITY */
 
 #ifdef CONFIG_SECURITY_NETWORK
@@ -1800,4 +1805,3 @@ static inline void security_bpf_prog_free(struct bpf_prog_aux *aux)
 #endif /* CONFIG_BPF_SYSCALL */
 
 #endif /* ! __LINUX_SECURITY_H */
-
diff --git a/security/Kconfig b/security/Kconfig
index 1d6463fb1450..c35aa72103df 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -236,12 +236,13 @@ source "security/apparmor/Kconfig"
 source "security/loadpin/Kconfig"
 source "security/yama/Kconfig"
 source "security/safesetid/Kconfig"
+source "security/lockdown/Kconfig"
 
 source "security/integrity/Kconfig"
 
 config LSM
 	string "Ordered list of enabled LSMs"
-	default "yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor"
+	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
diff --git a/security/Makefile b/security/Makefile
index c598b904938f..be1dd9d2cb2f 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -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
diff --git a/security/lockdown/Kconfig b/security/lockdown/Kconfig
new file mode 100644
index 000000000000..431cd2b9a14e
--- /dev/null
+++ b/security/lockdown/Kconfig
@@ -0,0 +1,46 @@
+config SECURITY_LOCKDOWN_LSM
+	bool "Basic module for enforcing kernel lockdown"
+	depends on SECURITY
+	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.
+
+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
index 000000000000..e3634b9017e7
--- /dev/null
+++ b/security/lockdown/Makefile
@@ -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
index 000000000000..6c707492344b
--- /dev/null
+++ b/security/lockdown/lockdown.c
@@ -0,0 +1,157 @@
+// 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_level kernel_locked_down;
+
+char *lockdown_levels[LOCKDOWN_MAX] = {"none", "integrity", "confidentiality"};
+
+/*
+ * Put the kernel into lock-down mode.
+ */
+static int lock_kernel_down(const char *where, enum lockdown_level 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);
+	else if (strcmp(level, "confidentiality") == 0)
+		lock_kernel_down("command line", LOCKDOWN_CONFIDENTIALITY);
+	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(const char *what, enum lockdown_level level)
+{
+	if ((kernel_locked_down >= level) && what)
+		pr_notice("Lockdown: %s is restricted; see man kernel_lockdown.7\n",
+			  what);
+	return (kernel_locked_down >= level);
+}
+
+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);
+#elif defined(CONFIG_LOCK_DOWN_KERNEL_FORCE_CONFIDENTIALITY)
+	lock_kernel_down("Kernel configuration", LOCKDOWN_CONFIDENTIALITY);
+#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 = LOCKDOWN_NONE; i < LOCKDOWN_MAX; i++) {
+		if (lockdown_levels[i]) {
+			const char *label = lockdown_levels[i];
+
+			if (kernel_locked_down == i)
+				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 < LOCKDOWN_MAX; i++) {
+		const char *label = lockdown_levels[i];
+
+		if (label && !strcmp(state, label))
+			err = lock_kernel_down("securityfs", i);
+	}
+
+	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);
+	if (IS_ERR(dentry))
+		return PTR_ERR(dentry);
+
+	return 0;
+}
+
+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,
+};
diff --git a/security/security.c b/security/security.c
index e3a7d4e96541..73ccf3be55bf 100644
--- a/security/security.c
+++ b/security/security.c
@@ -2370,3 +2370,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_is_locked_down(const char *where, enum lockdown_level level)
+{
+	return call_int_hook(locked_down, 0, where, level);
+}
+EXPORT_SYMBOL(security_is_locked_down);
-- 
2.21.0.1020.gf2820cf01a-goog


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] Turn lockdown into an LSM
  2019-05-21 22:40 [RFC] Turn lockdown into an LSM Matthew Garrett
  2019-05-21 22:40 ` [RFC 1/2] security: Support early LSMs Matthew Garrett
  2019-05-21 22:40 ` [RFC 2/2] Add the ability to lock down access to the running kernel image Matthew Garrett
@ 2019-05-22  2:40 ` James Morris
  2019-05-22 16:48   ` Matthew Garrett
  2 siblings, 1 reply; 12+ messages in thread
From: James Morris @ 2019-05-22  2:40 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: linux-security-module, linux-kernel, Andy Lutomirski

On Tue, 21 May 2019, Matthew Garrett wrote:

> Hi James,
> 
> This is a quick attempt to integrate lockdown into the existing LSM
> framework. It adds a new lockdown security hook and an LSM that defines
> the existing coarse-grained policy, and also adds a new
> DEFINE_EARLY_LSM() definition in order to permit lockdown (and
> potentially other modules) to be initialised at the top of kernel init
> in order to allow policy to be imposed on stuff that happens in
> setup_arch(). The goal here is to allow policy to be devolved to other
> LSMs on systems that have a secure mechanism for loading LSM policy
> early in boot, allowing creation of arbitrarily complicated policies
> without interfering with the common-case coarse-grained approach.
> 
> This should probably be extended so a uapi-exposed constant is passed to
> the hook in order to make it easier to write policy in other LSMs, but
> does this broadly look like you were imagining?

This looks promising!

An LSM could also potentially implement its own policy for the hook.

-- 
James Morris
<jmorris@namei.org>


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC 2/2] Add the ability to lock down access to the running kernel image
  2019-05-21 22:40 ` [RFC 2/2] Add the ability to lock down access to the running kernel image Matthew Garrett
@ 2019-05-22  2:48   ` James Morris
  0 siblings, 0 replies; 12+ messages in thread
From: James Morris @ 2019-05-22  2:48 UTC (permalink / raw)
  To: Matthew Garrett; +Cc: linux-security-module, linux-kernel, David Howells

On Tue, 21 May 2019, Matthew Garrett wrote:

> +	int (*locked_down)(const char *where, enum lockdown_level level);

> +static int lockdown_is_locked_down(const char *what, enum lockdown_level level)

I'm guessing 'what' is the best option here.


-- 
James Morris
<jmorris@namei.org>


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] Turn lockdown into an LSM
  2019-05-22  2:40 ` [RFC] Turn lockdown into an LSM James Morris
@ 2019-05-22 16:48   ` Matthew Garrett
  2019-05-22 17:08     ` Andy Lutomirski
  0 siblings, 1 reply; 12+ messages in thread
From: Matthew Garrett @ 2019-05-22 16:48 UTC (permalink / raw)
  To: James Morris; +Cc: LSM List, Linux Kernel Mailing List, Andy Lutomirski

On Tue, May 21, 2019 at 7:40 PM James Morris <jmorris@namei.org> wrote:
> An LSM could also potentially implement its own policy for the hook.

That was my plan. Right now the hook just gets an ASCII description of
the reason for the lockdown - that seems suboptimal for cases like
SELinux. What information would you want? My initial thinking was to
just have a stable enum of lockdown reasons that's in the UAPI headers
and then let other LSM tooling consume that, but I haven't spent
enough time with the internals of SELinux to know if there'd be a more
attractive solution.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] Turn lockdown into an LSM
  2019-05-22 16:48   ` Matthew Garrett
@ 2019-05-22 17:08     ` Andy Lutomirski
  2019-05-22 18:05       ` James Morris
  2019-05-22 18:30       ` Stephen Smalley
  0 siblings, 2 replies; 12+ messages in thread
From: Andy Lutomirski @ 2019-05-22 17:08 UTC (permalink / raw)
  To: Matthew Garrett
  Cc: James Morris, LSM List, Linux Kernel Mailing List, Andy Lutomirski

On Wed, May 22, 2019 at 9:49 AM Matthew Garrett <mjg59@google.com> wrote:
>
> On Tue, May 21, 2019 at 7:40 PM James Morris <jmorris@namei.org> wrote:
> > An LSM could also potentially implement its own policy for the hook.
>
> That was my plan. Right now the hook just gets an ASCII description of
> the reason for the lockdown - that seems suboptimal for cases like
> SELinux. What information would you want? My initial thinking was to
> just have a stable enum of lockdown reasons that's in the UAPI headers
> and then let other LSM tooling consume that, but I haven't spent
> enough time with the internals of SELinux to know if there'd be a more
> attractive solution.

I may be in the minority here, but I see this issue as a significant
downside of making lockdown more flexible.  If we stick with just
"this may violate integrity" and "this may violate confidentiality",
then the ABI surface is nice and narrow.  If we start having a big
uapi list of things that might qualify for lockdown, we need to worry
about compatibility issues.

This isn't purely theoretical.  Lockdown has some interesting
interactions with eBPF.  I don't want to be in a situation where v1 of
lockdown has a few eBPF hooks, but a later update improves the eBPF vs
lockdown interaction so that you can do more with eBPF on a locked
down kernel.  But now any such change has to worry about breaking the
lockdown LSM ABI.

And I still think it would be nice to have some credible use case for
a more fine grained policy than just the tri-state.  Having a lockdown
policy of "may not violate kernel confidentiality except using
kprobes" may be convenient, but it's also basically worthless, since
kernel confidentiality is gone.

All this being said, I do see one big benefit for LSM integration:
SELinux or another LSM could allow certain privileged tasks to bypass
lockdown.  This seems fine, except that there's potential nastiness
where current->cred isn't actually a valid thing to look at in the
current context.


So I guess my proposal is: use LSM, but make the hook very coarse
grained: int security_violate_confidentiality(const struct cred *) and
int security_violate_integrity(const struct cred *).

--Andy

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] Turn lockdown into an LSM
  2019-05-22 17:08     ` Andy Lutomirski
@ 2019-05-22 18:05       ` James Morris
  2019-05-22 18:30       ` Stephen Smalley
  1 sibling, 0 replies; 12+ messages in thread
From: James Morris @ 2019-05-22 18:05 UTC (permalink / raw)
  To: Andy Lutomirski; +Cc: Matthew Garrett, LSM List, Linux Kernel Mailing List

On Wed, 22 May 2019, Andy Lutomirski wrote:

> And I still think it would be nice to have some credible use case for
> a more fine grained policy than just the tri-state.  Having a lockdown
> policy of "may not violate kernel confidentiality except using
> kprobes" may be convenient, but it's also basically worthless, since
> kernel confidentiality is gone.

This is an important point, but there's also "can't use any lockdown 
features because the admin might need to use kprobes".  I mention a 
use-case below.

I think it's fine (and probably preferred) to keep the default behavior 
tri-state and allow LSMs to implement finer-grained policies.

> All this being said, I do see one big benefit for LSM integration:
> SELinux or another LSM could allow certain privileged tasks to bypass
> lockdown.  

Some environments _need_ a "break glass" option, and a well-defined policy
(e.g. an SELinux domain which can only be entered via serial console, with
2FA or JIT credentials) to selectively un-lock the kernel lockdown in  
production would mean the difference between having a fleet of millions of
nodes 99.999% locked down vs 0%.

> This seems fine, except that there's potential nastiness
> where current->cred isn't actually a valid thing to look at in the
> current context.

Right.

Can we identify any such cases in the current patchset?

One option would be for the LSM to assign a default (untrusted/unknown) 
value for the subject and then apply policy as needed (e.g. allow or deny 
these).

> So I guess my proposal is: use LSM, but make the hook very coarse
> grained: int security_violate_confidentiality(const struct cred *) and
> int security_violate_integrity(const struct cred *).

Perhaps security_kernel_unlock_*



-- 
James Morris
<jmorris@namei.org>


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] Turn lockdown into an LSM
  2019-05-22 17:08     ` Andy Lutomirski
  2019-05-22 18:05       ` James Morris
@ 2019-05-22 18:30       ` Stephen Smalley
  2019-05-22 19:19         ` James Morris
  1 sibling, 1 reply; 12+ messages in thread
From: Stephen Smalley @ 2019-05-22 18:30 UTC (permalink / raw)
  To: Andy Lutomirski, Matthew Garrett
  Cc: James Morris, LSM List, Linux Kernel Mailing List

On 5/22/19 1:08 PM, Andy Lutomirski wrote:
> On Wed, May 22, 2019 at 9:49 AM Matthew Garrett <mjg59@google.com> wrote:
>>
>> On Tue, May 21, 2019 at 7:40 PM James Morris <jmorris@namei.org> wrote:
>>> An LSM could also potentially implement its own policy for the hook.
>>
>> That was my plan. Right now the hook just gets an ASCII description of
>> the reason for the lockdown - that seems suboptimal for cases like
>> SELinux. What information would you want? My initial thinking was to
>> just have a stable enum of lockdown reasons that's in the UAPI headers
>> and then let other LSM tooling consume that, but I haven't spent
>> enough time with the internals of SELinux to know if there'd be a more
>> attractive solution.
> 
> I may be in the minority here, but I see this issue as a significant
> downside of making lockdown more flexible.  If we stick with just
> "this may violate integrity" and "this may violate confidentiality",
> then the ABI surface is nice and narrow.  If we start having a big
> uapi list of things that might qualify for lockdown, we need to worry
> about compatibility issues.
> 
> This isn't purely theoretical.  Lockdown has some interesting
> interactions with eBPF.  I don't want to be in a situation where v1 of
> lockdown has a few eBPF hooks, but a later update improves the eBPF vs
> lockdown interaction so that you can do more with eBPF on a locked
> down kernel.  But now any such change has to worry about breaking the
> lockdown LSM ABI.

I think we could keep the enum itself private to the kernel, and 
internally each security module could map the values to whatever 
permissions it wants to define.  SELinux itself has the ability to 
add/remove/re-arrange its permission definitions without breaking 
userspace/policy; they are dynamically mapped at policy load time. If a 
given permission doesn't exist in the loaded policy, there is a policy 
flag to control whether the policy should be rejected, or the permission 
should always be denied, or the permission should always be allowed. 
There is also support for an extensible set of "policy capabilities" to 
control whether new/changed permission checking logic for a given 
permission/operation should be enabled for that policy or if the kernel 
should continue using the older logic.

> And I still think it would be nice to have some credible use case for
> a more fine grained policy than just the tri-state.  Having a lockdown
> policy of "may not violate kernel confidentiality except using
> kprobes" may be convenient, but it's also basically worthless, since
> kernel confidentiality is gone.

I would expect that distros and users would have to make pragmatic 
tradeoffs in this area, and it would be nice if they could have a choice 
beyond all-or-nothing.  Do all of the interfaces being locked down 
expose the same degree of confidentiality or integrity risk?

> All this being said, I do see one big benefit for LSM integration:
> SELinux or another LSM could allow certain privileged tasks to bypass
> lockdown.

That seems to violate the intent of lockdown as I understood it, and 
turns security_is_locked_down() into a finer-grained capable() call.
Also, if I understand correctly, this could only be done if one were to 
disable the lockdown module in the lsm list, since the security 
framework will return non-zero (i.e. the operation is locked down) if 
any module that implements the hook returns non-zero; LSM is 
"restrictive". At that point, SELinux or the other LSM would be the sole 
arbiter of lockdown decisions.  SELinux or the other LSM also wouldn't 
have access to the kernel_locked_down level unless that was exported in 
some manner from the lockdown module.  Not sure how to compose these.

   This seems fine, except that there's potential nastiness
> where current->cred isn't actually a valid thing to look at in the
> current context.
> 
> So I guess my proposal is: use LSM, but make the hook very coarse
> grained: int security_violate_confidentiality(const struct cred *) and
> int security_violate_integrity(const struct cred *).

Not sure one can construct a useful policy at that granularity.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] Turn lockdown into an LSM
  2019-05-22 18:30       ` Stephen Smalley
@ 2019-05-22 19:19         ` James Morris
  2019-05-22 19:57           ` Casey Schaufler
  2019-05-22 20:03           ` Stephen Smalley
  0 siblings, 2 replies; 12+ messages in thread
From: James Morris @ 2019-05-22 19:19 UTC (permalink / raw)
  To: Stephen Smalley
  Cc: Andy Lutomirski, Matthew Garrett, LSM List, Linux Kernel Mailing List

On Wed, 22 May 2019, Stephen Smalley wrote:

> That seems to violate the intent of lockdown as I understood it, and 
> turns security_is_locked_down() into a finer-grained capable() call. 
> Also, if I understand correctly, this could only be done if one were to 
> disable the lockdown module in the lsm list, since the security 
> framework will return non-zero (i.e. the operation is locked down) if 
> any module that implements the hook returns non-zero; LSM is 
> "restrictive". At that point SELinux or the other LSM would be the sole 
> arbiter of lockdown decisions. SELinux or the other LSM also wouldn't 
> have access to the kernel_locked_down level unless that was exported in 
> some manner from the lockdown module.  Not sure how to compose these.

Right, I was envisaging the LSM replacing the default.

i.e. the default is tristate OR fine grained LSM policy.

They could in theory be composed restrictively, but this is likely not 
useful given the coarse grained default policy.  All the LSM could do is 
either further restrict none or integrity.

We'd need to figure out how to avoid confusing users in the case where 
multiple LSMs are registered for the hooks, possibly by having the 
lockdown LSM gate this and update the securityfs lockdown node with 
something like "lsm:smack".


-- 
James Morris
<jmorris@namei.org>


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] Turn lockdown into an LSM
  2019-05-22 19:19         ` James Morris
@ 2019-05-22 19:57           ` Casey Schaufler
  2019-05-22 20:03           ` Stephen Smalley
  1 sibling, 0 replies; 12+ messages in thread
From: Casey Schaufler @ 2019-05-22 19:57 UTC (permalink / raw)
  To: James Morris, Stephen Smalley
  Cc: Andy Lutomirski, Matthew Garrett, LSM List, Linux Kernel Mailing List

On 5/22/2019 12:19 PM, James Morris wrote:
> On Wed, 22 May 2019, Stephen Smalley wrote:
>
>> That seems to violate the intent of lockdown as I understood it, and 
>> turns security_is_locked_down() into a finer-grained capable() call. 
>> Also, if I understand correctly, this could only be done if one were to 
>> disable the lockdown module in the lsm list, since the security 
>> framework will return non-zero (i.e. the operation is locked down) if 
>> any module that implements the hook returns non-zero; LSM is 
>> "restrictive". At that point SELinux or the other LSM would be the sole 
>> arbiter of lockdown decisions. SELinux or the other LSM also wouldn't 
>> have access to the kernel_locked_down level unless that was exported in 
>> some manner from the lockdown module.  Not sure how to compose these.
> Right, I was envisaging the LSM replacing the default.
>
> i.e. the default is tristate OR fine grained LSM policy.
>
> They could in theory be composed restrictively, but this is likely not 
> useful given the coarse grained default policy.  All the LSM could do is 
> either further restrict none or integrity.
>
> We'd need to figure out how to avoid confusing users in the case where 
> multiple LSMs are registered for the hooks, possibly by having the 
> lockdown LSM gate this and update the securityfs lockdown node with 
> something like "lsm:smack".

The way I'd propose dealing with multiple LSMs using the
securityfs interface is the same as I'm proposing for
/proc/.../attr/current and SO_PEERSEC. A new interface
/proc/self/attr/display contains the name of the LSM that
the current process will see when looking at process or
security attributes that are "shared". Writing to display
is unprivileged and changes which LSM you get information
for.

Adornments like "lsm:smack" often require modification of
programs that fear change. The same would be true of a prctl().
The "display" file approach is no harder for applications that
are getting modified and much easier for scripts.


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [RFC] Turn lockdown into an LSM
  2019-05-22 19:19         ` James Morris
  2019-05-22 19:57           ` Casey Schaufler
@ 2019-05-22 20:03           ` Stephen Smalley
  1 sibling, 0 replies; 12+ messages in thread
From: Stephen Smalley @ 2019-05-22 20:03 UTC (permalink / raw)
  To: James Morris
  Cc: Andy Lutomirski, Matthew Garrett, LSM List, Linux Kernel Mailing List

On 5/22/19 3:19 PM, James Morris wrote:
> On Wed, 22 May 2019, Stephen Smalley wrote:
> 
>> That seems to violate the intent of lockdown as I understood it, and
>> turns security_is_locked_down() into a finer-grained capable() call.
>> Also, if I understand correctly, this could only be done if one were to
>> disable the lockdown module in the lsm list, since the security
>> framework will return non-zero (i.e. the operation is locked down) if
>> any module that implements the hook returns non-zero; LSM is
>> "restrictive". At that point SELinux or the other LSM would be the sole
>> arbiter of lockdown decisions. SELinux or the other LSM also wouldn't
>> have access to the kernel_locked_down level unless that was exported in
>> some manner from the lockdown module.  Not sure how to compose these.
> 
> Right, I was envisaging the LSM replacing the default.
> 
> i.e. the default is tristate OR fine grained LSM policy.
> 
> They could in theory be composed restrictively, but this is likely not
> useful given the coarse grained default policy.  All the LSM could do is
> either further restrict none or integrity.
> 
> We'd need to figure out how to avoid confusing users in the case where
> multiple LSMs are registered for the hooks, possibly by having the
> lockdown LSM gate this and update the securityfs lockdown node with
> something like "lsm:smack".

Some kind of transition from the lockdown module to other security 
modules might be needed, e.g. you might need to start with 
lockdown=integrity to protect the kernel up to the point where a policy 
is loaded, then hand off to SELinux or another security module to handle 
further requests.



^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2019-05-22 20:03 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-05-21 22:40 [RFC] Turn lockdown into an LSM Matthew Garrett
2019-05-21 22:40 ` [RFC 1/2] security: Support early LSMs Matthew Garrett
2019-05-21 22:40 ` [RFC 2/2] Add the ability to lock down access to the running kernel image Matthew Garrett
2019-05-22  2:48   ` James Morris
2019-05-22  2:40 ` [RFC] Turn lockdown into an LSM James Morris
2019-05-22 16:48   ` Matthew Garrett
2019-05-22 17:08     ` Andy Lutomirski
2019-05-22 18:05       ` James Morris
2019-05-22 18:30       ` Stephen Smalley
2019-05-22 19:19         ` James Morris
2019-05-22 19:57           ` Casey Schaufler
2019-05-22 20:03           ` Stephen Smalley

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).