Skip to contentSkip to conversation

Safe LSM (un)loading, and immutable hooks

PATCH Superseded Sargun Dhillon 5 participants 31 messages Apr 25, 2018 – May 1, 2018 1 message not in the archive

Subsystems (9)
lore
Share & tools
lore
#0 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

The primary security benefit of this patchset is the introduction of read-only hooks, even if some security modules have mutable hooks. Currently, if you have any LSMs with mutable hooks it will render all heads, and list nodes mutable. These are a prime place to attack because being able to manipulate those hooks is a way to bypass all LSMs easily and to create a persistent, covert channel to intercept nearly all calls.

There is a shares SRCU between all security hooks to facilitate safe LSM- unloading. This SRCU is very cheap for runtime overhead on reads, but there is synchronization around it for unloads. There is only a cost to pay at unload time, which is based on the execution time of longest chain of callbacks after synchronization begins.

If CONFIG_SECURITY_WRITABLE_HOOKS is enabled, then hooks can be loaded at runtime. The module can check the return code of security_add_hooks to determine whether or not they can install their hooks, independently of checking for the Kconfig value. In the future, we may make it so that runtime hook loading is also disabled, after boot. We can do that in a follow-up patch.

If CONFIG_SECURITY_UNREGISTRABLE_HOOKS is enabled, then hooks can be unloaded at runtime. This behaviour can be disabled by setting security.allow_unregister_hooks to 0. Once set, it requires a reboot to be reset.

SELinux is exempt from the CONFIG_SECURITY_UNREGISTRABLE_HOOKS rule, and can unregister at any time. SELinux exposes a mechanism to disable itself, prior to policy loading. This can be disabled at compilation time, and it depends on CONFIG_SECURITY_WRITABLE_HOOKS. Changing this behaviour would require breaking the uapi.

This patch can allow for a new style of LSMs. There are many cases where LSM "policies" would be better defined in some formal programming language, like C. This is either due to flexibility, or performance, for functions in the hot path.

It also unlocks development usecases, but those come as a secondary benefit to the earlier feature. There has been an appetite for out-of-tree LSMs:
http://kernsec.org/pipermail/linux-security-module-archive/2017-March/000119.html
Casey's work has been great enabling minor LSM stacking, and a variety of LSMs have been proposed on list that haven't made it into the kernel but fit into the minor LSM stacking model.

One of the other big changes is the introduction of the lsm_info structure. This encapsulates various information about the LSM itself, and can be used at a later time.

This patch was tested with CONFIG_PROVE_LOCKING to prove the correctness of the locking in lock_existing_hooks. It grabs the module lock to prevent new hooks from being loaded, or current hooks from being unloaded. It then grabs the lsm_info_lock, which is only grabbed on hook modification, or inspection of the loaded LSMs.

The other thing that this introduces is monitoring of LKM unloading. If an LSM's LKM is unloaded when it is not supposed to be, it will crash the system.

Thanks to Tetsuo for their review, and feedback.

Changes since:
v6:
* introduce LSM info
* Crash by default instead of leaving it up to module authors to deal with things going wrong
* Monitor module unloading, to ensure that attempts to unload the LKM for an LSM crashes the system.
v5:
* Rename "CONFIG_SECURITY_UNLOADABLE_MODULES" to "CONFIG_SECURITY_UNREGISTRABLE_HOOKS" which enables arbitrary hook deregistration. If disabled, hooks are not allowed to deregister. SELinux is exempt.
* Rename security.allow_unload_hooks to security.allow_unregister_hooks
* Make it so that both security_add_hooks, and security_delete_hooks return errors upon being unsuccessful, allowing module authors to make the best decision for their use case.
* Get rid of LSM_HOOK_INIT_MUTABLE
v4:
* Introduce the configuration flag "CONFIG_SECURITY_UNLOADABLE_MODULES" to disable module unloading
* Introduce the kernel parameter security.allow_unload_hooks
v3:
* Instead of taking the approach of a "null hook", using the approach of a second set of hooks -- this was mostly done through the FOR_EACH_SECURITY_HOOK_MUTABLE macro, which gets compiled out if CONFIG_SECURITY_WRITABLE_HOOKS is disabled.
v2:
* Split out hlist_head patch
* Apply Tetsuo's changes to clean up functions which are not covered by call_int_hook / call_void_hook
* Disable NULL hook checking when uneeded
v1:
* Add SRCU to allow for code-unloading
* Add concurrency control around hook mutation

Sargun Dhillon (6):
security: Move LSM registration arguments to struct lsm_info
security: Make security_hook_heads private
security: Introduce mutable (RW) hooks
security: Expose security_add_hooks externally and add error handling
security: Panic on forced unloading of security module
security: Add SECURITY_UNREGISTRABLE_HOOKS to allow for hook removal

 include/linux/lsm_hooks.h                     |  67 ++--
 scripts/gcc-plugins/randomize_layout_plugin.c |   2 -
 security/Kconfig                              |  12 +
 security/apparmor/lsm.c                       |   8 +-
 security/commoncap.c                          |   8 +-
 security/inode.c                              |  56 +++-
 security/loadpin/loadpin.c                    |   6 +-
 security/security.c                           | 421 +++++++++++++++++++++-----
 security/security.h                           |  11 +
 security/selinux/hooks.c                      |  16 +-
 security/smack/smack_lsm.c                    |   5 +-
 security/tomoyo/tomoyo.c                      |   6 +-
 security/yama/yama_lsm.c                      |   6 +-
 13 files changed, 491 insertions(+), 133 deletions(-)
 create mode 100644 security/security.h
Signature · 6 lines
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#1 lore raw

PATCH v7 1/6 security: Move LSM registration arguments to struct lsm_info

Previously, when LSMs registered, they independently passed their name and hook count. This had two implications:

1) Is required us to clone the name, so we could present it in security FS. This required memory allocation at start time.
2) Every time we wanted to tie more information back from the security hooks, to the LSM, we would have to add duplicated fields in struct security_hook_list.

It also introduces a new file -- security/security.h, which is meant to be private headers to be shared only between pieces of security "infrastructure".

---
 include/linux/lsm_hooks.h  | 44 ++++++++++-------------
 security/apparmor/lsm.c    |  6 ++--
 security/commoncap.c       |  8 +++--
 security/inode.c           | 56 +++++++++++++++++++++++++----
 security/loadpin/loadpin.c |  4 ++-
 security/security.c        | 89 +++++++++++++++++++++++-----------------------
 security/security.h        | 10 ++++++
 security/selinux/hooks.c   |  6 ++--
 security/smack/smack_lsm.c |  3 +-
 security/tomoyo/tomoyo.c   |  4 ++-
 security/yama/yama_lsm.c   |  4 ++-
 11 files changed, 147 insertions(+), 87 deletions(-)
 create mode 100644 security/security.h
include/linux/lsm_hooks.h +19 -25
index 9d0b286f3dba..65f346cb6639 100644
@@ -2004,11 +2004,20 @@ struct security_hook_heads {
  * Security module hook list structure.
  * For use with generic list macros for common operations.
  */
+struct security_hook_list;
+struct lsm_info {
+	struct hlist_node		list;
+	const char			*name;
+	const unsigned int		count;
+	struct security_hook_list	*hooks;
+} __randomize_layout;
+
 struct security_hook_list {
 	struct hlist_node		list;
 	struct hlist_head		*head;
 	union security_list_options	hook;
-	char				*lsm;
+	/* This field is not currently in use */
+	struct lsm_info			*info;
 } __randomize_layout;
 
 /*
@@ -2020,33 +2029,18 @@ struct security_hook_list {
 #define LSM_HOOK_INIT(HEAD, HOOK) \
 	{ .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
 
-extern struct security_hook_heads security_hook_heads;
-extern char *lsm_names;
+#define LSM_MODULE_INIT(NAME, HOOKS)		\
+	{					\
+		.name	= NAME,			\
+		.hooks	= HOOKS,		\
+		.count	= ARRAY_SIZE(HOOKS),	\
+	}
 
-extern void security_add_hooks(struct security_hook_list *hooks, int count,
-				char *lsm);
+extern struct security_hook_heads security_hook_heads;
+extern void security_add_hooks(struct lsm_info *lsm);
 
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
-/*
- * Assuring the safety of deleting a security module is up to
- * the security module involved. This may entail ordering the
- * module's hook list in a particular way, refusing to disable
- * the module once a policy is loaded or any number of other
- * actions better imagined than described.
- *
- * The name of the configuration option reflects the only module
- * that currently uses the mechanism. Any developer who thinks
- * disabling their module is a good idea needs to be at least as
- * careful as the SELinux team.
- */
-static inline void security_delete_hooks(struct security_hook_list *hooks,
-						int count)
-{
-	int i;
-
-	for (i = 0; i < count; i++)
-		hlist_del_rcu(&hooks[i].list);
-}
+void security_delete_hooks(struct lsm_info *lsm);
 #endif /* CONFIG_SECURITY_SELINUX_DISABLE */
 
 /* Currently required to handle SELinux runtime hook disable. */
security/apparmor/lsm.c +4 -2
index ce2b89e9ad94..d0b3f9a6d488 100644
@@ -1190,6 +1190,9 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(task_kill, apparmor_task_kill),
 };
 
+static struct lsm_info apparmor_info =
+	LSM_MODULE_INIT("apparmor", apparmor_hooks);
+
 /*
  * AppArmor sysfs module parameters
  */
@@ -1561,8 +1564,7 @@ static int __init apparmor_init(void)
 		aa_free_root_ns();
 		goto buffers_out;
 	}
-	security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),
-				"apparmor");
+	security_add_hooks(&apparmor_info);
 
 	/* Report that AppArmor successfully initialized */
 	apparmor_initialized = 1;
security/commoncap.c +5 -3
index 48620c93d697..d25891adda81 100644
@@ -1339,7 +1339,7 @@ int cap_mmap_file(struct file *file, unsigned long reqprot,
 
 #ifdef CONFIG_SECURITY
 
-struct security_hook_list capability_hooks[] __lsm_ro_after_init = {
+static struct security_hook_list capability_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(capable, cap_capable),
 	LSM_HOOK_INIT(settime, cap_settime),
 	LSM_HOOK_INIT(ptrace_access_check, cap_ptrace_access_check),
@@ -1360,10 +1360,12 @@ struct security_hook_list capability_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(vm_enough_memory, cap_vm_enough_memory),
 };
 
+static struct lsm_info capability_info =
+	LSM_MODULE_INIT("capability", capability_hooks);
+
 void __init capability_add_hooks(void)
 {
-	security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks),
-				"capability");
+	security_add_hooks(&capability_info);
 }
 
 #endif /* CONFIG_SECURITY */
security/inode.c +50 -6

72 lines not on this page · raw message

security/loadpin/loadpin.c +3 -1
index 5fa191252c8f..dabc49b855f7 100644
@@ -178,10 +178,12 @@ static struct security_hook_list loadpin_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(kernel_read_file, loadpin_read_file),
 };
 
+static struct lsm_info loadpin_info = LSM_MODULE_INIT("loadpin", loadpin_hooks);
+
 void __init loadpin_add_hooks(void)
 {
 	pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
-	security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin");
+	security_add_hooks(&loadpin_info);
 }
 
 /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
security/security.c +44 -45

126 lines not on this page · raw message

security/security.h new file +10 -0
new file mode 100644
index 000000000000..79d1388fb038
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/lsm_hooks.h>
+
+#ifndef __SECURITY_SECURITY_H
+#define __SECURITY_SECURITY_H
+extern struct hlist_head lsm_info_head;
+extern struct mutex lsm_info_lock;
+#endif
security/selinux/hooks.c +4 -2
index 4cafe6a19167..a6d1bc76340e 100644
@@ -7083,6 +7083,8 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
 #endif
 };
 
+static struct lsm_info selinux_info = LSM_MODULE_INIT("selinux", selinux_hooks);
+
 static __init int selinux_init(void)
 {
 	if (!security_module_enable("selinux")) {
@@ -7122,7 +7124,7 @@ static __init int selinux_init(void)
 
 	hashtab_cache_init();
 
-	security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux");
+	security_add_hooks(&selinux_info);
 
 	if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
 		panic("SELinux: Unable to register AVC netcache callback\n");
@@ -7269,7 +7271,7 @@ int selinux_disable(struct selinux_state *state)
 
 	selinux_enabled = 0;
 
-	security_delete_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
+	security_delete_hooks(&selinux_info);
 
 	/* Try to destroy the avc node cache */
 	avc_disable();
security/smack/smack_lsm.c +2 -1
index 0b414836bebd..a8959cc29bde 100644
@@ -4764,6 +4764,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(dentry_create_files_as, smack_dentry_create_files_as),
 };
 
+static struct lsm_info smack_info = LSM_MODULE_INIT("smack", smack_hooks);
 
 static __init void init_smack_known_list(void)
 {
@@ -4842,7 +4843,7 @@ static __init int smack_init(void)
 	/*
 	 * Register with LSM
 	 */
-	security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
+	security_add_hooks(&smack_info);
 
 	return 0;
 }
security/tomoyo/tomoyo.c +3 -1
index 213b8c593668..e8f08c10e339 100644
@@ -528,6 +528,8 @@ static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(socket_sendmsg, tomoyo_socket_sendmsg),
 };
 
+static struct lsm_info tomoyo_info = LSM_MODULE_INIT("tomoyo", tomoyo_hooks);
+
 /* Lock for GC. */
 DEFINE_SRCU(tomoyo_ss);
 
@@ -543,7 +545,7 @@ static int __init tomoyo_init(void)
 	if (!security_module_enable("tomoyo"))
 		return 0;
 	/* register ourselves with the security framework */
-	security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo");
+	security_add_hooks(&tomoyo_info);
 	printk(KERN_INFO "TOMOYO Linux initialized\n");
 	cred->security = &tomoyo_kernel_domain;
 	tomoyo_mm_init();
security/yama/yama_lsm.c +3 -1
index ffda91a4a1aa..4f80b7031e20 100644
@@ -430,6 +430,8 @@ static struct security_hook_list yama_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(task_free, yama_task_free),
 };
 
+static struct lsm_info yama_info = LSM_MODULE_INIT("yama", yama_hooks);
+
 #ifdef CONFIG_SYSCTL
 static int yama_dointvec_minmax(struct ctl_table *table, int write,
 				void __user *buffer, size_t *lenp, loff_t *ppos)
@@ -480,6 +482,6 @@ static inline void yama_init_sysctl(void) { }
 void __init yama_add_hooks(void)
 {
 	pr_info("Yama: becoming mindful.\n");
-	security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama");
+	security_add_hooks(&yama_info);
 	yama_init_sysctl();
 }
Signature · 6 lines
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
maintainer
#2 lore raw

PATCH v7 1/6 security: Move LSM registration arguments to struct lsm_info

include/linux/lsm_hooks.h struct security_hook_heads { · line 2020

Quoted 2 lines · Sargun Dhillon
include/linux/lsm_hooks.h +2 -0
+	/* This field is not currently in use */
+	struct lsm_info			*info;

It does seem to be in use.

security/inode.c EXPORT_SYMBOL_GPL(securityfs_remove); · line 366

Quoted 8 lines

static const struct file_operations lsm_ops = {

security/inode.c +4 -2
-	.read = lsm_read,
-	.llseek = generic_file_llseek,
+	.open		= lsm_ops_open,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= seq_release,
 };

Please mention this change in the changelog, or probably better, make it a separate patch.

Signature · 7 lines
James Morris
<[email protected]>

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#3 lore raw

PATCH v7 1/6 security: Move LSM registration arguments to struct lsm_info

In reply to Sargun Dhillon
Quoted 42 lines · Sargun Dhillon

Previously, when LSMs registered, they independently passed their name and hook count. This had two implications:

1) Is required us to clone the name, so we could present it in security FS. This required memory allocation at start time.
2) Every time we wanted to tie more information back from the security hooks, to the LSM, we would have to add duplicated fields in struct security_hook_list.

It also introduces a new file -- security/security.h, which is meant to be private headers to be shared only between pieces of security "infrastructure".

---
 include/linux/lsm_hooks.h  | 44 ++++++++++-------------
 security/apparmor/lsm.c    |  6 ++--
 security/commoncap.c       |  8 +++--
 security/inode.c           | 56 +++++++++++++++++++++++++----
 security/loadpin/loadpin.c |  4 ++-
 security/security.c        | 89 +++++++++++++++++++++++-----------------------
 security/security.h        | 10 ++++++
 security/selinux/hooks.c   |  6 ++--
 security/smack/smack_lsm.c |  3 +-
 security/tomoyo/tomoyo.c   |  4 ++-
 security/yama/yama_lsm.c   |  4 ++-
 11 files changed, 147 insertions(+), 87 deletions(-)
 create mode 100644 security/security.h
include/linux/lsm_hooks.h +5 -0
index 9d0b286f3dba..65f346cb6639 100644
@@ -2004,11 +2004,20 @@ struct security_hook_heads {
  * Security module hook list structure.
  * For use with generic list macros for common operations.
  */
+struct security_hook_list;
+struct lsm_info {
+       struct hlist_node               list;
+       const char                      *name;
+       const unsigned int              count;

include/linux/lsm_hooks.h struct security_hook_heads { · line 2020

Quoted 10 lines
include/linux/lsm_hooks.h +5 -1
+       struct security_hook_list       *hooks;
+} __randomize_layout;
+
 struct security_hook_list {
        struct hlist_node               list;
        struct hlist_head               *head;
        union security_list_options     hook;
-       char                            *lsm;
+       /* This field is not currently in use */
+       struct lsm_info                 *info;

const?

Quoted 5 lines
include/linux/lsm_hooks.h +0 -0
 } __randomize_layout;
 
 /*
@@ -2020,33 +2029,18 @@ struct security_hook_list {
 #define LSM_HOOK_INIT(HEAD, HOOK) \

include/linux/lsm_hooks.h struct security_hook_list { · line 2037

Quoted 10 lines
include/linux/lsm_hooks.h +6 -2
        { .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
 
-extern struct security_hook_heads security_hook_heads;
-extern char *lsm_names;
+#define LSM_MODULE_INIT(NAME, HOOKS)           \
+       {                                       \
+               .name   = NAME,                 \
+               .hooks  = HOOKS,                \
+               .count  = ARRAY_SIZE(HOOKS),    \
+       }

Instead of leaving this so every LSM has to do all the declarations, how about:

#define LSM_MODULE(NAME) \
    static const struct lsm_info NAME ## _info = { \
        .name = #NAME, \
        .hooks = NAME ## _hooks, \
        .count = ARRAY_SIZE(NAME ## _hooks), \
    }

include/linux/lsm_hooks.h struct security_hook_list { · line 2039

Quoted 2 lines
include/linux/lsm_hooks.h +2 -0
+static struct lsm_info apparmor_info =
+       LSM_MODULE_INIT("apparmor", apparmor_hooks);

This becomes just:

LSM_MODULE(apparmor);

include/linux/lsm_hooks.h struct security_hook_list { · line 2041

Quoted 2 lines
include/linux/lsm_hooks.h +2 -0
+static struct lsm_info capability_info =
+       LSM_MODULE_INIT("capability", capability_hooks);

LSM_MODULE(capability);

etc...

-Kees

Signature · 6 lines
Kees Cook
Pixel Security
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#4 lore raw

PATCH v7 1/6 security: Move LSM registration arguments to struct lsm_info

Quoted 84 lines · Kees Cook
Quoted 42 lines · Sargun Dhillon

Previously, when LSMs registered, they independently passed their name and hook count. This had two implications:

1) Is required us to clone the name, so we could present it in security FS. This required memory allocation at start time.
2) Every time we wanted to tie more information back from the security hooks, to the LSM, we would have to add duplicated fields in struct security_hook_list.

It also introduces a new file -- security/security.h, which is meant to be private headers to be shared only between pieces of security "infrastructure".

---
 include/linux/lsm_hooks.h  | 44 ++++++++++-------------
 security/apparmor/lsm.c    |  6 ++--
 security/commoncap.c       |  8 +++--
 security/inode.c           | 56 +++++++++++++++++++++++++----
 security/loadpin/loadpin.c |  4 ++-
 security/security.c        | 89 +++++++++++++++++++++++-----------------------
 security/security.h        | 10 ++++++
 security/selinux/hooks.c   |  6 ++--
 security/smack/smack_lsm.c |  3 +-
 security/tomoyo/tomoyo.c   |  4 ++-
 security/yama/yama_lsm.c   |  4 ++-
 11 files changed, 147 insertions(+), 87 deletions(-)
 create mode 100644 security/security.h
include/linux/lsm_hooks.h +5 -0
index 9d0b286f3dba..65f346cb6639 100644
@@ -2004,11 +2004,20 @@ struct security_hook_heads {
  * Security module hook list structure.
  * For use with generic list macros for common operations.
  */
+struct security_hook_list;
+struct lsm_info {
+       struct hlist_node               list;
+       const char                      *name;
+       const unsigned int              count;

include/linux/lsm_hooks.h struct security_hook_heads { · line 2020

Quoted 10 lines
include/linux/lsm_hooks.h +5 -1
+       struct security_hook_list       *hooks;
+} __randomize_layout;
+
 struct security_hook_list {
        struct hlist_node               list;
        struct hlist_head               *head;
        union security_list_options     hook;
-       char                            *lsm;
+       /* This field is not currently in use */
+       struct lsm_info                 *info;

const?

Quoted 5 lines
include/linux/lsm_hooks.h +0 -0
 } __randomize_layout;
 
 /*
@@ -2020,33 +2029,18 @@ struct security_hook_list {
 #define LSM_HOOK_INIT(HEAD, HOOK) \

include/linux/lsm_hooks.h struct security_hook_list { · line 2037

Quoted 10 lines
include/linux/lsm_hooks.h +6 -2
        { .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
 
-extern struct security_hook_heads security_hook_heads;
-extern char *lsm_names;
+#define LSM_MODULE_INIT(NAME, HOOKS)           \
+       {                                       \
+               .name   = NAME,                 \
+               .hooks  = HOOKS,                \
+               .count  = ARRAY_SIZE(HOOKS),    \
+       }

Instead of leaving this so every LSM has to do all the declarations, how about:

#define LSM_MODULE(NAME) \
    static const struct lsm_info NAME ## _info = { \
        .name = #NAME, \
        .hooks = NAME ## _hooks, \
        .count = ARRAY_SIZE(NAME ## _hooks), \
    }

include/linux/lsm_hooks.h struct security_hook_list { · line 2039

Quoted 2 lines
include/linux/lsm_hooks.h +2 -0
+static struct lsm_info apparmor_info =
+       LSM_MODULE_INIT("apparmor", apparmor_hooks);
Quoted 8 lines

This becomes just:

LSM_MODULE(apparmor);

include/linux/lsm_hooks.h struct security_hook_list { · line 2041

Quoted 2 lines
include/linux/lsm_hooks.h +2 -0
+static struct lsm_info capability_info =
+       LSM_MODULE_INIT("capability", capability_hooks);

LSM_MODULE(capability);

I thought about this, but I think that the implicit aspect of statically defining "X_hooks" in the same file is kind of confusing.

Quoted 8 lines

etc...

-Kees

--
Kees Cook
Pixel Security

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

#5 lore raw

PATCH v7 2/6 security: Make security_hook_heads private

In reply to Sargun Dhillon

This is in preparation for future patches. The pointer to the hook head is no longer embedded in the security_hook_list structure, and instead, now there is an offset.

In addition, since struct security_hook_heads is a static defined in security.c, and it's full of hlist_heads, who's initialization involves setting them to null, there's no more reason for explicit initialization.

---
 include/linux/lsm_hooks.h                     | 10 +++++---
 scripts/gcc-plugins/randomize_layout_plugin.c |  2 --
 security/security.c                           | 34 +++++++++++++++++----------
 security/security.h                           |  3 ++-
 4 files changed, 30 insertions(+), 19 deletions(-)
include/linux/lsm_hooks.h +7 -3
index 65f346cb6639..93bb0f8a597f 100644
@@ -2014,7 +2014,7 @@ struct lsm_info {
 
 struct security_hook_list {
 	struct hlist_node		list;
-	struct hlist_head		*head;
+	const unsigned int		offset;
 	union security_list_options	hook;
 	/* This field is not currently in use */
 	struct lsm_info			*info;
@@ -2026,8 +2026,13 @@ struct security_hook_list {
  * care of the common case and reduces the amount of
  * text involved.
  */
+#define HOOK_OFFSET(HEAD)	offsetof(struct security_hook_heads, HEAD)
+
 #define LSM_HOOK_INIT(HEAD, HOOK) \
-	{ .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
+	{					\
+		.offset	= HOOK_OFFSET(HEAD),	\
+		.hook	= { .HEAD = HOOK }	\
+	}
 
 #define LSM_MODULE_INIT(NAME, HOOKS)		\
 	{					\
@@ -2036,7 +2041,6 @@ struct security_hook_list {
 		.count	= ARRAY_SIZE(HOOKS),	\
 	}
 
-extern struct security_hook_heads security_hook_heads;
 extern void security_add_hooks(struct lsm_info *lsm);
 
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
scripts/gcc-plugins/randomize_layout_plugin.c +0 -2
index 6d5bbd31db7f..d94138999427 100644
@@ -52,8 +52,6 @@ static const struct whitelist_entry whitelist[] = {
 	{ "net/unix/af_unix.c", "unix_skb_parms", "char" },
 	/* big_key payload.data struct splashing */
 	{ "security/keys/big_key.c", "path", "void *" },
-	/* walk struct security_hook_heads as an array of struct hlist_head */
-	{ "security/security.c", "hlist_head", "security_hook_heads" },
 	{ }
 };
 
security/security.c +21 -13
index 36b9d2b0a135..acdbf65bd752 100644
@@ -41,9 +41,13 @@
 
 DEFINE_MUTEX(lsm_info_lock);
 struct hlist_head lsm_info_head __lsm_ro_after_init = HLIST_HEAD_INIT;
-struct security_hook_heads security_hook_heads __lsm_ro_after_init;
+static struct security_hook_heads security_hook_heads __lsm_ro_after_init;
 static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
 
+struct security_hook_heads *get_security_hook_heads(void)
+{
+	return &security_hook_heads;
+}
 
 /* Boot-time LSM user choice */
 static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] =
@@ -70,12 +74,6 @@ static void __init do_security_initcalls(void)
  */
 int __init security_init(void)
 {
-	int i;
-	struct hlist_head *list = (struct hlist_head *) &security_hook_heads;
-
-	for (i = 0; i < sizeof(security_hook_heads) / sizeof(struct hlist_head);
-	     i++)
-		INIT_HLIST_HEAD(&list[i]);
 	pr_info("Security Framework initialized\n");
 
 	/*
@@ -152,6 +150,20 @@ void security_delete_hooks(struct lsm_info *info)
 	mutex_unlock(&lsm_info_lock);
 }
 #endif /* CONFIG_SECURITY_SELINUX_DISABLE */
+
+static void __init security_add_hook(struct security_hook_list *hook,
+					struct lsm_info *info)
+{
+	const unsigned int offset = hook->offset;
+	const unsigned int idx = offset / sizeof(struct hlist_head);
+	struct hlist_head *head;
+
+	WARN_ON(offset % sizeof(struct hlist_head));
+
+	hook->info = info;
+	head = (struct hlist_head *)(&security_hook_heads) + idx;
+	hlist_add_tail_rcu(&hook->list, head);
+}
 /**
  * security_add_hooks - Add a modules hooks to the hook lists.
  * @lsm_info: The lsm_info struct for this security module
@@ -160,14 +172,10 @@ void security_delete_hooks(struct lsm_info *info)
  */
 void __init security_add_hooks(struct lsm_info *info)
 {
-	struct security_hook_list *hook;
 	int i;
 
-	for (i = 0; i < info->count; i++) {
-		hook = &info->hooks[i];
-		hook->info = info;
-		hlist_add_tail_rcu(&hook->list, hook->head);
-	}
+	for (i = 0; i < info->count; i++)
+		security_add_hook(&info->hooks[i], info);
 
 	mutex_lock(&lsm_info_lock);
 	hlist_add_tail_rcu(&info->list, &lsm_info_head);
security/security.h +2 -1
index 79d1388fb038..b4d1a60862c3 100644
@@ -5,6 +5,7 @@
 
 #ifndef __SECURITY_SECURITY_H
 #define __SECURITY_SECURITY_H
-extern struct hlist_head lsm_info_head;
 extern struct mutex lsm_info_lock;
+extern struct hlist_head lsm_info_head;
+extern struct security_hook_heads *get_security_hook_heads(void);
 #endif
Signature · 6 lines
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#6 lore raw

PATCH v7 3/6 security: Introduce mutable (RW) hooks

In reply to Sargun Dhillon

This PR introduces a separate set of immutable, versus mutable security hooks. This means that modules (like SELinux) can safely unload themselves after boot time, while not forcing the entire rest of the LSMs to suffer from the same security issues.

Mutable (RW) hooks are only evaluated after all Immutable (RO) hooks are successfully run.

---
 include/linux/lsm_hooks.h  |   9 +--
 security/apparmor/lsm.c    |   4 +-
 security/commoncap.c       |   4 +-
 security/loadpin/loadpin.c |   4 +-
 security/security.c        | 137 +++++++++++++++++++++++++++++++++++++--------
 security/security.h        |   2 +-
 security/selinux/hooks.c   |  12 +++-
 security/smack/smack_lsm.c |   4 +-
 security/tomoyo/tomoyo.c   |   4 +-
 security/yama/yama_lsm.c   |   4 +-
 10 files changed, 137 insertions(+), 47 deletions(-)
include/linux/lsm_hooks.h +1 -8
index 93bb0f8a597f..5846b010de0d 100644
@@ -2041,19 +2041,12 @@ struct security_hook_list {
 		.count	= ARRAY_SIZE(HOOKS),	\
 	}
 
-extern void security_add_hooks(struct lsm_info *lsm);
+extern void security_add_hooks(struct lsm_info *lsm, bool is_mutable);
 
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 void security_delete_hooks(struct lsm_info *lsm);
 #endif /* CONFIG_SECURITY_SELINUX_DISABLE */
 
-/* Currently required to handle SELinux runtime hook disable. */
-#ifdef CONFIG_SECURITY_WRITABLE_HOOKS
-#define __lsm_ro_after_init
-#else
-#define __lsm_ro_after_init	__ro_after_init
-#endif /* CONFIG_SECURITY_WRITABLE_HOOKS */
-
 extern int __init security_module_enable(const char *module);
 extern void __init capability_add_hooks(void);
 #ifdef CONFIG_SECURITY_YAMA
security/apparmor/lsm.c +2 -2
index d0b3f9a6d488..85ad007bcb92 100644
@@ -1117,7 +1117,7 @@ static void apparmor_sock_graft(struct sock *sk, struct socket *parent)
 		ctx->label = aa_get_current_label();
 }
 
-static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
+static struct security_hook_list apparmor_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check),
 	LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme),
 	LSM_HOOK_INIT(capget, apparmor_capget),
@@ -1564,7 +1564,7 @@ static int __init apparmor_init(void)
 		aa_free_root_ns();
 		goto buffers_out;
 	}
-	security_add_hooks(&apparmor_info);
+	security_add_hooks(&apparmor_info, false);
 
 	/* Report that AppArmor successfully initialized */
 	apparmor_initialized = 1;
security/commoncap.c +2 -2
index d25891adda81..6ce5703075d7 100644
@@ -1339,7 +1339,7 @@ int cap_mmap_file(struct file *file, unsigned long reqprot,
 
 #ifdef CONFIG_SECURITY
 
-static struct security_hook_list capability_hooks[] __lsm_ro_after_init = {
+static struct security_hook_list capability_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(capable, cap_capable),
 	LSM_HOOK_INIT(settime, cap_settime),
 	LSM_HOOK_INIT(ptrace_access_check, cap_ptrace_access_check),
@@ -1365,7 +1365,7 @@ static struct lsm_info capability_info =
 
 void __init capability_add_hooks(void)
 {
-	security_add_hooks(&capability_info);
+	security_add_hooks(&capability_info, false);
 }
 
 #endif /* CONFIG_SECURITY */
security/loadpin/loadpin.c +2 -2
index dabc49b855f7..c31907ab9724 100644
@@ -173,7 +173,7 @@ static int loadpin_read_file(struct file *file, enum kernel_read_file_id id)
 	return 0;
 }
 
-static struct security_hook_list loadpin_hooks[] __lsm_ro_after_init = {
+static struct security_hook_list loadpin_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security),
 	LSM_HOOK_INIT(kernel_read_file, loadpin_read_file),
 };
@@ -183,7 +183,7 @@ static struct lsm_info loadpin_info = LSM_MODULE_INIT("loadpin", loadpin_hooks);
 void __init loadpin_add_hooks(void)
 {
 	pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
-	security_add_hooks(&loadpin_info);
+	security_add_hooks(&loadpin_info, false);
 }
 
 /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
security/security.c +113 -24

261 lines not on this page · raw message

security/security.h +1 -1
index b4d1a60862c3..3e757f55cc1d 100644
@@ -7,5 +7,5 @@
 #define __SECURITY_SECURITY_H
 extern struct mutex lsm_info_lock;
 extern struct hlist_head lsm_info_head;
-extern struct security_hook_heads *get_security_hook_heads(void);
+extern struct security_hook_heads *get_security_hook_heads(bool is_mutable);
 #endif
security/selinux/hooks.c +10 -2
index a6d1bc76340e..b324f03ae723 100644
@@ -6848,7 +6848,13 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
 }
 #endif
 
-static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
+#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+#define __selinux_ro_after_init
+#else
+#define __selinux_ro_after_init	__ro_after_init
+#endif
+
+static struct security_hook_list selinux_hooks[] __selinux_ro_after_init = {
 	LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr),
 	LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction),
 	LSM_HOOK_INIT(binder_transfer_binder, selinux_binder_transfer_binder),
@@ -7087,6 +7093,8 @@ static struct lsm_info selinux_info = LSM_MODULE_INIT("selinux", selinux_hooks);
 
 static __init int selinux_init(void)
 {
+	const bool is_mutable = IS_ENABLED(CONFIG_SECURITY_SELINUX_DISABLE);
+
 	if (!security_module_enable("selinux")) {
 		selinux_enabled = 0;
 		return 0;
@@ -7124,7 +7132,7 @@ static __init int selinux_init(void)
 
 	hashtab_cache_init();
 
-	security_add_hooks(&selinux_info);
+	security_add_hooks(&selinux_info, is_mutable);
 
 	if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
 		panic("SELinux: Unable to register AVC netcache callback\n");
security/smack/smack_lsm.c +2 -2
index a8959cc29bde..86fe6467e0fb 100644
@@ -4623,7 +4623,7 @@ static int smack_dentry_create_files_as(struct dentry *dentry, int mode,
 	return 0;
 }
 
-static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
+static struct security_hook_list smack_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check),
 	LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
 	LSM_HOOK_INIT(syslog, smack_syslog),
@@ -4843,7 +4843,7 @@ static __init int smack_init(void)
 	/*
 	 * Register with LSM
 	 */
-	security_add_hooks(&smack_info);
+	security_add_hooks(&smack_info, false);
 
 	return 0;
 }
security/tomoyo/tomoyo.c +2 -2
index e8f08c10e339..879d358198b8 100644
@@ -497,7 +497,7 @@ static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg,
  * tomoyo_security_ops is a "struct security_operations" which is used for
  * registering TOMOYO.
  */
-static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = {
+static struct security_hook_list tomoyo_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(cred_alloc_blank, tomoyo_cred_alloc_blank),
 	LSM_HOOK_INIT(cred_prepare, tomoyo_cred_prepare),
 	LSM_HOOK_INIT(cred_transfer, tomoyo_cred_transfer),
@@ -545,7 +545,7 @@ static int __init tomoyo_init(void)
 	if (!security_module_enable("tomoyo"))
 		return 0;
 	/* register ourselves with the security framework */
-	security_add_hooks(&tomoyo_info);
+	security_add_hooks(&tomoyo_info, false);
 	printk(KERN_INFO "TOMOYO Linux initialized\n");
 	cred->security = &tomoyo_kernel_domain;
 	tomoyo_mm_init();
security/yama/yama_lsm.c +2 -2
index 4f80b7031e20..7f2ce8f7c11f 100644
@@ -423,7 +423,7 @@ int yama_ptrace_traceme(struct task_struct *parent)
 	return rc;
 }
 
-static struct security_hook_list yama_hooks[] __lsm_ro_after_init = {
+static struct security_hook_list yama_hooks[] __ro_after_init = {
 	LSM_HOOK_INIT(ptrace_access_check, yama_ptrace_access_check),
 	LSM_HOOK_INIT(ptrace_traceme, yama_ptrace_traceme),
 	LSM_HOOK_INIT(task_prctl, yama_task_prctl),
@@ -482,6 +482,6 @@ static inline void yama_init_sysctl(void) { }
 void __init yama_add_hooks(void)
 {
 	pr_info("Yama: becoming mindful.\n");
-	security_add_hooks(&yama_info);
+	security_add_hooks(&yama_info, false);
 	yama_init_sysctl();
 }
Signature · 6 lines
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#7 lore raw

PATCH v7 4/6 security: Expose security_add_hooks externally and add error handling

In reply to Sargun Dhillon

This exports security_add_hooks. In order to safely export it, we need have proper error handling for when mutable hooks are not available, rather than crashing the kernel.

It now returns an error if it's unable to get a lock to install hooks. For built-in modules, this should never happen, so by default, they crash the kernel. It is up to 3rd party module authors to handle this case for their own modules.

---
 include/linux/lsm_hooks.h  |  7 +++++--
 security/apparmor/lsm.c    |  2 +-
 security/commoncap.c       |  2 +-
 security/loadpin/loadpin.c |  2 +-
 security/security.c        | 23 ++++++++++++++++-------
 security/selinux/hooks.c   |  2 +-
 security/smack/smack_lsm.c |  2 +-
 security/tomoyo/tomoyo.c   |  2 +-
 security/yama/yama_lsm.c   |  2 +-
 9 files changed, 28 insertions(+), 16 deletions(-)
include/linux/lsm_hooks.h +5 -2
index 5846b010de0d..c794c8f2f8b9 100644
@@ -28,7 +28,7 @@
 #include <linux/security.h>
 #include <linux/init.h>
 #include <linux/rculist.h>
-
+#include <linux/module.h>
 /**
  * union security_list_options - Linux Security Module hook function list
  *
@@ -2010,6 +2010,7 @@ struct lsm_info {
 	const char			*name;
 	const unsigned int		count;
 	struct security_hook_list	*hooks;
+	struct module			*owner;
 } __randomize_layout;
 
 struct security_hook_list {
@@ -2039,9 +2040,11 @@ struct security_hook_list {
 		.name	= NAME,			\
 		.hooks	= HOOKS,		\
 		.count	= ARRAY_SIZE(HOOKS),	\
+		.owner	= THIS_MODULE,		\
 	}
 
-extern void security_add_hooks(struct lsm_info *lsm, bool is_mutable);
+extern int __must_check security_add_hooks(struct lsm_info *lsm,
+						bool is_mutable);
 
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 void security_delete_hooks(struct lsm_info *lsm);
security/apparmor/lsm.c +1 -1
index 85ad007bcb92..6477e9b088b1 100644
@@ -1564,7 +1564,7 @@ static int __init apparmor_init(void)
 		aa_free_root_ns();
 		goto buffers_out;
 	}
-	security_add_hooks(&apparmor_info, false);
+	BUG_ON(security_add_hooks(&apparmor_info, false));
 
 	/* Report that AppArmor successfully initialized */
 	apparmor_initialized = 1;
security/commoncap.c +1 -1
index 6ce5703075d7..3739e11d02e9 100644
@@ -1365,7 +1365,7 @@ static struct lsm_info capability_info =
 
 void __init capability_add_hooks(void)
 {
-	security_add_hooks(&capability_info, false);
+	BUG_ON(security_add_hooks(&capability_info, false));
 }
 
 #endif /* CONFIG_SECURITY */
security/loadpin/loadpin.c +1 -1
index c31907ab9724..b0a357034736 100644
@@ -183,7 +183,7 @@ static struct lsm_info loadpin_info = LSM_MODULE_INIT("loadpin", loadpin_hooks);
 void __init loadpin_add_hooks(void)
 {
 	pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis");
-	security_add_hooks(&loadpin_info, false);
+	BUG_ON(security_add_hooks(&loadpin_info, false));
 }
 
 /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */
security/security.c +16 -7
index 6b090d53bf9d..8d8a227eeeea 100644
@@ -174,9 +174,9 @@ void security_delete_hooks(struct lsm_info *info)
 }
 #endif /* CONFIG_SECURITY_SELINUX_DISABLE */
 
-static void __init security_add_hook(struct security_hook_list *hook,
-					struct lsm_info *info,
-					struct security_hook_heads *heads)
+static void security_add_hook(struct security_hook_list *hook,
+				struct lsm_info *info,
+				struct security_hook_heads *heads)
 {
 	const unsigned int offset = hook->offset;
 	const unsigned int idx = offset / sizeof(struct hlist_head);
@@ -194,23 +194,32 @@ static void __init security_add_hook(struct security_hook_list *hook,
  * @is_mutable: Can these hooks be loaded or unloaded after boot time
  *
  * Each LSM has to register its hooks with the infrastructure.
+ * Return 0 on success
  */
-void __init security_add_hooks(struct lsm_info *info, bool is_mutable)
+int __must_check security_add_hooks(struct lsm_info *info, bool is_mutable)
 {
 	struct security_hook_heads *heads;
 	int i;
 
 	heads = get_security_hook_heads(is_mutable);
 	if (IS_ERR(heads))
-		panic("Failed to get security heads: %ld\n", PTR_ERR(heads));
+		return PTR_ERR(heads);
+	if (!try_module_get(info->owner))
+		return -EINVAL;
+
+	if (mutex_lock_killable(&lsm_info_lock)) {
+		module_put(info->owner);
+		return -EINTR;
+	}
 
 	for (i = 0; i < info->count; i++)
 		security_add_hook(&info->hooks[i], info, heads);
-
-	mutex_lock(&lsm_info_lock);
 	hlist_add_tail_rcu(&info->list, &lsm_info_head);
 	mutex_unlock(&lsm_info_lock);
+
+	return 0;
 }
+EXPORT_SYMBOL_GPL(security_add_hooks);
 
 int call_lsm_notifier(enum lsm_event event, void *data)
 {
security/selinux/hooks.c +1 -1
index b324f03ae723..fb830dc80e15 100644
@@ -7132,7 +7132,7 @@ static __init int selinux_init(void)
 
 	hashtab_cache_init();
 
-	security_add_hooks(&selinux_info, is_mutable);
+	BUG_ON(security_add_hooks(&selinux_info, is_mutable));
 
 	if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
 		panic("SELinux: Unable to register AVC netcache callback\n");
security/smack/smack_lsm.c +1 -1
index 86fe6467e0fb..0844776c4d18 100644
@@ -4843,7 +4843,7 @@ static __init int smack_init(void)
 	/*
 	 * Register with LSM
 	 */
-	security_add_hooks(&smack_info, false);
+	BUG_ON(security_add_hooks(&smack_info, false));
 
 	return 0;
 }
security/tomoyo/tomoyo.c +1 -1
index 879d358198b8..4b08021338dc 100644
@@ -545,7 +545,7 @@ static int __init tomoyo_init(void)
 	if (!security_module_enable("tomoyo"))
 		return 0;
 	/* register ourselves with the security framework */
-	security_add_hooks(&tomoyo_info, false);
+	BUG_ON(security_add_hooks(&tomoyo_info, false));
 	printk(KERN_INFO "TOMOYO Linux initialized\n");
 	cred->security = &tomoyo_kernel_domain;
 	tomoyo_mm_init();
security/yama/yama_lsm.c +1 -1
index 7f2ce8f7c11f..06dd2eb5d0ca 100644
@@ -482,6 +482,6 @@ static inline void yama_init_sysctl(void) { }
 void __init yama_add_hooks(void)
 {
 	pr_info("Yama: becoming mindful.\n");
-	security_add_hooks(&yama_info, false);
+	BUG_ON(security_add_hooks(&yama_info, false));
 	yama_init_sysctl();
 }
Signature · 6 lines
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#8 lore raw

PATCH v7 5/6 security: Panic on forced unloading of security module

In reply to Sargun Dhillon

Although, when LSMs are loaded, the kernel takes a refcount on them, the administrator can force unload the module if the CONFIG_MODULE_FORCE_UNLOAD is set. Although this may be fine for some cases, in the case of security modules, this is problematic, as it may leave the system unsecure, or unaudited.

Although, a kernel panic will occur on the next attempt to make a callback for that hook, new code could be loaded, which would not trigger a panic, allowing for silent failure. Therefore, we must panic on an attempt to forcefully unload an LSM.

---
 security/security.c | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)
security/security.c +31 -0
index 8d8a227eeeea..204b9978ba24 100644
@@ -89,6 +89,34 @@ static void __init do_security_initcalls(void)
 	}
 }
 
+/*
+ * Check if one of our modules is being unloaded. This can happen if
+ * CONFIG_MODULE_FORCE_UNLOAD is enabled.
+ * If it is being unloaded, panic and let the user know what's going on
+ */
+static int security_module_cb(struct notifier_block *nb, unsigned long val,
+				void *data)
+{
+	struct module *mod = data;
+	struct lsm_info *info;
+
+	if (val != MODULE_STATE_GOING)
+		return NOTIFY_DONE;
+
+	mutex_lock(&lsm_info_lock);
+	hlist_for_each_entry(info, &lsm_info_head, list)
+		if (mod == info->owner)
+			panic("Security module %s is being unloaded forcefully\n",
+				info->name);
+	mutex_unlock(&lsm_info_lock);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block security_nb = {
+	.notifier_call	= security_module_cb,
+};
+
 /**
  * security_init - initializes the security framework
  *
@@ -99,6 +127,9 @@ int __init security_init(void)
 	pr_info("Security Framework initialized with%s writable hooks\n",
 		IS_ENABLED(CONFIG_SECURITY_WRITABLE_HOOKS) ? "" : "out");
 
+	if (IS_ENABLED(CONFIG_SECURITY_WRITABLE_HOOKS) &&
+		IS_ENABLED(CONFIG_MODULE_FORCE_UNLOAD))
+		register_module_notifier(&security_nb);
 	/*
 	 * Load minor LSMs, with the capability module always first.
 	 */
Signature · 6 lines
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#9 lore raw

PATCH v7 6/6 security: Add SECURITY_UNREGISTRABLE_HOOKS to allow for hook removal

In reply to Sargun Dhillon

This enables safe security hook module unloading, and long with code unloading. It also comes with a knob to prevent unloading modules at runtime, or after boot time, in case the administrator wants to lock down their system as such.

---
 include/linux/lsm_hooks.h |   5 +-
 security/Kconfig          |  12 +++
 security/security.c       | 225 +++++++++++++++++++++++++++++++++++++---------
 security/selinux/hooks.c  |   2 +-
 4 files changed, 199 insertions(+), 45 deletions(-)
include/linux/lsm_hooks.h +4 -1
index c794c8f2f8b9..5c227bbb2883 100644
@@ -2047,8 +2047,11 @@ extern int __must_check security_add_hooks(struct lsm_info *lsm,
 						bool is_mutable);
 
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
-void security_delete_hooks(struct lsm_info *lsm);
+void __security_delete_hooks(struct lsm_info *lsm);
 #endif /* CONFIG_SECURITY_SELINUX_DISABLE */
+#ifdef CONFIG_SECURITY_UNREGISTRABLE_HOOKS
+void security_delete_hooks(struct lsm_info *lsm);
+#endif /* CONFIG_SECURITY_UNREGISTRABLE_HOOKS */
 
 extern int __init security_module_enable(const char *module);
 extern void __init capability_add_hooks(void);
security/Kconfig +12 -0
index c4302067a3ad..c7039f625241 100644
@@ -36,6 +36,18 @@ config SECURITY_WRITABLE_HOOKS
 	bool
 	default n
 
+config SECURITY_UNREGISTRABLE_HOOKS
+	depends on SECURITY_WRITABLE_HOOKS && SRCU
+	default n
+	bool "Enable security module unloading"
+	help
+		This allows arbitrary security modules to be unloaded by the
+		administrator after boot time. After boot time, this behaviour
+		can still be disabled via sysfs, or disabled at boot time via
+		a kernel parameter.
+
+		If you are unsure how to answer this question, answer N.
+
 config SECURITYFS
 	bool "Enable the securityfs filesystem"
 	help
security/security.c +182 -43

411 lines not on this page · raw message

security/selinux/hooks.c +1 -1
index fb830dc80e15..a6a4c7218282 100644
@@ -7279,7 +7279,7 @@ int selinux_disable(struct selinux_state *state)
 
 	selinux_enabled = 0;
 
-	security_delete_hooks(&selinux_info);
+	__security_delete_hooks(&selinux_info);
 
 	/* Try to destroy the avc node cache */
 	avc_disable();
Signature · 6 lines
2.14.1

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
maintainer
#10 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

In reply to Sargun Dhillon

Suggested changes on top of your series.

But I think that your series still lacks critical code which is also not yet implemented in Casey's work. The reason call_int_hook() can work for hooks which change state (e.g. security_inode_alloc()) is that there is no need to call undo function because a hook which might change state (so-called Major LSM modules) is always the last entry of the list. If we allow runtime registration of LSM modules, there is a possibility that call_int_hook() returns an error after some LSM module allocated memory. You need to implement safe transaction in order to allow FOR_EACH_SECURITY_HOOK_RW hooks to return an error.

---
 include/linux/lsm_hooks.h |  4 +--
 security/security.c       | 86 +++++++++++++----------------------------------
 security/security.h       |  5 ---
 3 files changed, 25 insertions(+), 70 deletions(-)
include/linux/lsm_hooks.h +2 -2
index 5c227bb..98809d6 100644
@@ -2010,7 +2010,7 @@ struct lsm_info {
 	const char			*name;
 	const unsigned int		count;
 	struct security_hook_list	*hooks;
-	struct module			*owner;
+	const struct module		*owner;
 } __randomize_layout;
 
 struct security_hook_list {
@@ -2044,7 +2044,7 @@ struct security_hook_list {
 	}
 
 extern int __must_check security_add_hooks(struct lsm_info *lsm,
-						bool is_mutable);
+					   bool is_writable);
 
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 void __security_delete_hooks(struct lsm_info *lsm);
security/security.c +23 -63

185 lines not on this page · raw message

security/security.h +0 -5
index 3e757f5..ed056d5 100644
@@ -1,11 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #include <linux/mutex.h>
 #include <linux/list.h>
-#include <linux/lsm_hooks.h>
 
-#ifndef __SECURITY_SECURITY_H
-#define __SECURITY_SECURITY_H
 extern struct mutex lsm_info_lock;
 extern struct hlist_head lsm_info_head;
-extern struct security_hook_heads *get_security_hook_heads(bool is_mutable);
-#endif
Signature · 6 lines
1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#11 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 11 lines · Tetsuo Handa

Suggested changes on top of your series.

But I think that your series still lacks critical code which is also not yet implemented in Casey's work. The reason call_int_hook() can work for hooks which change state (e.g. security_inode_alloc()) is that there is no need to call undo function because a hook which might change state (so-called Major LSM modules) is always the last entry of the list. If we allow runtime registration of LSM modules, there is a possibility that call_int_hook() returns an error after some LSM module allocated memory. You need to implement safe transaction in order to allow FOR_EACH_SECURITY_HOOK_RW hooks to return an error.

My suggestion is we create a whitelist of "minor" hooks". These hooks should have no state, or blob manipulation. The only downside here, is sometimes you can cheat, and do what Yama does, in the sense of keeping its own relationships completely outside of the blobs which live on the actually tasks structs themselves. Given that Yama has done this successfully, I think we should trust module authors to not make these mistakes.

Quoted 42 lines
---
 include/linux/lsm_hooks.h |  4 +--
 security/security.c       | 86 +++++++++++++----------------------------------
 security/security.h       |  5 ---
 3 files changed, 25 insertions(+), 70 deletions(-)
include/linux/lsm_hooks.h +2 -2
index 5c227bb..98809d6 100644
@@ -2010,7 +2010,7 @@ struct lsm_info {
        const char                      *name;
        const unsigned int              count;
        struct security_hook_list       *hooks;
-       struct module                   *owner;
+       const struct module             *owner;
 } __randomize_layout;
 
 struct security_hook_list {
@@ -2044,7 +2044,7 @@ struct security_hook_list {
        }
 
 extern int __must_check security_add_hooks(struct lsm_info *lsm,
-                                               bool is_mutable);
+                                          bool is_writable);
 
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 void __security_delete_hooks(struct lsm_info *lsm);
security/security.c +1 -1
index 3606512..a4bc9cd 100644
@@ -48,18 +48,15 @@
 
 /*
  * security_allow_unregister_hooks blocks the delete_module syscall for
- * hooks that are loaded into the  set of mutable hooks by getting a reference
+ * hooks that are loaded into the set of writable hooks by getting a reference
  * on those modules. This allows built-in modules to still delete their security
  * hooks, so SELinux will still be able to deregister.
  *
  * If an arbitrary module tries to deregister, it will get a return code

security/security.c line 60

Quoted 10 lines
security/security.c +2 -5
  * indicating failure.
- *
- * When this value turns true -> false -- Once, lock_existing_hooks will be
- * called.
  */
-static atomic_t security_allow_unregister_hooks =
-       ATOMIC_INIT(IS_ENABLED(CONFIG_SECURITY_UNREGISTRABLE_HOOKS) ? 1 : 0);
+static bool security_allow_unregister_hooks =
+       IS_ENABLED(CONFIG_SECURITY_UNREGISTRABLE_HOOKS) ? 1 : 0;
 

If this is a static bool, can't it just be IS_ENABLED?

Quoted 35 lines
security/security.c +5 -5
 #define FOR_EACH_SECURITY_HOOK_RO(ITERATOR, HEAD) \
        hlist_for_each_entry_rcu(ITERATOR, &security_hook_heads_ro.HEAD, list)
@@ -90,18 +87,18 @@ void __security_delete_hooks(struct lsm_info *info)
 #define FOR_EACH_SECURITY_HOOK_RW(ITERATOR, HEAD) \
        hlist_for_each_entry_rcu(ITERATOR, &security_hook_heads_rw.HEAD, list)
 
-struct security_hook_heads *get_security_hook_heads(bool is_mutable)
+static struct security_hook_heads *get_security_hook_heads(bool is_writable)
 {
-       if (is_mutable)
+       if (is_writable)
                return &security_hook_heads_rw;
        return &security_hook_heads_ro;
 }
 #else
 #define FOR_EACH_SECURITY_HOOK_RW(ITERATOR, HEAD)      while (0)
 
-struct security_hook_heads *get_security_hook_heads(bool is_mutable)
+static struct security_hook_heads *get_security_hook_heads(bool is_writable)
 {
-       if (is_mutable)
+       if (is_writable)
                return ERR_PTR(-ENOTSUPP);
        return &security_hook_heads_ro;
 }
@@ -120,7 +117,7 @@ static inline void unlock_lsm(int idx)
 }
 
 /**
- * security_delete_hooks - Remove modules hooks from the mutable hooks lists.
+ * security_delete_hooks - Remove modules hooks from the writable hooks lists.
  * @lsm_info: The lsm_info struct for this security module
  *
  * If LSM unloading is disabled, this function will panic. It should not

security/security.c static inline void unlock_lsm(int idx) · line 129

Quoted 8 lines
security/security.c +2 -3
@@ -128,9 +125,8 @@ static inline void unlock_lsm(int idx)
  */
 void security_delete_hooks(struct lsm_info *info)
 {
-       if (!atomic_read(&security_allow_unregister_hooks))
-               panic("Security module %s is attempting to delete hooks.\n",
-                       info->name);
+       if (!security_allow_unregister_hooks)
+               panic("Security hooks are already locked.\n");

I don't understand the benefit of changing the language to this. Why not something like panic("Security module is attempted to delete locked hooks")? Otherwise, it can be misconstrued that this method actually locks hooks. Also, given you have the same error phrase everywhere, should it just be a #define?

Quoted 16 lines
security/security.c +0 -9
        __security_delete_hooks(info);
 
@@ -138,39 +134,17 @@ void security_delete_hooks(struct lsm_info *info)
 }
 EXPORT_SYMBOL_GPL(security_delete_hooks);
 
-static void lock_existing_hooks(void)
-{
-       struct lsm_info *info;
-
-       /*
-        * Prevent module unloading while we're doing this
-        * try_module_get may fail (safely), if the module
-        * is already unloading -- allow that.
-        */

security/security.c void security_delete_hooks(struct lsm_info *info) · line 159

Quoted 10 lines
security/security.c +0 -10
-       mutex_lock(&module_mutex);
-       mutex_lock(&lsm_info_lock);
-       pr_info("Locking security hooks\n");
-
-       hlist_for_each_entry(info, &lsm_info_head, list)
-               WARN_ON(!try_module_get(info->owner));
-
-       mutex_unlock(&lsm_info_lock);
-       mutex_unlock(&module_mutex);
-}

Why do you think we should remove this code? I think that it's valuable to keep in there because it prevents an administrator from accidentally panicing the system. For example, if you have admin A lock the security hooks, and then admin B comes along and tries to unload the module, IMHO they shouldn't get a panic, and by default (unless they rmmod -f) they should just a nice warning that suggests they're doing something wrong (like the module is in use).

security/security.c void security_delete_hooks(struct lsm_info *info) · line 140

Quoted 6 lines
security/security.c +1 -2
-
 static int allow_unregister_hooks_set(const char *val,
                                        const struct kernel_param *kp)
 {
-       const int current_val = atomic_read(&security_allow_unregister_hooks);
+       const bool current_val = security_allow_unregister_hooks;

Do we need READ_ONCE here to prevent this from being read multiple times in the function?

security/security.c void security_delete_hooks(struct lsm_info *info) · line 144

Quoted 7 lines
security/security.c +1 -3
        bool new_val;
-       int ret;
 
-       ret = strtobool(val, &new_val);
-       if (ret)
+       if (strtobool(val, &new_val))
                return 0;

I think I actually made an error here -- and it should have read
+ ret = strtobool(val, &new_val);
+ if (ret)
+ return ret;

Otherwise the user would not get an error if they did something like echo "badvalue" into it.

Quoted 31 lines
security/security.c +4 -5
        /* Noop */
-       if (!!new_val == !!current_val)
+       if (new_val == current_val)
                return 0;
 
        /* Do not allow for the transition from false -> true */
@@ -178,16 +152,15 @@ static int allow_unregister_hooks_set(const char *val,
                return -EPERM;
 
        /* The only legal transition true -> false */
-       if (atomic_cmpxchg(&security_allow_unregister_hooks, 1, 0) == 1)
-               lock_existing_hooks();
-
+       if (!xchg(&security_allow_unregister_hooks, 1))
+               pr_info("Locked security hooks.\n");
        return 0;
 }
 
 static int allow_unregister_hooks_get(char *buffer,
                                        const struct kernel_param *kp)
 {
-       const int current_val = atomic_read(&security_allow_unregister_hooks);
+       const bool current_val = security_allow_unregister_hooks;
        int ret;
 
        ret = sprintf(buffer, "%c\n", current_val ? '1' : '0');
@@ -241,14 +214,13 @@ static int security_module_cb(struct notifier_block *nb, unsigned long val,
        struct module *mod = data;
        struct lsm_info *info;
 

security/security.c static int security_module_cb(struct notifier_block *nb, unsigned long val, · line 223

Quoted 10 lines
security/security.c +2 -3
-       if (val != MODULE_STATE_GOING)
+       if (val != MODULE_STATE_GOING || security_allow_unregister_hooks)
                return NOTIFY_DONE;
 
        mutex_lock(&lsm_info_lock);
        hlist_for_each_entry(info, &lsm_info_head, list)
                if (mod == info->owner)
-                       panic("Security module %s is being unloaded without deregistering hooks",
-                               info->name);
+                       panic("Security hooks are already locked.\n");

See above.

Quoted 55 lines
security/security.c +5 -17
        mutex_unlock(&lsm_info_lock);
 
        return NOTIFY_DONE;
@@ -334,41 +306,29 @@ static void security_add_hook(struct security_hook_list *hook,
 /**
  * security_add_hooks - Add a modules hooks to the hook lists.
  * @lsm_info: The lsm_info struct for this security module
- * @is_mutable: Can these hooks be loaded or unloaded after boot time
+ * @is_writable: Can these hooks be loaded or unloaded after boot time
  *
  * Each LSM has to register its hooks with the infrastructure.
  * Return 0 on success
  */
-int __must_check security_add_hooks(struct lsm_info *info, bool is_mutable)
+int __must_check security_add_hooks(struct lsm_info *info, bool is_writable)
 {
        struct security_hook_heads *heads;
-       int i, ret = 0;
+       int i;
 
-       heads = get_security_hook_heads(is_mutable);
+       heads = get_security_hook_heads(is_writable);
        if (IS_ERR(heads))
                return PTR_ERR(heads);
 
        if (mutex_lock_killable(&lsm_info_lock))
                return -EINTR;
 
-       if (!atomic_read(&security_allow_unregister_hooks)) {
-               /*
-                * Because hook deregistration is not allowed, we need to try
-                * to get a refcount on the module owner.
-                */
-               if (!try_module_get(info->owner)) {
-                       ret = -EINVAL;
-                       goto out;
-               }
-       }
-
        for (i = 0; i < info->count; i++)
                security_add_hook(&info->hooks[i], info, heads);
        hlist_add_tail_rcu(&info->list, &lsm_info_head);
-out:
        mutex_unlock(&lsm_info_lock);
 
-       return ret;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(security_add_hooks);
 
security/security.h +0 -0
index 3e757f5..ed056d5 100644

security/security.h line 7

Quoted 7 lines
security/security.h +0 -3
@@ -1,11 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 #include <linux/mutex.h>
 #include <linux/list.h>
-#include <linux/lsm_hooks.h>
 
-#ifndef __SECURITY_SECURITY_H
-#define __SECURITY_SECURITY_H

Why get rid of these?

Quoted 11 lines
security/security.h +0 -3
 extern struct mutex lsm_info_lock;
 extern struct hlist_head lsm_info_head;
-extern struct security_hook_heads *get_security_hook_heads(bool is_mutable);
-#endif
--

1.8.3.1

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

Thanks for the feedback. I think we're getting there at last.

I'd like Casey and James' feedback as well. I think the big questions are:
1) Can we assume that LSM authors will not shoot themselves in the foot and break major LSMs?
2) Should we protect the administrator from themselves (try_module_get)?
3) Is it okay to allow hooks to be unloaded at all?

I think no matter what the answers to 1-3 are, we can apply patch 1, 2, and 3 once I include the fix-ups that you suggested here.

Patch 4 is heavily dependent on the answer to (1), and patches 6 is heavily dependent on the answer to (3).
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

maintainer
#12 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 13 lines · Sargun Dhillon
Quoted 11 lines · Tetsuo Handa

Suggested changes on top of your series.

But I think that your series still lacks critical code which is also not yet implemented in Casey's work. The reason call_int_hook() can work for hooks which change state (e.g. security_inode_alloc()) is that there is no need to call undo function because a hook which might change state (so-called Major LSM modules) is always the last entry of the list. If we allow runtime registration of LSM modules, there is a possibility that call_int_hook() returns an error after some LSM module allocated memory. You need to implement safe transaction in order to allow FOR_EACH_SECURITY_HOOK_RW hooks to return an error.

Quoted 7 lines

My suggestion is we create a whitelist of "minor" hooks". These hooks should have no state, or blob manipulation. The only downside here, is sometimes you can cheat, and do what Yama does, in the sense of keeping its own relationships completely outside of the blobs which live on the actually tasks structs themselves. Given that Yama has done this successfully, I think we should trust module authors to not make these mistakes.

I didn't understand what you mean. Basically, what this series is doing is

int security_inode_alloc(struct inode *inode)

{
	struct security_hook_list *P;
	inode->i_security = NULL;
	hlist_for_each_entry(P, &security_hook_heads_ro.inode_alloc_security, list) {
		int RC = P->hook.inode_alloc_security(inode);
		if (RC != 0)
			return RC; // <= So far only one major module can stay on the list.
	}
	/*** Start of changes made by this series ***/
	hlist_for_each_entry(P, &security_hook_heads_rw.inode_alloc_security, list) {
		int RC = P->hook.inode_alloc_security(inode);
		if (RC != 0)
			return RC; // <= Not calling inode_free_security() for corresponding successful inode_alloc_security().
	}
	/*** End of changes made by this series ***/
	return 0;
}

and what Casey's series is doing is

int security_inode_alloc(struct inode *inode)

{
	struct security_hook_list *P;
	inode->i_security = NULL;
	hlist_for_each_entry(P, &security_hook_heads.inode_alloc_security, list) {
		int RC = P->hook.inode_alloc_security(inode);
		if (RC != 0) {
			/*** Start of changes made by Casey's series ***/
			hlist_for_each_entry(P, &security_hook_heads.inode_free_security, list) {
				P->hook.inode_free_security(inode); // <= Might call inode_free_security() without corresponding successful inode_alloc_security().
			}
			/*** End of changes made by Casey's series ***/
			return RC;
		}
	}
	return 0;
}

. We need to keep track of which module's inode_free_security() needs to be called

int security_inode_alloc(struct inode *inode)

{
	struct security_hook_list *P1;
	struct security_hook_list *P2;
	inode->i_security = NULL;
	hlist_for_each_entry(P1, &security_hook_heads_ro.inode_alloc_security, list) {
		int RC = P1->hook.inode_alloc_security(inode);
		if (RC != 0)
			goto out;
	}
	hlist_for_each_entry(P1, &security_hook_heads_rw.inode_alloc_security, list) {
		int RC = P1->hook.inode_alloc_security(inode);
		if (RC != 0)
			goto out;
	}
	return 0;
out:
	hlist_for_each_entry(P2, &security_hook_heads_ro.inode_free_security, list) {
		if (P1 == P2)
			goto done;
		P2->hook.inode_free_security(inode);
	}
	hlist_for_each_entry(P2, &security_hook_heads_rw.inode_free_security, list) {
		if (P1 == P2)
			goto done;
		P2->hook.inode_free_security(inode);
	}
done:
	return ret;
}

while handling race condition that foo->inode_alloc_security(inode) returned an error and foo is removed from the list and is waiting for SRCU grace period before hitting P1 == P2 check and therefore by error calls all LSM modules' ->inode_free_security(inode) hooks.

Quoted 22 lines
Quoted 22 lines
security/security.c +0 -18
@@ -138,39 +134,17 @@ void security_delete_hooks(struct lsm_info *info)
 }
 EXPORT_SYMBOL_GPL(security_delete_hooks);
 
-static void lock_existing_hooks(void)
-{
-       struct lsm_info *info;
-
-       /*
-        * Prevent module unloading while we're doing this
-        * try_module_get may fail (safely), if the module
-        * is already unloading -- allow that.
-        */
-       mutex_lock(&module_mutex);
-       mutex_lock(&lsm_info_lock);
-       pr_info("Locking security hooks\n");
-
-       hlist_for_each_entry(info, &lsm_info_head, list)
-               WARN_ON(!try_module_get(info->owner));
-
-       mutex_unlock(&lsm_info_lock);
-       mutex_unlock(&module_mutex);
Quoted 8 lines

Review void security_delete_hooks(struct lsm_info *info) · line 159

Quoted 1 line
(quoted diff) +0 -1
-}

Why do you think we should remove this code? I think that it's valuable to keep in there because it prevents an administrator from accidentally panicing the system. For example, if you have admin A lock the security hooks, and then admin B comes along and tries to unload the module, IMHO they shouldn't get a panic, and by default (unless they rmmod -f) they should just a nice warning that suggests they're doing something wrong (like the module is in use).

What I suggested is

/* Doing init or already dying? */
if (mod->state != MODULE_STATE_LIVE) {
/* FIXME: if (force), slam module count damn the torpedoes */ pr_debug("%s already dying\n", mod->name);
ret = -EBUSY;
goto out;
}

+ ret = security_remove_module(mod);
+ if (ret)
+ goto out;
/* If it has an init func, it must have an exit func to unload */ if (mod->init && !mod->exit) {
forced = try_force_unload(flags);

and check like

int security_remove_module(struct module *mod)

{
	struct lsm_info *info;
	int ret = 0;

	if (security_allow_unregister_hooks)
		return 0;

	mutex_lock(&lsm_info_lock);
	hlist_for_each_entry(info, &lsm_info_head, list)
		if (mod == info->owner)
			ret = -EPERM;
	mutex_unlock(&lsm_info_lock);
	return ret;
}

rather than using register_module_notifier().

Quoted 5 lines

Thanks for the feedback. I think we're getting there at last.

I'd like Casey and James' feedback as well. I think the big questions are:
1) Can we assume that LSM authors will not shoot themselves in the foot and break major LSMs?

What "break major LSMs" means? Regardless of whether LKM-based LSMs use inode->i_security, we need to handle race condition described above.

Quoted 1 line

2) Should we protect the administrator from themselves (try_module_get)?

What I suggested is "not to play with module usage count".

Quoted 7 lines

3) Is it okay to allow hooks to be unloaded at all?

I think no matter what the answers to 1-3 are, we can apply patch 1, 2, and 3 once I include the fix-ups that you suggested here.

Patch 4 is heavily dependent on the answer to (1), and patches 6 is heavily dependent on the answer to (3).

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

#13 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 25 lines · Tetsuo Handa
Quoted 13 lines · Sargun Dhillon
Quoted 11 lines · Tetsuo Handa

Suggested changes on top of your series.

But I think that your series still lacks critical code which is also not yet implemented in Casey's work. The reason call_int_hook() can work for hooks which change state (e.g. security_inode_alloc()) is that there is no need to call undo function because a hook which might change state (so-called Major LSM modules) is always the last entry of the list. If we allow runtime registration of LSM modules, there is a possibility that call_int_hook() returns an error after some LSM module allocated memory. You need to implement safe transaction in order to allow FOR_EACH_SECURITY_HOOK_RW hooks to return an error.

Quoted 7 lines

My suggestion is we create a whitelist of "minor" hooks". These hooks should have no state, or blob manipulation. The only downside here, is sometimes you can cheat, and do what Yama does, in the sense of keeping its own relationships completely outside of the blobs which live on the actually tasks structs themselves. Given that Yama has done this successfully, I think we should trust module authors to not make these mistakes.

I didn't understand what you mean. Basically, what this series is doing is

int security_inode_alloc(struct inode *inode)

Quoted 8 lines
{
        struct security_hook_list *P;
        inode->i_security = NULL;
        hlist_for_each_entry(P, &security_hook_heads_ro.inode_alloc_security, list) {
                int RC = P->hook.inode_alloc_security(inode);
                if (RC != 0)
                        return RC; // <= So far only one major module can stay on the list.
        }

If no major LSMs exist in the RW series, it'll fall through to here.

Quoted 5 lines
        /*** Start of changes made by this series ***/
        hlist_for_each_entry(P, &security_hook_heads_rw.inode_alloc_security, list) {
                int RC = P->hook.inode_alloc_security(inode);
                if (RC != 0)
                        return RC; // <= Not calling inode_free_security() for corresponding successful inode_alloc_security().

inode_free_security is called on all LSM callbacks -- so, it should be, as long as people don't load unloadable major LSMs. If we want to be super conservative here, we can do a try_module_get, and a module_put if RC != 0 on RW hooks. So, we could have something like call_int_hook_major, and call_void_hook_major.

Quoted 8 lines
        }
        /*** End of changes made by this series ***/
        return 0;
}

and what Casey's series is doing is

int security_inode_alloc(struct inode *inode)

Quoted 16 lines
{
        struct security_hook_list *P;
        inode->i_security = NULL;
        hlist_for_each_entry(P, &security_hook_heads.inode_alloc_security, list) {
                int RC = P->hook.inode_alloc_security(inode);
                if (RC != 0) {
                        /*** Start of changes made by Casey's series ***/
                        hlist_for_each_entry(P, &security_hook_heads.inode_free_security, list) {
                                P->hook.inode_free_security(inode); // <= Might call inode_free_security() without corresponding successful inode_alloc_security().
                        }
                        /*** End of changes made by Casey's series ***/
                        return RC;
                }
        }
        return 0;
}

Right, but this could be taken care of by just ensuring that nobody allocates blobs (major LSM), and only one LSM returns a non-zero to the *alloc* callbacks? Right now, we have this because one has to be a "major" LSM in order to use these callbacks, and we ensure only one major LSM is active at a time.

I suggest that either in the short term we:
1) Trust people not to load a second major LSM,
2) See below.

Quoted 128 lines

. We need to keep track of which module's inode_free_security() needs to be called

int security_inode_alloc(struct inode *inode)

{
        struct security_hook_list *P1;
        struct security_hook_list *P2;
        inode->i_security = NULL;
        hlist_for_each_entry(P1, &security_hook_heads_ro.inode_alloc_security, list) {
                int RC = P1->hook.inode_alloc_security(inode);
                if (RC != 0)
                        goto out;
        }
        hlist_for_each_entry(P1, &security_hook_heads_rw.inode_alloc_security, list) {
                int RC = P1->hook.inode_alloc_security(inode);
                if (RC != 0)
                        goto out;
        }
        return 0;
out:
        hlist_for_each_entry(P2, &security_hook_heads_ro.inode_free_security, list) {
                if (P1 == P2)
                        goto done;
                P2->hook.inode_free_security(inode);
        }
        hlist_for_each_entry(P2, &security_hook_heads_rw.inode_free_security, list) {
                if (P1 == P2)
                        goto done;
                P2->hook.inode_free_security(inode);
        }
done:
        return ret;
}

while handling race condition that foo->inode_alloc_security(inode) returned an error and foo is removed from the list and is waiting for SRCU grace period before hitting P1 == P2 check and therefore by error calls all LSM modules' ->inode_free_security(inode) hooks.

Quoted 22 lines
Quoted 22 lines
(quoted diff) +0 -18
@@ -138,39 +134,17 @@ void security_delete_hooks(struct lsm_info *info)
 }
 EXPORT_SYMBOL_GPL(security_delete_hooks);
 
-static void lock_existing_hooks(void)
-{
-       struct lsm_info *info;
-
-       /*
-        * Prevent module unloading while we're doing this
-        * try_module_get may fail (safely), if the module
-        * is already unloading -- allow that.
-        */
-       mutex_lock(&module_mutex);
-       mutex_lock(&lsm_info_lock);
-       pr_info("Locking security hooks\n");
-
-       hlist_for_each_entry(info, &lsm_info_head, list)
-               WARN_ON(!try_module_get(info->owner));
-
-       mutex_unlock(&lsm_info_lock);
-       mutex_unlock(&module_mutex);
Quoted 8 lines

Review void security_delete_hooks(struct lsm_info *info) · line 159

Quoted 1 line
(quoted diff) +0 -1
-}

Why do you think we should remove this code? I think that it's valuable to keep in there because it prevents an administrator from accidentally panicing the system. For example, if you have admin A lock the security hooks, and then admin B comes along and tries to unload the module, IMHO they shouldn't get a panic, and by default (unless they rmmod -f) they should just a nice warning that suggests they're doing something wrong (like the module is in use).

What I suggested is

        /* Doing init or already dying? */
        if (mod->state != MODULE_STATE_LIVE) {
                /* FIXME: if (force), slam module count damn the torpedoes */
                pr_debug("%s already dying\n", mod->name);
                ret = -EBUSY;
                goto out;
        }
(quoted diff) +3 -0
+       ret = security_remove_module(mod);
+       if (ret)
+               goto out;
        /* If it has an init func, it must have an exit func to unload */
        if (mod->init && !mod->exit) {
                forced = try_force_unload(flags);

and check like

int security_remove_module(struct module *mod)

{
        struct lsm_info *info;
        int ret = 0;

        if (security_allow_unregister_hooks)
                return 0;

        mutex_lock(&lsm_info_lock);
        hlist_for_each_entry(info, &lsm_info_head, list)
                if (mod == info->owner)
                        ret = -EPERM;
        mutex_unlock(&lsm_info_lock);
        return ret;
}

rather than using register_module_notifier().

Quoted 5 lines

Thanks for the feedback. I think we're getting there at last.

I'd like Casey and James' feedback as well. I think the big questions are:
1) Can we assume that LSM authors will not shoot themselves in the foot and break major LSMs?

What "break major LSMs" means? Regardless of whether LKM-based LSMs use inode->i_security, we need to handle race condition described above.

Quoted 1 line

2) Should we protect the administrator from themselves (try_module_get)?

What I suggested is "not to play with module usage count".

Quoted 5 lines

3) Is it okay to allow hooks to be unloaded at all?

I think no matter what the answers to 1-3 are, we can apply patch 1, 2, and 3 once I include the fix-ups that you suggested here.

Quoted 6 lines
Quoted 2 lines

Patch 4 is heavily dependent on the answer to (1), and patches 6 is heavily dependent on the answer to (3).

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html'

What about something as stupid as:

security/security.c +0 -0
index 36065128c6c5..895bdb0b1381 100644
@@ -339,7 +339,8 @@ static void security_add_hook(struct

security_hook_list *hook,
* Each LSM has to register its hooks with the infrastructure.
* Return 0 on success
*/
-int __must_check security_add_hooks(struct lsm_info *info, bool is_mutable) +int __must_check security_add_hooks(struct lsm_info *info, bool is_mutable,
+ bool is_major)

 {
        struct security_hook_heads *heads;
        int i, ret = 0;
@@ -348,6 +349,9 @@ int __must_check security_add_hooks(struct
lsm_info *info, bool is_mutable)
        if (IS_ERR(heads))
                return PTR_ERR(heads);

+ if (!info->owner && is_major)
+ return -EPERM;
+

        if (mutex_lock_killable(&lsm_info_lock))
                return -EINTR;

OR

include/linux/lsm_hooks.h +8 -6
index 5c227bbb2883..9cec723e7cea 100644
@@ -2011,6 +2011,7 @@ struct lsm_info {
        const unsigned int              count;
        struct security_hook_list       *hooks;
        struct module                   *owner;
+       bool                            is_major;
 } __randomize_layout;
 
 struct security_hook_list {
@@ -2035,12 +2036,13 @@ struct security_hook_list {
                .hook   = { .HEAD = HOOK }      \
        }
 
-#define LSM_MODULE_INIT(NAME, HOOKS)           \
-       {                                       \
-               .name   = NAME,                 \
-               .hooks  = HOOKS,                \
-               .count  = ARRAY_SIZE(HOOKS),    \
-               .owner  = THIS_MODULE,          \
+#define LSM_MODULE_INIT(NAME, HOOKS, IS_MAJOR)         \
+       {                                               \
+               .name           = NAME,                 \
+               .hooks          = HOOKS,                \
+               .count          = ARRAY_SIZE(HOOKS),    \
+               .owner          = THIS_MODULE,          \
+               .is_major       = IS_MAJOR,             \
        }
 
 extern int __must_check security_add_hooks(struct lsm_info *lsm,
security/security.c +2 -0
index 36065128c6c5..a3bfe735c0e2 100644
@@ -80,6 +80,8 @@ void __security_delete_hooks(struct lsm_info *info)
 
        mutex_lock(&lsm_info_lock);
        hlist_del(&info->list);
+       if (info->is_major)
+               major_modules_loaded--;
        mutex_unlock(&lsm_info_lock);
 }
 #endif
@@ -331,6 +333,8 @@ static void security_add_hook(struct

security_hook_list *hook,

        head = (struct hlist_head *)(heads) + idx;
        hlist_add_tail_rcu(&hook->list, head);

}
+static int major_modules_loaded;
+
/**
* security_add_hooks - Add a modules hooks to the hook lists.
* @lsm_info: The lsm_info struct for this security module
@@ -351,6 +355,12 @@ int __must_check security_add_hooks(struct lsm_info *info, bool is_mutable)

        if (mutex_lock_killable(&lsm_info_lock))
                return -EINTR;

+ if (info->is_major && major_modules_loaded) {
+ ret = -EBUSY;
+ goto out;
+ }
+
+

        if (!atomic_read(&security_allow_unregister_hooks)) {
                /*
                 * Because hook deregistration is not allowed, we need to try
@@ -365,6 +375,8 @@ int __must_check security_add_hooks(struct
lsm_info *info, bool is_mutable)
        for (i = 0; i < info->count; i++)
                security_add_hook(&info->hooks[i], info, heads);
        hlist_add_tail_rcu(&info->list, &lsm_info_head);
+       if (info->is_major)
+               major_modules_loaded++;
 out:
        mutex_unlock(&lsm_info_lock);

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

#14 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 315 lines · Sargun Dhillon
Quoted 25 lines · Tetsuo Handa
Quoted 13 lines · Sargun Dhillon
Quoted 11 lines · Tetsuo Handa

Suggested changes on top of your series.

But I think that your series still lacks critical code which is also not yet implemented in Casey's work. The reason call_int_hook() can work for hooks which change state (e.g. security_inode_alloc()) is that there is no need to call undo function because a hook which might change state (so-called Major LSM modules) is always the last entry of the list. If we allow runtime registration of LSM modules, there is a possibility that call_int_hook() returns an error after some LSM module allocated memory. You need to implement safe transaction in order to allow FOR_EACH_SECURITY_HOOK_RW hooks to return an error.

Quoted 7 lines

My suggestion is we create a whitelist of "minor" hooks". These hooks should have no state, or blob manipulation. The only downside here, is sometimes you can cheat, and do what Yama does, in the sense of keeping its own relationships completely outside of the blobs which live on the actually tasks structs themselves. Given that Yama has done this successfully, I think we should trust module authors to not make these mistakes.

I didn't understand what you mean. Basically, what this series is doing is

int security_inode_alloc(struct inode *inode)

Quoted 8 lines
{
        struct security_hook_list *P;
        inode->i_security = NULL;
        hlist_for_each_entry(P, &security_hook_heads_ro.inode_alloc_security, list) {
                int RC = P->hook.inode_alloc_security(inode);
                if (RC != 0)
                        return RC; // <= So far only one major module can stay on the list.
        }

If no major LSMs exist in the RW series, it'll fall through to here.

Quoted 5 lines
        /*** Start of changes made by this series ***/
        hlist_for_each_entry(P, &security_hook_heads_rw.inode_alloc_security, list) {
                int RC = P->hook.inode_alloc_security(inode);
                if (RC != 0)
                        return RC; // <= Not calling inode_free_security() for corresponding successful inode_alloc_security().

inode_free_security is called on all LSM callbacks -- so, it should be, as long as people don't load unloadable major LSMs. If we want to be super conservative here, we can do a try_module_get, and a module_put if RC != 0 on RW hooks. So, we could have something like call_int_hook_major, and call_void_hook_major.

Quoted 8 lines
        }
        /*** End of changes made by this series ***/
        return 0;
}

and what Casey's series is doing is

int security_inode_alloc(struct inode *inode)

Quoted 16 lines
{
        struct security_hook_list *P;
        inode->i_security = NULL;
        hlist_for_each_entry(P, &security_hook_heads.inode_alloc_security, list) {
                int RC = P->hook.inode_alloc_security(inode);
                if (RC != 0) {
                        /*** Start of changes made by Casey's series ***/
                        hlist_for_each_entry(P, &security_hook_heads.inode_free_security, list) {
                                P->hook.inode_free_security(inode); // <= Might call inode_free_security() without corresponding successful inode_alloc_security().
                        }
                        /*** End of changes made by Casey's series ***/
                        return RC;
                }
        }
        return 0;
}

Right, but this could be taken care of by just ensuring that nobody allocates blobs (major LSM), and only one LSM returns a non-zero to the *alloc* callbacks? Right now, we have this because one has to be a "major" LSM in order to use these callbacks, and we ensure only one major LSM is active at a time.

I suggest that either in the short term we:
1) Trust people not to load a second major LSM,
2) See below.

Quoted 128 lines

. We need to keep track of which module's inode_free_security() needs to be called

int security_inode_alloc(struct inode *inode)

{
        struct security_hook_list *P1;
        struct security_hook_list *P2;
        inode->i_security = NULL;
        hlist_for_each_entry(P1, &security_hook_heads_ro.inode_alloc_security, list) {
                int RC = P1->hook.inode_alloc_security(inode);
                if (RC != 0)
                        goto out;
        }
        hlist_for_each_entry(P1, &security_hook_heads_rw.inode_alloc_security, list) {
                int RC = P1->hook.inode_alloc_security(inode);
                if (RC != 0)
                        goto out;
        }
        return 0;
out:
        hlist_for_each_entry(P2, &security_hook_heads_ro.inode_free_security, list) {
                if (P1 == P2)
                        goto done;
                P2->hook.inode_free_security(inode);
        }
        hlist_for_each_entry(P2, &security_hook_heads_rw.inode_free_security, list) {
                if (P1 == P2)
                        goto done;
                P2->hook.inode_free_security(inode);
        }
done:
        return ret;
}

while handling race condition that foo->inode_alloc_security(inode) returned an error and foo is removed from the list and is waiting for SRCU grace period before hitting P1 == P2 check and therefore by error calls all LSM modules' ->inode_free_security(inode) hooks.

Quoted 22 lines
Quoted 22 lines
(quoted diff) +0 -18
@@ -138,39 +134,17 @@ void security_delete_hooks(struct lsm_info *info)
 }
 EXPORT_SYMBOL_GPL(security_delete_hooks);
 
-static void lock_existing_hooks(void)
-{
-       struct lsm_info *info;
-
-       /*
-        * Prevent module unloading while we're doing this
-        * try_module_get may fail (safely), if the module
-        * is already unloading -- allow that.
-        */
-       mutex_lock(&module_mutex);
-       mutex_lock(&lsm_info_lock);
-       pr_info("Locking security hooks\n");
-
-       hlist_for_each_entry(info, &lsm_info_head, list)
-               WARN_ON(!try_module_get(info->owner));
-
-       mutex_unlock(&lsm_info_lock);
-       mutex_unlock(&module_mutex);
Quoted 8 lines

Review void security_delete_hooks(struct lsm_info *info) · line 159

Quoted 1 line
(quoted diff) +0 -1
-}

Why do you think we should remove this code? I think that it's valuable to keep in there because it prevents an administrator from accidentally panicing the system. For example, if you have admin A lock the security hooks, and then admin B comes along and tries to unload the module, IMHO they shouldn't get a panic, and by default (unless they rmmod -f) they should just a nice warning that suggests they're doing something wrong (like the module is in use).

What I suggested is

        /* Doing init or already dying? */
        if (mod->state != MODULE_STATE_LIVE) {
                /* FIXME: if (force), slam module count damn the torpedoes */
                pr_debug("%s already dying\n", mod->name);
                ret = -EBUSY;
                goto out;
        }
(quoted diff) +3 -0
+       ret = security_remove_module(mod);
+       if (ret)
+               goto out;
        /* If it has an init func, it must have an exit func to unload */
        if (mod->init && !mod->exit) {
                forced = try_force_unload(flags);

and check like

int security_remove_module(struct module *mod)

{
        struct lsm_info *info;
        int ret = 0;

        if (security_allow_unregister_hooks)
                return 0;

        mutex_lock(&lsm_info_lock);
        hlist_for_each_entry(info, &lsm_info_head, list)
                if (mod == info->owner)
                        ret = -EPERM;
        mutex_unlock(&lsm_info_lock);
        return ret;
}

rather than using register_module_notifier().

Quoted 5 lines

Thanks for the feedback. I think we're getting there at last.

I'd like Casey and James' feedback as well. I think the big questions are:
1) Can we assume that LSM authors will not shoot themselves in the foot and break major LSMs?

What "break major LSMs" means? Regardless of whether LKM-based LSMs use inode->i_security, we need to handle race condition described above.

Quoted 1 line

2) Should we protect the administrator from themselves (try_module_get)?

What I suggested is "not to play with module usage count".

Quoted 5 lines

3) Is it okay to allow hooks to be unloaded at all?

I think no matter what the answers to 1-3 are, we can apply patch 1, 2, and 3 once I include the fix-ups that you suggested here.

Quoted 6 lines
Quoted 2 lines

Patch 4 is heavily dependent on the answer to (1), and patches 6 is heavily dependent on the answer to (3).

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html'

What about something as stupid as:

security/security.c +2 -1
index 36065128c6c5..895bdb0b1381 100644
@@ -339,7 +339,8 @@ static void security_add_hook(struct
 security_hook_list *hook,
  * Each LSM has to register its hooks with the infrastructure.
  * Return 0 on success
  */
-int __must_check security_add_hooks(struct lsm_info *info, bool is_mutable)
+int __must_check security_add_hooks(struct lsm_info *info, bool is_mutable,
+                                       bool is_major)
 {
        struct security_hook_heads *heads;
        int i, ret = 0;
security/security.c +3 -0
@@ -348,6 +349,9 @@ int __must_check security_add_hooks(struct
 lsm_info *info, bool is_mutable)
        if (IS_ERR(heads))
                return PTR_ERR(heads);
 
+       if (!info->owner && is_major)
+               return -EPERM;
+
        if (mutex_lock_killable(&lsm_info_lock))
                return -EINTR;

OR

include/linux/lsm_hooks.h +8 -6
index 5c227bbb2883..9cec723e7cea 100644
@@ -2011,6 +2011,7 @@ struct lsm_info {
        const unsigned int              count;
        struct security_hook_list       *hooks;
        struct module                   *owner;
+       bool                            is_major;
 } __randomize_layout;
 
 struct security_hook_list {
@@ -2035,12 +2036,13 @@ struct security_hook_list {
                .hook   = { .HEAD = HOOK }      \
        }
 
-#define LSM_MODULE_INIT(NAME, HOOKS)           \
-       {                                       \
-               .name   = NAME,                 \
-               .hooks  = HOOKS,                \
-               .count  = ARRAY_SIZE(HOOKS),    \
-               .owner  = THIS_MODULE,          \
+#define LSM_MODULE_INIT(NAME, HOOKS, IS_MAJOR)         \
+       {                                               \
+               .name           = NAME,                 \
+               .hooks          = HOOKS,                \
+               .count          = ARRAY_SIZE(HOOKS),    \
+               .owner          = THIS_MODULE,          \
+               .is_major       = IS_MAJOR,             \
        }
 
 extern int __must_check security_add_hooks(struct lsm_info *lsm,
security/security.c +4 -0
index 36065128c6c5..a3bfe735c0e2 100644
@@ -80,6 +80,8 @@ void __security_delete_hooks(struct lsm_info *info)
 
        mutex_lock(&lsm_info_lock);
        hlist_del(&info->list);
+       if (info->is_major)
+               major_modules_loaded--;
        mutex_unlock(&lsm_info_lock);
 }
 #endif
@@ -331,6 +333,8 @@ static void security_add_hook(struct
 security_hook_list *hook,
        head = (struct hlist_head *)(heads) + idx;
        hlist_add_tail_rcu(&hook->list, head);
 }
+static int major_modules_loaded;
+
 /**
  * security_add_hooks - Add a modules hooks to the hook lists.

* @lsm_info: The lsm_info struct for this security module

security/security.c +6 -0
@@ -351,6 +355,12 @@ int __must_check security_add_hooks(struct
 lsm_info *info, bool is_mutable)
        if (mutex_lock_killable(&lsm_info_lock))
                return -EINTR;
 
+       if (info->is_major && major_modules_loaded) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+
        if (!atomic_read(&security_allow_unregister_hooks)) {
                /*

* Because hook deregistration is not allowed, we need to try

security/security.c int __must_check security_add_hooks(struct · line 382

Quoted 8 lines
security/security.c +2 -0
@@ -365,6 +375,8 @@ int __must_check security_add_hooks(struct
 lsm_info *info, bool is_mutable)
        for (i = 0; i < info->count; i++)
                security_add_hook(&info->hooks[i], info, heads);
        hlist_add_tail_rcu(&info->list, &lsm_info_head);
+       if (info->is_major)
+               major_modules_loaded++;
 out:
        mutex_unlock(&lsm_info_lock);

---
One other aspect that may not be obvious is on the *_alloc_*, callbacks, we wouldn't want to call callback's of LSMs which are non-major.
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

maintainer
#15 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 5 lines · Sargun Dhillon
Quoted 5 lines

inode_free_security is called on all LSM callbacks -- so, it should be, as long as people don't load unloadable major LSMs. If we want to be super conservative here, we can do a try_module_get, and a module_put if RC != 0 on RW hooks. So, we could have something like call_int_hook_major, and call_void_hook_major.

I don't think we want to play with module usage count.

Quoted 25 lines
Quoted 25 lines
Quoted 25 lines
        }
        /*** End of changes made by this series ***/
        return 0;
}

and what Casey's series is doing is

int security_inode_alloc(struct inode *inode)

{
        struct security_hook_list *P;
        inode->i_security = NULL;
        hlist_for_each_entry(P, &security_hook_heads.inode_alloc_security, list) {
                int RC = P->hook.inode_alloc_security(inode);
                if (RC != 0) {
                        /*** Start of changes made by Casey's series ***/
                        hlist_for_each_entry(P, &security_hook_heads.inode_free_security, list) {
                                P->hook.inode_free_security(inode); // <= Might call inode_free_security() without corresponding successful inode_alloc_security().
                        }
                        /*** End of changes made by Casey's series ***/
                        return RC;
                }
        }
        return 0;
}
Quoted 5 lines
Quoted 5 lines

Right, but this could be taken care of by just ensuring that nobody allocates blobs (major LSM), and only one LSM returns a non-zero to the *alloc* callbacks? Right now, we have this because one has to be a "major" LSM in order to use these callbacks, and we ensure only one major LSM is active at a time.

What Casey is trying to do is allow multiple major modules to use their own blob. Thus,

Quoted 3 lines
Quoted 3 lines

I suggest that either in the short term we:
1) Trust people not to load a second major LSM,

this is not an option.

Quoted 3 lines
Quoted 3 lines

2) See below.

What about something as stupid as:

I don't think we want to do this.

Quoted 3 lines

One other aspect that may not be obvious is on the *_alloc_*, callbacks, we wouldn't want to call callback's of LSMs which are non-major.

What both your series and Casey's series are missing will be as simple as below. Apart from whether memory for security blobs should be managed by infrastructure or by individual module, we will need to do something like below.

PATCH 1/2: Baseline for allowing safe runtime registration.

This patch allows multiple major LSM modules to safely manage their blobs (though memory for security blobs need to be managed by individual module), by calling only release hooks where corresponding alloc hook succeeded when one of alloc hook returned an error. And thereby this patch makes it possible to load LKM-based LSM modules.

---
 include/linux/lsm_hooks.h                     |  21 +--
 scripts/gcc-plugins/randomize_layout_plugin.c |   2 -
 security/Kconfig                              |  14 +-
 security/security.c                           | 184 +++++++++++++++++---------
 4 files changed, 148 insertions(+), 73 deletions(-)
include/linux/lsm_hooks.h +11 -10
index 9d0b286..083dea6 100644
@@ -2005,10 +2005,10 @@ struct security_hook_heads {
  * For use with generic list macros for common operations.
  */
 struct security_hook_list {
-	struct hlist_node		list;
-	struct hlist_head		*head;
-	union security_list_options	hook;
-	char				*lsm;
+	struct hlist_node			list;
+	const unsigned int			offset;
+	const union security_list_options	hook;
+	const char				*lsm;
 } __randomize_layout;
 
 /*
@@ -2017,14 +2017,16 @@ struct security_hook_list {
  * care of the common case and reduces the amount of
  * text involved.
  */
-#define LSM_HOOK_INIT(HEAD, HOOK) \
-	{ .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } }
+#define LSM_HOOK_INIT(HEAD, HOOK)				\
+	{							\
+		.offset = offsetof(struct security_hook_heads, HEAD),	\
+		.hook = { .HEAD = HOOK }				\
+	}
 
-extern struct security_hook_heads security_hook_heads;
 extern char *lsm_names;
 
-extern void security_add_hooks(struct security_hook_list *hooks, int count,
-				char *lsm);
+extern int security_add_hooks(struct security_hook_list *hooks, int count,
+			      const char *lsm);
 
 #ifdef CONFIG_SECURITY_SELINUX_DISABLE
 /*
@@ -2049,7 +2051,6 @@ static inline void security_delete_hooks(struct security_hook_list *hooks,
 }
 #endif /* CONFIG_SECURITY_SELINUX_DISABLE */
 
-/* Currently required to handle SELinux runtime hook disable. */
 #ifdef CONFIG_SECURITY_WRITABLE_HOOKS
 #define __lsm_ro_after_init
 #else
scripts/gcc-plugins/randomize_layout_plugin.c +0 -2
index 6d5bbd3..d941389 100644
@@ -52,8 +52,6 @@ struct whitelist_entry {
 	{ "net/unix/af_unix.c", "unix_skb_parms", "char" },
 	/* big_key payload.data struct splashing */
 	{ "security/keys/big_key.c", "path", "void *" },
-	/* walk struct security_hook_heads as an array of struct hlist_head */
-	{ "security/security.c", "hlist_head", "security_hook_heads" },
 	{ }
 };
 
security/Kconfig +13 -1
index c430206..84342d1 100644
@@ -277,5 +277,17 @@ config DEFAULT_SECURITY
 	default "apparmor" if DEFAULT_SECURITY_APPARMOR
 	default "" if DEFAULT_SECURITY_DAC
 
-endmenu
+config SECURITY_RUNTIME_LOADING
+	bool "Allow adding security modules after boot"
+	depends on SECURITY
+	select SECURITY_WRITABLE_HOOKS
+	default n
+	help
+	  This option allows adding security modules which are implemented as
+	  loadable kernel modules after boot.
+
+	  NOTE: selecting this option will disable the '__ro_after_init'
+	  kernel hardening feature for security hooks. If you are unsure
+	  how to answer this question, answer N.
 
+endmenu
security/security.c +124 -60

354 lines not on this page · raw message

Signature · 21 lines
1.8.3.1


PATCH 2/2: Baseline for allowing safe runtime unregistration.

This patch makes it possible to unload LKM-based LSM modules, by using
another list for closing possible race window that release hooks are
by error called without corresponding successful alloc hook calls.

Though, it is questionable whether we want to support it at infrastructure
level. We can use a stacking LSM module which allows stacked LSM modules to
register/unregister. In that way, we can avoid lock_lsm()/unlock_lsm() at
infrastructure level. The stacking approach could also be applied for
separating mutable and immutable hooks. That is, the stacking LSM module
itself is chained to immutable (__ro_after_init) hook, and LKM-based LSMs
are chained to mutable (!__ro_after_init) hook.
---
 include/linux/lsm_hooks.h | 27 +++-----------
 security/Kconfig          |  8 +++++
 security/security.c       | 89 +++++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 94 insertions(+), 30 deletions(-)
include/linux/lsm_hooks.h +4 -23
index 083dea6..548e00b 100644
@@ -2006,6 +2006,9 @@ struct security_hook_heads {
  */
 struct security_hook_list {
 	struct hlist_node			list;
+#if defined(CONFIG_SECURITY_SELINUX_DISABLE) || defined(CONFIG_SECURITY_RUNTIME_UNLOADING)
+	struct hlist_node			undo_list;
+#endif
 	const unsigned int			offset;
 	const union security_list_options	hook;
 	const char				*lsm;
@@ -2027,29 +2030,7 @@ struct security_hook_list {
 
 extern int security_add_hooks(struct security_hook_list *hooks, int count,
 			      const char *lsm);
-
-#ifdef CONFIG_SECURITY_SELINUX_DISABLE
-/*
- * Assuring the safety of deleting a security module is up to
- * the security module involved. This may entail ordering the
- * module's hook list in a particular way, refusing to disable
- * the module once a policy is loaded or any number of other
- * actions better imagined than described.
- *
- * The name of the configuration option reflects the only module
- * that currently uses the mechanism. Any developer who thinks
- * disabling their module is a good idea needs to be at least as
- * careful as the SELinux team.
- */
-static inline void security_delete_hooks(struct security_hook_list *hooks,
-						int count)
-{
-	int i;
-
-	for (i = 0; i < count; i++)
-		hlist_del_rcu(&hooks[i].list);
-}
-#endif /* CONFIG_SECURITY_SELINUX_DISABLE */
+extern void security_delete_hooks(struct security_hook_list *hooks, int count);
 
 #ifdef CONFIG_SECURITY_WRITABLE_HOOKS
 #define __lsm_ro_after_init
security/Kconfig +8 -0
index 84342d1..9be8788 100644
@@ -290,4 +290,12 @@ config SECURITY_RUNTIME_LOADING
 	  kernel hardening feature for security hooks. If you are unsure
 	  how to answer this question, answer N.
 
+config SECURITY_RUNTIME_UNLOADING
+	bool "Allow removing security modules after boot"
+	depends on SECURITY_RUNTIME_LOADING && SRCU
+	default n
+	help
+	  This option allows removing security modules which are implemented as
+	  loadable kernel modules after boot.
+
 endmenu
security/security.c +82 -7

228 lines not on this page · raw message

Signature · 5 lines
1.8.3.1
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#16 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 40 lines · Tetsuo Handa
Quoted 5 lines · Sargun Dhillon
Quoted 5 lines

inode_free_security is called on all LSM callbacks -- so, it should be, as long as people don't load unloadable major LSMs. If we want to be super conservative here, we can do a try_module_get, and a module_put if RC != 0 on RW hooks. So, we could have something like call_int_hook_major, and call_void_hook_major.

I don't think we want to play with module usage count.

Quoted 25 lines
Quoted 25 lines
Quoted 25 lines
        }
        /*** End of changes made by this series ***/
        return 0;
}

and what Casey's series is doing is

int security_inode_alloc(struct inode *inode)

{
        struct security_hook_list *P;
        inode->i_security = NULL;
        hlist_for_each_entry(P, &security_hook_heads.inode_alloc_security, list) {
                int RC = P->hook.inode_alloc_security(inode);
                if (RC != 0) {
                        /*** Start of changes made by Casey's series ***/
                        hlist_for_each_entry(P, &security_hook_heads.inode_free_security, list) {
                                P->hook.inode_free_security(inode); // <= Might call inode_free_security() without corresponding successful inode_alloc_security().
                        }
                        /*** End of changes made by Casey's series ***/
                        return RC;
                }
        }
        return 0;
}
Quoted 5 lines
Quoted 5 lines

Right, but this could be taken care of by just ensuring that nobody allocates blobs (major LSM), and only one LSM returns a non-zero to the *alloc* callbacks? Right now, we have this because one has to be a "major" LSM in order to use these callbacks, and we ensure only one major LSM is active at a time.

Quoted 9 lines

What Casey is trying to do is allow multiple major modules to use their own blob. Thus,

Quoted 3 lines
Quoted 3 lines

I suggest that either in the short term we:
1) Trust people not to load a second major LSM,

this is not an option.

This is exactly what people do today?

Quoted 6 lines
Quoted 3 lines
Quoted 3 lines

2) See below.

What about something as stupid as:

I don't think we want to do this.

We have the limit today of not allowing people to load two major LSMs. Why not wait till later to solve this problem, and for now, reject when people install two major LSMs? I think we can fix the dynamic loading problem _first_ and the multiple major LSM problem _second_

Quoted 3 lines
Quoted 3 lines

One other aspect that may not be obvious is on the *_alloc_*, callbacks, we wouldn't want to call callback's of LSMs which are non-major.

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

maintainer
#17 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 9 lines · Sargun Dhillon
Quoted 6 lines · Tetsuo Handa

Sargun Dhillon wrote:
...

Quoted 2 lines
Quoted 2 lines

I suggest that either in the short term we:
1) Trust people not to load a second major LSM,

this is not an option.

This is exactly what people do today?

The existing code provides no mechanism whereby multiple "major" modules can be used at the same time. If you added a "minor" module, and it used security blobs Bad Things(tm) could happen.

Quoted 9 lines
Quoted 5 lines
Quoted 3 lines
Quoted 3 lines

2) See below.

What about something as stupid as:

I don't think we want to do this.

We have the limit today of not allowing people to load two major LSMs. Why not wait till later to solve this problem, and for now, reject when people install two major LSMs? I think we can fix the dynamic loading problem _first_ and the multiple major LSM problem _second_

I think that we're on the verge of having a major merge collision. I hope to have the multiple major module code seriously reviewed as of 4.18 and start putting real pressure on getting it in for 4.19/4.20. The advent of the Age of Containers is driving this.

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

maintainer
#18 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 1 line · Casey Schaufler

I think that we're on the verge of having a major merge collision.

I think that the reason of major merge collision is that your patchset is too big to review.

Quoted 6 lines

That's a lot of overhead. The smaller the patches, the more work required to make the patch set. You're right that the current patches are too big. Moving forward I will be providing smaller, easier to deal with patches. The sheer overhead of rebaseing a large patch set has slowed down the development considerably. Minions. What I need are minions.

You are holding the global lock for patchset which you did not get response. If you agree with breaking into smaller patches, we will be able to solve the major merge collision.

Quoted 2 lines

I hope to have the multiple major module code seriously reviewed as of 4.18 and start putting real pressure on getting it in for 4.19/4.20.

Before you repost your patchset, please answer questions below.

Q1: Apart from whether memory for security blobs should be managed by
infrastructure or by individual module, how do you guarantee that only release hook where corresponding alloc hook succeeded when one of alloc hooks returned an error?

My patch 1/2 provides that mechanism. Please show your patch which implements only that functionality instead of whole patchset.

Quoted 10 lines

It is my firm hope that the multiple major module changes are going to start being seriously considered in the next release or two. That would reduce the complexity of what you're trying to accomplish because at the point all modules will be equal. I have always committed to making design choices that aren't going to prevent loadable/unloadable modules. I have also expressed no interest in doing it myself. From my selfish perspective I would like to see module loading follow my work, as having yet another major merge effort will delay the clean-up I know I'll have to do after 4.18.

Depending on your patch, it is possible that managing memory for security blobs by infrastructure can become harmful for LKM-based LSMs, for the amount of memory which will be needed by LKM-based LSMs is not known until LKM-based LSMs are loaded.

LKM-based LSMs might be forced to manage their security blobs without using infrastructure-managed ->security field. Then, that will not make "all modules equal".

So, please show your approach which allows LKM-based LSMs to handle infrastructure-managed ->security field (if you want to use infrastructure-managed ->security field).

Quoted 1 line

The advent of the Age of Containers is driving this.

I've been asking for runtime loading of LSMs before the Age of Containers comes. ;-)

Q2: What functionality are Containers people want LSM?
Use different LSM module for different container?
Runtime load/unload of LSM modules?
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

maintainer
#19 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 4 lines · Tetsuo Handa
Quoted 1 line · Casey Schaufler

I think that we're on the verge of having a major merge collision.

I think that the reason of major merge collision is that your patchset is too big to review.

My patch set is large because it has to address all of the aspects of multiple modules running on the system before some important members of the community will look at it seriously.

Quoted 11 lines
Quoted 7 lines

That's a lot of overhead. The smaller the patches, the more work required to make the patch set. You're right that the current patches are too big. Moving forward I will be providing smaller, easier to deal with patches. The sheer overhead of rebaseing a large patch set has slowed down the development considerably. Minions. What I need are minions.

You are holding the global lock for patchset which you did not get response. If you agree with breaking into smaller patches, we will be able to solve the major merge collision.

I will be presenting the set of smaller patches once I finish merging them into 4.18. It's a bigger jobs than usual because of the change from lists to hlists, the significant changes to AppArmor and the early stages of SELinux namespacing. I hope to have it done mid week, but the changes need to get tested, and there are lots of configurations involved.

I am perfectly amenable to the patches going in in logical stages. In fact, that is what I would recommend.

Quoted 8 lines
Quoted 2 lines

I hope to have the multiple major module code seriously reviewed as of 4.18 and start putting real pressure on getting it in for 4.19/4.20.

Before you repost your patchset, please answer questions below.

Q1: Apart from whether memory for security blobs should be managed by
infrastructure or by individual module, how do you guarantee that only release hook where corresponding alloc hook succeeded when one of alloc hooks returned an error?

The infrastructure allocates a zeroed blob. Module "alloc" hooks are called. The module specific hooks set their data as desired. If a module alloc hook fails the complete set of free hooks can be called because the module data will either have been set or will be still be zeroed. This means that module free hooks have to accommodate the possibility the the module specific data was never set.

The reality is that many of the free hooks go away with the infrastructure blob management. Those that remain can easily be confirmed to be safe in the case where all the modules data is NULL.
?

Quoted 2 lines

My patch 1/2 provides that mechanism. Please show your patch which implements only that functionality instead of whole patchset.

It's not an explicit change. It is an artifact of the infrastructure allocating and freeing the data. You can't tease the removal of the module free hooks apart from the infrastructure freeing the data.

Quoted 11 lines
Quoted 10 lines

It is my firm hope that the multiple major module changes are going to start being seriously considered in the next release or two. That would reduce the complexity of what you're trying to accomplish because at the point all modules will be equal. I have always committed to making design choices that aren't going to prevent loadable/unloadable modules. I have also expressed no interest in doing it myself. From my selfish perspective I would like to see module loading follow my work, as having yet another major merge effort will delay the clean-up I know I'll have to do after 4.18.

Quoted 4 lines

Depending on your patch, it is possible that managing memory for security blobs by infrastructure can become harmful for LKM-based LSMs, for the amount of memory which will be needed by LKM-based LSMs is not known until LKM-based LSMs are loaded.

It's certainly not more "harmful" than the single blob mechanism we have now. The patches I'm proposing don't make the situation any worse than it is today. As I've maintained all along, I'm not trying to address LKM-based modules.

There is no reason I can see that blob sizes couldn't change while a system is running (yes, I have looked into it) provided that the blob size and content list is recorded in the blob and that the dynamic modules check this information before trying to use it.

	smack_inode(struct inode *inode)
	{
		return inode->i_security + smack_blob_sizes.lbs_inode;
	}

	lkm_based_module_inode(struct inode *inode)
	{
		struct lsm_inode_blob_header *ibh = inode->security;
		if (some_appropriate_check(ibh, LKM_BASED_MODULE_ID))
			return inode->i_security + lkm_based_module_blob_sizes.lbs_inode;
		/* No blob for this object */
		return NULL;
	}

A refinement to this might keep the offset for dynamic module data in the blob header. That would be more flexible, but would consume more memory for each blob.

Quoted 3 lines

LKM-based LSMs might be forced to manage their security blobs without using infrastructure-managed ->security field. Then, that will not make "all modules equal".

Regardless of how the data is managed the information about how much is being used and where to find it has to be kept somewhere. With static modules it is easier because you only need to figure that out once, at the beginning. If you're going to have blob data changing you have to look it up or figure it out at every reference. It's not impossible. It's not even really that hard, but it won't be cheap.

Quoted 3 lines

So, please show your approach which allows LKM-based LSMs to handle infrastructure-managed ->security field (if you want to use infrastructure-managed ->security field).

I'll repeat, I don't have code to do it because I have explicitly excluded lkm-based modules from my todo list. If I were doing it, I would propose an infrastructure managed header in the security blobs containing sufficient information for the modules to determine if the data they are looking for is present. lkm-based modules would have to be coded to accept that they might get blobs that were created before the module was loaded, and that do not contain data for that module.

The blob header mechanism would only be included if lkm-based security module support was configured.

Quoted 3 lines
Quoted 1 line

The advent of the Age of Containers is driving this.

I've been asking for runtime loading of LSMs before the Age of Containers comes. ;-)

Yes, you have!

Quoted 1 line

Q2: What functionality are Containers people want LSM?

It varies. Container people are a diverse lot.

Quoted 1 line

Use different LSM module for different container?

That is definitely one use case. You might run Smack on the base system and label each container based on who payed for it, and let the security module set within the container be whatever the job demands.

Quoted 1 line

Runtime load/unload of LSM modules?

I would see that as something you might do on container startup, but managing the module set could get ugly.

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

#20 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 22 lines · Casey Schaufler
Quoted 4 lines · Tetsuo Handa
Quoted 1 line · Casey Schaufler

I think that we're on the verge of having a major merge collision.

I think that the reason of major merge collision is that your patchset is too big to review.

My patch set is large because it has to address all of the aspects of multiple modules running on the system before some important members of the community will look at it seriously.

Quoted 11 lines
Quoted 7 lines

That's a lot of overhead. The smaller the patches, the more work required to make the patch set. You're right that the current patches are too big. Moving forward I will be providing smaller, easier to deal with patches. The sheer overhead of rebaseing a large patch set has slowed down the development considerably. Minions. What I need are minions.

You are holding the global lock for patchset which you did not get response. If you agree with breaking into smaller patches, we will be able to solve the major merge collision.

Quoted 9 lines

I will be presenting the set of smaller patches once I finish merging them into 4.18. It's a bigger jobs than usual because of the change from lists to hlists, the significant changes to AppArmor and the early stages of SELinux namespacing. I hope to have it done mid week, but the changes need to get tested, and there are lots of configurations involved.

I am perfectly amenable to the patches going in in logical stages. In fact, that is what I would recommend.

I guess I'm just a little bit frustrated, because, in my mind, some of my patches provide immediate value, and are ready to be reviewed, and or respun. Testuo did a good job reviewing 1-5, and I think that if those are his only issues, then we're good to go.
I've:
1) Do safe hook loading for sane security modules (those that don't try to overload major hooks), without compromising the security of other hooks
2) Suggested a whitelist of major hooks to prevent insane modules from being loaded

If Casey's patches land, we can just not do (2), and we're good to go.

Quoted 103 lines
Quoted 8 lines
Quoted 2 lines

I hope to have the multiple major module code seriously reviewed as of 4.18 and start putting real pressure on getting it in for 4.19/4.20.

Before you repost your patchset, please answer questions below.

Q1: Apart from whether memory for security blobs should be managed by
infrastructure or by individual module, how do you guarantee that only release hook where corresponding alloc hook succeeded when one of alloc hooks returned an error?

The infrastructure allocates a zeroed blob. Module "alloc" hooks are called. The module specific hooks set their data as desired. If a module alloc hook fails the complete set of free hooks can be called because the module data will either have been set or will be still be zeroed. This means that module free hooks have to accommodate the possibility the the module specific data was never set.

The reality is that many of the free hooks go away with the infrastructure blob management. Those that remain can easily be confirmed to be safe in the case where all the modules data is NULL.

Quoted 2 lines

My patch 1/2 provides that mechanism. Please show your patch which implements only that functionality instead of whole patchset.

It's not an explicit change. It is an artifact of the infrastructure allocating and freeing the data. You can't tease the removal of the module free hooks apart from the infrastructure freeing the data.

Quoted 11 lines
Quoted 10 lines

It is my firm hope that the multiple major module changes are going to start being seriously considered in the next release or two. That would reduce the complexity of what you're trying to accomplish because at the point all modules will be equal. I have always committed to making design choices that aren't going to prevent loadable/unloadable modules. I have also expressed no interest in doing it myself. From my selfish perspective I would like to see module loading follow my work, as having yet another major merge effort will delay the clean-up I know I'll have to do after 4.18.

Quoted 4 lines

Depending on your patch, it is possible that managing memory for security blobs by infrastructure can become harmful for LKM-based LSMs, for the amount of memory which will be needed by LKM-based LSMs is not known until LKM-based LSMs are loaded.

It's certainly not more "harmful" than the single blob mechanism we have now. The patches I'm proposing don't make the situation any worse than it is today. As I've maintained all along, I'm not trying to address LKM-based modules.

There is no reason I can see that blob sizes couldn't change while a system is running (yes, I have looked into it) provided that the blob size and content list is recorded in the blob and that the dynamic modules check this information before trying to use it.

        smack_inode(struct inode *inode)
        {
                return inode->i_security + smack_blob_sizes.lbs_inode;
        }

        lkm_based_module_inode(struct inode *inode)
        {
                struct lsm_inode_blob_header *ibh = inode->security;
                if (some_appropriate_check(ibh, LKM_BASED_MODULE_ID))
                        return inode->i_security + lkm_based_module_blob_sizes.lbs_inode;
                /* No blob for this object */
                return NULL;
        }

A refinement to this might keep the offset for dynamic module data in the blob header. That would be more flexible, but would consume more memory for each blob.

Quoted 3 lines

LKM-based LSMs might be forced to manage their security blobs without using infrastructure-managed ->security field. Then, that will not make "all modules equal".

Regardless of how the data is managed the information about how much is being used and where to find it has to be kept somewhere. With static modules it is easier because you only need to figure that out once, at the beginning. If you're going to have blob data changing you have to look it up or figure it out at every reference. It's not impossible. It's not even really that hard, but it won't be cheap.

Quoted 3 lines

So, please show your approach which allows LKM-based LSMs to handle infrastructure-managed ->security field (if you want to use infrastructure-managed ->security field).

I'll repeat, I don't have code to do it because I have explicitly excluded lkm-based modules from my todo list. If I were doing it, I would propose an infrastructure managed header in the security blobs containing sufficient information for the modules to determine if the data they are looking for is present. lkm-based modules would have to be coded to accept that they might get blobs that were created before the module was loaded, and that do not contain data for that module.

The blob header mechanism would only be included if lkm-based security module support was configured.

Quoted 1 line
Quoted 1 line

The advent of the Age of Containers is driving this.

Quoted 11 lines
Quoted 2 lines

I've been asking for runtime loading of LSMs before the Age of Containers comes. ;-)

Yes, you have!

Quoted 1 line

Q2: What functionality are Containers people want LSM?

It varies. Container people are a diverse lot.

Quoted 1 line

Use different LSM module for different container?

One dumb use case I have is the ability to limit interactions with PTYs for containerized applications. Another aspect of this is being able to write policies in C, and actually being able to control the nitty gritty of what's going on, versus actively fighting with the LSM(s).

Quoted 10 lines

That is definitely one use case. You might run Smack on the base system and label each container based on who payed for it, and let the security module set within the container be whatever the job demands.

Quoted 1 line

Runtime load/unload of LSM modules?

I would see that as something you might do on container startup, but managing the module set could get ugly.

I can forgo this, if neccessary. I see benefits around it for surgical fixes, specifically, CVEs, but I can understand if it's not worth the complexity tradeoff.
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

maintainer
#21 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 40 lines · Sargun Dhillon
Quoted 19 lines · Casey Schaufler
Quoted 4 lines · Tetsuo Handa
Quoted 1 line · Casey Schaufler

I think that we're on the verge of having a major merge collision.

I think that the reason of major merge collision is that your patchset is too big to review.

My patch set is large because it has to address all of the aspects of multiple modules running on the system before some important members of the community will look at it seriously.

Quoted 10 lines
Quoted 6 lines

That's a lot of overhead. The smaller the patches, the more work required to make the patch set. You're right that the current patches are too big. Moving forward I will be providing smaller, easier to deal with patches. The sheer overhead of rebaseing a large patch set has slowed down the development considerably. Minions. What I need are minions.

You are holding the global lock for patchset which you did not get response. If you agree with breaking into smaller patches, we will be able to solve the major merge collision.

Quoted 9 lines

I will be presenting the set of smaller patches once I finish merging them into 4.18. It's a bigger jobs than usual because of the change from lists to hlists, the significant changes to AppArmor and the early stages of SELinux namespacing. I hope to have it done mid week, but the changes need to get tested, and there are lots of configurations involved.

I am perfectly amenable to the patches going in in logical stages. In fact, that is what I would recommend.

I guess I'm just a little bit frustrated, because, in my mind, some of my patches provide immediate value, and are ready to be reviewed, and or respun. Testuo did a good job reviewing 1-5, and I think that if those are his only issues, then we're good to go.
I've:
1) Do safe hook loading for sane security modules (those that don't try to overload major hooks), without compromising the security of other hooks
2) Suggested a whitelist of major hooks to prevent insane modules from being loaded

Quoted 1 line

If Casey's patches land, we can just not do (2), and we're good to go.

I understand your frustration. As much as I would like to avoid having the stacking work take any longer than it already has (I *do* have a boatload of other stuff I want to get done) I suggest that if you can get consensus that the lkm-based security module work should be accepted that you go forward and get it in. I know that I have at least one major round of review before the stacking can really be seriously considered. I knew the job was dangerous when I took it.

Quoted 6 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line

...

One dumb use case I have is the ability to limit interactions with PTYs for containerized applications. Another aspect of this is being able to write policies in C, and actually being able to control the nitty gritty of what's going on, versus actively fighting with the LSM(s).

You don't need lkm-based modules to do that, but I can see why you might want to do it that way. It's also something that I would expect LandLock to be a solution for, but again you'd need to be running LandLock when you identified the issue.

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

#22 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 43 lines · Casey Schaufler
Quoted 40 lines · Sargun Dhillon
Quoted 19 lines · Casey Schaufler
Quoted 4 lines · Tetsuo Handa
Quoted 1 line · Casey Schaufler

I think that we're on the verge of having a major merge collision.

I think that the reason of major merge collision is that your patchset is too big to review.

My patch set is large because it has to address all of the aspects of multiple modules running on the system before some important members of the community will look at it seriously.

Quoted 10 lines
Quoted 6 lines

That's a lot of overhead. The smaller the patches, the more work required to make the patch set. You're right that the current patches are too big. Moving forward I will be providing smaller, easier to deal with patches. The sheer overhead of rebaseing a large patch set has slowed down the development considerably. Minions. What I need are minions.

You are holding the global lock for patchset which you did not get response. If you agree with breaking into smaller patches, we will be able to solve the major merge collision.

Quoted 9 lines

I will be presenting the set of smaller patches once I finish merging them into 4.18. It's a bigger jobs than usual because of the change from lists to hlists, the significant changes to AppArmor and the early stages of SELinux namespacing. I hope to have it done mid week, but the changes need to get tested, and there are lots of configurations involved.

I am perfectly amenable to the patches going in in logical stages. In fact, that is what I would recommend.

I guess I'm just a little bit frustrated, because, in my mind, some of my patches provide immediate value, and are ready to be reviewed, and or respun. Testuo did a good job reviewing 1-5, and I think that if those are his only issues, then we're good to go.
I've:
1) Do safe hook loading for sane security modules (those that don't try to overload major hooks), without compromising the security of other hooks
2) Suggested a whitelist of major hooks to prevent insane modules from being loaded

Quoted 1 line

If Casey's patches land, we can just not do (2), and we're good to go.

Quoted 10 lines

I understand your frustration. As much as I would like to avoid having the stacking work take any longer than it already has (I *do* have a boatload of other stuff I want to get done) I suggest that if you can get consensus that the lkm-based security module work should be accepted that you go forward and get it in. I know that I have at least one major round of review before the stacking can really be seriously considered. I knew the job was dangerous when I took it.

Curious, consensus from whom? I thought you owned this part of the tree.

Quoted 7 lines
Quoted 6 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line

...

One dumb use case I have is the ability to limit interactions with PTYs for containerized applications. Another aspect of this is being able to write policies in C, and actually being able to control the nitty gritty of what's going on, versus actively fighting with the LSM(s).

Quoted 5 lines

You don't need lkm-based modules to do that, but I can see why you might want to do it that way. It's also something that I would expect LandLock to be a solution for, but again you'd need to be running LandLock when you identified the issue.

I've been a long-time proponent of programmable LSMs (before it was cool): https://lkml.org/lkml/2016/8/4/58

Do you know where Landlock is in terms of getting merged?
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

maintainer
#23 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 53 lines · Sargun Dhillon
Quoted 42 lines · Casey Schaufler
Quoted 40 lines · Sargun Dhillon
Quoted 19 lines · Casey Schaufler
Quoted 4 lines · Tetsuo Handa
Quoted 1 line · Casey Schaufler

I think that we're on the verge of having a major merge collision.

I think that the reason of major merge collision is that your patchset is too big to review.

My patch set is large because it has to address all of the aspects of multiple modules running on the system before some important members of the community will look at it seriously.

Quoted 10 lines
Quoted 6 lines

That's a lot of overhead. The smaller the patches, the more work required to make the patch set. You're right that the current patches are too big. Moving forward I will be providing smaller, easier to deal with patches. The sheer overhead of rebaseing a large patch set has slowed down the development considerably. Minions. What I need are minions.

You are holding the global lock for patchset which you did not get response. If you agree with breaking into smaller patches, we will be able to solve the major merge collision.

Quoted 9 lines

I will be presenting the set of smaller patches once I finish merging them into 4.18. It's a bigger jobs than usual because of the change from lists to hlists, the significant changes to AppArmor and the early stages of SELinux namespacing. I hope to have it done mid week, but the changes need to get tested, and there are lots of configurations involved.

I am perfectly amenable to the patches going in in logical stages. In fact, that is what I would recommend.

I guess I'm just a little bit frustrated, because, in my mind, some of my patches provide immediate value, and are ready to be reviewed, and or respun. Testuo did a good job reviewing 1-5, and I think that if those are his only issues, then we're good to go.
I've:
1) Do safe hook loading for sane security modules (those that don't try to overload major hooks), without compromising the security of other hooks
2) Suggested a whitelist of major hooks to prevent insane modules from being loaded

Quoted 1 line

If Casey's patches land, we can just not do (2), and we're good to go.

Quoted 10 lines

I understand your frustration. As much as I would like to avoid having the stacking work take any longer than it already has (I *do* have a boatload of other stuff I want to get done) I suggest that if you can get consensus that the lkm-based security module work should be accepted that you go forward and get it in. I know that I have at least one major round of review before the stacking can really be seriously considered. I knew the job was dangerous when I took it.

Quoted 1 line

Curious, consensus from whom? I thought you owned this part of the tree.

James Morris owns the security tree in general, while various other people own the module sub-trees. Consensus would require at least James, John Johansen (AppArmor), Paul Moore (SELinux, Audit and Netlabel), Tetsuo Handa (TOMOYO), Kees Cook (Yama, LoadPin), Serge Hallyn (Capabilities), Mimi Zohar (Integrity), David Howells (Keys) and Myself (Smack). Beyond that, there will have to be buy in from many vocal developers, not the least of which is Linus.

I don't own this part of the tree, but I have fixed it up a bit.

Quoted 5 lines
Quoted 3 lines
Quoted 2 lines
Quoted 1 line
Quoted 1 line
Quoted 1 line

...

Do you know where Landlock is in terms of getting merged?

Probably behind the general module stacking. :-o
It's a prime example of a major module that would
be used in conjunction with other major modules.

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

A message that is not in the archive (sent off-list or not yet received): [email protected]

maintainer
#25 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 7 lines · Micka?l Sala?n
Quoted 2 lines

I've been a long-time proponent of programmable LSMs (before it was cool): https://lkml.org/lkml/2016/8/4/58

Happy to know that Landlock started before programmable access-control was cool, too. :)
https://lkml.kernel.org/r/1458784008-16277-1-git-send-email-mic at digikod.net

Not to brag or anything, but this was c.2000 (AD)
https://linux.die.net/man/3/libipq ;-)

Landlock is by far the better approach to this than C, and may generally solve the use-case of (un)loadable, simple, ad-hoc policies.

Signature · 2 lines
James Morris
<[email protected]>
maintainer
#26 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

In reply to Sargun Dhillon
Quoted 3 lines · Sargun Dhillon

I guess I'm just a little bit frustrated, because, in my mind, some of my patches provide immediate value, and are ready to be reviewed, and or respun.

I'm not seeing much value in this functionality, given that SELinux is the only unloadable LSM, and that is really just an historical workaround which may be normalized at some point.

Patch 1 may be useful on its own.

Signature · 7 lines
James Morris
<[email protected]>

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#27 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 11 lines · James Morris
Quoted 3 lines · Sargun Dhillon

I guess I'm just a little bit frustrated, because, in my mind, some of my patches provide immediate value, and are ready to be reviewed, and or respun.

I'm not seeing much value in this functionality, given that SELinux is the only unloadable LSM, and that is really just an historical workaround which may be normalized at some point.

Patch 1 may be useful on its own.

Do you not think patch 2 is also useful? Is it worth me re-rolling 1-2 independently?

Do you think not think that minor loadable LSMs are valuable? -- And if so, do you think it's okay with, or without guardrails?

Quoted 5 lines

--
James Morris
<[email protected]>

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

maintainer
#28 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 7 lines · Sargun Dhillon
Quoted 6 lines · James Morris
Quoted 3 lines · Sargun Dhillon

I guess I'm just a little bit frustrated, because, in my mind, some of my patches provide immediate value, and are ready to be reviewed, and or respun.

Quoted 8 lines
Quoted 5 lines

I'm not seeing much value in this functionality, given that SELinux is the only unloadable LSM, and that is really just an historical workaround which may be normalized at some point.

Patch 1 may be useful on its own.

Do you not think patch 2 is also useful? Is it worth me re-rolling 1-2 independently?

Yes, please split out the seq file change and submit them as independent changes.

Quoted 2 lines

Do you think not think that minor loadable LSMs are valuable? -- And if so, do you think it's okay with, or without guardrails?

Potentially, but we can't add infrastructure for non-existent or out of tree code.

Signature · 7 lines
James Morris
<[email protected]>

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
maintainer
#29 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

In reply to Sargun Dhillon
Quoted 9 lines · Sargun Dhillon
Quoted 4 lines

It varies. Container people are a diverse lot.

Quoted 1 line

Use different LSM module for different container?

One dumb use case I have is the ability to limit interactions with PTYs for containerized applications. Another aspect of this is being able to write policies in C, and actually being able to control the nitty gritty of what's going on, versus actively fighting with the LSM(s).

Writing policies in C sounds like a breach of the principle of separating mechanism and policy. This has been an important aspect of the LSM API and also of the major LSMs. Hard-coded security policy is likely to be brittle, inflexible, and difficult to manage. It's also blurring the boundary of LSM with the rest of the kernel, and making it even more difficult for core kernel developers to know what might break in LSM land when they make changes elsewhere.

Can you provide an example of what you want to do?

Signature · 7 lines
James Morris
<[email protected]>

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#30 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

In reply to Sargun Dhillon
Quoted 122 lines · Sargun Dhillon

The primary security benefit of this patchset is the introduction of read-only hooks, even if some security modules have mutable hooks. Currently, if you have any LSMs with mutable hooks it will render all heads, and list nodes mutable. These are a prime place to attack because being able to manipulate those hooks is a way to bypass all LSMs easily and to create a persistent, covert channel to intercept nearly all calls.

There is a shares SRCU between all security hooks to facilitate safe LSM- unloading. This SRCU is very cheap for runtime overhead on reads, but there is synchronization around it for unloads. There is only a cost to pay at unload time, which is based on the execution time of longest chain of callbacks after synchronization begins.

If CONFIG_SECURITY_WRITABLE_HOOKS is enabled, then hooks can be loaded at runtime. The module can check the return code of security_add_hooks to determine whether or not they can install their hooks, independently of checking for the Kconfig value. In the future, we may make it so that runtime hook loading is also disabled, after boot. We can do that in a follow-up patch.

If CONFIG_SECURITY_UNREGISTRABLE_HOOKS is enabled, then hooks can be unloaded at runtime. This behaviour can be disabled by setting security.allow_unregister_hooks to 0. Once set, it requires a reboot to be reset.

SELinux is exempt from the CONFIG_SECURITY_UNREGISTRABLE_HOOKS rule, and can unregister at any time. SELinux exposes a mechanism to disable itself, prior to policy loading. This can be disabled at compilation time, and it depends on CONFIG_SECURITY_WRITABLE_HOOKS. Changing this behaviour would require breaking the uapi.

This patch can allow for a new style of LSMs. There are many cases where LSM "policies" would be better defined in some formal programming language, like C. This is either due to flexibility, or performance, for functions in the hot path.

It also unlocks development usecases, but those come as a secondary benefit to the earlier feature. There has been an appetite for out-of-tree LSMs:
http://kernsec.org/pipermail/linux-security-module-archive/2017-March/000119.html
Casey's work has been great enabling minor LSM stacking, and a variety of LSMs have been proposed on list that haven't made it into the kernel but fit into the minor LSM stacking model.

One of the other big changes is the introduction of the lsm_info structure. This encapsulates various information about the LSM itself, and can be used at a later time.

This patch was tested with CONFIG_PROVE_LOCKING to prove the correctness of the locking in lock_existing_hooks. It grabs the module lock to prevent new hooks from being loaded, or current hooks from being unloaded. It then grabs the lsm_info_lock, which is only grabbed on hook modification, or inspection of the loaded LSMs.

The other thing that this introduces is monitoring of LKM unloading. If an LSM's LKM is unloaded when it is not supposed to be, it will crash the system.

Thanks to Tetsuo for their review, and feedback.

Changes since:
v6:
* introduce LSM info
* Crash by default instead of leaving it up to module authors to deal with things going wrong
* Monitor module unloading, to ensure that attempts to unload the LKM for an LSM crashes the system.
v5:
* Rename "CONFIG_SECURITY_UNLOADABLE_MODULES" to "CONFIG_SECURITY_UNREGISTRABLE_HOOKS" which enables arbitrary hook deregistration. If disabled, hooks are not allowed to deregister. SELinux is exempt.
* Rename security.allow_unload_hooks to security.allow_unregister_hooks
* Make it so that both security_add_hooks, and security_delete_hooks return errors upon being unsuccessful, allowing module authors to make the best decision for their use case.
* Get rid of LSM_HOOK_INIT_MUTABLE
v4:
* Introduce the configuration flag "CONFIG_SECURITY_UNLOADABLE_MODULES" to disable module unloading
* Introduce the kernel parameter security.allow_unload_hooks
v3:
* Instead of taking the approach of a "null hook", using the approach of a second set of hooks -- this was mostly done through the FOR_EACH_SECURITY_HOOK_MUTABLE macro, which gets compiled out if CONFIG_SECURITY_WRITABLE_HOOKS is disabled.
v2:
* Split out hlist_head patch
* Apply Tetsuo's changes to clean up functions which are not covered by call_int_hook / call_void_hook
* Disable NULL hook checking when uneeded
v1:
* Add SRCU to allow for code-unloading
* Add concurrency control around hook mutation

Sargun Dhillon (6):
security: Move LSM registration arguments to struct lsm_info
security: Make security_hook_heads private
security: Introduce mutable (RW) hooks
security: Expose security_add_hooks externally and add error handling
security: Panic on forced unloading of security module
security: Add SECURITY_UNREGISTRABLE_HOOKS to allow for hook removal

 include/linux/lsm_hooks.h                     |  67 ++--
 scripts/gcc-plugins/randomize_layout_plugin.c |   2 -
 security/Kconfig                              |  12 +
 security/apparmor/lsm.c                       |   8 +-
 security/commoncap.c                          |   8 +-
 security/inode.c                              |  56 +++-
 security/loadpin/loadpin.c                    |   6 +-
 security/security.c                           | 421 +++++++++++++++++++++-----
 security/security.h                           |  11 +
 security/selinux/hooks.c                      |  16 +-
 security/smack/smack_lsm.c                    |   5 +-
 security/tomoyo/tomoyo.c                      |   6 +-
 security/yama/yama_lsm.c                      |   6 +-
 13 files changed, 491 insertions(+), 133 deletions(-)
 create mode 100644 security/security.h
Quoted 3 lines

--
2.14.1

James, Casey,
Should I respin patches 1-5 with the fixes that Tetsuo suggested, and do you want to pick those up? It seems like other than the few errors, those look good. And then we can figure out to deal with patch 6 later?
--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html

maintainer
#31 lore raw

v7 cover Safe LSM (un)loading, and immutable hooks

Quoted 10 lines · Sargun Dhillon
Quoted 4 lines · Sargun Dhillon

The primary security benefit of this patchset is the introduction of read-only hooks, even if some security modules have mutable hooks.
...

James, Casey,
Should I respin patches 1-5 with the fixes that Tetsuo suggested, and do you want to pick those up? It seems like other than the few errors, those look good. And then we can figure out to deal with patch 6 later?

It is my firm hope that the multiple major module changes are going to start being seriously considered in the next release or two. That would reduce the complexity of what you're trying to accomplish because at the point all modules will be equal. I have always committed to making design choices that aren't going to prevent loadable/unloadable modules. I have also expressed no interest in doing it myself. From my selfish perspective I would like to see module loading follow my work, as having yet another major merge effort will delay the clean-up I know I'll have to do after 4.18.

My suggestion/request would be that you rebase your patches on the stacking set, which should be out for 4.18 mid-week.

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo at vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html