@@ -28,6 +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
@@ -1963,10 +1964,11 @@ 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 hook_idx;
+ const union security_list_options hook;
+ struct module *owner;
+ char *lsm;
} __randomize_layout;
/*
@@ -1975,17 +1977,25 @@ 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 HOOK_OFFSET(HEAD) offsetof(struct security_hook_heads, HEAD)
+#define HOOK_SIZE(HEAD) FIELD_SIZEOF(struct security_hook_heads, HEAD)
+#define LSM_HOOK_IDX(HEAD) (HOOK_OFFSET(HEAD) / HOOK_SIZE(HEAD))
-extern struct security_hook_heads security_hook_heads;
+#define LSM_HOOK_INIT(HEAD, HOOK) \
+ { \
+ .hook = { .HEAD = HOOK }, \
+ .owner = THIS_MODULE, \
+ .hook_idx = LSM_HOOK_IDX(HEAD), \
+ }
extern char *lsm_names;
-extern void security_add_hooks(struct security_hook_list *hooks, int count,
- char *lsm);
-
-#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+extern int __must_check security_add_hooks(struct security_hook_list *hooks,
+ const int count, char *lsm,
+ const bool is_mutable);
+#ifdef CONFIG_SECURITY_UNREGISTRABLE_HOOKS
/*
+ * Used to facilitate runtime hook unloading
+ *
* 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
@@ -1997,22 +2007,9 @@ extern void security_add_hooks(struct security_hook_list *hooks, int count,
* 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 */
-
-/* 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 __must_check security_delete_hooks(struct security_hook_list *hooks,
+ int count);
+#endif /* CONFIG_SECURITY_UNREGISTRABLE_HOOKS */
extern int __init security_module_enable(const char *module);
extern void __init capability_add_hooks(void);
@@ -33,8 +33,24 @@ config SECURITY
config SECURITY_WRITABLE_HOOKS
depends on SECURITY
- bool
default n
+ bool "Enable mutable security module hooks"
+ help
+ This allows for arbitrary security modules to be loaded by the user
+ after boot time.
+
+ If you are unsure how to answer this question, answer 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 user
+ after boot time. This feature can be disabled via the kernel command
+ line or via sysfs after boot time.
+
+ If you are unsure how to answer this question, answer N.
config SECURITYFS
bool "Enable the securityfs filesystem"
@@ -743,7 +743,7 @@ static int apparmor_task_kill(struct task_struct *target, struct siginfo *info,
return error;
}
-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),
@@ -1161,8 +1161,8 @@ static int __init apparmor_init(void)
aa_free_root_ns();
goto buffers_out;
}
- security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),
- "apparmor");
+ BUG_ON(security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks),
+ "apparmor", false));
/* Report that AppArmor successfully initialized */
apparmor_initialized = 1;
@@ -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 = {
+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),
@@ -1362,8 +1362,9 @@ struct security_hook_list capability_hooks[] __lsm_ro_after_init = {
void __init capability_add_hooks(void)
{
- security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks),
- "capability");
+ BUG_ON(security_add_hooks(capability_hooks,
+ ARRAY_SIZE(capability_hooks),
+ "capability", false));
}
#endif /* CONFIG_SECURITY */
@@ -174,7 +174,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),
};
@@ -29,14 +29,35 @@
#include <linux/backing-dev.h>
#include <linux/string.h>
#include <net/flow.h>
+#include <linux/srcu.h>
+#include <linux/mutex.h>
+
+#define SECURITY_HOOK_COUNT \
+ (sizeof(security_hook_heads) / sizeof(struct hlist_head))
#define MAX_LSM_EVM_XATTR 2
/* Maximum number of letters for an LSM name string */
#define SECURITY_NAME_MAX 10
-struct security_hook_heads security_hook_heads __lsm_ro_after_init;
+static struct security_hook_heads security_hook_heads __ro_after_init;
+
static ATOMIC_NOTIFIER_HEAD(lsm_notifier_chain);
+static DEFINE_MUTEX(security_hook_mutex);
+/*
+ * security_allow_unregister_hooks blocks the delete_module syscall for
+ * hooks that are loaded into the set of mutable 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
+ * indicating failure.
+ *
+ * When this value turns true -> false -- Once, lock_existing_hooks will be
+ * called.
+ */
+static bool security_allow_unregister_hooks =
+ IS_ENABLED(CONFIG_SECURITY_UNREGISTRABLE_HOOKS);
char *lsm_names;
/* Boot-time LSM user choice */
@@ -53,6 +74,193 @@ static void __init do_security_initcalls(void)
}
}
+#define FOR_EACH_SECURITY_HOOK(ITERATOR, HEAD) \
+ hlist_for_each_entry(ITERATOR, &security_hook_heads.HEAD, list)
+
+#ifdef CONFIG_SECURITY_WRITABLE_HOOKS
+/*
+ * If mutable security hooks are enabled, it exposes a second set of
+ * security_hook_heads. These security_hook_heads will only be executed
+ * if all immutable hooks are executed successfully.
+ */
+static struct security_hook_heads security_hook_heads_mutable;
+#define FOR_EACH_SECURITY_HOOK_MUTABLE(ITERATOR, HEAD) \
+ hlist_for_each_entry(ITERATOR, &security_hook_heads_mutable.HEAD, list)
+
+static struct hlist_head *get_heads(bool is_mutable)
+{
+ if (is_mutable)
+ return (struct hlist_head *)&security_hook_heads_mutable;
+ return (struct hlist_head *)&security_hook_heads;
+}
+#else
+#define FOR_EACH_SECURITY_HOOK_MUTABLE(ITERATOR, HEAD) while (0)
+
+static struct hlist_head *get_heads(bool is_mutable)
+{
+ if (is_mutable)
+ return ERR_PTR(-EINVAL);
+ return (struct hlist_head *)&security_hook_heads;
+}
+#endif /* CONFIG_SECURITY_WRITABLE_HOOKS */
+
+#ifdef CONFIG_SECURITY_UNREGISTRABLE_HOOKS
+DEFINE_STATIC_SRCU(security_hook_srcu);
+static inline int lock_lsm(void)
+{
+ return srcu_read_lock(&security_hook_srcu);
+}
+
+static inline void unlock_lsm(int idx)
+{
+ srcu_read_unlock(&security_hook_srcu, idx);
+}
+
+static inline void synchronize_lsm(void)
+{
+ synchronize_srcu(&security_hook_srcu);
+}
+
+/**
+ * security_delete_hooks - Remove modules hooks from the mutable hooks lists.
+ * @hooks: the hooks to remove
+ * @count: the number of hooks to remove
+ *
+ * 0 is returned on success, otherwise -errno is returned on failure.
+ * If an error is returned, it is up to the LSM author to handle the error
+ * appropriately. If they do not, their code could be unloaded, while
+ * leaving dangling pointers.
+ *
+ * At best, this will cause a kernel oops. At worst case scenario, this can
+ * lead to arbitrary code execution. Therefore, it is critical that module
+ * authors check the return code here, and act appropriately. In most cases
+ * a failure should result in panic.
+ */
+int __must_check security_delete_hooks(struct security_hook_list *hooks,
+ int count)
+{
+ int i, ret = 0;
+
+ mutex_lock(&security_hook_mutex);
+ if (security_allow_unregister_hooks)
+ for (i = 0; i < count; i++)
+ hlist_del_rcu(&hooks[i].list);
+ else
+ ret = -EPERM;
+ mutex_unlock(&security_hook_mutex);
+
+ if (!ret)
+ synchronize_lsm();
+ return ret;
+}
+EXPORT_SYMBOL_GPL(security_delete_hooks);
+
+static void lock_existing_hooks(void)
+{
+ struct hlist_head *list = (struct hlist_head *)
+ &security_hook_heads_mutable;
+ struct security_hook_list *P;
+ int i;
+
+ /*
+ * 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);
+ for (i = 0; i < SECURITY_HOOK_COUNT; i++)
+ hlist_for_each_entry(P, &list[i], list)
+ if (P->owner)
+ WARN_ON(!try_module_get(P->owner));
+ mutex_unlock(&module_mutex);
+}
+#else
+
+static inline int lock_lsm(void)
+{
+ return 0;
+}
+
+static inline void lock_existing_hooks(void) {}
+static inline void unlock_lsm(int idx) {}
+static inline void synchronize_lsm(void) {}
+#endif /* CONFIG_SECURITY_UNREGISTRABLE_HOOKS */
+
+#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+/*
+ * Always succeeds.
+ * Only to be called by legacy code, like SELinux's delete hooks mechanism
+ * as it ignores whether or not allow_unregister_hooks is set.
+ */
+void __security_delete_hooks(struct security_hook_list *hooks, int count)
+{
+ int i;
+
+ mutex_lock(&security_hook_mutex);
+ for (i = 0; i < count; i++)
+ hlist_del_rcu(&hooks[i].list);
+ mutex_unlock(&security_hook_mutex);
+
+ synchronize_lsm();
+}
+#endif /* CONFIG_SECURITY_SELINUX_DISABLE */
+
+static int allow_unregister_hooks_set(const char *val,
+ const struct kernel_param *kp)
+{
+ bool new_val;
+ int ret = 0;
+
+ ret = strtobool(val, &new_val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&security_hook_mutex);
+ /*
+ * This is a noop
+ * false -> false
+ * true -> true
+ */
+ if (new_val == security_allow_unregister_hooks)
+ goto out;
+
+ /* Do not allow for the transition from false -> true */
+ if (new_val) {
+ ret = -EPERM;
+ goto out;
+ }
+
+ /* The only legal transition true -> false */
+ security_allow_unregister_hooks = new_val;
+ /* We now need to "lock" all of the existing hooks */
+ lock_existing_hooks();
+out:
+ mutex_unlock(&security_hook_mutex);
+ return ret;
+}
+
+static int allow_unregister_hooks_get(char *buffer,
+ const struct kernel_param *kp)
+{
+ int ret;
+
+ mutex_lock(&security_hook_mutex);
+ ret = sprintf(buffer, "%c\n",
+ security_allow_unregister_hooks ? '1' : '0');
+ mutex_unlock(&security_hook_mutex);
+
+ return ret;
+}
+
+static struct kernel_param_ops allow_unregister_hooks_param_ops = {
+ .set = allow_unregister_hooks_set,
+ .get = allow_unregister_hooks_get,
+};
+
+module_param_cb(allow_unregister_hooks, &allow_unregister_hooks_param_ops, NULL,
+ 0644);
+MODULE_PARM_DESC(allow_unregister_hooks, "Allow deregistering security hooks");
+
/**
* security_init - initializes the security framework
*
@@ -60,12 +268,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");
/*
@@ -153,21 +355,41 @@ int __init security_module_enable(const char *module)
* @hooks: the hooks to add
* @count: the number of hooks to add
* @lsm: the name of the security module
+ * @is_mutable: True if dynamic registration and/or unregistration is needed.
*
* Each LSM has to register its hooks with the infrastructure.
+ * 0 is returned on success, otherwise -errno is returned on failure.
*/
-void __init security_add_hooks(struct security_hook_list *hooks, int count,
- char *lsm)
+int __must_check security_add_hooks(struct security_hook_list *hooks,
+ const int count, char *lsm,
+ const bool is_mutable)
{
- int i;
+ struct hlist_head *heads;
+ int i, hook_idx, ret = 0;
+
+ mutex_lock(&security_hook_mutex);
+ heads = get_heads(is_mutable);
+ if (IS_ERR(heads)) {
+ ret = PTR_ERR(heads);
+ goto out;
+ }
for (i = 0; i < count; i++) {
+ hook_idx = hooks[i].hook_idx;
hooks[i].lsm = lsm;
- hlist_add_tail_rcu(&hooks[i].list, hooks[i].head);
+ hlist_add_tail_rcu(&hooks[i].list, &heads[hook_idx]);
+ if (!security_allow_unregister_hooks && hooks[i].owner)
+ WARN_ON(!try_module_get(hooks->owner));
}
+
if (lsm_append(lsm, &lsm_names) < 0)
- panic("%s - Cannot get early memory.\n", __func__);
+ panic("%s - Cannot get memory.\n", __func__);
+out:
+ mutex_unlock(&security_hook_mutex);
+
+ return ret;
}
+EXPORT_SYMBOL_GPL(security_add_hooks);
int call_lsm_notifier(enum lsm_event event, void *data)
{
@@ -200,25 +422,49 @@ EXPORT_SYMBOL(unregister_lsm_notifier);
#define call_void_hook(FUNC, ...) \
do { \
struct security_hook_list *P; \
+ int srcu_idx; \
\
- hlist_for_each_entry(P, &security_hook_heads.FUNC, list) \
+ FOR_EACH_SECURITY_HOOK(P, FUNC) \
+ P->hook.FUNC(__VA_ARGS__); \
+ srcu_idx = lock_lsm(); \
+ FOR_EACH_SECURITY_HOOK_MUTABLE(P, FUNC) \
P->hook.FUNC(__VA_ARGS__); \
+ unlock_lsm(srcu_idx); \
} while (0)
-#define call_int_hook(FUNC, IRC, ...) ({ \
+/*
+ * In certain functions, call_int_hook is invoked multiple times, therefore
+ * the flow-control labels for goto have to be unique with that namespace.
+ * In order to work around this, we wrap call_int_hook2 with a unique-label
+ * defined below in call_int_hook.
+ */
+#define call_int_hook2(LABEL, FUNC, IRC, ...) ({ \
int RC = IRC; \
+ \
do { \
struct security_hook_list *P; \
+ int srcu_idx; \
\
- hlist_for_each_entry(P, &security_hook_heads.FUNC, list) { \
+ FOR_EACH_SECURITY_HOOK(P, FUNC) { \
+ RC = P->hook.FUNC(__VA_ARGS__); \
+ if (RC != 0) \
+ goto LABEL; \
+ } \
+ srcu_idx = lock_lsm(); \
+ FOR_EACH_SECURITY_HOOK_MUTABLE(P, FUNC) { \
RC = P->hook.FUNC(__VA_ARGS__); \
if (RC != 0) \
break; \
} \
+ unlock_lsm(srcu_idx); \
} while (0); \
+LABEL: \
RC; \
})
+#define call_int_hook(FUNC, IRC, ...) \
+ call_int_hook2(__UNIQUE_ID(FUNC), FUNC, IRC, __VA_ARGS__)
+
/* Security operations */
int security_binder_set_context_mgr(struct task_struct *mgr)
@@ -308,8 +554,7 @@ int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
{
struct security_hook_list *hp;
int cap_sys_admin = 1;
- int rc;
-
+ int rc, srcu_idx;
/*
* The module will respond with a positive value if
* it thinks the __vm_enough_memory() call should be
@@ -317,13 +562,25 @@ int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
* agree that it should be set it will. If any module
* thinks it should not be set it won't.
*/
- hlist_for_each_entry(hp, &security_hook_heads.vm_enough_memory, list) {
+ FOR_EACH_SECURITY_HOOK(hp, vm_enough_memory) {
+ rc = hp->hook.vm_enough_memory(mm, pages);
+ if (rc <= 0) {
+ cap_sys_admin = 0;
+ goto out;
+ }
+ }
+
+ srcu_idx = lock_lsm();
+ FOR_EACH_SECURITY_HOOK_MUTABLE(hp, vm_enough_memory) {
rc = hp->hook.vm_enough_memory(mm, pages);
if (rc <= 0) {
cap_sys_admin = 0;
break;
}
}
+ unlock_lsm(srcu_idx);
+
+out:
return __vm_enough_memory(mm, pages, cap_sys_admin);
}
@@ -798,40 +1055,64 @@ int security_inode_killpriv(struct dentry *dentry)
int security_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc)
{
struct security_hook_list *hp;
- int rc;
+ int srcu_idx, rc = EOPNOTSUPP;
if (unlikely(IS_PRIVATE(inode)))
return -EOPNOTSUPP;
/*
* Only one module will provide an attribute with a given name.
*/
- hlist_for_each_entry(hp, &security_hook_heads.inode_getsecurity, list) {
+ FOR_EACH_SECURITY_HOOK(hp, inode_getsecurity) {
rc = hp->hook.inode_getsecurity(inode, name, buffer, alloc);
if (rc != -EOPNOTSUPP)
- return rc;
+ goto out;
}
- return -EOPNOTSUPP;
+
+ srcu_idx = lock_lsm();
+ FOR_EACH_SECURITY_HOOK_MUTABLE(hp, inode_getsecurity) {
+ rc = hp->hook.inode_getsecurity(inode, name, buffer, alloc);
+ if (rc != -EOPNOTSUPP)
+ break;
+ }
+ unlock_lsm(srcu_idx);
+
+out:
+ return rc;
}
int security_inode_setsecurity(struct inode *inode, const char *name, const void *value, size_t size, int flags)
{
struct security_hook_list *hp;
- int rc;
+ int srcu_idx, rc = EOPNOTSUPP;
if (unlikely(IS_PRIVATE(inode)))
return -EOPNOTSUPP;
/*
* Only one module will provide an attribute with a given name.
*/
- hlist_for_each_entry(hp, &security_hook_heads.inode_setsecurity, list) {
+
+ FOR_EACH_SECURITY_HOOK(hp, inode_setsecurity) {
rc = hp->hook.inode_setsecurity(inode, name, value, size,
- flags);
+ flags);
if (rc != -EOPNOTSUPP)
- return rc;
+ goto out;
}
- return -EOPNOTSUPP;
+
+ srcu_idx = lock_lsm();
+ FOR_EACH_SECURITY_HOOK_MUTABLE(hp, inode_setsecurity) {
+ rc = hp->hook.inode_setsecurity(inode, name, value, size,
+ flags);
+ if (rc != -EOPNOTSUPP)
+ break;
+ }
+ unlock_lsm(srcu_idx);
+
+out:
+ return rc;
}
+
+
int security_inode_listsecurity(struct inode *inode, char *buffer, size_t buffer_size)
{
if (unlikely(IS_PRIVATE(inode)))
@@ -1120,13 +1401,23 @@ int security_task_kill(struct task_struct *p, struct siginfo *info,
}
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
- unsigned long arg4, unsigned long arg5)
+ unsigned long arg4, unsigned long arg5)
{
- int thisrc;
- int rc = -ENOSYS;
+ int srcu_idx, thisrc, rc = -ENOSYS;
struct security_hook_list *hp;
- hlist_for_each_entry(hp, &security_hook_heads.task_prctl, list) {
+ FOR_EACH_SECURITY_HOOK(hp, task_prctl) {
+ thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5);
+ if (thisrc != -ENOSYS) {
+ rc = thisrc;
+ if (thisrc != 0)
+ goto out;
+ }
+
+ }
+
+ srcu_idx = lock_lsm();
+ FOR_EACH_SECURITY_HOOK_MUTABLE(hp, task_prctl) {
thisrc = hp->hook.task_prctl(option, arg2, arg3, arg4, arg5);
if (thisrc != -ENOSYS) {
rc = thisrc;
@@ -1134,6 +1425,9 @@ int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
break;
}
}
+ unlock_lsm(srcu_idx);
+
+out:
return rc;
}
@@ -1618,7 +1912,7 @@ int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
const struct flowi *fl)
{
struct security_hook_list *hp;
- int rc = 1;
+ int srcu_idx, rc = 1;
/*
* Since this function is expected to return 0 or 1, the judgment
@@ -1629,11 +1923,19 @@ int security_xfrm_state_pol_flow_match(struct xfrm_state *x,
* For speed optimization, we explicitly break the loop rather than
* using the macro
*/
- hlist_for_each_entry(hp, &security_hook_heads.xfrm_state_pol_flow_match,
- list) {
+ FOR_EACH_SECURITY_HOOK(hp, xfrm_state_pol_flow_match) {
+ rc = hp->hook.xfrm_state_pol_flow_match(x, xp, fl);
+ goto out;
+ }
+
+ srcu_idx = lock_lsm();
+ FOR_EACH_SECURITY_HOOK_MUTABLE(hp, xfrm_state_pol_flow_match) {
rc = hp->hook.xfrm_state_pol_flow_match(x, xp, fl);
break;
}
+ unlock_lsm(srcu_idx);
+
+out:
return rc;
}
@@ -6396,7 +6396,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),
@@ -6629,6 +6635,8 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
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;
@@ -6654,7 +6662,8 @@ static __init int selinux_init(void)
0, SLAB_PANIC, NULL);
avc_init();
- security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux");
+ BUG_ON(security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks),
+ "selinux", is_mutable));
if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET))
panic("SELinux: Unable to register AVC netcache callback\n");
@@ -6783,6 +6792,13 @@ static void selinux_nf_ip_exit(void)
#endif /* CONFIG_NETFILTER */
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
+/*
+ * __security_delete_hooks does not respect security.allow_unload_hooks, and
+ * it not meant to be used by new LSMs. Therefore, this is defined here, rather
+ * than in a shared header.
+ */
+extern void __security_delete_hooks(struct security_hook_list *hooks,
+ int count);
static int selinux_disabled;
int selinux_disable(void)
@@ -6802,7 +6818,7 @@ int selinux_disable(void)
selinux_disabled = 1;
selinux_enabled = 0;
- security_delete_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
+ __security_delete_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks));
/* Try to destroy the avc node cache */
avc_disable();
@@ -4682,7 +4682,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),
@@ -4900,7 +4900,8 @@ static __init int smack_init(void)
/*
* Register with LSM
*/
- security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack");
+ BUG_ON(security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack",
+ false));
return 0;
}
@@ -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),
@@ -543,7 +543,8 @@ 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");
+ BUG_ON(security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo",
+ false));
printk(KERN_INFO "TOMOYO Linux initialized\n");
cred->security = &tomoyo_kernel_domain;
tomoyo_mm_init();
@@ -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),
@@ -480,6 +480,7 @@ 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");
+ BUG_ON(security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama",
+ false));
yama_init_sysctl();
}