From 62b6061f5714aa71e2d5fdb14db9d1341bb82dd4 Mon Sep 17 00:00:00 2001 From: huntazhang Date: Mon, 24 Jun 2024 20:02:36 +0800 Subject: [PATCH] [PATCH] security: add security hook point Adapted to tkernel5 and fixed some bugs. Signed-off-by: Zhiping Du Signed-off-by: zhiguang peng Signed-off-by: Haiquan zhang Signed-off-by: jit xie --- arch/x86/configs/tencent.config | 3 + fs/exec.c | 5 + fs/fcntl.c | 29 + include/linux/fs.h | 1 + include/linux/hook_frame.h | 56 ++ include/linux/sched.h | 13 + include/net/sock.h | 1 + include/uapi/linux/fcntl.h | 3 + kernel/exit.c | 11 + kernel/fork.c | 17 + kernel/sysctl.c | 106 ++++ kernel/tkernel/Kconfig | 14 +- kernel/tkernel/Makefile | 2 + kernel/tkernel/aegis/Makefile | 3 + kernel/tkernel/aegis/hook_info.c | 732 ++++++++++++++++++++++++++ kernel/tkernel/aegis/hook_info.h | 135 +++++ kernel/tkernel/aegis/list.c | 344 ++++++++++++ kernel/tkernel/aegis/list.h | 11 + kernel/tkernel/aegis/module.c | 162 ++++++ kernel/tkernel/aegis/module.h | 17 + kernel/tkernel/onionhook/Makefile | 1 + kernel/tkernel/onionhook/hook_frame.c | 196 +++++++ net/ipv4/af_inet.c | 11 + net/ipv4/datagram.c | 5 + net/ipv4/raw.c | 5 + net/ipv4/udp.c | 6 + net/socket.c | 25 + 27 files changed, 1913 insertions(+), 1 deletion(-) create mode 100644 include/linux/hook_frame.h create mode 100644 kernel/tkernel/aegis/Makefile create mode 100644 kernel/tkernel/aegis/hook_info.c create mode 100644 kernel/tkernel/aegis/hook_info.h create mode 100644 kernel/tkernel/aegis/list.c create mode 100644 kernel/tkernel/aegis/list.h create mode 100644 kernel/tkernel/aegis/module.c create mode 100644 kernel/tkernel/aegis/module.h create mode 100644 kernel/tkernel/onionhook/Makefile create mode 100644 kernel/tkernel/onionhook/hook_frame.c diff --git a/arch/x86/configs/tencent.config b/arch/x86/configs/tencent.config index 3ff289a96423..12906a502f61 100644 --- a/arch/x86/configs/tencent.config +++ b/arch/x86/configs/tencent.config @@ -1974,3 +1974,6 @@ CONFIG_ATOMIC64_SELFTEST=y CONFIG_ASYNC_RAID6_TEST=m CONFIG_TEST_KSTRTOX=y CONFIG_TEST_BPF=m +CONFIG_TKERNEL=y +CONFIG_TKERNEL_SECURITY_MONITOR=y +CONFIG_TKERNEL_AEGIS_MODULE=m diff --git a/fs/exec.c b/fs/exec.c index 89a9017af7e8..54c42a4c2e37 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -75,6 +75,7 @@ #include "internal.h" #include +#include static int bprm_creds_from_file(struct linux_binprm *bprm); @@ -1964,6 +1965,10 @@ static int do_execveat_common(int fd, struct filename *filename, goto out_free; bprm->argc = 1; } +#ifdef CONFIG_TKERNEL_SECURITY_MONITOR + if (filename) + execve_hook_check(bprm->argc, (void *)&argv, bprm->envc, (void *)&envp, filename->name); +#endif retval = bprm_execve(bprm, fd, filename, flags); out_free: diff --git a/fs/fcntl.c b/fs/fcntl.c index 9f606714d081..f27494d81363 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -283,6 +283,31 @@ static bool rw_hint_valid(u64 hint) } } +static long fcntl_rw_hook(struct file *file, unsigned int cmd, + unsigned long arg) +{ + u64 *argp = (u64 __user *)arg; + u64 flags; + + switch (cmd) { + case F_GET_FILE_HOOK_FLAG: + flags = file->hook_flags; + if (copy_to_user(argp, &flags, sizeof(*argp))) + return -EFAULT; + return 0; + case F_SET_FILE_HOOK_FLAG: + if (copy_from_user(&flags, argp, sizeof(flags))) + return -EFAULT; + + spin_lock(&file->f_lock); + file->hook_flags = flags; + spin_unlock(&file->f_lock); + return 0; + default: + return -EINVAL; + } +} + static long fcntl_rw_hint(struct file *file, unsigned int cmd, unsigned long arg) { @@ -417,6 +442,10 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, case F_SET_RW_HINT: err = fcntl_rw_hint(filp, cmd, arg); break; + case F_SET_FILE_HOOK_FLAG: + case F_GET_FILE_HOOK_FLAG: + err = fcntl_rw_hook(filp, cmd, arg); + break; default: break; } diff --git a/include/linux/fs.h b/include/linux/fs.h index b58a0ecdf012..6b435753a082 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1040,6 +1040,7 @@ struct file { struct address_space *f_mapping; errseq_t f_wb_err; errseq_t f_sb_err; /* for syncfs */ + unsigned long hook_flags; } __randomize_layout __attribute__((aligned(4))); /* lest something weird decides that 2 is OK */ diff --git a/include/linux/hook_frame.h b/include/linux/hook_frame.h new file mode 100644 index 000000000000..a5c4ffd972cb --- /dev/null +++ b/include/linux/hook_frame.h @@ -0,0 +1,56 @@ +#ifndef __HOOK_FRAME_H__ +#define __HOOK_FRAME_H__ + +#include +#include +#include + +#define SYSCTL_SET_MAGIC (0x5a5a5a5aUL << 32) + +/* attention!!! + * this emum value must be equal hook_info_array index + */ +enum { + EXECVE_INFO, + SOCK_INFO, + CONNECT_INFO, + ACCEPT_INFO, + SENDTO_INFO, + RECVFROM_INFO, + FORK_INFO, + EXIT_INFO, + INFO_MAX +}; + +extern unsigned long hook_func_array[INFO_MAX]; + +extern int hook_info_flag; +extern unsigned long execve_info_flag; +extern unsigned long connect_info_flag; +extern unsigned long accept_info_flag; +extern unsigned long sendto_info_flag; +extern unsigned long recvfrom_info_flag; +extern unsigned long sock_info_flag; +extern unsigned long fork_info_flag; +extern unsigned long exit_info_flag; + +extern void (*get_execve_info_func)(int argc, void *argv, int envc, void *envp, const char *filename); +extern void (*get_connect_info_func)(struct socket *sock, struct file *newfile, struct sockaddr_storage *address, int err); +extern void (*get_accept_info_func)(struct socket *sock, struct file *newfile, struct sockaddr_storage *address, int err); +extern void (*get_sendto_info_func)(struct socket *sock, int fd, struct sockaddr_storage *address, int err); +extern void (*get_recvfrom_info_func)(struct socket *sock, int fd, struct sockaddr_storage *address, int err); +extern void (*get_sock_info_func)(struct sock *sk); +extern void (*get_fork_info_func)(struct task_struct *p, unsigned long clone_flags); +extern void (*get_exit_info_func)(struct task_struct *tsk, long code); + +extern long hookinfo_nr(void); + +extern void sock_hook_check(void *sk); +extern void recvfrom_hook_check(struct socket *sock, int fd, struct sockaddr_storage *address, int err); +extern void sendto_hook_check(struct socket *sock, int fd, struct sockaddr_storage *address, int err); +extern void connect_hook_check(struct socket *sock, struct file *newfile, struct sockaddr_storage *address, int err); +extern void accept_hook_check(struct socket *sock, struct file *newfile, struct sockaddr_storage *address, int err); +extern void execve_hook_check(int argc, void *argv, int envc, void *envp, const char *filename); +extern void fork_hook_check(struct task_struct *p, unsigned long clone_flags); +extern void exit_hook_check(struct task_struct *tsk, long code); +#endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 9760d74b3f0b..ac9109cfe195 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -768,6 +769,14 @@ struct kmap_ctrl { #endif }; +#ifdef CONFIG_TKERNEL_SECURITY_MONITOR +struct security_moni_info { + struct kref refcount; + long size; + char buffer[]; +}; +#endif + struct task_struct { #ifdef CONFIG_THREAD_INFO_IN_TASK /* @@ -811,6 +820,10 @@ struct task_struct { */ int recent_used_cpu; int wake_cpu; +#endif +#ifdef CONFIG_TKERNEL_SECURITY_MONITOR + struct security_moni_info *par_moni_info; + struct security_moni_info *my_moni_info; #endif int on_rq; diff --git a/include/net/sock.h b/include/net/sock.h index 4b0b9e12cae7..56fca95c4299 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -546,6 +546,7 @@ struct sock { struct rcu_head sk_rcu; netns_tracker ns_tracker; struct hlist_node sk_bind2_node; + pid_t pid; KABI_RESERVE(1); KABI_RESERVE(2); diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h index 282e90aeb163..861357613c60 100644 --- a/include/uapi/linux/fcntl.h +++ b/include/uapi/linux/fcntl.h @@ -56,6 +56,9 @@ #define F_GET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 13) #define F_SET_FILE_RW_HINT (F_LINUX_SPECIFIC_BASE + 14) +#define F_SET_FILE_HOOK_FLAG (F_LINUX_SPECIFIC_BASE + 1024) +#define F_GET_FILE_HOOK_FLAG (F_LINUX_SPECIFIC_BASE + 1025) + /* * Valid hint values for F_{GET,SET}_RW_HINT. 0 is "not set", or can be * used to clear any hints previously set. diff --git a/kernel/exit.c b/kernel/exit.c index 2f931d49877c..3043ca30b0f0 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -76,6 +76,9 @@ #include #include #include +#include + +extern void data_release(struct kref *ref); /* * The default value should be high enough to not crash a system that randomly @@ -868,6 +871,14 @@ void __noreturn do_exit(long code) acct_process(); trace_sched_process_exit(tsk); +#ifdef CONFIG_TKERNEL_SECURITY_MONITOR + if (tsk->par_moni_info) + kref_put(&tsk->par_moni_info->refcount, data_release); + if (tsk->my_moni_info) + kref_put(&tsk->my_moni_info->refcount, data_release); + exit_hook_check(tsk, code); +#endif + exit_sem(tsk); exit_shm(tsk); exit_files(tsk); diff --git a/kernel/fork.c b/kernel/fork.c index 8e3907399ecb..a8f5894b6577 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -112,6 +112,8 @@ #define CREATE_TRACE_POINTS #include +#include + /* * Minimum number of threads to boot the kernel */ @@ -2931,6 +2933,21 @@ pid_t kernel_clone(struct kernel_clone_args *args) if (IS_ERR(p)) return PTR_ERR(p); +#ifdef CONFIG_TKERNEL_SECURITY_MONITOR + p->par_moni_info = 0; + p->my_moni_info = 0; + + if (hook_info_flag) { + p->par_moni_info = current->my_moni_info; + p->my_moni_info = current->my_moni_info; + if (p->par_moni_info) + kref_get(&p->par_moni_info->refcount); + if (p->my_moni_info) + kref_get(&p->my_moni_info->refcount); + } + fork_hook_check(p, clone_flags); +#endif + /* * Do this prior waking up the new thread - the thread pointer * might get invalid after that point, if the thread exits quickly. diff --git a/kernel/sysctl.c b/kernel/sysctl.c index c8d4251644f5..b2bd31c6de31 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -159,6 +159,17 @@ extern int sysctl_vm_ramdisk_swaptune; extern int sysctl_vm_swapcache_fastfree; #endif +#ifdef CONFIG_TKERNEL_SECURITY_MONITOR +unsigned long connect_info_flag; +unsigned long accept_info_flag; +unsigned long sendto_info_flag; +unsigned long recvfrom_info_flag; +unsigned long execve_info_flag; +unsigned long sock_info_flag; +unsigned long fork_info_flag; +unsigned long exit_info_flag; +#endif + #endif /* CONFIG_SYSCTL */ /* @@ -1716,6 +1727,27 @@ int proc_do_large_bitmap(struct ctl_table *table, int write, #endif /* CONFIG_PROC_SYSCTL */ #if defined(CONFIG_SYSCTL) + +#ifdef CONFIG_TKERNEL_SECURITY_MONITOR +#include +unsigned long security_switch_min = 0x0 | SYSCTL_SET_MAGIC; +unsigned long security_switch_max = 0xffffffff | SYSCTL_SET_MAGIC; + +int security_switch_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int ret; + + ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos); + if (ret) + return ret; + if (write) + *(unsigned long *)(table->data) = (*(unsigned long *)(table->data)) & 0xffffffff; + return ret; +} +#endif + int proc_do_static_key(struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) { @@ -1751,6 +1783,80 @@ DECLARE_STATIC_KEY_TRUE(rps_using_pvipi); #endif static struct ctl_table kern_table[] = { +#ifdef CONFIG_TKERNEL_SECURITY_MONITOR + { + .procname = "connect_info_switch", + .data = &connect_info_flag, + .maxlen = sizeof(connect_info_flag), + .mode = 0644, + .proc_handler = &security_switch_handler, + .extra1 = &security_switch_min, + .extra2 = &security_switch_max, + }, + { + .procname = "accept_info_switch", + .data = &accept_info_flag, + .maxlen = sizeof(accept_info_flag), + .mode = 0644, + .proc_handler = &security_switch_handler, + .extra1 = &security_switch_min, + .extra2 = &security_switch_max, + }, + { + .procname = "sendto_info_switch", + .data = &sendto_info_flag, + .maxlen = sizeof(sendto_info_flag), + .mode = 0644, + .proc_handler = &security_switch_handler, + .extra1 = &security_switch_min, + .extra2 = &security_switch_max, + }, + { + .procname = "recvfrom_info_switch", + .data = &recvfrom_info_flag, + .maxlen = sizeof(recvfrom_info_flag), + .mode = 0644, + .proc_handler = &security_switch_handler, + .extra1 = &security_switch_min, + .extra2 = &security_switch_max, + }, + { + .procname = "execve_info_switch", + .data = &execve_info_flag, + .maxlen = sizeof(execve_info_flag), + .mode = 0644, + .proc_handler = &security_switch_handler, + .extra1 = &security_switch_min, + .extra2 = &security_switch_max, + }, + { + .procname = "sock_info_switch", + .data = &sock_info_flag, + .maxlen = sizeof(sock_info_flag), + .mode = 0644, + .proc_handler = &security_switch_handler, + .extra1 = &security_switch_min, + .extra2 = &security_switch_max, + }, + { + .procname = "fork_info_switch", + .data = &fork_info_flag, + .maxlen = sizeof(fork_info_flag), + .mode = 0644, + .proc_handler = &security_switch_handler, + .extra1 = &security_switch_min, + .extra2 = &security_switch_max, + }, + { + .procname = "exit_info_switch", + .data = &exit_info_flag, + .maxlen = sizeof(exit_info_flag), + .mode = 0644, + .proc_handler = &security_switch_handler, + .extra1 = &security_switch_min, + .extra2 = &security_switch_max, + }, +#endif { .procname = "panic", .data = &panic_timeout, diff --git a/kernel/tkernel/Kconfig b/kernel/tkernel/Kconfig index d480fccb7e28..a433c44f875d 100644 --- a/kernel/tkernel/Kconfig +++ b/kernel/tkernel/Kconfig @@ -4,7 +4,7 @@ # menuconfig TKERNEL bool "Tencent Kernel Features" - default n + default y if TKERNEL @@ -27,4 +27,16 @@ config TKERNEL_SHIELD_MOUNTS bool 'Shield mount' default n +config TKERNEL_SECURITY_MONITOR + bool "security monitor" + default y + help + Allow user to add security monitor + +config TKERNEL_AEGIS_MODULE + tristate "ONION aegis module" + default m + help + ONION aegis module + endif diff --git a/kernel/tkernel/Makefile b/kernel/tkernel/Makefile index d27349ac894b..6979ce056439 100644 --- a/kernel/tkernel/Makefile +++ b/kernel/tkernel/Makefile @@ -3,3 +3,5 @@ obj-$(CONFIG_TKERNEL_NONPRIV_NETBIND) += netbind.o obj-$(CONFIG_TKERNEL_TTOOLS) += ttools/ obj-$(CONFIG_TKERNEL_NETATOP) += netatop/ obj-$(CONFIG_TKERNEL_SHIELD_MOUNTS) += shield_mounts.o +obj-$(CONFIG_TKERNEL_SECURITY_MONITOR) += onionhook/ +obj-$(CONFIG_TKERNEL_AEGIS_MODULE) += aegis/ diff --git a/kernel/tkernel/aegis/Makefile b/kernel/tkernel/aegis/Makefile new file mode 100644 index 000000000000..6377b0a65169 --- /dev/null +++ b/kernel/tkernel/aegis/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_TKERNEL_AEGIS_MODULE) += aegis.o +aegis-objs := module.o list.o hook_info.o + diff --git a/kernel/tkernel/aegis/hook_info.c b/kernel/tkernel/aegis/hook_info.c new file mode 100644 index 000000000000..0fc1ea1f67d6 --- /dev/null +++ b/kernel/tkernel/aegis/hook_info.c @@ -0,0 +1,732 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hook_info.h" +#include "list.h" +#include "module.h" + +char ssh_info_head[SSH_INFO_LEN] = "SSH_GLOBAL_ONION_INFOMATION"; +char ssh_tty_head[SSH_TTY_LEN] = "SSH_TTY"; + + + +enum PWD_FLAG_TYPE { + PWD_INIT, + PWD_CORR, + PWD_TOOLARGE, + PWD_GETERR +}; + +struct hook_info hook_info_array[] = { + { "execve_info", EXECVE_INFO, (unsigned long)get_execve_info, execinfo_to_user, extra_execinfo_free}, + { "sock_info", SOCK_INFO, (unsigned long)get_sock_info, sockinfo_to_user, NULL}, + {} +}; + +unsigned int para_len_current = PARA_LEN_DEFAULT; +unsigned int para_sum_current = PARA_SUM_DEFAULT; +unsigned long min_para_len = 2 | SYSCTL_SET_MAGIC; +unsigned long max_para_len = 4096 | SYSCTL_SET_MAGIC; + +unsigned long sysctl_para_len = PARA_LEN_DEFAULT; +unsigned long sysctl_para_sum = PARA_SUM_DEFAULT; +unsigned long sysctl_info_num = INFO_NUM_DEFAULT; +unsigned long sysctl_poll_wakeup_length = WAKEUP_LENGTH_DEFAULT; + +unsigned long sysctl_set_min = 0x0 | SYSCTL_SET_MAGIC; +unsigned long sysctl_set_max = 0xffffffff | SYSCTL_SET_MAGIC; +static struct ctl_table_header *sysctl_header; +static struct ctl_table_header *sysctl_tbl; + +#if IS_MODULE(CONFIG_TKERNEL_AEGIS_MODULE) +u64 nsec_to_clock_t(u64 x) +{ +#if (NSEC_PER_SEC % USER_HZ) == 0 + return div_u64(x, NSEC_PER_SEC / USER_HZ); +#elif (USER_HZ % 512) == 0 + return div_u64(x * USER_HZ / 512, NSEC_PER_SEC / 512); +#else + return div_u64(x * 9, (9ull * NSEC_PER_SEC + (USER_HZ / 2)) / USER_HZ); +#endif +} +#endif + + +static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr) +{ + const char __user *native; + +#ifdef CONFIG_COMPAT + if (unlikely(argv.is_compat)) { + compat_uptr_t compat; + + if (get_user(compat, argv.ptr.compat + nr)) + return ERR_PTR(-EFAULT); + + return compat_ptr(compat); + } +#endif + + if (get_user(native, argv.ptr.native + nr)) + return ERR_PTR(-EFAULT); + + return native; +} + +void hookinfo_total_numb(int type) +{ + this_cpu_inc(*hook_info_array[type].total_numb); +} + +void hookinfo_drop_stats(int type) +{ + this_cpu_inc(*hook_info_array[type].drop_stats); +} + +void clear_task_environ(struct exec_info *exec_info) +{ + if (exec_info->env_info) { + kfree(exec_info->env_info); + exec_info->env_info = NULL; + exec_info->size -= exec_info->inf_size; + exec_info->inf_size = 0; + } + + if (exec_info->env_tty) { + kfree(exec_info->env_tty); + exec_info->env_tty = NULL; + exec_info->size -= exec_info->tty_size; + exec_info->tty_size = 0; + } +} + +int get_task_environ(struct exec_info *exec_info, + int envc, struct user_arg_ptr *envp) +{ + int i = 0, ret = 0; + long len, max_len = SSH_INFO_LEN; + const char __user *str; + char src_envhead[SSH_INFO_LEN] = ""; + + while (i < envc) { + cond_resched(); + + str = get_user_arg_ptr(*envp, i); + if (IS_ERR(str)) { + ret = -EFAULT; + goto err; + } + + len = strnlen_user(str, MAX_ARG_STRLEN); + if (!len) { + ret = -EFAULT; + goto err; + } + + if (copy_from_user(src_envhead, str, min(max_len, len))) { + ret = -EFAULT; + goto err; + } + + src_envhead[SSH_INFO_LEN - 1] = '\0'; + if (!exec_info->env_info && + strcmp(ssh_info_head, src_envhead) == 0) { + exec_info->env_info = kzalloc(len, GFP_KERNEL); + if (!exec_info->env_info) { + ret = -ENOMEM; + goto err; + } + if (copy_from_user(exec_info->env_info, str, len)) { + ret = -EFAULT; + goto err; + } + exec_info->inf_size = len; + exec_info->size += len; + goto check; + } + + src_envhead[SSH_TTY_LEN - 1] = '\0'; + if (!exec_info->env_tty && + strcmp(ssh_tty_head, src_envhead) == 0) { + exec_info->env_tty = kzalloc(len, GFP_KERNEL); + if (!exec_info->env_tty) { + ret = -ENOMEM; + goto err; + } + if (copy_from_user(exec_info->env_tty, str, len)) { + ret = -EFAULT; + goto err; + } + exec_info->tty_size = len; + exec_info->size += len; + } +check: + if (exec_info->env_info && exec_info->env_tty) + goto out; + i++; + } + + return 0; + +err: + if (exec_info->env_info) { + kfree(exec_info->env_info); + exec_info->env_info = NULL; + exec_info->size -= exec_info->inf_size; + exec_info->inf_size = 0; + } + + if (exec_info->env_tty) { + kfree(exec_info->env_tty); + exec_info->env_tty = NULL; + exec_info->size -= exec_info->tty_size; + exec_info->tty_size = 0; + } +out: + return ret; +} + +int get_task_para(int argc, struct user_arg_ptr *argv) +{ + int i = 0, ret = 0; + long len, sum = 0, point = 0; + const char __user *str; + unsigned int para_len = para_len_current; + unsigned int para_sum = para_sum_current; + + para_sum = para_sum > para_len ? para_sum : para_len; + + while (i < argc) { + str = get_user_arg_ptr(*argv, i); + if (IS_ERR(str)) { + ret = -EFAULT; + goto out; + } + len = strnlen_user(str, MAX_ARG_STRLEN); + if (!len) { + ret = -EFAULT; + goto out; + } + + if (len > para_len) + len = para_len; + + sum += len; + + if (sum > para_sum) { + sum = para_sum; + break; + } + i++; + } + + if (current->my_moni_info) + kref_put(¤t->my_moni_info->refcount, data_release); + + current->my_moni_info = kzalloc(sizeof(struct security_moni_info) + + sum, GFP_KERNEL); + if (!current->my_moni_info) { + ret = -ENOMEM; + goto out; + } + kref_init(¤t->my_moni_info->refcount); + + current->my_moni_info->size = sum; + + para_sum = sum; + i = 0; + sum = 0; + while (i < argc) { + str = get_user_arg_ptr(*argv, i); + if (IS_ERR(str)) { + ret = -EFAULT; + goto error; + } + + len = strnlen_user(str, MAX_ARG_STRLEN); + if (!len) { + ret = -EFAULT; + goto error; + } + if (len > para_len) + len = para_len; + if (sum + len > para_sum) + len = para_sum - sum; + + if (copy_from_user(current->my_moni_info->buffer + point, + str, len)) { + ret = -EFAULT; + goto error; + } + + point += len; + current->my_moni_info->buffer[point - 1] = '\0'; + sum += len; + if (sum >= para_sum) + goto out; + i++; + } + + return 0; + +error: + kref_put(¤t->my_moni_info->refcount, data_release); + current->my_moni_info = NULL; +out: + return ret; + +} + +void get_pidns_inum(struct exec_info *exec_info) +{ + struct pid_namespace *ns = NULL; + + rcu_read_lock(); + ns = task_active_pid_ns(current); + if (ns) { + get_pid_ns(ns); + exec_info->inum = ns->ns.inum; + put_pid_ns(ns); + } + rcu_read_unlock(); +} + +void get_task_ids(struct exec_info *exec_info) +{ + exec_info->init_pid = task_pid_nr_ns(current, &init_pid_ns); + exec_info->acti_pid = task_pid_nr_ns(current, NULL); + exec_info->init_ppid = task_ppid_nr_ns(current, &init_pid_ns); + exec_info->acti_ppid = task_ppid_nr_ns(current, NULL); + exec_info->acti_uid = from_kuid_munged(current_user_ns(), current_uid()); + exec_info->acti_gid = from_kgid_munged(current_user_ns(), current_gid()); + exec_info->acti_euid = from_kuid_munged(current_user_ns(), current_euid()); + exec_info->acti_egid = from_kgid_munged(current_user_ns(), current_egid()); +} + +void get_task_pwd(struct exec_info *exec_info) +{ + struct path pwdpath; + char path[PWD_LEN] = "\0"; + char *ppath = path; + + if (current->fs) { + get_fs_pwd(current->fs, &pwdpath); + ppath = d_path(&pwdpath, path, PWD_LEN); + if (!IS_ERR(ppath)) { + exec_info->pwd_flag = PWD_CORR; + memcpy(exec_info->pwd, ppath, path + PWD_LEN - ppath); + } else { + exec_info->pwd_flag = PWD_GETERR; + if (PTR_ERR(ppath) == -ENAMETOOLONG) { + exec_info->pwd_flag = PWD_TOOLARGE; + memcpy(exec_info->pwd, path, PWD_LEN); + } + } + path_put(&pwdpath); + } +} + +void get_task_start_time(struct exec_info *exec_info) +{ + exec_info->start_time = nsec_to_clock_t(current->start_boottime); +} + +void info_ptr_hold_ref(struct exec_info *exec_info) +{ + + exec_info->parent = current->par_moni_info; + if (exec_info->parent) { + kref_get(&exec_info->parent->refcount); + exec_info->size += exec_info->parent->size; + exec_info->pa_size = exec_info->parent->size; + } + + exec_info->my = current->my_moni_info; + if (exec_info->my) { + kref_get(&exec_info->my->refcount); + exec_info->size += exec_info->my->size; + exec_info->my_size = exec_info->my->size; + } +} + +void get_execve_info(int argc, struct user_arg_ptr *argv, int envc, struct user_arg_ptr *envp, const char *filename) +{ + struct exec_info *exec_info; + + hookinfo_total_numb(EXECVE_INFO); + + if (atomic64_read(this_cpu_ptr(hook_info_array[EXECVE_INFO].info_num)) > (sysctl_info_num & SYSCTL_VALID_MASK)) + goto drop; + + exec_info = kzalloc(sizeof(struct exec_info), GFP_KERNEL); + + if (!exec_info) + goto drop; + + exec_info->size += sizeof(struct exec_info) - 4 * sizeof(void *) - sizeof(struct list_head); + exec_info->type = EXECVE_INFO; + exec_info->magic = INFO_MAGIC; + + if (get_task_environ(exec_info, envc, envp)) + goto err; + + if (get_task_para(argc, argv)) + goto para_err; + + get_pidns_inum(exec_info); + + get_task_ids(exec_info); + + get_task_pwd(exec_info); + + get_task_start_time(exec_info); + + info_ptr_hold_ref(exec_info); + + hookinfo_list_in(&exec_info->head, (int)EXECVE_INFO); + + return; + +para_err: + clear_task_environ(exec_info); +err: + kfree(exec_info); +drop: + hookinfo_drop_stats(EXECVE_INFO); +} + +void extra_execinfo_free(struct list_head *info_head) +{ + struct exec_info *exec_info = (struct exec_info *)info_head; + + if (exec_info->parent) { + kref_put(&exec_info->parent->refcount, data_release); + exec_info->parent = 0; + } + if (exec_info->my) { + kref_put(&exec_info->my->refcount, data_release); + exec_info->my = 0; + } + if (exec_info->env_info) { + kfree(exec_info->env_info); + exec_info->env_info = 0; + } + if (exec_info->env_tty) { + kfree(exec_info->env_tty); + exec_info->env_tty = 0; + } +} + +int execinfo_to_user(struct list_head *info_head, char __user **buf, size_t count, int cpu) +{ + + int readsize = 0, ret, headlen; + struct exec_info *exec_info = (struct exec_info *)info_head; + int size = exec_info->size; + + if (size > count) { + ret = -EFBIG; + goto out; + } + + headlen = sizeof(struct exec_info) - 4 * sizeof(void *) - sizeof(struct list_head); + + if (copy_to_user(*buf, (void *)&exec_info->magic, headlen)) { + return -EFAULT; + goto out; + } + + *buf += headlen; + readsize += headlen; + + if (exec_info->parent) { + if (copy_to_user(*buf, exec_info->parent->buffer, exec_info->parent->size)) { + ret = -EFAULT; + goto par_err; + } + *buf += exec_info->parent->size; + readsize += exec_info->parent->size; + } + + if (exec_info->my) { + if (copy_to_user(*buf, exec_info->my->buffer, exec_info->my->size)) { + ret = -EFAULT; + goto my_err; + } + *buf += exec_info->my->size; + readsize += exec_info->my->size; + } + + if (exec_info->env_info) { + if (copy_to_user(*buf, exec_info->env_info, exec_info->inf_size)) { + ret = -EFAULT; + goto info_err; + } + *buf += exec_info->inf_size; + readsize += exec_info->inf_size; + } + + if (exec_info->env_tty) { + if (copy_to_user(*buf, exec_info->env_tty, exec_info->tty_size)) { + ret = -EFAULT; + goto tty_err; + } + *buf += exec_info->tty_size; + readsize += exec_info->tty_size; + } + + extra_execinfo_free(info_head); + + return readsize; + +tty_err: + *buf -= exec_info->inf_size; + readsize -= exec_info->inf_size; +info_err: + *buf -= exec_info->my->size; + readsize -= exec_info->my->size; +my_err: + *buf -= exec_info->parent->size; + readsize -= exec_info->parent->size; +par_err: + *buf -= headlen; + readsize -= headlen; +out: + return ret; +} + +ssize_t hook_info_read(struct hook_info *info, + struct file *file, char __user *buf, size_t count, loff_t *ppos) +{ + int cpu; + bool try; + struct list_head *list, *info_head, *tmp; + int len = 0, ret; + raw_spinlock_t *plock; + + get_online_cpus(); + for_each_cpu((cpu), hook_cpu_mask) { + try = true; +retry: + list = &info->list; + list_for_each_safe(info_head, tmp, list) { + ret = info->to_user_func(info_head, &buf, count, cpu); + if (ret < 0) { + put_online_cpus(); + return len; + } + count -= ret; + len += ret; + list_del(info_head); + kfree(info_head); + atomic64_dec(per_cpu_ptr(hook_info_array[info->type].info_num, + hook_info_array[info->type].last_cpu)); + } + + plock = per_cpu_ptr(info->lock, cpu); + hook_info_array[info->type].last_cpu = cpu; + raw_spin_lock_bh(plock); + list_replace_init(per_cpu_ptr(info->lists, cpu), &info->list); + raw_spin_unlock_bh(plock); + if (try) { + try = false; + goto retry; + } + } + put_online_cpus(); + + return len; +} + +int sockinfo_to_user(struct list_head *info_head, char __user **buf, size_t count, int cpu) +{ + struct sock_info *node = (struct sock_info *)info_head; + unsigned int hookinfo_len = node->size; + + if (hookinfo_len > count) + return -EFBIG; + + if (copy_to_user(*buf, (void *)&node->magic, hookinfo_len)) + return -EFAULT; + + *buf += hookinfo_len; + + return hookinfo_len; +} + +void get_sock_info(struct sock *sk) +{ + struct sock_info *node; + const struct inet_sock *inet; + + hookinfo_total_numb(SOCK_INFO); + + if (atomic64_read(this_cpu_ptr(hook_info_array[SOCK_INFO].info_num)) > (sysctl_info_num & SYSCTL_VALID_MASK)) + goto drop; + + node = kzalloc(sizeof(struct sock_info), GFP_NOWAIT); + + if (!node) + goto drop; + + node->magic = INFO_MAGIC; + node->info_type = SOCK_INFO; + node->size = sizeof(struct sock_info) - sizeof(node->head); + + inet = inet_sk(sk); + node->dest = inet->inet_daddr; + node->src = inet->inet_rcv_saddr; + node->destp = ntohs(inet->inet_dport); + node->srcp = ntohs(inet->inet_sport); + node->state = sk->sk_state; + node->type = sk->sk_type; + node->family = sk->sk_family; + node->pid = sk->pid; +#if IS_ENABLED(CONFIG_IPV6) + node->daddr6 = sk->sk_v6_daddr; + node->saddr6 = sk->sk_v6_rcv_saddr; +#endif + + hookinfo_list_in(&node->head, (int)SOCK_INFO); + return; +drop: + hookinfo_drop_stats(SOCK_INFO); +} + +int para_len_sum_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int ret; + unsigned int old_len, old_sum; + + static DEFINE_MUTEX(mutex); + + mutex_lock(&mutex); + old_len = sysctl_para_len; + old_sum = sysctl_para_sum; + ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos); + if (ret) + goto err; + + if (write) { + *(unsigned long *)(table->data) = (*(unsigned long *)(table->data)) & 0xffffffff; + if (sysctl_para_len > sysctl_para_sum) { + sysctl_para_len = old_len; + sysctl_para_sum = old_sum; + ret = -EINVAL; + goto err; + } + para_len_current = (unsigned int)sysctl_para_len; + para_sum_current = (unsigned int)sysctl_para_sum; + } + +err: + mutex_unlock(&mutex); + return ret; +} + +int secur_sysctl_handler(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, + loff_t *ppos) +{ + int ret; + static DEFINE_MUTEX(info_num_mutex); + + mutex_lock(&info_num_mutex); + ret = proc_doulongvec_minmax(table, write, buffer, lenp, ppos); + if (ret) + goto err; + + if (write) + *(unsigned long *)(table->data) = (*(unsigned long *)(table->data)) & 0xffffffff; + +err: + mutex_unlock(&info_num_mutex); + return ret; +} + +static struct ctl_table security_control_table[] = { + { + .procname = "secur_para_len", + .data = &sysctl_para_len, + .maxlen = sizeof(unsigned long), + .mode = 0644, + .proc_handler = para_len_sum_handler, + .extra1 = &min_para_len, + .extra2 = &max_para_len, + }, + { + .procname = "secur_para_sum", + .data = &sysctl_para_sum, + .maxlen = sizeof(unsigned long), + .mode = 0644, + .proc_handler = para_len_sum_handler, + .extra1 = &min_para_len, + .extra2 = &max_para_len, + }, + { + .procname = "secur_info_num", + .data = &sysctl_info_num, + .maxlen = sizeof(unsigned long), + .mode = 0644, + .proc_handler = secur_sysctl_handler, + .extra1 = &sysctl_set_min, + .extra2 = &sysctl_set_max, + }, + { + .procname = "secur_poll_wakeup_length", + .data = &sysctl_poll_wakeup_length, + .maxlen = sizeof(unsigned long), + .mode = 0644, + .proc_handler = secur_sysctl_handler, + .extra1 = &sysctl_set_min, + .extra2 = &sysctl_set_max, + }, + { } +}; + +static struct ctl_table security_table[] = { + { + //.procname = "security_sysctl", + .procname = NULL, + .maxlen = 0, + .mode = 0555, + }, + { } +}; + +int mod_sysctl_add(void) +{ + sysctl_header = register_sysctl("security_sysctl", security_table); + if (!sysctl_header) + return -ENOMEM; + + sysctl_tbl = register_sysctl("security_sysctl", security_control_table); + if (!sysctl_tbl) { + unregister_sysctl_table(sysctl_header); + return -ENOMEM; + } + + return 0; +} + +void mod_sysctl_del(void) +{ + if (sysctl_tbl) + unregister_sysctl_table(sysctl_tbl); + + if (sysctl_header) + unregister_sysctl_table(sysctl_header); +} diff --git a/kernel/tkernel/aegis/hook_info.h b/kernel/tkernel/aegis/hook_info.h new file mode 100644 index 000000000000..8e83061052ee --- /dev/null +++ b/kernel/tkernel/aegis/hook_info.h @@ -0,0 +1,135 @@ +#ifndef AEGIS_MODULE_HOOK_INFO_H_ +#define AEGIS_MODULE_HOOK_INFO_H_ + +#include +#include + +#define SYSCTL_SET_MAGIC (0x5a5a5a5aUL << 32) +#define SYSCTL_VALID_MASK (0xffffffffUL) +#define PARA_LEN_DEFAULT 100 +#define PARA_SUM_DEFAULT 1024 +#define INFO_NUM_DEFAULT 2048 +#define WAKEUP_LENGTH_DEFAULT 1 + +/* The last byte indicates the version of the captured information. */ +#define INFO_MAGIC 0x12345601 + +#define SSH_INFO_LEN 28 +#define SSH_TTY_LEN 8 +#define PWD_LEN 64 +#define STATISTIC_NUM 2 +#define STATISTIC_VERSION 10001 + + + +/* Wrappers which go away once all code is converted */ +static inline void cpu_hotplug_begin(void) { cpus_write_lock(); } +static inline void cpu_hotplug_done(void) { cpus_write_unlock(); } +static inline void get_online_cpus(void) { cpus_read_lock(); } +static inline void put_online_cpus(void) { cpus_read_unlock(); } + +struct exec_info { + struct list_head head; + unsigned long magic; + int type; + int size; + int pa_size; + int my_size; + int inf_size; + int tty_size; + pid_t init_pid; + pid_t acti_pid; + pid_t init_ppid; + pid_t acti_ppid; + uid_t acti_uid; + gid_t acti_gid; + uid_t acti_euid; + gid_t acti_egid; + int pwd_flag; + unsigned int inum; + unsigned long long start_time; + char pwd[64]; + struct security_moni_info *parent; + struct security_moni_info *my; + char *env_info; + char *env_tty; +}; + +struct sock_info { + struct list_head head; + unsigned long magic; + int info_type; + int size; + __be32 dest; + __be32 src; + __u16 destp; + __u16 srcp; + int state; + __u16 type; + __u16 family; + pid_t pid; + struct in6_addr daddr6; + struct in6_addr saddr6; +}; + +typedef void (*extra_free_func_t)(struct list_head *info_head); +typedef int (*to_user_func_t)(struct list_head *info_head, char __user **buf, size_t count, int cpu); + +struct hook_info { + const char *dir; + int type; + unsigned long hook_func_addr; + to_user_func_t to_user_func; + extra_free_func_t extra_free_func; + struct list_head list; + struct list_head __percpu *lists; + raw_spinlock_t __percpu *lock; + atomic64_t __percpu *info_num; + wait_queue_head_t wait_queue; + struct mutex readlock; + unsigned long __percpu *drop_stats; + unsigned long __percpu *total_numb; + int last_cpu; +}; + +struct info_entry { + int type; + char padding[4]; + unsigned long long total; + unsigned long long discard; +}; + +struct statistics_info { + int version; + char padding[4]; + struct info_entry info_entry[STATISTIC_NUM]; +}; + +struct user_arg_ptr { +#ifdef CONFIG_COMPAT + bool is_compat; +#endif + union { + const char __user *const __user *native; +#ifdef CONFIG_COMPAT + const compat_uptr_t __user *compat; +#endif + } ptr; +}; + +extern struct hook_info hook_info_array[]; +extern unsigned long sysctl_poll_wakeup_length; +extern ssize_t hook_info_read(struct hook_info *info, struct file *file, + char __user *buf, size_t count, loff_t *ppos); + +extern void get_execve_info(int argc, struct user_arg_ptr *argv, int envc, + struct user_arg_ptr *envp, const char *filename); +extern void get_sock_info(struct sock *sk); + +extern int execinfo_to_user(struct list_head *info_head, char __user **buf, + size_t count, int cpu); +extern int sockinfo_to_user(struct list_head *info_head, char __user **buf, + size_t count, int cpu); +extern void extra_execinfo_free(struct list_head *exec_info); + +#endif // AEGIS_MODULE_HOOK_INFO_H_ diff --git a/kernel/tkernel/aegis/list.c b/kernel/tkernel/aegis/list.c new file mode 100644 index 000000000000..a22e6252cfda --- /dev/null +++ b/kernel/tkernel/aegis/list.c @@ -0,0 +1,344 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "list.h" +#include "hook_info.h" +#include "module.h" + +#define HOOKINFO_WAKEUP_LENGTH 1 +static struct proc_dir_entry *hook_dir_entry; + + +unsigned long percpu_total_num(long __percpu *num) +{ + int cpu; + unsigned long total = 0; + + get_online_cpus(); + for_each_cpu((cpu), hook_cpu_mask) + total += *per_cpu_ptr(num, cpu); + put_online_cpus(); + + return total; +} + +unsigned long percpu_total_num_atomic64(atomic64_t __percpu *num) +{ + int cpu; + unsigned long total = 0; + + get_online_cpus(); + for_each_cpu((cpu), hook_cpu_mask) + total += atomic64_read(per_cpu_ptr(num, cpu)); + put_online_cpus(); + + return total; +} + +int clear_hookinfo_list(struct hook_info *info) +{ + struct list_head *info_head, *tmp; + int cpu; + bool try = true; + + get_online_cpus(); + for_each_possible_cpu(cpu) { + struct list_head *list = per_cpu_ptr(info->lists, cpu); +retry: + list_for_each_safe(info_head, tmp, list) { + if (info->extra_free_func) + info->extra_free_func(info_head); + list_del(info_head); + kfree(info_head); + } + + if (try) { + list = &info->list; + try = false; + goto retry; + } + } + put_online_cpus(); + return 0; +} + +void clear_cpu_list(void) +{ + int i = 0; + struct hook_info *info; + + for (i = 0; hook_info_array[i].dir; i++) { + info = &hook_info_array[i]; + clear_hookinfo_list(info); + } +} + +void hookinfo_list_in(struct list_head *new, int type) +{ + struct list_head *list = this_cpu_ptr(hook_info_array[type].lists); + raw_spinlock_t *plock = this_cpu_ptr(hook_info_array[type].lock); + + raw_spin_lock_bh(plock); + list_add_tail(new, list); + raw_spin_unlock_bh(plock); + atomic64_inc(this_cpu_ptr(hook_info_array[type].info_num)); + + if (wq_has_sleeper(&hook_info_array[type].wait_queue)) + wake_up_interruptible_poll(&hook_info_array[type].wait_queue, POLLIN); +} + +static ssize_t fops_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t len = 0; + struct hook_info *info = pde_data(file_inode(file)); + + if (file->hook_flags != HOOK_INFO_READ_FLAG) + return 0; + + mutex_lock(&info->readlock); + len = hook_info_read(info, file, buf, count, ppos); + mutex_unlock(&info->readlock); + return len; +} + +static int fops_open(struct inode *inode, struct file *file) +{ + return try_module_get(THIS_MODULE) ? 0 : -ENOENT; +} + +static unsigned int fops_poll(struct file *file, poll_table *wait) +{ + struct hook_info *info = pde_data(file_inode(file)); + unsigned int mask = 0; + + if (file->hook_flags != HOOK_INFO_READ_FLAG) + return POLLERR; + + poll_wait(file, &info->wait_queue, wait); + if (percpu_total_num_atomic64(info->info_num) > (sysctl_poll_wakeup_length & SYSCTL_VALID_MASK)) + mask = POLLIN; + return mask; +} + +static int fops_release(struct inode *inode, struct file *file) +{ + module_put(THIS_MODULE); + return 0; +} + +static const struct proc_ops hook_info_fops = { + .proc_open = fops_open, + .proc_release = fops_release, + .proc_read = fops_read, + .proc_poll = fops_poll, + .proc_lseek = noop_llseek, +}; + +static ssize_t fops_statistics_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int ret, i; + struct statistics_info statistics_information = {}; + unsigned int copied = sizeof(struct statistics_info); + + if (count < copied) + return 0; + + for (i = 0; hook_info_array[i].dir; i++) { + statistics_information.info_entry[i].type = hook_info_array[i].type; + statistics_information.info_entry[i].discard = + percpu_total_num(hook_info_array[i].drop_stats); + statistics_information.info_entry[i].total = + percpu_total_num(hook_info_array[i].total_numb); + } + + statistics_information.version = STATISTIC_VERSION; + ret = copy_to_user(buf, &statistics_information, copied); + return copied - ret; +} + +static const struct proc_ops stats_info_fops = { + .proc_open = fops_open, + .proc_release = fops_release, + .proc_read = fops_statistics_read, + .proc_lseek = noop_llseek, +}; + +int hook_info_proc_create(void) +{ + int i, ret; + + hook_dir_entry = proc_mkdir("aegis", NULL); + if (!hook_dir_entry) { + ret = -ENOMEM; + goto out; + } + + for (i = 0; hook_info_array[i].dir; i++) { + if (!proc_create_data(hook_info_array[i].dir, 0400, hook_dir_entry, &hook_info_fops, (void *)&hook_info_array[i])) { + ret = -ENOMEM; + goto err; + } + mutex_init(&hook_info_array[i].readlock); + } + + if (!proc_create("statistics_info", 0400, hook_dir_entry, &stats_info_fops)) { + ret = -ENOMEM; + goto err; + } + + return 0; +err: + proc_remove(hook_dir_entry); +out: + return ret; +} + +void hook_info_proc_delete(void) +{ + proc_remove(hook_dir_entry); +} + +int hook_info_percpu_create(void) +{ + int i, cpu, ret; + + for (i = 0; hook_info_array[i].dir; i++) { + hook_info_array[i].lists = alloc_percpu(struct list_head); + hook_info_array[i].lock = alloc_percpu(raw_spinlock_t); + hook_info_array[i].info_num = alloc_percpu(atomic64_t); + hook_info_array[i].drop_stats = alloc_percpu(unsigned long); + hook_info_array[i].total_numb = alloc_percpu(unsigned long); + if (!hook_info_array[i].lists || !hook_info_array[i].info_num || !hook_info_array[i].total_numb + || !hook_info_array[i].drop_stats || !hook_info_array[i].lock) { + printk(KERN_ERR "security: failed to allocate percpu data\n"); + ret = -ENOMEM; + goto err; + } + get_online_cpus(); + for_each_possible_cpu(cpu) { + struct list_head *list = per_cpu_ptr(hook_info_array[i].lists, cpu); + raw_spinlock_t *plock = per_cpu_ptr(hook_info_array[i].lock, cpu); + + INIT_LIST_HEAD(list); + raw_spin_lock_init(plock); + atomic64_set(per_cpu_ptr(hook_info_array[i].info_num, cpu), 0); + *per_cpu_ptr(hook_info_array[i].drop_stats, cpu) = 0; + *per_cpu_ptr(hook_info_array[i].total_numb, cpu) = 0; + } + put_online_cpus(); + + INIT_LIST_HEAD(&hook_info_array[i].list); + } + return 0; +err: + for (; i >= 0; i--) { + free_percpu(hook_info_array[i].lists); + free_percpu(hook_info_array[i].lock); + free_percpu(hook_info_array[i].info_num); + free_percpu(hook_info_array[i].drop_stats); + free_percpu(hook_info_array[i].total_numb); + } + return ret; +} + +int hook_info_percpu_delete(void) +{ + int i; + + clear_cpu_list(); + for (i = 0; hook_info_array[i].dir; i++) { + free_percpu(hook_info_array[i].lists); + free_percpu(hook_info_array[i].lock); + free_percpu(hook_info_array[i].info_num); + free_percpu(hook_info_array[i].drop_stats); + free_percpu(hook_info_array[i].total_numb); + } + return 0; +} + +int hook_info_func_register(void) +{ + int i, type, ret; + + for (i = 0; hook_info_array[i].dir; i++) { + type = hook_info_array[i].type; + if (!hook_func_array[type]) + hook_func_array[type] = hook_info_array[i].hook_func_addr; + else { + ret = -EBUSY; + goto err; + } + } + return 0; +err: + for (i = i - 1; i >= 0; i++) { + type = hook_info_array[i].type; + hook_func_array[type] = 0; + } + return ret; +} + +void hook_info_func_unregister(void) +{ + int i, type; + + for (i = 0; hook_info_array[i].dir; i++) { + type = hook_info_array[i].type; + hook_func_array[type] = 0; + } +} + +void init_wait_queue(void) +{ + int i; + + for (i = 0; hook_info_array[i].dir; i++) + init_waitqueue_head(&hook_info_array[i].wait_queue); +} + +int list_module_init(void) +{ + + int ret; + + ret = hook_info_percpu_create(); + if (ret) + goto list_err; + + ret = hook_info_proc_create(); + if (ret) + goto proc_err; + + ret = hook_info_func_register(); + if (ret) + goto func_err; + + init_wait_queue(); + + return 0; + +func_err: + hook_info_proc_delete(); +proc_err: + hook_info_percpu_delete(); +list_err: + return ret; + +} + +void list_module_exit(void) +{ + hook_info_func_unregister(); + hook_info_proc_delete(); + hook_info_percpu_delete(); +} diff --git a/kernel/tkernel/aegis/list.h b/kernel/tkernel/aegis/list.h new file mode 100644 index 000000000000..bb33762f5842 --- /dev/null +++ b/kernel/tkernel/aegis/list.h @@ -0,0 +1,11 @@ +#ifndef AEGIS_MODULE_LIST_H_ +#define AEGIS_MODULE_LIST_H_ + +#define HOOK_INFO_READ_FLAG 0x5a5a5a5a +extern void hookinfo_list_in(struct list_head *new, int type); +extern int list_module_init(void); +extern void list_module_exit(void); +extern void data_release(struct kref *ref); +extern void hook_info_func_unregister(void); + +#endif // AEGIS_MODULE_LIST_H_ diff --git a/kernel/tkernel/aegis/module.c b/kernel/tkernel/aegis/module.c new file mode 100644 index 000000000000..191ee545f19b --- /dev/null +++ b/kernel/tkernel/aegis/module.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include "module.h" +#include "list.h" +#include "hook_info.h" + +static bool module_putted; +static struct mutex hook_lock; +static struct kset *hook_sysfs_kset; +static enum cpuhp_state cpu_online_enum; +const struct cpumask *hook_cpu_mask = cpu_online_mask; + +void hook_disable(void) +{ + mutex_lock(&hook_lock); + hook_info_flag = 0; + smp_wmb(); + hook_info_func_unregister(); + if (!module_putted && !hookinfo_nr()) { + module_put(THIS_MODULE); + module_putted = true; + } + mutex_unlock(&hook_lock); +} + +static ssize_t disable_show(struct kobject *kobj, struct kobj_attribute *attr, + char *buf) +{ + return sprintf(buf, "%s\t%s\t%ld\n", + hook_info_flag ? "enabled" : "disabled", + module_putted ? "unused" : "inuse", hookinfo_nr()); +} + +static ssize_t disable_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + unsigned long v; + + if (kstrtoul(buf, 0, &v)) + return -EINVAL; + + if ((v & 0xffffffff00000000) != SYSCTL_SET_MAGIC) + return -EINVAL; + + if (v & 0x00000000ffffffff) + hook_disable(); + + return count; +} + +KERNEL_ATTR_RW(disable); + +static struct attribute *security_moni_attrs[] = { + &disable_attr.attr, + NULL +}; + +static struct attribute_group hook_attrs_group = { + .attrs = security_moni_attrs, +}; + +int hook_sysfs_init(void) +{ + int ret; + + hook_sysfs_kset = kset_create_and_add("aegis", NULL, kernel_kobj); + if (!hook_sysfs_kset) + return -ENOMEM; + + ret = sysfs_create_group(&hook_sysfs_kset->kobj, &hook_attrs_group); + if (ret) + kset_unregister(hook_sysfs_kset); + + return ret; +} + +void hook_sysfs_exit(void) +{ + sysfs_remove_group(&hook_sysfs_kset->kobj, &hook_attrs_group); + kset_unregister(hook_sysfs_kset); +} + +static int cpu_online_func(unsigned int cpu) +{ + return 0; +} + +static int cpu_offline_func(unsigned int cpu) +{ + hook_cpu_mask = cpu_possible_mask; + return 0; +} + +static __init int security_moni_init(void) +{ + int ret; + + if (hook_info_flag || hookinfo_nr()) { + ret = -EBUSY; + goto err_out; + } + + if (!try_module_get(THIS_MODULE)) { + ret = -EFAULT; + goto err_out; + } + + mutex_init(&hook_lock); + + ret = list_module_init(); + if (ret) + goto list_err; + + ret = hook_sysfs_init(); + if (ret) + goto sysfs_err; + + ret = mod_sysctl_add(); + if (ret) + goto sysctl_err; + + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "security/module:online", + cpu_online_func, cpu_offline_func); + if (ret < 0) + goto cpuhp_err; + + cpu_online_enum = ret; + + hook_info_flag = 1; + + return 0; + +cpuhp_err: + mod_sysctl_del(); +sysctl_err: + hook_sysfs_exit(); +sysfs_err: + list_module_exit(); +list_err: + module_put(THIS_MODULE); +err_out: + return ret; +} + +static __exit void security_moni_exit(void) +{ + list_module_exit(); + hook_sysfs_exit(); + mod_sysctl_del(); + cpuhp_remove_state(cpu_online_enum); +} + +module_init(security_moni_init); +module_exit(security_moni_exit); + +MODULE_AUTHOR("zhipingdu/zgpeng/huntazhang"); +MODULE_VERSION("0.1"); +MODULE_LICENSE("GPL"); + diff --git a/kernel/tkernel/aegis/module.h b/kernel/tkernel/aegis/module.h new file mode 100644 index 000000000000..afa4ce77f87d --- /dev/null +++ b/kernel/tkernel/aegis/module.h @@ -0,0 +1,17 @@ +#ifndef AEGIS_MODULE_MODULE_H_ +#include +#define AEGIS_MODULE_MODULE_H_ + +#define KERNEL_ATTR_RW(_name) \ + static struct kobj_attribute _name##_attr = \ +__ATTR(_name, 0644, _name##_show, _name##_store) + +extern int hook_info_flag; +extern long nr_hook_count(int flag); +extern long nr_execve_count(void); + +extern int mod_sysctl_add(void); +extern void mod_sysctl_del(void); +extern const struct cpumask *hook_cpu_mask; + +#endif // AEGIS_MODULE_MODULE_H_ diff --git a/kernel/tkernel/onionhook/Makefile b/kernel/tkernel/onionhook/Makefile new file mode 100644 index 000000000000..b46c3c1f99b5 --- /dev/null +++ b/kernel/tkernel/onionhook/Makefile @@ -0,0 +1 @@ +obj-y := hook_frame.o diff --git a/kernel/tkernel/onionhook/hook_frame.c b/kernel/tkernel/onionhook/hook_frame.c new file mode 100644 index 000000000000..4dd1fb4eccae --- /dev/null +++ b/kernel/tkernel/onionhook/hook_frame.c @@ -0,0 +1,196 @@ +#include +#include +#include + +#ifdef CONFIG_TKERNEL_SECURITY_MONITOR +int hook_info_flag; +EXPORT_SYMBOL(hook_info_flag); + +unsigned long hook_func_array[INFO_MAX]; +EXPORT_SYMBOL(hook_func_array); + +void (*get_execve_info_func)(int argc, void *argv, int envc, void *envp, const char *filename); +EXPORT_SYMBOL(get_execve_info_func); + +void (*get_connect_info_func)(struct socket *sock, struct file *newfile, struct sockaddr_storage *address, int err); +EXPORT_SYMBOL(get_connect_info_func); + +void (*get_accept_info_func)(struct socket *sock, struct file *newfile, struct sockaddr_storage *address, int err); +EXPORT_SYMBOL(get_accept_info_func); + +void (*get_sendto_info_func)(struct socket *sock, int fd, struct sockaddr_storage *address, int err); +EXPORT_SYMBOL(get_sendto_info_func); + +void (*get_recvfrom_info_func)(struct socket *sock, int fd, struct sockaddr_storage *address, int err); +EXPORT_SYMBOL(get_recvfrom_info_func); + +void (*get_sock_info_func)(struct sock *sk); +EXPORT_SYMBOL(get_sock_info_func); + +void (*get_fork_info_func)(struct task_struct *p, unsigned long clone_flags); +EXPORT_SYMBOL(get_fork_info_func); + +void (*get_exit_info_func)(struct task_struct *tsk, long code); +EXPORT_SYMBOL(get_exit_info_func); + +static DEFINE_PER_CPU(long, hook_info_count) __aligned(64); +long hookinfo_nr(void) +{ + int cpu; + long total = 0; + + for_each_possible_cpu(cpu) { + total += per_cpu(hook_info_count, cpu); + } + return total; +} +EXPORT_SYMBOL(hookinfo_nr); + +void data_release(struct kref *ref) +{ + struct security_moni_info *data = container_of(ref, struct security_moni_info, refcount); + + kfree(data); +} +EXPORT_SYMBOL(data_release); + +void execve_hook_check(int argc, void *argv, int envc, void *envp, const char *filename) +{ + if (execve_info_flag && hook_info_flag) { + __this_cpu_inc(hook_info_count); + smp_rmb(); + get_execve_info_func = (typeof(get_execve_info_func))hook_func_array[EXECVE_INFO]; + if (get_execve_info_func) { + get_execve_info_func(argc, argv, envc, envp, filename); + } + + __this_cpu_dec(hook_info_count); + } +} + +void accept_hook_check(struct socket *newsock, struct file *newfile, struct sockaddr_storage *address, int err) +{ + if (accept_info_flag && hook_info_flag) { + __this_cpu_inc(hook_info_count); + smp_rmb(); + get_accept_info_func = (typeof(get_accept_info_func))hook_func_array[ACCEPT_INFO]; + if (get_accept_info_func) { + get_accept_info_func(newsock, newfile, NULL, err); + } + + __this_cpu_dec(hook_info_count); + } +} + +void connect_hook_check(struct socket *sock, struct file *newfile, struct sockaddr_storage *address, int err) +{ + if (connect_info_flag && hook_info_flag) { + __this_cpu_inc(hook_info_count); + smp_rmb(); + get_connect_info_func = (typeof(get_connect_info_func))hook_func_array[CONNECT_INFO]; + if (get_connect_info_func) { + get_connect_info_func(sock, newfile, address, err); + } + __this_cpu_dec(hook_info_count); + } +} + +void sendto_hook_check(struct socket *sock, int fd, struct sockaddr_storage *address, int err) +{ + if (sendto_info_flag && hook_info_flag && err >= 0) { + __this_cpu_inc(hook_info_count); + smp_rmb(); + get_sendto_info_func = (typeof(get_sendto_info_func))hook_func_array[SENDTO_INFO]; + if (get_sendto_info_func) { + get_sendto_info_func(sock, fd, address, err); + } + + __this_cpu_dec(hook_info_count); + } +} + +void recvfrom_hook_check(struct socket *sock, int fd, struct sockaddr_storage *address, int err) +{ + if (recvfrom_info_flag && hook_info_flag && err >= 0) { + __this_cpu_inc(hook_info_count); + smp_rmb(); + get_recvfrom_info_func = (typeof(get_recvfrom_info_func))hook_func_array[RECVFROM_INFO]; + if (get_recvfrom_info_func) { + get_recvfrom_info_func(sock, fd, address, err); + } + + __this_cpu_dec(hook_info_count); + } +} + +void sock_hook_check(void *sock) +{ + struct sock *sk = sock; + + if (sock_info_flag && hook_info_flag) { + __this_cpu_inc(hook_info_count); + smp_rmb(); + get_sock_info_func = (typeof(get_sock_info_func))hook_func_array[SOCK_INFO]; + if (get_sock_info_func) { + get_sock_info_func(sk); + } + __this_cpu_dec(hook_info_count); + } +} +EXPORT_SYMBOL(sock_hook_check); + +void fork_hook_check(struct task_struct *p, unsigned long clone_flags) +{ + if (fork_info_flag && hook_info_flag) { + __this_cpu_inc(hook_info_count); + smp_rmb(); + get_fork_info_func = (typeof(get_fork_info_func))hook_func_array[FORK_INFO]; + if (get_fork_info_func) { + get_fork_info_func(p, clone_flags); + } + + __this_cpu_dec(hook_info_count); + } +} + +void exit_hook_check(struct task_struct *tsk, long code) +{ + if (exit_info_flag && hook_info_flag) { + __this_cpu_inc(hook_info_count); + smp_rmb(); + get_exit_info_func = (typeof(get_exit_info_func))hook_func_array[EXIT_INFO]; + if (get_exit_info_func) { + get_exit_info_func(tsk, code); + } + + __this_cpu_dec(hook_info_count); + } +} + +#else + +void sock_hook_check(void *sock) +{ +} +void recvfrom_hook_check(struct socket *sock, int fd, struct sockaddr_storage *address, int err) +{ +} +void sendto_hook_check(struct socket *sock, int fd, struct sockaddr_storage *address, int err) +{ +} +void connect_hook_check(struct socket *sock, struct file *newfile, struct sockaddr_storage *address, int err) +{ +} +void accept_hook_check(struct socket *sock, struct file *newfile, struct sockaddr_storage *address, int err) +{ +} +void execve_hook_check(int argc, void *argv, int envc, void *envp, const char *filename) +{ +} +void fork_hook_check(struct task_struct *p, unsigned long clone_flags) +{ +} +void exit_hook_check(struct task_struct *tsk, long code) +{ +} +#endif diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index f5bf29d23d28..e6aaef8c6f77 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -123,6 +123,7 @@ #include #include +#include /* The inetsw table contains everything that inet_create needs to * build a new socket. @@ -1366,6 +1367,16 @@ void inet_sk_state_store(struct sock *sk, int newstate) { trace_inet_sock_set_state(sk, sk->sk_state, newstate); smp_store_release(&sk->sk_state, newstate); + #ifdef CONFIG_TKERNEL_SECURITY_MONITOR + switch (sk->sk_state) { + case TCP_ESTABLISHED: + case TCP_LISTEN: + sock_hook_check(sk); + break; + default: + break; + } +#endif } struct sk_buff *inet_gso_segment(struct sk_buff *skb, diff --git a/net/ipv4/datagram.c b/net/ipv4/datagram.c index c85dd03b9a7a..78e14911e4fb 100644 --- a/net/ipv4/datagram.c +++ b/net/ipv4/datagram.c @@ -15,6 +15,7 @@ #include #include #include +#include int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len) { @@ -77,6 +78,10 @@ int __ip4_datagram_connect(struct sock *sk, struct sockaddr *uaddr, int addr_len sk_dst_set(sk, &rt->dst); err = 0; +#ifdef CONFIG_SECURITY_MONITOR + sock_hook_check(sk); +#endif + out: return err; } diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index f196016e9a71..0d95bea75850 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -75,6 +75,7 @@ #include #include #include +#include struct raw_frag_vec { struct msghdr *msg; @@ -101,6 +102,10 @@ int raw_hash_sk(struct sock *sk) spin_unlock(&h->lock); sock_prot_inuse_add(sock_net(sk), sk->sk_prot, 1); +#ifdef CONFIG_SECURITY_MONITOR + sock_hook_check(sk); +#endif + return 0; } EXPORT_SYMBOL_GPL(raw_hash_sk); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 16ca211c8619..b074e87b235c 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -115,6 +115,7 @@ #include #include #include +#include #if IS_ENABLED(CONFIG_IPV6) #include #endif @@ -344,6 +345,11 @@ found: } sock_set_flag(sk, SOCK_RCU_FREE); error = 0; + +#ifdef CONFIG_SECURITY_MONITOR + sock_hook_check(sk); +#endif + fail_unlock: spin_unlock_bh(&hslot->lock); fail: diff --git a/net/socket.c b/net/socket.c index 8d83c4bb163b..fc32b6be0328 100644 --- a/net/socket.c +++ b/net/socket.c @@ -109,6 +109,7 @@ #include #include #include +#include #ifdef CONFIG_NET_RX_BUSY_POLL unsigned int sysctl_net_busy_read __read_mostly; @@ -1572,6 +1573,11 @@ int __sock_create(struct net *net, int family, int type, int protocol, if (err < 0) goto out_module_put; +#ifdef CONFIG_TKERNEL_SECURITY_MONITOR + if (sock->sk) + sock->sk->pid = task_tgid_nr(current); +#endif + /* * Now to bump the refcnt of the [loadable] module that owns this * socket at sock_release time we decrement its refcnt. @@ -1935,6 +1941,12 @@ struct file *do_accept(struct file *file, unsigned file_flags, if (err < 0) goto out_fd; +#ifdef CONFIG_SECURITY_MONITOR + if (newsock->sk) + newsock->sk->pid = task_tgid_nr(current); + accept_hook_check(newsock, newfile, &address, err); +#endif + if (upeer_sockaddr) { len = ops->getname(newsock, (struct sockaddr *)&address, 2); if (len < 0) { @@ -2051,6 +2063,11 @@ int __sys_connect_file(struct file *file, struct sockaddr_storage *address, err = READ_ONCE(sock->ops)->connect(sock, (struct sockaddr *)address, addrlen, sock->file->f_flags | file_flags); + +#ifdef CONFIG_SECURITY_MONITOR + connect_hook_check(sock, file, address, err); +#endif + out: return err; } @@ -2195,6 +2212,10 @@ int __sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags, msg.msg_flags = flags; err = __sock_sendmsg(sock, &msg); +#ifdef CONFIG_SECURITY_MONITOR + sendto_hook_check(sock, fd, &address, err); +#endif + out_put: fput_light(sock->file, fput_needed); out: @@ -2254,6 +2275,10 @@ int __sys_recvfrom(int fd, void __user *ubuf, size_t size, unsigned int flags, err = err2; } +#ifdef CONFIG_SECURITY_MONITOR + recvfrom_hook_check(sock, fd, &address, err); +#endif + fput_light(sock->file, fput_needed); out: return err;