From f690c6792f5ca5737627ebf800086d7408f17456 Mon Sep 17 00:00:00 2001 From: Kernel Bot Date: Wed, 21 Aug 2019 16:11:26 +0300 Subject: [PATCH 1/2] AltHa LSM module * ignore SUID on binaries (with exceptions possible); * prevent running selected script interprers in interactive move; * disable open file unlinking in selected dirs; Changelog: * ported to 5.2 as ordered LSM * introduced OLock * no more WxorX * error handling, path_puts and locking added * lists handling rewritten * indentation fixed Rediffed for vanilla kernel 5.4.25 Signed-off-by: Mikhail Novosyolov --- security/Kconfig | 3 +- security/Makefile | 2 + security/altha/Kconfig | 11 ++ security/altha/Makefile | 3 + security/altha/altha_lsm.c | 325 +++++++++++++++++++++++++++++++++++++ 5 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 security/altha/Kconfig create mode 100644 security/altha/Makefile create mode 100644 security/altha/altha_lsm.c diff --git a/security/Kconfig b/security/Kconfig index 2a1a2d396228..d21a120b66d4 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -238,6 +238,7 @@ source "security/loadpin/Kconfig" source "security/yama/Kconfig" source "security/safesetid/Kconfig" source "security/lockdown/Kconfig" +source "security/altha/Kconfig" source "security/integrity/Kconfig" @@ -281,7 +282,7 @@ config LSM default "lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo" if DEFAULT_SECURITY_APPARMOR default "lockdown,yama,loadpin,safesetid,integrity,tomoyo" if DEFAULT_SECURITY_TOMOYO default "lockdown,yama,loadpin,safesetid,integrity" if DEFAULT_SECURITY_DAC - default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor" + default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,altha" help A comma-separated list of LSMs, in initialization order. Any LSMs left off this list will be ignored. This can be diff --git a/security/Makefile b/security/Makefile index be1dd9d2cb2f..7c9628c2017e 100644 --- a/security/Makefile +++ b/security/Makefile @@ -12,6 +12,7 @@ subdir-$(CONFIG_SECURITY_YAMA) += yama subdir-$(CONFIG_SECURITY_LOADPIN) += loadpin subdir-$(CONFIG_SECURITY_SAFESETID) += safesetid subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown +subdir-$(CONFIG_SECURITY_ALTHA) += altha # always enable default capabilities obj-y += commoncap.o @@ -29,6 +30,7 @@ obj-$(CONFIG_SECURITY_YAMA) += yama/ obj-$(CONFIG_SECURITY_LOADPIN) += loadpin/ obj-$(CONFIG_SECURITY_SAFESETID) += safesetid/ obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/ +obj-$(CONFIG_SECURITY_ALTHA) += altha/ obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o # Object integrity file lists diff --git a/security/altha/Kconfig b/security/altha/Kconfig new file mode 100644 index 000000000000..4bafdef4e58e --- /dev/null +++ b/security/altha/Kconfig @@ -0,0 +1,11 @@ +config SECURITY_ALTHA + bool "AltHa security module" + depends on SECURITY + default n + help + Some hardening options: + * ignore SUID on binaries (with exceptions possible); + * prevent running selected script interprers in interactive move; + * WxorX for filesystems (with exceptions possible); + + If you are unsure how to answer this question, answer N. diff --git a/security/altha/Makefile b/security/altha/Makefile new file mode 100644 index 000000000000..56735b157567 --- /dev/null +++ b/security/altha/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_SECURITY_ALTHA) := altha.o + +altha-y := altha_lsm.o diff --git a/security/altha/altha_lsm.c b/security/altha/altha_lsm.c new file mode 100644 index 000000000000..7d1cc8f8a1a7 --- /dev/null +++ b/security/altha/altha_lsm.c @@ -0,0 +1,325 @@ +/* + * AltHa Linux Security Module + * + * Author: Anton Boyarshinov + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ALTHA_PARAMS_SIZE 4096 +char proc_nosuid_exceptions[ALTHA_PARAMS_SIZE]; +char proc_interpreters[ALTHA_PARAMS_SIZE]; +char proc_olock_dirs[ALTHA_PARAMS_SIZE]; + +/* Boot time disable flag */ +static bool altha_enabled = 0; + +/* sysctl flags */ +static int nosuid_enabled; +static int rstrscript_enabled; +static int olock_enabled; + +/* Boot parameter handing */ +module_param_named(enabled, altha_enabled, bool, S_IRUGO); + +static int __init altha_enabled_setup(char *str) +{ + unsigned long enabled; + int error = kstrtoul(str, 0, &enabled); + if (!error) + altha_enabled = enabled ? 1 : 0; + return 1; +} + +__setup("altha=", altha_enabled_setup); + +struct altha_list_struct { + struct path path; + struct list_head list; +}; + +/* Lists handling */ +DECLARE_RWSEM(nosuid_exceptions_sem); +DECLARE_RWSEM(interpreters_sem); +DECLARE_RWSEM(olock_dirs_sem); +LIST_HEAD(nosuid_exceptions_list); +LIST_HEAD(interpreters_list); +LIST_HEAD(olock_dirs_list); + +static int altha_list_handler(struct ctl_table *table, int write, + void __user * buffer, size_t * lenp, + loff_t * ppos) +{ + struct altha_list_struct *item, *tmp; + struct list_head *list_struct; + char *p, *fluid; + char *copy_buffer; + struct rw_semaphore *sem = table->extra2; + unsigned long error = proc_dostring(table, write, buffer, lenp, ppos); + down_write(sem); + if (error) + goto out; + + if (write && !error) { + copy_buffer = kmalloc(ALTHA_PARAMS_SIZE, GFP_KERNEL); + if (!copy_buffer) { + pr_err + ("AltHa: can't get memory for copy_buffer processing sysctl\n"); + error = -1; + goto out; + } + + list_struct = (struct list_head *)(table->extra1); + /*empty list and that fill with new info */ + list_for_each_entry_safe(item, tmp, list_struct, list) { + list_del(&item->list); + path_put(&item->path); + kfree(item); + } + + strlcpy(copy_buffer, table->data, ALTHA_PARAMS_SIZE); + + /* buffer can have a garbage after \n */ + p = strchrnul(copy_buffer, '\n'); + *p = 0; + + /* for strsep usage */ + fluid = copy_buffer; + + while ((p = strsep(&fluid, ":\n")) != NULL) { + if (strlen(p)) { + item = kmalloc(sizeof(*item), GFP_KERNEL); + if (!item) { + pr_err + ("AltHa: can't get memory processing sysctl\n"); + kfree(copy_buffer); + error = -1; + goto out; + } + if (kern_path(p, LOOKUP_FOLLOW, &item->path)) { + pr_info + ("AltHa: error lookup '%s'\n", p); + kfree(item); + } else { + list_add_tail(&item->list, list_struct); + } + } + } + kfree(copy_buffer); + } +out: + up_write(sem); + return error; +} + +struct ctl_path nosuid_sysctl_path[] = { + {.procname = "kernel",}, + {.procname = "altha",}, + {.procname = "nosuid",}, + {} +}; + +static struct ctl_table nosuid_sysctl_table[] = { + { + .procname = "enabled", + .data = &nosuid_enabled, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + }, + { + .procname = "exceptions", + .data = proc_nosuid_exceptions, + .maxlen = ALTHA_PARAMS_SIZE, + .mode = 0644, + .proc_handler = altha_list_handler, + .extra1 = &nosuid_exceptions_list, + .extra2 = &nosuid_exceptions_sem, + }, + {} +}; + +struct ctl_path rstrscript_sysctl_path[] = { + {.procname = "kernel",}, + {.procname = "altha",}, + {.procname = "rstrscript",}, + {} +}; + +static struct ctl_table rstrscript_sysctl_table[] = { + { + .procname = "enabled", + .data = &rstrscript_enabled, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + }, + { + .procname = "interpreters", + .data = proc_interpreters, + .maxlen = ALTHA_PARAMS_SIZE, + .mode = 0644, + .proc_handler = altha_list_handler, + .extra1 = &interpreters_list, + .extra2 = &interpreters_sem, + }, + {} +}; + +struct ctl_path olock_sysctl_path[] = { + {.procname = "kernel",}, + {.procname = "altha",}, + {.procname = "olock",}, + {} +}; + +static struct ctl_table olock_sysctl_table[] = { + { + .procname = "enabled", + .data = &olock_enabled, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = &proc_dointvec_minmax, + }, + { + .procname = "dirs", + .data = proc_olock_dirs, + .maxlen = ALTHA_PARAMS_SIZE, + .mode = 0644, + .proc_handler = altha_list_handler, + .extra1 = &olock_dirs_list, + .extra2 = &olock_dirs_sem, + }, + {} +}; + +struct altha_readdir_callback { + struct dir_context ctx; + u64 inode; + int found; +}; + + +int is_olock_dir(struct inode *inode) +{ + struct altha_list_struct *node; + down_read(&olock_dirs_sem); + list_for_each_entry(node, &olock_dirs_list, list) { + struct inode *exc_inode = node->path.dentry->d_inode; + if (exc_inode == inode) { + up_read(&olock_dirs_sem); + return 1; + } + } + up_read(&olock_dirs_sem); + return 0; +} + +/* Hooks */ +static int altha_bprm_set_creds(struct linux_binprm *bprm) +{ + struct altha_list_struct *node; + /* when it's not a shebang issued script interpreter */ + if (rstrscript_enabled && !bprm->called_set_creds) { + down_read(&interpreters_sem); + list_for_each_entry(node, &interpreters_list, list) { + if (path_equal(&bprm->file->f_path, &node->path)) { + uid_t cur_uid = from_kuid(bprm->cred->user_ns, + bprm->cred->uid); + pr_notice_ratelimited + ("AltHa/RestrScript: %s is blocked to run directly by %d\n", + bprm->filename, cur_uid); + up_read(&interpreters_sem); + return -EPERM; + } + } + up_read(&interpreters_sem); + } + if (unlikely(nosuid_enabled && + !uid_eq(bprm->cred->uid, bprm->cred->euid))) { + uid_t cur_uid = from_kuid(bprm->cred->user_ns, bprm->cred->uid); + down_read(&nosuid_exceptions_sem); + list_for_each_entry(node, &nosuid_exceptions_list, list) { + if (path_equal(&bprm->file->f_path, &node->path)) { + pr_notice_ratelimited + ("AltHa/NoSUID: %s permitted to setuid from %d\n", + bprm->filename, cur_uid); + up_read(&nosuid_exceptions_sem); + return 0; + } + } + up_read(&nosuid_exceptions_sem); + pr_notice_ratelimited + ("AltHa/NoSUID: %s prevented to setuid from %d\n", + bprm->filename, cur_uid); + bprm->cred->euid = bprm->cred->uid; + } + return 0; +} + +/* For OLock */ +static int altha_inode_unlink(struct inode *inode, struct dentry *dentry) +{ + if (olock_enabled && (atomic_read(&dentry->d_inode->i_writecount) +#ifdef CONFIG_IMA + || atomic_read(&dentry->d_inode->i_readcount) +#endif + )) { + if (is_olock_dir(inode)) + return -EPERM; + } + return 0; +} + +/* Initialization */ + +static struct security_hook_list altha_hooks[] = { + LSM_HOOK_INIT(bprm_set_creds, altha_bprm_set_creds), + LSM_HOOK_INIT(inode_unlink, altha_inode_unlink), +}; + +static int __init altha_init(void) +{ + if (altha_enabled) { + pr_info("AltHa enabled.\n"); + security_add_hooks(altha_hooks, ARRAY_SIZE(altha_hooks),"altha"); + + if (!register_sysctl_paths + (nosuid_sysctl_path, nosuid_sysctl_table)) + panic("AltHa: NoSUID sysctl registration failed.\n"); + + if (!register_sysctl_paths + (rstrscript_sysctl_path, rstrscript_sysctl_table)) + panic + ("AltHa: RestrScript sysctl registration failed.\n"); + + if (!register_sysctl_paths + (olock_sysctl_path, olock_sysctl_table)) + panic("AltHa: OLock sysctl registration failed.\n"); + } else + pr_info("AltHa disabled.\n"); + return 0; +} + +DEFINE_LSM(altha) = { + .name = "altha", + .init = altha_init, +}; + -- 2.20.1