Safe LSM (un)loading, and immutable hooks
Subsystems (9)
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
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
James Morris
maintainerPATCH 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_operationslsm_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
PATCH v7 1/6 security: Move LSM registration arguments to struct lsm_info
In reply to Sargun DhillonQuoted 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 structsecurity_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".Signed-off-by Sargun Dhillon <[email protected]>--- 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.hinclude/linux/lsm_hooks.h +5
-0@@ -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
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 structsecurity_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".Signed-off-by Sargun Dhillon <[email protected]>--- 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.hinclude/linux/lsm_hooks.h +5
-0@@ -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
PATCH v7 2/6 security: Make security_hook_heads private
In reply to Sargun DhillonThis 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
@@ -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
@@ -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
@@ -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
@@ -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
PATCH v7 3/6 security: Introduce mutable (RW) hooks
In reply to Sargun DhillonThis 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -7,5 +7,5 @@#define __SECURITY_SECURITY_Hextern 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
@@ -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
@@ -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
@@ -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
@@ -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
PATCH v7 4/6 security: Expose security_add_hooks externally and add error handling
In reply to Sargun DhillonThis 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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
PATCH v7 5/6 security: Panic on forced unloading of security module
In reply to Sargun DhillonAlthough, 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
@@ -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
PATCH v7 6/6 security: Add SECURITY_UNREGISTRABLE_HOOKS to allow for hook removal
In reply to Sargun DhillonThis 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
@@ -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
@@ -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
@@ -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
Tetsuo Handa
maintainerv7 cover Safe LSM (un)loading, and immutable hooks
In reply to Sargun DhillonSuggested 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
@@ -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
@@ -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_Hextern 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
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 thatcall_int_hook()returns an error after some LSM module allocated memory. You need to implement safe transaction in order to allowFOR_EACH_SECURITY_HOOK_RWhooks 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@@ -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@@ -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
-3bool 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
-17mutex_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
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
-3extern 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
Tetsuo Handa
maintainerv7 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 thatcall_int_hook()returns an error after some LSM module allocated memory. You need to implement safe transaction in order to allowFOR_EACH_SECURITY_HOOK_RWhooks 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
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 thatcall_int_hook()returns an error after some LSM module allocated memory. You need to implement safe transaction in order to allowFOR_EACH_SECURITY_HOOK_RWhooks 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 calledint
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
@@ -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
@@ -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
@@ -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
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 thatcall_int_hook()returns an error after some LSM module allocated memory. You need to implement safe transaction in order to allowFOR_EACH_SECURITY_HOOK_RWhooks 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_securityis 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 atry_module_get, and amodule_putif RC != 0 on RW hooks. So, we could have something likecall_int_hook_major, andcall_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 calledint
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@@ -339,7 +339,8 @@ static void security_add_hook(structsecurity_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@@ -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@@ -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(structsecurity_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_infostruct for this security modulesecurity/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
Tetsuo Handa
maintainerv7 cover Safe LSM (un)loading, and immutable hooks
Quoted 5 lines · Sargun Dhillon
Quoted 5 lines
inode_free_securityis 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 atry_module_get, and amodule_putif RC != 0 on RW hooks. So, we could have something likecall_int_hook_major, andcall_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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
v7 cover Safe LSM (un)loading, and immutable hooks
Quoted 40 lines · Tetsuo Handa
Quoted 5 lines · Sargun Dhillon
Quoted 5 lines
inode_free_securityis 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 atry_module_get, and amodule_putif RC != 0 on RW hooks. So, we could have something likecall_int_hook_major, andcall_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
Casey Schaufler
maintainerv7 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
Tetsuo Handa
maintainerv7 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
Casey Schaufler
maintainerv7 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
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
Casey Schaufler
maintainerv7 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
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 loadedQuoted 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
Casey Schaufler
maintainerv7 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 loadedQuoted 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]
James Morris
maintainerv7 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]>
James Morris
maintainerv7 cover Safe LSM (un)loading, and immutable hooks
In reply to Sargun DhillonQuoted 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
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
James Morris
maintainerv7 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
James Morris
maintainerv7 cover Safe LSM (un)loading, and immutable hooks
In reply to Sargun DhillonQuoted 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
v7 cover Safe LSM (un)loading, and immutable hooks
In reply to Sargun DhillonQuoted 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_HOOKSis enabled, then hooks can be loaded at runtime. The module can check the return code ofsecurity_add_hooksto determine whether or not they can install their hooks, independently of checking for theKconfigvalue. 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_HOOKSis 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_HOOKSrule, 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 onCONFIG_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_infostructure. This encapsulates various information about the LSM itself, and can be used at a later time.This patch was tested with
CONFIG_PROVE_LOCKINGto prove the correctness of the locking inlock_existing_hooks. It grabs the module lock to prevent new hooks from being loaded, or current hooks from being unloaded. It then grabs thelsm_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 bothsecurity_add_hooks, andsecurity_delete_hooksreturn errors upon being unsuccessful, allowing module authors to make the best decision for their use case.
* Get rid ofLSM_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 theFOR_EACH_SECURITY_HOOK_MUTABLEmacro, which gets compiled out ifCONFIG_SECURITY_WRITABLE_HOOKSis disabled.
v2:
* Split outhlist_headpatch
* Apply Tetsuo's changes to clean up functions which are not covered bycall_int_hook/call_void_hook
* Disable NULL hook checking when uneeded
v1:
* Add SRCU to allow for code-unloading
* Add concurrency control around hook mutationSargun Dhillon (6):
security: Move LSM registration arguments to structlsm_info
security: Makesecurity_hook_headsprivate
security: Introduce mutable (RW) hooks
security: Exposesecurity_add_hooksexternally and add error handling
security: Panic on forced unloading of security module
security: AddSECURITY_UNREGISTRABLE_HOOKSto allow for hook removalinclude/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
Casey Schaufler
maintainerv7 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