mirror of
https://abf.rosa.ru/djam/kernel-6.6.git
synced 2025-02-24 11:22:47 +00:00

https://git.altlinux.org/gears/k/kernel-image-un-def.git?p=kernel-image-un-def.git;a=history;f=security/altha;h=90d1618c238171cff6517d93fd1f2e000c72d977;hb=HEAD
430 lines
12 KiB
Diff
430 lines
12 KiB
Diff
From ec191b7add477993e6f9a5c829a295defba9f69a Mon Sep 17 00:00:00 2001
|
|
From: Kernel Bot <kernelbot@altlinux.org>
|
|
Date: Wed, 21 Aug 2019 16:11:26 +0300
|
|
Subject: [PATCH] altha: 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
|
|
|
|
Authored-by: Anton V. Boyarshinov <boyarsh@altlinux.org>
|
|
[ kernelbot: build fixed with kernel 5.8. ]
|
|
[ vt: Fix build on 6.4. ]
|
|
Signed-off-by: Vitaly Chikunov <vt@altlinux.org>
|
|
---
|
|
security/Kconfig | 3 +-
|
|
security/Makefile | 1 +
|
|
security/altha/Kconfig | 11 ++
|
|
security/altha/Makefile | 3 +
|
|
security/altha/altha_lsm.c | 330 +++++++++++++++++++++++++++++++++++++
|
|
5 files changed, 347 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 52c9af08ad35..e98ca31a58fc 100644
|
|
--- a/security/Kconfig
|
|
+++ b/security/Kconfig
|
|
@@ -194,6 +194,7 @@ source "security/yama/Kconfig"
|
|
source "security/safesetid/Kconfig"
|
|
source "security/lockdown/Kconfig"
|
|
source "security/landlock/Kconfig"
|
|
+source "security/altha/Kconfig"
|
|
|
|
source "security/integrity/Kconfig"
|
|
|
|
@@ -237,7 +238,7 @@ config LSM
|
|
default "landlock,lockdown,yama,loadpin,safesetid,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
|
|
default "landlock,lockdown,yama,loadpin,safesetid,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
|
|
default "landlock,lockdown,yama,loadpin,safesetid,bpf" if DEFAULT_SECURITY_DAC
|
|
- default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,bpf"
|
|
+ default "landlock,lockdown,yama,loadpin,safesetid,selinux,smack,tomoyo,apparmor,bpf,altha,kiosk"
|
|
help
|
|
A comma-separated list of LSMs, in initialization order.
|
|
Any LSMs left off this list, except for those with order
|
|
diff --git a/security/Makefile b/security/Makefile
|
|
index 18121f8f85cd..a0f04e1cd8cf 100644
|
|
--- a/security/Makefile
|
|
+++ b/security/Makefile
|
|
@@ -24,6 +24,7 @@ obj-$(CONFIG_SECURITY_LOCKDOWN_LSM) += lockdown/
|
|
obj-$(CONFIG_CGROUPS) += device_cgroup.o
|
|
obj-$(CONFIG_BPF_LSM) += bpf/
|
|
obj-$(CONFIG_SECURITY_LANDLOCK) += landlock/
|
|
+obj-$(CONFIG_SECURITY_ALTHA) += altha/
|
|
|
|
# Object integrity file lists
|
|
obj-$(CONFIG_INTEGRITY) += integrity/
|
|
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..050a113b1513
|
|
--- /dev/null
|
|
+++ b/security/altha/altha_lsm.c
|
|
@@ -0,0 +1,330 @@
|
|
+/*
|
|
+ * AltHa Linux Security Module
|
|
+ *
|
|
+ * Author: Anton Boyarshinov <boyarsh@altlinux.org>
|
|
+ *
|
|
+ * 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 <linux/lsm_hooks.h>
|
|
+#include <linux/cred.h>
|
|
+#include <linux/sysctl.h>
|
|
+#include <linux/binfmts.h>
|
|
+#include <linux/file.h>
|
|
+#include <linux/ratelimit.h>
|
|
+#include <linux/moduleparam.h>
|
|
+#include <linux/list.h>
|
|
+#include <linux/namei.h>
|
|
+#include <linux/namei.h>
|
|
+#include <linux/printk.h>
|
|
+#include <linux/rwsem.h>
|
|
+#include <asm/uaccess.h>
|
|
+
|
|
+#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 compare_paths(const struct path *path1, const struct path *path2)
|
|
+{
|
|
+ char a1[PATH_MAX];
|
|
+ char a2[PATH_MAX];
|
|
+ char* p1, *p2;
|
|
+ p1=d_path(path1,a1,PATH_MAX);
|
|
+ p2=d_path(path2,a2,PATH_MAX);
|
|
+ return strcmp(p1,p2);
|
|
+}
|
|
+
|
|
+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_creds_from_file(struct linux_binprm *bprm, struct file * fi)
|
|
+{
|
|
+ struct altha_list_struct *node;
|
|
+ /* when it's not a shebang issued script interpreter */
|
|
+ if (rstrscript_enabled && bprm->filename == bprm->interp) {
|
|
+ down_read(&interpreters_sem);
|
|
+ list_for_each_entry(node, &interpreters_list, list) {
|
|
+ if (compare_paths(&bprm->file->f_path, &node->path) == 0) {
|
|
+ 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 (compare_paths(&bprm->file->f_path, &node->path) == 0) {
|
|
+ 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_creds_from_file, altha_bprm_creds_from_file),
|
|
+ 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("kernel/altha/nosuid", nosuid_sysctl_table))
|
|
+ panic("AltHa: NoSUID sysctl registration failed.\n");
|
|
+
|
|
+ if (!register_sysctl("kernel/altha/rstrscript", rstrscript_sysctl_table))
|
|
+ panic("AltHa: RestrScript sysctl registration failed.\n");
|
|
+
|
|
+ if (!register_sysctl("kernel/altha/olock", 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.40.1
|
|
|