From f50ce0058c63a7819e9066fab83df802de899a31 Mon Sep 17 00:00:00 2001 From: Rifat Azad Date: Wed, 4 Dec 2024 16:30:28 +0600 Subject: [PATCH] kernel: implement SuSFS v1.5.2 --- kernel/Kconfig | 140 +++++++++++++++++ kernel/Makefile | 113 +++++++++++++- kernel/allowlist.c | 2 + kernel/arch.h | 20 +++ kernel/core_hook.c | 314 +++++++++++++++++++++++++++++++++++++- kernel/kernel_compat.c | 91 ++++++++++- kernel/kernel_compat.h | 5 + kernel/ksu.c | 10 ++ kernel/ksud.c | 63 +++++++- kernel/selinux/rules.c | 23 ++- kernel/selinux/selinux.c | 102 ++++++++++++- kernel/selinux/selinux.h | 16 ++ kernel/selinux/sepolicy.c | 186 ++++++++++++++++++++++ kernel/setup.sh | 2 +- kernel/sucompat.c | 108 ++++++++++++- kernel/throne_tracker.c | 4 + 16 files changed, 1186 insertions(+), 13 deletions(-) diff --git a/kernel/Kconfig b/kernel/Kconfig index 67f177f4..076c61dd 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -16,4 +16,144 @@ config KSU_DEBUG help Enable KernelSU debug mode. +menu "KernelSU - SUSFS" +config KSU_SUSFS + bool "KernelSU addon - SUSFS" + depends on KSU + default y + help + Patch and Enable SUSFS to kernel with KernelSU. + +config KSU_SUSFS_SUS_PATH + bool "Enable to hide suspicious path (NOT recommended)" + depends on KSU_SUSFS + default y + help + - Allow hiding the user-defined path and all its sub-paths from various system calls. + - tmpfs filesystem is not allowed to be added. + - Effective on process with uid > 2000 only. + - Use with cautious as it may cause performance loss and will be vulnerable to side channel attacks, + just disable this feature if it doesn't work for you or you don't need it at all. + +config KSU_SUSFS_SUS_MOUNT + bool "Enable to hide suspicious mounts" + depends on KSU_SUSFS + default y + help + - Allow hiding the user-defined mount paths from /proc/self/[mounts|mountinfo|mountstat]. + - Effective on all processes for hiding mount entries. + - Mounts mounted by process with ksu domain will be forced to be assigned the dev name "KSU". + - mnt_id and mnt_group_id of the sus mount will be assigned to a much bigger number to solve the issue of id not being contiguous. + +config KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT + bool "Enable to hide KSU's default mounts automatically (experimental)" + depends on KSU_SUSFS_SUS_MOUNT + default y + help + - Automatically add KSU's default mounts to sus_mount. + - No susfs command is needed in userspace. + - Only mount operation from process with ksu domain will be checked. + +config KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT + bool "Enable to hide suspicious bind mounts automatically (experimental)" + depends on KSU_SUSFS_SUS_MOUNT + default y + help + - Automatically add binded mounts to sus_mount. + - No susfs command is needed in userspace. + - Only mount operation from process with ksu domain will be checked. + +config KSU_SUSFS_SUS_KSTAT + bool "Enable to spoof suspicious kstat" + depends on KSU_SUSFS + default y + help + - Allow spoofing the kstat of user-defined file/directory. + - Effective on all processes. + +config KSU_SUSFS_SUS_OVERLAYFS + bool "Enable to automatically spoof kstat and kstatfs for overlayed files/directories" + depends on KSU_SUSFS + default y + help + - Automatically spoof the kstat and kstatfs for overlayed files/directories. + - No susfs command is needed in userspace. + - Effective on all processes. + +config KSU_SUSFS_TRY_UMOUNT + bool "Enable to use ksu's try_umount" + depends on KSU_SUSFS + default y + help + - Allow using ksu's umount to umount other user-defined mount paths prior to ksu's default umount paths. + - Effective on all NO-root-access-granted processes. + +config KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT + bool "Enable to add bind mounts to ksu's try_umount automatically (experimental)" + depends on KSU_SUSFS_TRY_UMOUNT + default y + help + - Automatically add binded mounts to ksu's try_umount. + - No susfs command is needed in userspace. + - Only mount operation from process with ksu domain will be checked. + +config KSU_SUSFS_SPOOF_UNAME + bool "Enable to spoof uname" + depends on KSU_SUSFS + default y + help + - Allow spoofing the string returned by uname syscall to user-defined string. + - Effective on all processes. + +config KSU_SUSFS_ENABLE_LOG + bool "Enable logging susfs log to kernel" + depends on KSU_SUSFS + default y + help + - Allow logging susfs log to kernel, uncheck it to completely disable all susfs log. + +config KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS + bool "Enable to automatically hide ksu and susfs symbols from /proc/kallsyms" + depends on KSU_SUSFS + default y + help + - Automatically hide ksu and susfs symbols from '/proc/kallsyms'. + - Effective on all processes. + +config KSU_SUSFS_SPOOF_BOOTCONFIG + bool "Enable to spoof /proc/bootconfig" + depends on KSU_SUSFS + default y + help + - Spoof the output of /proc/bootconfig with a user-defined file. + - Effective on all processes. + +config KSU_SUSFS_OPEN_REDIRECT + bool "Enable to redirect a path to be opened with another path (experimental)" + depends on KSU_SUSFS + default y + help + - Allow redirecting a target path to be opened with another user-defined path. + - Effective only on processes with uid < 2000. + - Please be reminded that process with open access to the target and redirected path can be detected. + +config KSU_SUSFS_SUS_SU + bool "Enable SUS-SU in runtime temporarily" + depends on KSU_SUSFS && KPROBES && HAVE_KPROBES && KPROBE_EVENTS + default y + help + - Allow user to enable or disable core ksu kprobes hooks temporarily in runtime. There are 2 modes for sus_su. + - Mode 1: when enabling sus_su, ksu kprobes will be disabled, and a fifo driver will be created in + /dev/[random_string] (for first time only), then user needs to mount the 'sus_su' to /system/bin/su using overlayfs or whatever + techniques, and run 'su' to get root shell from the fifo driver. Or, refer to the method in service.sh from module template. + ** sus_su userspace tool and an overlay mount is required ** + - Mode 2: When enabling sus_su, ksu kprobes will be disabled, and the kernel inline hooks will be enabled, + just same as the su implementaion of non-gki kernel without kprobe supported. + ** Needs no extra userspace tools and mounts ** + - When disabling sus_su, ksu kprobes will be enabled again, and the fifo driver will be deleted. + - Only apps with root access granted by ksu manager are allowed to get root. + - Also overlayfs is required. + +endmenu + endmenu diff --git a/kernel/Makefile b/kernel/Makefile index 9fec3ef3..9c53247f 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -26,7 +26,15 @@ $(info -- KernelSU version: $(KSU_VERSION)) ccflags-y += -DKSU_VERSION=$(KSU_VERSION) else # If there is no .git file, the default version will be passed. $(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git submodule!") -ccflags-y += -DKSU_VERSION=16 +ccflags-y += -DKSU_VERSION=11986 +endif + +ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0) +ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID +endif + +ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0) +ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE endif ifndef KSU_EXPECTED_SIZE @@ -48,7 +56,110 @@ $(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH)) ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE) ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\" +#ifeq ($(shell grep -q "int path_umount" $(srctree)/fs/namespace.c; echo $$?),0) +#ccflags-y += -DKSU_UMOUNT +#else +#$(info -- Did you know you can backport path_umount to fs/namespace.c from 5.9?) +#$(info -- Read: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#how-to-backport-path-umount) +#endif + +ccflags-y += -DKSU_UMOUNT + +ifneq ($(shell grep -Eq "^static int can_umount" $(srctree)/fs/namespace.c; echo $$?),0) +$(info -- KSU_SUSFS: adding function 'static int can_umount(const struct path *path, int flags);' to $(srctree)/fs/namespace.c) +CAN_UMOUNT = static int can_umount(const struct path *path, int flags)\n\ +{\n\t\ + struct mount *mnt = real_mount(path->mnt);\n\t\ + if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))\n\t\t\ + return -EINVAL;\n\t\ + if (!may_mount())\n\t\t\ + return -EPERM;\n\t\ + if (path->dentry != path->mnt->mnt_root)\n\t\t\ + return -EINVAL;\n\t\ + if (!check_mnt(mnt))\n\t\t\ + return -EINVAL;\n\t\ + if (mnt->mnt.mnt_flags & MNT_LOCKED)\n\t\t\ + return -EINVAL;\n\t\ + if (flags & MNT_FORCE && !capable(CAP_SYS_ADMIN))\n\t\t\ + return -EPERM;\n\t\ + return 0;\n\ +}\n +$(shell sed -i '/^static bool is_mnt_ns_file/i $(CAN_UMOUNT)' $(srctree)/fs/namespace.c;) +endif + +ifneq ($(shell grep -Eq "^int path_umount" $(srctree)/fs/namespace.c; echo $$?),0) +$(info -- KSU_SUSFS: adding function 'int path_umount(struct path *path, int flags);' to $(srctree)/fs/namespace.c) +PATH_UMOUNT = int path_umount(struct path *path, int flags)\n\ +{\n\t\ + struct mount *mnt = real_mount(path->mnt);\n\t\ + int ret;\n\t\ + ret = can_umount(path, flags);\n\t\ + if (!ret)\n\t\t\ + ret = do_umount(mnt, flags);\n\t\ + dput(path->dentry);\n\t\ + mntput_no_expire(mnt);\n\t\ + return ret;\n\ +}\n +$(shell sed -i '/^static bool is_mnt_ns_file/i $(PATH_UMOUNT)' $(srctree)/fs/namespace.c;) +endif + +ifneq ($(shell grep -Eq "^int path_umount" $(srctree)/fs/internal.h; echo $$?),0) +$(shell sed -i '/^extern void __init mnt_init/a int path_umount(struct path *path, int flags);' $(srctree)/fs/internal.h;) +$(info -- KSU_SUSFS: adding 'int path_umount(struct path *path, int flags);' to $(srctree)/fs/internal.h) +endif + ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat ccflags-y += -Wno-declaration-after-statement -Wno-unused-function +ifeq ($(shell test -e $(srctree)/fs/susfs.c; echo $$?),0) +ifdef KSU_SUSFS +ccflags-y += -DKSU_SUSFS +endif +ifdef KSU_SUSFS_SUS_PATH +ccflags-y += -DKSU_SUSFS_SUS_PATH +endif +ifdef KSU_SUSFS_SUS_MOUNT +ccflags-y += -DKSU_SUSFS_SUS_MOUNT +endif +ifdef KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT +ccflags-y += -DKSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT +endif +ifdef KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT +ccflags-y += -DKSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT +endif +ifdef KSU_SUSFS_SUS_KSTAT +ccflags-y += -DKSU_SUSFS_SUS_KSTAT +endif +ifdef KSU_SUSFS_SUS_OVERLAYFS +ccflags-y += -DKSU_SUSFS_SUS_OVERLAYFS +endif +ifdef KSU_SUSFS_TRY_UMOUNT +ccflags-y += -DKSU_SUSFS_TRY_UMOUNT +endif +ifdef KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT +ccflags-y += -DKSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT +endif +ifdef KSU_SUSFS_SPOOF_UNAME +ccflags-y += -DKSU_SUSFS_SPOOF_UNAME +endif +ifdef KSU_SUSFS_ENABLE_LOG +ccflags-y += -DKSU_SUSFS_ENABLE_LOG +endif +ifdef KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS +ccflags-y += -DKSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS +endif +ifdef KSU_SUSFS_SPOOF_BOOTCONFIG +ccflags-y += -DKSU_SUSFS_SPOOF_BOOTCONFIG +endif +ifdef KSU_SUSFS_OPEN_REDIRECT +ccflags-y += -DKSU_SUSFS_OPEN_REDIRECT +endif +ifdef KSU_SUSFS_SUS_SU +ccflags-y += -DKSU_SUSFS_SUS_SU +endif +else +$(info -- You have not integrate susfs in your kernel.) +$(info -- Read: https://gitlab.com/simonpunk/susfs4ksu) +endif + # Keep a new line here!! Because someone may append config diff --git a/kernel/allowlist.c b/kernel/allowlist.c index 9daceef2..4fbba935 100644 --- a/kernel/allowlist.c +++ b/kernel/allowlist.c @@ -7,7 +7,9 @@ #include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) #include +#endif #include "ksu.h" #include "klog.h" // IWYU pragma: keep diff --git a/kernel/arch.h b/kernel/arch.h index eec38c28..f36ec5f5 100644 --- a/kernel/arch.h +++ b/kernel/arch.h @@ -18,11 +18,19 @@ #define __PT_SP_REG sp #define __PT_IP_REG pc +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) #define PRCTL_SYMBOL "__arm64_sys_prctl" #define SYS_READ_SYMBOL "__arm64_sys_read" #define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat" #define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat" #define SYS_EXECVE_SYMBOL "__arm64_sys_execve" +#else +#define PRCTL_SYMBOL "sys_prctl" +#define SYS_READ_SYMBOL "sys_read" +#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat" +#define SYS_FACCESSAT_SYMBOL "sys_faccessat" +#define SYS_EXECVE_SYMBOL "sys_execve" +#endif #elif defined(__x86_64__) @@ -39,11 +47,19 @@ #define __PT_RC_REG ax #define __PT_SP_REG sp #define __PT_IP_REG ip +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) #define PRCTL_SYMBOL "__x64_sys_prctl" #define SYS_READ_SYMBOL "__x64_sys_read" #define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat" #define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat" #define SYS_EXECVE_SYMBOL "__x64_sys_execve" +#else +#define PRCTL_SYMBOL "sys_prctl" +#define SYS_READ_SYMBOL "sys_read" +#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat" +#define SYS_FACCESSAT_SYMBOL "sys_faccessat" +#define SYS_EXECVE_SYMBOL "sys_execve" +#endif #else #error "Unsupported arch" @@ -67,6 +83,10 @@ #define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG) #define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) #define PT_REAL_REGS(regs) ((struct pt_regs *)PT_REGS_PARM1(regs)) +#else +#define PT_REAL_REGS(regs) ((regs)) +#endif #endif diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 913867a5..9cc12812 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -33,6 +33,10 @@ #include #endif +#ifdef CONFIG_KSU_SUSFS +#include +#endif // #ifdef CONFIG_KSU_SUSFS + #include "allowlist.h" #include "arch.h" #include "core_hook.h" @@ -45,6 +49,20 @@ #include "throne_tracker.h" #include "kernel_compat.h" +#ifdef CONFIG_KSU_SUSFS +bool susfs_is_allow_su(void) +{ + if (is_manager()) { + // we are manager, allow! + return true; + } + return ksu_is_allow_uid(current_uid().val); +} + +extern u32 susfs_zygote_sid; +extern void susfs_run_try_umount_for_current_mnt_ns(void); +#endif // #ifdef CONFIG_KSU_SUSFS + static bool ksu_module_mounted = false; extern int handle_sepolicy(unsigned long arg3, void __user *arg4); @@ -99,7 +117,11 @@ static void setup_groups(struct root_profile *profile, struct cred *cred) put_group_info(group_info); return; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) group_info->gid[i] = kgid; +#else + GROUP_AT(group_info, i) = kgid; +#endif } groups_sort(group_info); @@ -372,6 +394,205 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, return 0; } +#ifdef CONFIG_KSU_SUSFS + if (current_uid_val == 0) { +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (arg2 == CMD_SUSFS_ADD_SUS_PATH) { + int error = 0; + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_path))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_PATH -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_PATH -> arg5 is not accessible\n"); + return 0; + } + error = susfs_add_sus_path((struct st_susfs_sus_path __user*)arg3); + pr_info("susfs: CMD_SUSFS_ADD_SUS_PATH -> ret: %d\n", error); + if (copy_to_user((void __user*)arg5, &error, sizeof(error))) + pr_info("susfs: copy_to_user() failed\n"); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_PATH +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (arg2 == CMD_SUSFS_ADD_SUS_MOUNT) { + int error = 0; + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_mount))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_MOUNT -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_MOUNT -> arg5 is not accessible\n"); + return 0; + } + error = susfs_add_sus_mount((struct st_susfs_sus_mount __user*)arg3); + pr_info("susfs: CMD_SUSFS_ADD_SUS_MOUNT -> ret: %d\n", error); + if (copy_to_user((void __user*)arg5, &error, sizeof(error))) + pr_info("susfs: copy_to_user() failed\n"); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + if (arg2 == CMD_SUSFS_ADD_SUS_KSTAT) { + int error = 0; + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_kstat))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_KSTAT -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_KSTAT -> arg5 is not accessible\n"); + return 0; + } + error = susfs_add_sus_kstat((struct st_susfs_sus_kstat __user*)arg3); + pr_info("susfs: CMD_SUSFS_ADD_SUS_KSTAT -> ret: %d\n", error); + if (copy_to_user((void __user*)arg5, &error, sizeof(error))) + pr_info("susfs: copy_to_user() failed\n"); + return 0; + } + if (arg2 == CMD_SUSFS_UPDATE_SUS_KSTAT) { + int error = 0; + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_kstat))) { + pr_err("susfs: CMD_SUSFS_UPDATE_SUS_KSTAT -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_UPDATE_SUS_KSTAT -> arg5 is not accessible\n"); + return 0; + } + error = susfs_update_sus_kstat((struct st_susfs_sus_kstat __user*)arg3); + pr_info("susfs: CMD_SUSFS_UPDATE_SUS_KSTAT -> ret: %d\n", error); + if (copy_to_user((void __user*)arg5, &error, sizeof(error))) + pr_info("susfs: copy_to_user() failed\n"); + return 0; + } + if (arg2 == CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY) { + int error = 0; + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_kstat))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY -> arg5 is not accessible\n"); + return 0; + } + error = susfs_add_sus_kstat((struct st_susfs_sus_kstat __user*)arg3); + pr_info("susfs: CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY -> ret: %d\n", error); + if (copy_to_user((void __user*)arg5, &error, sizeof(error))) + pr_info("susfs: copy_to_user() failed\n"); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT + if (arg2 == CMD_SUSFS_ADD_TRY_UMOUNT) { + int error = 0; + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_try_umount))) { + pr_err("susfs: CMD_SUSFS_ADD_TRY_UMOUNT -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_ADD_TRY_UMOUNT -> arg5 is not accessible\n"); + return 0; + } + error = susfs_add_try_umount((struct st_susfs_try_umount __user*)arg3); + pr_info("susfs: CMD_SUSFS_ADD_TRY_UMOUNT -> ret: %d\n", error); + if (copy_to_user((void __user*)arg5, &error, sizeof(error))) + pr_info("susfs: copy_to_user() failed\n"); + return 0; + } + if (arg2 == CMD_SUSFS_RUN_UMOUNT_FOR_CURRENT_MNT_NS) { + int error = 0; + susfs_run_try_umount_for_current_mnt_ns(); + pr_info("susfs: CMD_SUSFS_RUN_UMOUNT_FOR_CURRENT_MNT_NS -> ret: %d\n", error); + } +#endif //#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT +#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME + if (arg2 == CMD_SUSFS_SET_UNAME) { + int error = 0; + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_uname))) { + pr_err("susfs: CMD_SUSFS_SET_UNAME -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_SET_UNAME -> arg5 is not accessible\n"); + return 0; + } + error = susfs_set_uname((struct st_susfs_uname __user*)arg3); + pr_info("susfs: CMD_SUSFS_SET_UNAME -> ret: %d\n", error); + if (copy_to_user((void __user*)arg5, &error, sizeof(error))) + pr_info("susfs: copy_to_user() failed\n"); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME +#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG + if (arg2 == CMD_SUSFS_ENABLE_LOG) { + int error = 0; + if (arg3 != 0 && arg3 != 1) { + pr_err("susfs: CMD_SUSFS_ENABLE_LOG -> arg3 can only be 0 or 1\n"); + return 0; + } + susfs_set_log(arg3); + if (copy_to_user((void __user*)arg5, &error, sizeof(error))) + pr_info("susfs: copy_to_user() failed\n"); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG +#ifdef CONFIG_KSU_SUSFS_SPOOF_BOOTCONFIG + if (arg2 == CMD_SUSFS_SET_BOOTCONFIG) { + int error = 0; + if (!ksu_access_ok((void __user*)arg3, SUSFS_FAKE_BOOT_CONFIG_SIZE)) { + pr_err("susfs: CMD_SUSFS_SET_BOOTCONFIG -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_SET_BOOTCONFIG -> arg5 is not accessible\n"); + return 0; + } + error = susfs_set_bootconfig((char __user*)arg3); + pr_info("susfs: CMD_SUSFS_SET_BOOTCONFIG -> ret: %d\n", error); + if (copy_to_user((void __user*)arg5, &error, sizeof(error))) + pr_info("susfs: copy_to_user() failed\n"); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SPOOF_BOOTCONFIG +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + if (arg2 == CMD_SUSFS_ADD_OPEN_REDIRECT) { + int error = 0; + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_open_redirect))) { + pr_err("susfs: CMD_SUSFS_ADD_OPEN_REDIRECT -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_ADD_OPEN_REDIRECT -> arg5 is not accessible\n"); + return 0; + } + error = susfs_add_open_redirect((struct st_susfs_open_redirect __user*)arg3); + pr_info("susfs: CMD_SUSFS_ADD_OPEN_REDIRECT -> ret: %d\n", error); + if (copy_to_user((void __user*)arg5, &error, sizeof(error))) + pr_info("susfs: copy_to_user() failed\n"); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +#ifdef CONFIG_KSU_SUSFS_SUS_SU + if (arg2 == CMD_SUSFS_SUS_SU) { + int error = 0; + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_sus_su))) { + pr_err("susfs: CMD_SUSFS_SUS_SU -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_SUS_SU -> arg5 is not accessible\n"); + return 0; + } + error = susfs_sus_su((struct st_sus_su __user*)arg3); + pr_info("susfs: CMD_SUSFS_SUS_SU -> ret: %d\n", error); + if (copy_to_user((void __user*)arg5, &error, sizeof(error))) + pr_info("susfs: copy_to_user() failed\n"); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_SU + } +#endif //#ifdef CONFIG_KSU_SUSFS + // all other cmds are for 'root manager' if (!from_manager) { return 0; @@ -446,15 +667,21 @@ static bool should_umount(struct path *path) return false; } -static void ksu_umount_mnt(struct path *path, int flags) +static int ksu_umount_mnt(struct path *path, int flags) { - int err = path_umount(path, flags); - if (err) { - pr_info("umount %s failed: %d\n", path->dentry->d_iname, err); - } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || defined(KSU_UMOUNT) + return path_umount(path, flags); +#else + // TODO: umount for non GKI kernel + return -ENOSYS; +#endif } +#ifdef CONFIG_KSU_SUSFS +void try_umount(const char *mnt, bool check_mnt, int flags) +#else static void try_umount(const char *mnt, bool check_mnt, int flags) +#endif { struct path path; int err = kern_path(mnt, 0, &path); @@ -472,9 +699,24 @@ static void try_umount(const char *mnt, bool check_mnt, int flags) return; } - ksu_umount_mnt(&path, flags); + err = ksu_umount_mnt(&path, flags); + if (err) { + pr_warn("umount %s failed: %d\n", mnt, err); + } } +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT +void susfs_try_umount_all(uid_t uid) { + susfs_try_umount(uid); + try_umount("/system", true, 0); + try_umount("/system_ext", true, 0); + try_umount("/vendor", true, 0); + try_umount("/product", true, 0); + try_umount("/data/adb/modules", false, MNT_DETACH); + try_umount("/debug_ramdisk", false, MNT_DETACH); +} +#endif + int ksu_handle_setuid(struct cred *new, const struct cred *old) { // this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it! @@ -494,6 +736,17 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) return 0; } +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + // check if current process is zygote + bool is_zygote_child = susfs_is_sid_equal(old->security, susfs_zygote_sid); + if (likely(is_zygote_child)) { + // if spawned process is non user app process, run try_umount() + if (unlikely(new_uid.val < 10000 && new_uid.val >= 1000)) { + goto out_try_umount; + } + } +#endif + if (!is_appuid(new_uid) || is_unsupported_uid(new_uid.val)) { // pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid.val); return 0; @@ -503,6 +756,12 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) // pr_info("handle setuid ignore allowed application: %d\n", new_uid.val); return 0; } +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + else { + // if new uid is not root granted, then drop a payload to inidicate that sus_path will be effective on this uid + new->user->android_kabi_reserved2 |= USER_STRUCT_KABI2_NON_ROOT_USER_APP_PROFILE; + } +#endif if (!ksu_uid_should_umount(new_uid.val)) { return 0; @@ -512,21 +771,31 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) #endif } +#ifndef CONFIG_KSU_SUSFS_SUS_MOUNT // check old process's selinux context, if it is not zygote, ignore it! // because some su apps may setuid to untrusted_app but they are in global mount namespace // when we umount for such process, that is a disaster! bool is_zygote_child = is_zygote(old->security); +#endif if (!is_zygote_child) { pr_info("handle umount ignore non zygote child: %d\n", current->pid); return 0; } + #ifdef CONFIG_KSU_DEBUG // umount the target mnt pr_info("handle umount for uid: %d, pid: %d\n", new_uid.val, current->pid); #endif +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +out_try_umount: +#endif +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT + // susfs come first, and lastly umount by ksu, make sure umount in reversed order + susfs_try_umount_all(new_uid.val); +#else // fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and // filter the mountpoint whose target is `/data/adb` try_umount("/system", true, 0); @@ -537,6 +806,7 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) // try umount ksu temp path try_umount("/debug_ramdisk", false, MNT_DETACH); try_umount("/sbin", false, MNT_DETACH); +#endif return 0; } @@ -549,8 +819,14 @@ static int handler_pre(struct kprobe *p, struct pt_regs *regs) int option = (int)PT_REGS_PARM1(real_regs); unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs); unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) // PRCTL_SYMBOL is the arch-specificed one, which receive raw pt_regs from syscall unsigned long arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs); +#else + // PRCTL_SYMBOL is the common one, called by C convention in do_syscall_64 + // https://elixir.bootlin.com/linux/v4.15.18/source/arch/x86/entry/common.c#L287 + unsigned long arg4 = (unsigned long)PT_REGS_CCALL_PARM4(real_regs); +#endif unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs); return ksu_handle_prctl(option, arg2, arg3, arg4, arg5); @@ -611,6 +887,24 @@ static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3, return -ENOSYS; } +// kernel 4.4 and 4.9 +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) +static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred, + unsigned perm) +{ + if (init_session_keyring != NULL) { + return 0; + } + if (strcmp(current->comm, "init")) { + // we are only interested in `init` process + return 0; + } + init_session_keyring = cred->session_keyring; + pr_info("kernel_compat: got init_session_keyring\n"); + return 0; +} +#endif + static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) { @@ -628,11 +922,19 @@ static struct security_hook_list ksu_hooks[] = { LSM_HOOK_INIT(task_prctl, ksu_task_prctl), LSM_HOOK_INIT(inode_rename, ksu_inode_rename), LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid), +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) + LSM_HOOK_INIT(key_permission, ksu_key_permission) +#endif }; void __init ksu_lsm_hook_init(void) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu"); +#else + // https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892 + security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks)); +#endif } #else diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c index d4ee546d..a80b6ffc 100644 --- a/kernel/kernel_compat.c +++ b/kernel/kernel_compat.c @@ -1,10 +1,34 @@ #include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) #include +#else +#include +#endif #include #include "klog.h" // IWYU pragma: keep -#include "kernel_compat.h" +#include "kernel_compat.h" // Add check Huawei Device +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) +#include +#include +#include +struct key *init_session_keyring = NULL; +static inline int install_session_keyring(struct key *keyring) +{ + struct cred *new; + int ret; + new = prepare_creds(); + if (!new) + return -ENOMEM; + ret = install_session_keyring_to_cred(new, keyring); + if (ret < 0) { + abort_creds(new); + return ret; + } + return commit_creds(new); +} +#endif extern struct task_struct init_task; @@ -48,8 +72,25 @@ void ksu_android_ns_fs_check() task_unlock(current); } +int ksu_access_ok(const void *addr, unsigned long size) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0) + /* For kernels before 5.0.0, pass the type argument to access_ok. */ + return access_ok(VERIFY_READ, addr, size); +#else + /* For kernels 5.0.0 and later, ignore the type argument. */ + return access_ok(addr, size); +#endif +} + struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) + if (init_session_keyring != NULL && !current_cred()->session_keyring && + (current->flags & PF_WQ_WORKER)) { + pr_info("installing init session keyring for older kernel\n"); + install_session_keyring(init_session_keyring); + } +#endif // switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns struct ksu_ns_fs_saved saved; if (android_context_saved_enabled) { @@ -72,17 +113,65 @@ struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode) ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, loff_t *pos) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) return kernel_read(p, buf, count, pos); +#else + loff_t offset = pos ? *pos : 0; + ssize_t result = kernel_read(p, offset, (char *)buf, count); + if (pos && result > 0) { + *pos = offset + result; + } + return result; +#endif } ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, loff_t *pos) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) return kernel_write(p, buf, count, pos); +#else + loff_t offset = pos ? *pos : 0; + ssize_t result = kernel_write(p, buf, count, offset); + if (pos && result > 0) { + *pos = offset + result; + } + return result; +#endif } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, long count) { return strncpy_from_user_nofault(dst, unsafe_addr, count); } +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) +long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, + long count) +{ + return strncpy_from_unsafe_user(dst, unsafe_addr, count); +} +#else +// Copied from: https://elixir.bootlin.com/linux/v4.9.337/source/mm/maccess.c#L201 +long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, + long count) +{ + mm_segment_t old_fs = get_fs(); + long ret; + if (unlikely(count <= 0)) + return 0; + set_fs(USER_DS); + pagefault_disable(); + ret = strncpy_from_user(dst, unsafe_addr, count); + pagefault_enable(); + set_fs(old_fs); + if (ret >= count) { + ret = count; + dst[ret - 1] = '\0'; + } else if (ret > 0) { + ret++; + } + return ret; +} +#endif diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h index 4bcfbf38..34050e73 100644 --- a/kernel/kernel_compat.h +++ b/kernel/kernel_compat.h @@ -24,7 +24,12 @@ extern long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, long count); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) +extern struct key *init_session_keyring; +#endif + extern void ksu_android_ns_fs_check(); +extern int ksu_access_ok(const void *addr, unsigned long size); extern struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode); extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, diff --git a/kernel/ksu.c b/kernel/ksu.c index d517c3b5..58ab8b78 100644 --- a/kernel/ksu.c +++ b/kernel/ksu.c @@ -11,6 +11,10 @@ #include "ksu.h" #include "throne_tracker.h" +#ifdef CONFIG_KSU_SUSFS +#include +#endif + static struct workqueue_struct *ksu_workqueue; bool ksu_queue_work(struct work_struct *work) @@ -49,6 +53,10 @@ int __init kernelsu_init(void) pr_alert("*************************************************************"); #endif +#ifdef CONFIG_KSU_SUSFS + susfs_init(); +#endif + ksu_core_init(); ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0); @@ -94,4 +102,6 @@ module_exit(kernelsu_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("weishu"); MODULE_DESCRIPTION("Android KernelSU"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif \ No newline at end of file diff --git a/kernel/ksud.c b/kernel/ksud.c index 98fee107..698dbd8f 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -6,7 +6,14 @@ #include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) #include +#else +#include +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +#include +#endif #include #include #include @@ -57,6 +64,10 @@ bool ksu_execveat_hook __read_mostly = true; bool ksu_input_hook __read_mostly = true; #endif +#ifdef CONFIG_KSU_SUSFS_SUS_SU +bool ksu_devpts_hook = false; +#endif + u32 ksu_devpts_sid; void on_post_fs_data(void) @@ -463,6 +474,26 @@ bool ksu_is_safe_mode() #ifdef CONFIG_KPROBES +// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864 +static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + int *fd = (int *)&PT_REGS_PARM1(regs); + struct filename **filename_ptr = + (struct filename **)&PT_REGS_PARM2(regs); + struct user_arg_ptr argv; +#ifdef CONFIG_COMPAT + argv.is_compat = PT_REGS_PARM3(regs); + if (unlikely(argv.is_compat)) { + argv.ptr.compat = PT_REGS_CCALL_PARM4(regs); + } else { + argv.ptr.native = PT_REGS_CCALL_PARM4(regs); + } +#else + argv.ptr.native = PT_REGS_PARM3(regs); +#endif + return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL); +} + static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) { struct pt_regs *real_regs = PT_REAL_REGS(regs); @@ -486,6 +517,17 @@ static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) NULL); } +// remove this later! +__maybe_unused static int vfs_read_handler_pre(struct kprobe *p, + struct pt_regs *regs) +{ + struct file **file_ptr = (struct file **)&PT_REGS_PARM1(regs); + char __user **buf_ptr = (char **)&PT_REGS_PARM2(regs); + size_t *count_ptr = (size_t *)&PT_REGS_PARM3(regs); + loff_t **pos_ptr = (loff_t **)&PT_REGS_CCALL_PARM4(regs); + return ksu_handle_vfs_read(file_ptr, buf_ptr, count_ptr, pos_ptr); +} + static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs) { struct pt_regs *real_regs = PT_REAL_REGS(regs); @@ -505,16 +547,35 @@ static int input_handle_event_handler_pre(struct kprobe *p, return ksu_handle_input_handle_event(type, code, value); } +#if 1 static struct kprobe execve_kp = { .symbol_name = SYS_EXECVE_SYMBOL, .pre_handler = sys_execve_handler_pre, }; +#else +static struct kprobe execve_kp = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + .symbol_name = "do_execveat_common", +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + .symbol_name = "__do_execve_file", +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + .symbol_name = "do_execveat_common", +#endif + .pre_handler = execve_handler_pre, +}; +#endif +#if 1 static struct kprobe vfs_read_kp = { .symbol_name = SYS_READ_SYMBOL, .pre_handler = sys_read_handler_pre, }; - +#else +static struct kprobe vfs_read_kp = { + .symbol_name = "vfs_read", + .pre_handler = vfs_read_handler_pre, +}; +#endif static struct kprobe input_event_kp = { .symbol_name = "input_event", diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index b4e6eae0..c7dada5a 100644 --- a/kernel/selinux/rules.c +++ b/kernel/selinux/rules.c @@ -9,7 +9,9 @@ #include "linux/lsm_audit.h" #include "xfrm.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) #define SELINUX_POLICY_INSTEAD_SELINUX_SS +#endif #define KERNEL_SU_DOMAIN "su" #define KERNEL_SU_FILE "ksu_file" @@ -19,8 +21,18 @@ static struct policydb *get_policydb(void) { struct policydb *db; +// selinux_state does not exists before 4.19 +#ifdef KSU_COMPAT_USE_SELINUX_STATE +#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS struct selinux_policy *policy = rcu_dereference(selinux_state.policy); db = &policy->policydb; +#else + struct selinux_ss *ss = rcu_dereference(selinux_state.ss); + db = &ss->policydb; +#endif +#else + db = &policydb; +#endif return db; } @@ -122,6 +134,14 @@ void apply_kernelsu_rules() ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid"); ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill"); +#ifdef CONFIG_KSU_SUSFS + // Allow umount in zygote process without installing zygisk + ksu_allow(db, "zygote", "labeledfs", "filesystem", "unmount"); + susfs_set_init_sid(); + susfs_set_ksu_sid(); + susfs_set_zygote_sid(); +#endif + rcu_read_unlock(); } @@ -169,7 +189,8 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz, // reset avc cache table, otherwise the new rules will not take effect if already denied static void reset_avc_cache() { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)) +#if ((!defined(KSU_COMPAT_USE_SELINUX_STATE)) || \ + LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)) avc_ss_reset(0); selnl_notify_policyload(0); selinux_status_update_policyload(0); diff --git a/kernel/selinux/selinux.c b/kernel/selinux/selinux.c index 17a25dae..4047d064 100644 --- a/kernel/selinux/selinux.c +++ b/kernel/selinux/selinux.c @@ -2,9 +2,20 @@ #include "objsec.h" #include "linux/version.h" #include "../klog.h" // IWYU pragma: keep +#ifndef KSU_COMPAT_USE_SELINUX_STATE +#include "avc.h" +#endif #define KERNEL_SU_DOMAIN "u:r:su:s0" +#ifdef CONFIG_KSU_SUSFS +#define KERNEL_INIT_DOMAIN "u:r:init:s0" +#define KERNEL_ZYGOTE_DOMAIN "u:r:zygote:s0" +u32 susfs_ksu_sid = 0; +u32 susfs_init_sid = 0; +u32 susfs_zygote_sid = 0; +#endif + static int transive_to_domain(const char *domain) { struct cred *cred; @@ -52,26 +63,38 @@ if (!is_domain_permissive) { void setenforce(bool enforce) { #ifdef CONFIG_SECURITY_SELINUX_DEVELOP +#ifdef KSU_COMPAT_USE_SELINUX_STATE selinux_state.enforcing = enforce; +#else + selinux_enforcing = enforce; +#endif #endif } bool getenforce() { #ifdef CONFIG_SECURITY_SELINUX_DISABLE +#ifdef KSU_COMPAT_USE_SELINUX_STATE if (selinux_state.disabled) { +#else + if (selinux_disabled) { +#endif return false; } #endif #ifdef CONFIG_SECURITY_SELINUX_DEVELOP +#ifdef KSU_COMPAT_USE_SELINUX_STATE return selinux_state.enforcing; +#else + return selinux_enforcing; +#endif #else return true; #endif } -#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) && \ !defined(KSU_COMPAT_HAS_CURRENT_SID) /* * get the subjective security ID of the current task @@ -116,6 +139,83 @@ bool is_zygote(void *sec) return result; } +#ifdef CONFIG_KSU_SUSFS +static inline void susfs_set_sid(const char *secctx_name, u32 *out_sid) +{ + int err; + + if (!secctx_name || !out_sid) { + pr_err("secctx_name || out_sid is NULL\n"); + return; + } + + err = security_secctx_to_secid(secctx_name, strlen(secctx_name), + out_sid); + if (err) { + pr_err("failed setting sid for '%s', err: %d\n", secctx_name, err); + return; + } + pr_info("sid '%u' is set for secctx_name '%s'\n", *out_sid, secctx_name); +} + +bool susfs_is_sid_equal(void *sec, u32 sid2) { + struct task_security_struct *tsec = (struct task_security_struct *)sec; + if (!tsec) { + return false; + } + return tsec->sid == sid2; +} + +u32 susfs_get_sid_from_name(const char *secctx_name) +{ + u32 out_sid = 0; + int err; + + if (!secctx_name) { + pr_err("secctx_name is NULL\n"); + return 0; + } + err = security_secctx_to_secid(secctx_name, strlen(secctx_name), + &out_sid); + if (err) { + pr_err("failed getting sid from secctx_name: %s, err: %d\n", secctx_name, err); + return 0; + } + return out_sid; +} + +u32 susfs_get_current_sid(void) { + return current_sid(); +} + +void susfs_set_zygote_sid(void) +{ + susfs_set_sid(KERNEL_ZYGOTE_DOMAIN, &susfs_zygote_sid); +} + +bool susfs_is_current_zygote_domain(void) { + return unlikely(current_sid() == susfs_zygote_sid); +} + +void susfs_set_ksu_sid(void) +{ + susfs_set_sid(KERNEL_SU_DOMAIN, &susfs_ksu_sid); +} + +bool susfs_is_current_ksu_domain(void) { + return unlikely(current_sid() == susfs_ksu_sid); +} + +void susfs_set_init_sid(void) +{ + susfs_set_sid(KERNEL_INIT_DOMAIN, &susfs_init_sid); +} + +bool susfs_is_current_init_domain(void) { + return unlikely(current_sid() == susfs_init_sid); +} +#endif + #define DEVPTS_DOMAIN "u:object_r:ksu_file:s0" u32 ksu_get_devpts_sid() diff --git a/kernel/selinux/selinux.h b/kernel/selinux/selinux.h index 88f1e7d3..91894c8c 100644 --- a/kernel/selinux/selinux.h +++ b/kernel/selinux/selinux.h @@ -4,6 +4,10 @@ #include "linux/types.h" #include "linux/version.h" +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || defined(KSU_COMPAT_HAS_SELINUX_STATE) +#define KSU_COMPAT_USE_SELINUX_STATE +#endif + void setup_selinux(const char *); void setenforce(bool); @@ -16,6 +20,18 @@ bool is_zygote(void *cred); void apply_kernelsu_rules(); +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +bool susfs_is_sid_equal(void *sec, u32 sid2); +u32 susfs_get_sid_from_name(const char *secctx_name); +u32 susfs_get_current_sid(void); +void susfs_set_zygote_sid(void); +bool susfs_is_current_zygote_domain(void); +void susfs_set_ksu_sid(void); +bool susfs_is_current_ksu_domain(void); +void susfs_set_init_sid(void); +bool susfs_is_current_init_domain(void); +#endif + u32 ksu_get_devpts_sid(); #endif diff --git a/kernel/selinux/sepolicy.c b/kernel/selinux/sepolicy.c index 7759602c..d7528b7a 100644 --- a/kernel/selinux/sepolicy.c +++ b/kernel/selinux/sepolicy.c @@ -524,6 +524,7 @@ static bool add_filename_trans(struct policydb *db, const char *s, return false; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) struct filename_trans_key key; key.ttype = tgt->value; key.tclass = cls->value; @@ -531,8 +532,13 @@ static bool add_filename_trans(struct policydb *db, const char *s, struct filename_trans_datum *last = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) struct filename_trans_datum *trans = policydb_filenametr_search(db, &key); +#else + struct filename_trans_datum *trans = + hashtab_search(&db->filename_trans, &key); +#endif while (trans) { if (ebitmap_get_bit(&trans->stypes, src->value - 1)) { // Duplicate, overwrite existing data and return @@ -561,6 +567,35 @@ static bool add_filename_trans(struct policydb *db, const char *s, db->compat_filename_trans_count++; return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0; +#else // < 5.7.0, has no filename_trans_key, but struct filename_trans + struct filename_trans key; + key.ttype = tgt->value; + key.tclass = cls->value; + key.name = (char *)o; + struct filename_trans_datum *trans = + hashtab_search(db->filename_trans, &key); + if (trans == NULL) { + trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans), + 1, GFP_ATOMIC); + if (!trans) { + pr_err("add_filename_trans: Failed to alloc datum\n"); + return false; + } + struct filename_trans *new_key = + (struct filename_trans *)kmalloc(sizeof(*new_key), + GFP_ATOMIC); + if (!new_key) { + pr_err("add_filename_trans: Failed to alloc new_key\n"); + return false; + } + *new_key = key; + new_key->name = kstrdup(key.name, GFP_ATOMIC); + trans->otype = def->value; + hashtab_insert(db->filename_trans, new_key, trans); + } + return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) == + 0; +#endif } static bool add_genfscon(struct policydb *db, const char *fs_name, @@ -587,6 +622,7 @@ static void *ksu_realloc(void *old, size_t new_size, size_t old_size) static bool add_type(struct policydb *db, const char *type_name, bool attr) { +#ifdef KSU_SUPPORT_ADD_TYPE struct type_datum *type = symtab_search(&db->p_types, type_name); if (type) { pr_warn("Type %s already exists\n", type_name); @@ -616,6 +652,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr) return false; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) struct ebitmap *new_type_attr_map_array = ksu_realloc(db->type_attr_map_array, value * sizeof(struct ebitmap), @@ -662,6 +699,144 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr) } return true; +#elif defined(CONFIG_IS_HW_HISI) + /* + * Huawei use type_attr_map and type_val_to_struct. + * And use ebitmap not flex_array. + */ + size_t new_size = sizeof(struct ebitmap) * db->p_types.nprim; + struct ebitmap *new_type_attr_map = + (krealloc(db->type_attr_map, new_size, GFP_ATOMIC)); + struct type_datum **new_type_val_to_struct = + krealloc(db->type_val_to_struct, + sizeof(*db->type_val_to_struct) * db->p_types.nprim, + GFP_ATOMIC); + if (!new_type_attr_map) { + pr_err("add_type: alloc type_attr_map failed\n"); + return false; + } + if (!new_type_val_to_struct) { + pr_err("add_type: alloc type_val_to_struct failed\n"); + return false; + } + char **new_val_to_name_types = + krealloc(db->sym_val_to_name[SYM_TYPES], + sizeof(char *) * db->symtab[SYM_TYPES].nprim, + GFP_KERNEL); + if (!new_val_to_name_types) { + pr_err("add_type: alloc val_to_name failed\n"); + return false; + } + db->type_attr_map = new_type_attr_map; + ebitmap_init(&db->type_attr_map[value - 1], HISI_SELINUX_EBITMAP_RO); + ebitmap_set_bit(&db->type_attr_map[value - 1], value - 1, 1); + db->type_val_to_struct = new_type_val_to_struct; + db->type_val_to_struct[value - 1] = type; + db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types; + db->sym_val_to_name[SYM_TYPES][value - 1] = key; + int i; + for (i = 0; i < db->p_roles.nprim; ++i) { + ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1, + 1); + } + return true; +#else + // flex_array is not extensible, we need to create a new bigger one instead + struct flex_array *new_type_attr_map_array = + flex_array_alloc(sizeof(struct ebitmap), db->p_types.nprim, + GFP_ATOMIC | __GFP_ZERO); + struct flex_array *new_type_val_to_struct = + flex_array_alloc(sizeof(struct type_datum *), db->p_types.nprim, + GFP_ATOMIC | __GFP_ZERO); + struct flex_array *new_val_to_name_types = + flex_array_alloc(sizeof(char *), db->symtab[SYM_TYPES].nprim, + GFP_ATOMIC | __GFP_ZERO); + if (!new_type_attr_map_array) { + pr_err("add_type: alloc type_attr_map_array failed\n"); + return false; + } + if (!new_type_val_to_struct) { + pr_err("add_type: alloc type_val_to_struct failed\n"); + return false; + } + if (!new_val_to_name_types) { + pr_err("add_type: alloc val_to_name failed\n"); + return false; + } + // preallocate so we don't have to worry about the put ever failing + if (flex_array_prealloc(new_type_attr_map_array, 0, db->p_types.nprim, + GFP_ATOMIC | __GFP_ZERO)) { + pr_err("add_type: prealloc type_attr_map_array failed\n"); + return false; + } + if (flex_array_prealloc(new_type_val_to_struct, 0, db->p_types.nprim, + GFP_ATOMIC | __GFP_ZERO)) { + pr_err("add_type: prealloc type_val_to_struct_array failed\n"); + return false; + } + if (flex_array_prealloc(new_val_to_name_types, 0, + db->symtab[SYM_TYPES].nprim, + GFP_ATOMIC | __GFP_ZERO)) { + pr_err("add_type: prealloc val_to_name_types failed\n"); + return false; + } + int j; + void *old_elem; + // copy the old data or pointers to new flex arrays + for (j = 0; j < db->type_attr_map_array->total_nr_elements; j++) { + old_elem = flex_array_get(db->type_attr_map_array, j); + if (old_elem) + flex_array_put(new_type_attr_map_array, j, old_elem, + GFP_ATOMIC | __GFP_ZERO); + } + for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) { + old_elem = flex_array_get_ptr(db->type_val_to_struct_array, j); + if (old_elem) + flex_array_put_ptr(new_type_val_to_struct, j, old_elem, + GFP_ATOMIC | __GFP_ZERO); + } + for (j = 0; j < db->symtab[SYM_TYPES].nprim; j++) { + old_elem = + flex_array_get_ptr(db->sym_val_to_name[SYM_TYPES], j); + if (old_elem) + flex_array_put_ptr(new_val_to_name_types, j, old_elem, + GFP_ATOMIC | __GFP_ZERO); + } + // store the pointer of old flex arrays first, when assigning new ones we + // should free it + struct flex_array *old_fa; + old_fa = db->type_attr_map_array; + db->type_attr_map_array = new_type_attr_map_array; + if (old_fa) { + flex_array_free(old_fa); + } + ebitmap_init(flex_array_get(db->type_attr_map_array, value - 1)); + ebitmap_set_bit(flex_array_get(db->type_attr_map_array, value - 1), + value - 1, 1); + old_fa = db->type_val_to_struct_array; + db->type_val_to_struct_array = new_type_val_to_struct; + if (old_fa) { + flex_array_free(old_fa); + } + flex_array_put_ptr(db->type_val_to_struct_array, value - 1, type, + GFP_ATOMIC | __GFP_ZERO); + old_fa = db->sym_val_to_name[SYM_TYPES]; + db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types; + if (old_fa) { + flex_array_free(old_fa); + } + flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1, key, + GFP_ATOMIC | __GFP_ZERO); + int i; + for (i = 0; i < db->p_roles.nprim; ++i) { + ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1, + 1); + } + return true; +#endif +#else + return false; +#endif } static bool set_type_state(struct policydb *db, const char *type_name, @@ -696,7 +871,18 @@ static bool set_type_state(struct policydb *db, const char *type_name, static void add_typeattribute_raw(struct policydb *db, struct type_datum *type, struct type_datum *attr) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1]; +#elif defined(CONFIG_IS_HW_HISI) + /* + * HISI_SELINUX_EBITMAP_RO is Huawei's unique features. + */ + struct ebitmap *sattr = &db->type_attr_map[type->value - 1], + HISI_SELINUX_EBITMAP_RO; +#else + struct ebitmap *sattr = + flex_array_get(db->type_attr_map_array, type->value - 1); +#endif ebitmap_set_bit(sattr, attr->value - 1, 1); struct hashtab_node *node; diff --git a/kernel/setup.sh b/kernel/setup.sh index e688dbaf..c5dd9d53 100755 --- a/kernel/setup.sh +++ b/kernel/setup.sh @@ -39,7 +39,7 @@ perform_cleanup() { # Sets up or update KernelSU environment setup_kernelsu() { echo "[+] Setting up KernelSU..." - test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/tiann/KernelSU && echo "[+] Repository cloned." + test -d "$GKI_ROOT/KernelSU" || git clone https://github.com/rifsxd/KernelSU && echo "[+] Repository cloned." cd "$GKI_ROOT/KernelSU" git stash && echo "[-] Stashed current changes." if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then diff --git a/kernel/sucompat.c b/kernel/sucompat.c index 966cbf8f..7b0b02c7 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -8,7 +8,11 @@ #include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) #include +#else +#include +#endif #include "objsec.h" #include "allowlist.h" @@ -180,7 +184,12 @@ int ksu_handle_devpts(struct inode *inode) return 0; if (ksu_devpts_sid) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) struct inode_security_struct *sec = selinux_inode(inode); +#else + struct inode_security_struct *sec = + (struct inode_security_struct *)inode->i_security; +#endif if (sec) { sec->sid = ksu_devpts_sid; } @@ -191,6 +200,17 @@ int ksu_handle_devpts(struct inode *inode) #ifdef CONFIG_KPROBES +__maybe_unused static int faccessat_handler_pre(struct kprobe *p, + struct pt_regs *regs) +{ + int *dfd = (int *)&PT_REGS_PARM1(regs); + const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs); + int *mode = (int *)&PT_REGS_PARM3(regs); + // Both sys_ and do_ is C function + int *flags = (int *)&PT_REGS_CCALL_PARM4(regs); + return ksu_handle_faccessat(dfd, filename_user, mode, flags); +} + static int sys_faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) { struct pt_regs *real_regs = PT_REAL_REGS(regs); @@ -202,16 +222,41 @@ static int sys_faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) return ksu_handle_faccessat(dfd, filename_user, mode, NULL); } +__maybe_unused static int newfstatat_handler_pre(struct kprobe *p, + struct pt_regs *regs) +{ + int *dfd = (int *)&PT_REGS_PARM1(regs); + const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) + // static int vfs_statx(int dfd, const char __user *filename, int flags, struct kstat *stat, u32 request_mask) + int *flags = (int *)&PT_REGS_PARM3(regs); +#else + // int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,int flag) + int *flags = (int *)&PT_REGS_CCALL_PARM4(regs); +#endif + return ksu_handle_stat(dfd, filename_user, flags); +} + static int sys_newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs) { struct pt_regs *real_regs = PT_REAL_REGS(regs); int *dfd = (int *)&PT_REGS_PARM1(real_regs); - const char __user **filename_user = (const char **)&PT_REGS_PARM2(real_regs); + const char __user **filename_user = + (const char **)&PT_REGS_PARM2(real_regs); int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs); return ksu_handle_stat(dfd, filename_user, flags); } +// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864 +static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + int *fd = (int *)&PT_REGS_PARM1(regs); + struct filename **filename_ptr = + (struct filename **)&PT_REGS_PARM2(regs); + return ksu_handle_execveat_sucompat(fd, filename_ptr, NULL, NULL, NULL); +} + static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) { struct pt_regs *real_regs = PT_REAL_REGS(regs); @@ -222,26 +267,65 @@ static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) NULL); } +#if 1 static struct kprobe faccessat_kp = { .symbol_name = SYS_FACCESSAT_SYMBOL, .pre_handler = sys_faccessat_handler_pre, }; +#else +static struct kprobe faccessat_kp = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) + .symbol_name = "do_faccessat", +#else + .symbol_name = "sys_faccessat", +#endif + .pre_handler = faccessat_handler_pre, +}; +#endif +#if 1 static struct kprobe newfstatat_kp = { .symbol_name = SYS_NEWFSTATAT_SYMBOL, .pre_handler = sys_newfstatat_handler_pre, }; +#else +static struct kprobe newfstatat_kp = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) + .symbol_name = "vfs_statx", +#else + .symbol_name = "vfs_fstatat", +#endif + .pre_handler = newfstatat_handler_pre, +}; +#endif +#if 1 static struct kprobe execve_kp = { .symbol_name = SYS_EXECVE_SYMBOL, .pre_handler = sys_execve_handler_pre, }; +#else +static struct kprobe execve_kp = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + .symbol_name = "do_execveat_common", +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + .symbol_name = "__do_execve_file", +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + .symbol_name = "do_execveat_common", +#endif + .pre_handler = execve_handler_pre, +}; +#endif static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs) { struct inode *inode; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) struct file *file = (struct file *)PT_REGS_PARM2(regs); inode = file->f_path.dentry->d_inode; +#else + inode = (struct inode *)PT_REGS_PARM2(regs); +#endif return ksu_handle_devpts(inode); } @@ -278,3 +362,25 @@ void ksu_sucompat_exit() unregister_kprobe(&pts_unix98_lookup_kp); #endif } + + +#ifdef CONFIG_KSU_SUSFS_SUS_SU +extern bool ksu_devpts_hook; + +void ksu_susfs_disable_sus_su(void) { + enable_kprobe(&execve_kp); + enable_kprobe(&newfstatat_kp); + enable_kprobe(&faccessat_kp); + enable_kprobe(&pts_unix98_lookup_kp); + ksu_devpts_hook = false; +} + +void ksu_susfs_enable_sus_su(void) { + disable_kprobe(&execve_kp); + disable_kprobe(&newfstatat_kp); + disable_kprobe(&faccessat_kp); + disable_kprobe(&pts_unix98_lookup_kp); + ksu_devpts_hook = true; +} +#endif + diff --git a/kernel/throne_tracker.c b/kernel/throne_tracker.c index d7c1dae1..725c9103 100644 --- a/kernel/throne_tracker.c +++ b/kernel/throne_tracker.c @@ -170,7 +170,11 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name, } else { if ((namelen == 8) && (strncmp(name, "base.apk", namelen) == 0)) { struct apk_path_hash *pos, *n; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + unsigned int hash = full_name_hash(dirpath, strlen(dirpath)); +#else unsigned int hash = full_name_hash(NULL, dirpath, strlen(dirpath)); +#endif list_for_each_entry(pos, &apk_path_hash_list, list) { if (hash == pos->hash) { pos->exists = true;