diff --git a/10_enable_susfs_for_ksu.patch b/10_enable_susfs_for_ksu.patch new file mode 100644 index 000000000000..f517954f3b5f --- /dev/null +++ b/10_enable_susfs_for_ksu.patch @@ -0,0 +1,450 @@ +diff --git a/kernel/Kconfig b/kernel/Kconfig +index 67f177f4..27cc7b1e 100644 +--- a/kernel/Kconfig ++++ b/kernel/Kconfig +@@ -16,4 +16,87 @@ 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" ++ depends on KSU_SUSFS ++ default y ++ help ++ Allow hiding the existence of user-defined file/directory from various system calls ++ ++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] ++ ++config KSU_SUSFS_SUS_MOUNT_MNT_ID_REORDER ++ bool "Enable to re-order mount id and parent mount id in mountinfo (experimental)" ++ depends on KSU_SUSFS_SUS_MOUNT ++ default n ++ help ++ - Enable this option will automatically and completely re-order the mount id and parent mount id in /proc/self/mountinfo ++ - The actual mnt_id/parent_mnt_id is not changed in kernel, only reflects on /proc/self/mountinfo ++ - You should not enable this if the mnt_id on your device is already not in order by default ++ - Moreover, be careful this feature may cause unknown system crashes / random reboot / bootloop on different kernels or devices, further testing is needed. ++ - Requires KSU_SUSFS_SUS_MOUNT to be ON ++ ++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 ++ ++config KSU_SUSFS_SUS_MAPS ++ bool "Enable to spoof suspicious maps" ++ depends on KSU_SUSFS ++ default y ++ help ++ Allow spoofing the user-defined mappings in /proc/self/[maps|smaps|map_files] ++ ++config KSU_SUSFS_SUS_PROC_FD_LINK ++ bool "Enable to spoof suspicous symbolic links (experimental)" ++ depends on KSU_SUSFS ++ default n ++ help ++ Allow spoofing the user-defined symbolic link in /proc/self/fd/ ++ ++config KSU_SUSFS_SUS_MEMFD ++ bool "Enable to hide or spoof suspicous memfd (experimental)" ++ depends on KSU_SUSFS ++ default n ++ help ++ Allow preventing the user-defined memfd name from being created ++ ++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 ++ ++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 ++ ++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 ++endmenu ++ + endmenu +diff --git a/kernel/Makefile b/kernel/Makefile +index 66929756..d25f36c7 100644 +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -66,4 +66,43 @@ 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_SUS_MOUNT_MNT_ID_REORDER ++ccflags-y += -DKSU_SUSFS_SUS_MOUNT_MNT_ID_REORDER ++endif ++ifdef KSU_SUSFS_SUS_KSTAT ++ccflags-y += -DKSU_SUSFS_SUS_KSTAT ++endif ++ifdef KSU_SUSFS_SUS_MAPS ++ccflags-y += -DKSU_SUSFS_SUS_MAPS ++endif ++ifdef KSU_SUSFS_SUS_PROC_FD_LINK ++ccflags-y += -DKSU_SUSFS_SUS_PROC_FD_LINK ++endif ++ifdef KSU_SUSFS_SUS_MEMFD ++ccflags-y += -DKSU_SUSFS_SUS_MEMFD ++endif ++ifdef KSU_SUSFS_TRY_UMOUNT ++ccflags-y += -DKSU_SUSFS_TRY_UMOUNT ++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 ++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/core_hook.c b/kernel/core_hook.c +index 429ba330..f016db75 100644 +--- a/kernel/core_hook.c ++++ b/kernel/core_hook.c +@@ -44,6 +44,10 @@ + #include "throne_tracker.h" + #include "kernel_compat.h" + ++#ifdef CONFIG_KSU_SUSFS ++#include ++#endif ++ + static bool ksu_module_mounted = false; + + extern int handle_sepolicy(unsigned long arg3, void __user *arg4); +@@ -375,6 +379,207 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, + return 0; + } + ++#ifdef CONFIG_KSU_SUSFS ++ if (current_uid().val == 0) { ++ int error = 0; ++#ifdef CONFIG_KSU_SUSFS_SUS_PATH ++ if (arg2 == CMD_SUSFS_ADD_SUS_PATH) { ++ 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); ++ copy_to_user((void __user*)arg5, &error, sizeof(error)); ++ return 0; ++ } ++#endif //#ifdef CONFIG_KSU_SUSFS_SUS_PATH ++#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT ++ if (arg2 == CMD_SUSFS_ADD_SUS_MOUNT) { ++ 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); ++ copy_to_user((void __user*)arg5, &error, sizeof(error)); ++ return 0; ++ } ++#endif //#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT ++#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT ++ if (arg2 == CMD_SUSFS_ADD_SUS_KSTAT) { ++ 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); ++ copy_to_user((void __user*)arg5, &error, sizeof(error)); ++ return 0; ++ } ++ if (arg2 == CMD_SUSFS_UPDATE_SUS_KSTAT) { ++ 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); ++ copy_to_user((void __user*)arg5, &error, sizeof(error)); ++ return 0; ++ } ++ if (arg2 == CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY) { ++ 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); ++ copy_to_user((void __user*)arg5, &error, sizeof(error)); ++ return 0; ++ } ++#endif //#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT ++#ifdef CONFIG_KSU_SUSFS_SUS_MAPS ++ if (arg2 == CMD_SUSFS_ADD_SUS_MAPS) { ++ if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_maps))) { ++ pr_err("susfs: CMD_SUSFS_ADD_SUS_MAPS -> arg3 is not accessible\n"); ++ return 0; ++ } ++ if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { ++ pr_err("susfs: CMD_SUSFS_ADD_SUS_MAPS -> arg5 is not accessible\n"); ++ return 0; ++ } ++ error = susfs_add_sus_maps((struct st_susfs_sus_maps __user*)arg3); ++ pr_info("susfs: CMD_SUSFS_ADD_SUS_MAPS -> ret: %d\n", error); ++ copy_to_user((void __user*)arg5, &error, sizeof(error)); ++ return 0; ++ } ++ if (arg2 == CMD_SUSFS_UPDATE_SUS_MAPS) { ++ if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_maps))) { ++ pr_err("susfs: CMD_SUSFS_UPDATE_SUS_MAPS -> arg3 is not accessible\n"); ++ return 0; ++ } ++ if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { ++ pr_err("susfs: CMD_SUSFS_UPDATE_SUS_MAPS -> arg5 is not accessible\n"); ++ return 0; ++ } ++ error = susfs_update_sus_maps((struct st_susfs_sus_maps __user*)arg3); ++ pr_info("susfs: CMD_SUSFS_UPDATE_SUS_MAPS -> ret: %d\n", error); ++ copy_to_user((void __user*)arg5, &error, sizeof(error)); ++ return 0; ++ } ++ if (arg2 == CMD_SUSFS_ADD_SUS_MAPS_STATICALLY) { ++ if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_maps))) { ++ pr_err("susfs: CMD_SUSFS_ADD_SUS_MAPS_STATICALLY -> arg3 is not accessible\n"); ++ return 0; ++ } ++ if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { ++ pr_err("susfs: CMD_SUSFS_ADD_SUS_MAPS_STATICALLY -> arg5 is not accessible\n"); ++ return 0; ++ } ++ error = susfs_add_sus_maps((struct st_susfs_sus_maps __user*)arg3); ++ pr_info("susfs: CMD_SUSFS_ADD_SUS_MAPS_STATICALLY -> ret: %d\n", error); ++ copy_to_user((void __user*)arg5, &error, sizeof(error)); ++ return 0; ++ } ++#endif //#ifdef CONFIG_KSU_SUSFS_SUS_MAPS ++#ifdef CONFIG_KSU_SUSFS_SUS_PROC_FD_LINK ++ if (arg2 == CMD_SUSFS_ADD_SUS_PROC_FD_LINK) { ++ if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_proc_fd_link))) { ++ pr_err("susfs: CMD_SUSFS_ADD_SUS_PROC_FD_LINK -> arg3 is not accessible\n"); ++ return 0; ++ } ++ if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { ++ pr_err("susfs: CMD_SUSFS_ADD_SUS_PROC_FD_LINK -> arg5 is not accessible\n"); ++ return 0; ++ } ++ error = susfs_add_sus_proc_fd_link((struct st_susfs_sus_proc_fd_link __user*)arg3); ++ pr_info("susfs: CMD_SUSFS_ADD_SUS_PROC_FD_LINK -> ret: %d\n", error); ++ copy_to_user((void __user*)arg5, &error, sizeof(error)); ++ return 0; ++ } ++#endif //#ifdef CONFIG_KSU_SUSFS_SUS_PROC_FD_LINK ++#ifdef CONFIG_KSU_SUSFS_SUS_MEMFD ++ if (arg2 == CMD_SUSFS_ADD_SUS_MEMFD) { ++ if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_memfd))) { ++ pr_err("susfs: CMD_SUSFS_ADD_SUS_MEMFD -> arg3 is not accessible\n"); ++ return 0; ++ } ++ if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { ++ pr_err("susfs: CMD_SUSFS_ADD_SUS_MEMFD -> arg5 is not accessible\n"); ++ return 0; ++ } ++ error = susfs_add_sus_memfd((struct st_susfs_sus_memfd __user*)arg3); ++ pr_info("susfs: CMD_SUSFS_ADD_SUS_MEMFD -> ret: %d\n", error); ++ copy_to_user((void __user*)arg5, &error, sizeof(error)); ++ return 0; ++ } ++#endif //#ifdef CONFIG_KSU_SUSFS_SUS_MEMFD ++#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT ++ if (arg2 == CMD_SUSFS_ADD_TRY_UMOUNT) { ++ 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); ++ copy_to_user((void __user*)arg5, &error, sizeof(error)); ++ return 0; ++ } ++#endif //#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT ++#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME ++ if (arg2 == CMD_SUSFS_SET_UNAME) { ++ 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); ++ copy_to_user((void __user*)arg5, &error, sizeof(error)); ++ return 0; ++ } ++#endif //#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME ++#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG ++ if (arg2 == CMD_SUSFS_ENABLE_LOG) { ++ 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); ++ copy_to_user((void __user*)arg5, &error, sizeof(error)); ++ return 0; ++ } ++#endif //#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG ++ } ++#endif //#ifdef CONFIG_KSU_SUSFS ++ + // all other cmds are for 'root manager' + if (!from_manager) { + return 0; +@@ -535,6 +740,11 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) + current->pid); + #endif + ++#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT ++ // susfs come first, and lastly umount by ksu, make sure umount in reversed order ++ susfs_try_umount(new_uid.val); ++#endif ++ + // 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); +diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c +index b242bc63..0ec3df62 100644 +--- a/kernel/kernel_compat.c ++++ b/kernel/kernel_compat.c +@@ -77,6 +77,16 @@ 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) +diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h +index ba998185..34050e73 100644 +--- a/kernel/kernel_compat.h ++++ b/kernel/kernel_compat.h +@@ -29,6 +29,7 @@ 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 3639edc2..4a9cfaba 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); +diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c +index 1ba6d853..7b24203c 100644 +--- a/kernel/selinux/rules.c ++++ b/kernel/selinux/rules.c +@@ -134,6 +134,11 @@ 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"); ++#endif ++ + rcu_read_unlock(); + } + diff --git a/kernel/Kconfig b/kernel/Kconfig index 67f177f4708a..27cc7b1edf26 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -16,4 +16,87 @@ 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" + depends on KSU_SUSFS + default y + help + Allow hiding the existence of user-defined file/directory from various system calls + +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] + +config KSU_SUSFS_SUS_MOUNT_MNT_ID_REORDER + bool "Enable to re-order mount id and parent mount id in mountinfo (experimental)" + depends on KSU_SUSFS_SUS_MOUNT + default n + help + - Enable this option will automatically and completely re-order the mount id and parent mount id in /proc/self/mountinfo + - The actual mnt_id/parent_mnt_id is not changed in kernel, only reflects on /proc/self/mountinfo + - You should not enable this if the mnt_id on your device is already not in order by default + - Moreover, be careful this feature may cause unknown system crashes / random reboot / bootloop on different kernels or devices, further testing is needed. + - Requires KSU_SUSFS_SUS_MOUNT to be ON + +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 + +config KSU_SUSFS_SUS_MAPS + bool "Enable to spoof suspicious maps" + depends on KSU_SUSFS + default y + help + Allow spoofing the user-defined mappings in /proc/self/[maps|smaps|map_files] + +config KSU_SUSFS_SUS_PROC_FD_LINK + bool "Enable to spoof suspicous symbolic links (experimental)" + depends on KSU_SUSFS + default n + help + Allow spoofing the user-defined symbolic link in /proc/self/fd/ + +config KSU_SUSFS_SUS_MEMFD + bool "Enable to hide or spoof suspicous memfd (experimental)" + depends on KSU_SUSFS + default n + help + Allow preventing the user-defined memfd name from being created + +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 + +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 + +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 +endmenu + endmenu diff --git a/kernel/Makefile b/kernel/Makefile index 669297563b1d..d25f36c7c154 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -66,4 +66,43 @@ 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_SUS_MOUNT_MNT_ID_REORDER +ccflags-y += -DKSU_SUSFS_SUS_MOUNT_MNT_ID_REORDER +endif +ifdef KSU_SUSFS_SUS_KSTAT +ccflags-y += -DKSU_SUSFS_SUS_KSTAT +endif +ifdef KSU_SUSFS_SUS_MAPS +ccflags-y += -DKSU_SUSFS_SUS_MAPS +endif +ifdef KSU_SUSFS_SUS_PROC_FD_LINK +ccflags-y += -DKSU_SUSFS_SUS_PROC_FD_LINK +endif +ifdef KSU_SUSFS_SUS_MEMFD +ccflags-y += -DKSU_SUSFS_SUS_MEMFD +endif +ifdef KSU_SUSFS_TRY_UMOUNT +ccflags-y += -DKSU_SUSFS_TRY_UMOUNT +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 +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/core_hook.c b/kernel/core_hook.c index 009216efa454..af4e7afa7fc7 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -45,6 +45,10 @@ #include "throne_tracker.h" #include "kernel_compat.h" +#ifdef CONFIG_KSU_SUSFS +#include +#endif + static bool ksu_module_mounted = false; extern int handle_sepolicy(unsigned long arg3, void __user *arg4); @@ -376,6 +380,207 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, return 0; } +#ifdef CONFIG_KSU_SUSFS + if (current_uid().val == 0) { + int error = 0; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (arg2 == CMD_SUSFS_ADD_SUS_PATH) { + 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); + copy_to_user((void __user*)arg5, &error, sizeof(error)); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_PATH +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (arg2 == CMD_SUSFS_ADD_SUS_MOUNT) { + 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); + copy_to_user((void __user*)arg5, &error, sizeof(error)); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + if (arg2 == CMD_SUSFS_ADD_SUS_KSTAT) { + 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); + copy_to_user((void __user*)arg5, &error, sizeof(error)); + return 0; + } + if (arg2 == CMD_SUSFS_UPDATE_SUS_KSTAT) { + 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); + copy_to_user((void __user*)arg5, &error, sizeof(error)); + return 0; + } + if (arg2 == CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY) { + 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); + copy_to_user((void __user*)arg5, &error, sizeof(error)); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +#ifdef CONFIG_KSU_SUSFS_SUS_MAPS + if (arg2 == CMD_SUSFS_ADD_SUS_MAPS) { + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_maps))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_MAPS -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_MAPS -> arg5 is not accessible\n"); + return 0; + } + error = susfs_add_sus_maps((struct st_susfs_sus_maps __user*)arg3); + pr_info("susfs: CMD_SUSFS_ADD_SUS_MAPS -> ret: %d\n", error); + copy_to_user((void __user*)arg5, &error, sizeof(error)); + return 0; + } + if (arg2 == CMD_SUSFS_UPDATE_SUS_MAPS) { + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_maps))) { + pr_err("susfs: CMD_SUSFS_UPDATE_SUS_MAPS -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_UPDATE_SUS_MAPS -> arg5 is not accessible\n"); + return 0; + } + error = susfs_update_sus_maps((struct st_susfs_sus_maps __user*)arg3); + pr_info("susfs: CMD_SUSFS_UPDATE_SUS_MAPS -> ret: %d\n", error); + copy_to_user((void __user*)arg5, &error, sizeof(error)); + return 0; + } + if (arg2 == CMD_SUSFS_ADD_SUS_MAPS_STATICALLY) { + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_maps))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_MAPS_STATICALLY -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_MAPS_STATICALLY -> arg5 is not accessible\n"); + return 0; + } + error = susfs_add_sus_maps((struct st_susfs_sus_maps __user*)arg3); + pr_info("susfs: CMD_SUSFS_ADD_SUS_MAPS_STATICALLY -> ret: %d\n", error); + copy_to_user((void __user*)arg5, &error, sizeof(error)); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_MAPS +#ifdef CONFIG_KSU_SUSFS_SUS_PROC_FD_LINK + if (arg2 == CMD_SUSFS_ADD_SUS_PROC_FD_LINK) { + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_proc_fd_link))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_PROC_FD_LINK -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_PROC_FD_LINK -> arg5 is not accessible\n"); + return 0; + } + error = susfs_add_sus_proc_fd_link((struct st_susfs_sus_proc_fd_link __user*)arg3); + pr_info("susfs: CMD_SUSFS_ADD_SUS_PROC_FD_LINK -> ret: %d\n", error); + copy_to_user((void __user*)arg5, &error, sizeof(error)); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_PROC_FD_LINK +#ifdef CONFIG_KSU_SUSFS_SUS_MEMFD + if (arg2 == CMD_SUSFS_ADD_SUS_MEMFD) { + if (!ksu_access_ok((void __user*)arg3, sizeof(struct st_susfs_sus_memfd))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_MEMFD -> arg3 is not accessible\n"); + return 0; + } + if (!ksu_access_ok((void __user*)arg5, sizeof(error))) { + pr_err("susfs: CMD_SUSFS_ADD_SUS_MEMFD -> arg5 is not accessible\n"); + return 0; + } + error = susfs_add_sus_memfd((struct st_susfs_sus_memfd __user*)arg3); + pr_info("susfs: CMD_SUSFS_ADD_SUS_MEMFD -> ret: %d\n", error); + copy_to_user((void __user*)arg5, &error, sizeof(error)); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_MEMFD +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT + if (arg2 == CMD_SUSFS_ADD_TRY_UMOUNT) { + 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); + copy_to_user((void __user*)arg5, &error, sizeof(error)); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT +#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME + if (arg2 == CMD_SUSFS_SET_UNAME) { + 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); + copy_to_user((void __user*)arg5, &error, sizeof(error)); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME +#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG + if (arg2 == CMD_SUSFS_ENABLE_LOG) { + 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); + copy_to_user((void __user*)arg5, &error, sizeof(error)); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG + } +#endif //#ifdef CONFIG_KSU_SUSFS + // all other cmds are for 'root manager' if (!from_manager) { return 0; @@ -536,6 +741,11 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) current->pid); #endif +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT + // susfs come first, and lastly umount by ksu, make sure umount in reversed order + susfs_try_umount(new_uid.val); +#endif + // 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); diff --git a/kernel/core_hook.c.orig b/kernel/core_hook.c.orig new file mode 100644 index 000000000000..009216efa454 --- /dev/null +++ b/kernel/core_hook.c.orig @@ -0,0 +1,857 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef MODULE +#include +#include +#include +#include +#include +#endif + +#include "allowlist.h" +#include "arch.h" +#include "core_hook.h" +#include "klog.h" // IWYU pragma: keep +#include "ksu.h" +#include "ksud.h" +#include "manager.h" +#include "selinux/selinux.h" +#include "throne_tracker.h" +#include "throne_tracker.h" +#include "kernel_compat.h" + +static bool ksu_module_mounted = false; + +extern int handle_sepolicy(unsigned long arg3, void __user *arg4); + +static inline bool is_allow_su() +{ + if (is_manager()) { + // we are manager, allow! + return true; + } + return ksu_is_allow_uid(current_uid().val); +} + +static inline bool is_unsupported_uid(uid_t uid) +{ +#define LAST_APPLICATION_UID 19999 + uid_t appid = uid % 100000; + return appid > LAST_APPLICATION_UID; +} + +static struct group_info root_groups = { .usage = ATOMIC_INIT(2) }; + +static void setup_groups(struct root_profile *profile, struct cred *cred) +{ + if (profile->groups_count > KSU_MAX_GROUPS) { + pr_warn("Failed to setgroups, too large group: %d!\n", + profile->uid); + return; + } + + if (profile->groups_count == 1 && profile->groups[0] == 0) { + // setgroup to root and return early. + if (cred->group_info) + put_group_info(cred->group_info); + cred->group_info = get_group_info(&root_groups); + return; + } + + u32 ngroups = profile->groups_count; + struct group_info *group_info = groups_alloc(ngroups); + if (!group_info) { + pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid); + return; + } + + int i; + for (i = 0; i < ngroups; i++) { + gid_t gid = profile->groups[i]; + kgid_t kgid = make_kgid(current_user_ns(), gid); + if (!gid_valid(kgid)) { + pr_warn("Failed to setgroups, invalid gid: %d\n", gid); + 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); + set_groups(cred, group_info); +} + +void escape_to_root(void) +{ + struct cred *cred; + + cred = (struct cred *)__task_cred(current); + + if (cred->euid.val == 0) { + pr_warn("Already root, don't escape!\n"); + return; + } + struct root_profile *profile = ksu_get_root_profile(cred->uid.val); + + cred->uid.val = profile->uid; + cred->suid.val = profile->uid; + cred->euid.val = profile->uid; + cred->fsuid.val = profile->uid; + + cred->gid.val = profile->gid; + cred->fsgid.val = profile->gid; + cred->sgid.val = profile->gid; + cred->egid.val = profile->gid; + + BUILD_BUG_ON(sizeof(profile->capabilities.effective) != + sizeof(kernel_cap_t)); + + // setup capabilities + // we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process + // we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec! + u64 cap_for_ksud = + profile->capabilities.effective | CAP_DAC_READ_SEARCH; + memcpy(&cred->cap_effective, &cap_for_ksud, + sizeof(cred->cap_effective)); + memcpy(&cred->cap_inheritable, &profile->capabilities.effective, + sizeof(cred->cap_inheritable)); + memcpy(&cred->cap_permitted, &profile->capabilities.effective, + sizeof(cred->cap_permitted)); + memcpy(&cred->cap_bset, &profile->capabilities.effective, + sizeof(cred->cap_bset)); + memcpy(&cred->cap_ambient, &profile->capabilities.effective, + sizeof(cred->cap_ambient)); + + // disable seccomp +#if defined(CONFIG_GENERIC_ENTRY) && \ + LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + current_thread_info()->syscall_work &= ~SYSCALL_WORK_SECCOMP; +#else + current_thread_info()->flags &= ~(TIF_SECCOMP | _TIF_SECCOMP); +#endif + +#ifdef CONFIG_SECCOMP + current->seccomp.mode = 0; + current->seccomp.filter = NULL; +#else +#endif + + setup_groups(profile, cred); + + setup_selinux(profile->selinux_domain); +} + +int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry) +{ + if (!current->mm) { + // skip kernel threads + return 0; + } + + if (current_uid().val != 1000) { + // skip non system uid + return 0; + } + + if (!old_dentry || !new_dentry) { + return 0; + } + + // /data/system/packages.list.tmp -> /data/system/packages.list + if (strcmp(new_dentry->d_iname, "packages.list")) { + return 0; + } + + char path[128]; + char *buf = dentry_path_raw(new_dentry, path, sizeof(path)); + if (IS_ERR(buf)) { + pr_err("dentry_path_raw failed.\n"); + return 0; + } + + if (!strstr(buf, "/system/packages.list")) { + return 0; + } + pr_info("renameat: %s -> %s, new path: %s\n", old_dentry->d_iname, + new_dentry->d_iname, buf); + + track_throne(); + + return 0; +} + +int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) +{ + // if success, we modify the arg5 as result! + u32 *result = (u32 *)arg5; + u32 reply_ok = KERNEL_SU_OPTION; + + if (KERNEL_SU_OPTION != option) { + return 0; + } + + // TODO: find it in throne tracker! + uid_t current_uid_val = current_uid().val; + uid_t manager_uid = ksu_get_manager_uid(); + if (current_uid_val != manager_uid && + current_uid_val % 100000 == manager_uid) { + ksu_set_manager_uid(current_uid_val); + } + + bool from_root = 0 == current_uid().val; + bool from_manager = is_manager(); + + if (!from_root && !from_manager) { + // only root or manager can access this interface + return 0; + } + +#ifdef CONFIG_KSU_DEBUG + pr_info("option: 0x%x, cmd: %ld\n", option, arg2); +#endif + + if (arg2 == CMD_BECOME_MANAGER) { + if (from_manager) { + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("become_manager: prctl reply error\n"); + } + return 0; + } + return 0; + } + + if (arg2 == CMD_GRANT_ROOT) { + if (is_allow_su()) { + pr_info("allow root for: %d\n", current_uid().val); + escape_to_root(); + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("grant_root: prctl reply error\n"); + } + } + return 0; + } + + // Both root manager and root processes should be allowed to get version + if (arg2 == CMD_GET_VERSION) { + u32 version = KERNEL_SU_VERSION; + if (copy_to_user(arg3, &version, sizeof(version))) { + pr_err("prctl reply error, cmd: %lu\n", arg2); + } +#ifdef MODULE + u32 is_lkm = 0x1; +#else + u32 is_lkm = 0x0; +#endif + if (arg4 && copy_to_user(arg4, &is_lkm, sizeof(is_lkm))) { + pr_err("prctl reply error, cmd: %lu\n", arg2); + } + return 0; + } + + if (arg2 == CMD_REPORT_EVENT) { + if (!from_root) { + return 0; + } + switch (arg3) { + case EVENT_POST_FS_DATA: { + static bool post_fs_data_lock = false; + if (!post_fs_data_lock) { + post_fs_data_lock = true; + pr_info("post-fs-data triggered\n"); + on_post_fs_data(); + } + break; + } + case EVENT_BOOT_COMPLETED: { + static bool boot_complete_lock = false; + if (!boot_complete_lock) { + boot_complete_lock = true; + pr_info("boot_complete triggered\n"); + } + break; + } + case EVENT_MODULE_MOUNTED: { + ksu_module_mounted = true; + pr_info("module mounted!\n"); + break; + } + default: + break; + } + return 0; + } + + if (arg2 == CMD_SET_SEPOLICY) { + if (!from_root) { + return 0; + } + if (!handle_sepolicy(arg3, arg4)) { + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("sepolicy: prctl reply error\n"); + } + } + + return 0; + } + + if (arg2 == CMD_CHECK_SAFEMODE) { + if (ksu_is_safe_mode()) { + pr_warn("safemode enabled!\n"); + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("safemode: prctl reply error\n"); + } + } + return 0; + } + + if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) { + u32 array[128]; + u32 array_length; + bool success = ksu_get_allow_list(array, &array_length, + arg2 == CMD_GET_ALLOW_LIST); + if (success) { + if (!copy_to_user(arg4, &array_length, + sizeof(array_length)) && + !copy_to_user(arg3, array, + sizeof(u32) * array_length)) { + if (copy_to_user(result, &reply_ok, + sizeof(reply_ok))) { + pr_err("prctl reply error, cmd: %lu\n", + arg2); + } + } else { + pr_err("prctl copy allowlist error\n"); + } + } + return 0; + } + + if (arg2 == CMD_UID_GRANTED_ROOT || arg2 == CMD_UID_SHOULD_UMOUNT) { + uid_t target_uid = (uid_t)arg3; + bool allow = false; + if (arg2 == CMD_UID_GRANTED_ROOT) { + allow = ksu_is_allow_uid(target_uid); + } else if (arg2 == CMD_UID_SHOULD_UMOUNT) { + allow = ksu_uid_should_umount(target_uid); + } else { + pr_err("unknown cmd: %lu\n", arg2); + } + if (!copy_to_user(arg4, &allow, sizeof(allow))) { + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("prctl reply error, cmd: %lu\n", arg2); + } + } else { + pr_err("prctl copy err, cmd: %lu\n", arg2); + } + return 0; + } + + // all other cmds are for 'root manager' + if (!from_manager) { + return 0; + } + + // we are already manager + if (arg2 == CMD_GET_APP_PROFILE) { + struct app_profile profile; + if (copy_from_user(&profile, arg3, sizeof(profile))) { + pr_err("copy profile failed\n"); + return 0; + } + + bool success = ksu_get_app_profile(&profile); + if (success) { + if (copy_to_user(arg3, &profile, sizeof(profile))) { + pr_err("copy profile failed\n"); + return 0; + } + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("prctl reply error, cmd: %lu\n", arg2); + } + } + return 0; + } + + if (arg2 == CMD_SET_APP_PROFILE) { + struct app_profile profile; + if (copy_from_user(&profile, arg3, sizeof(profile))) { + pr_err("copy profile failed\n"); + return 0; + } + + // todo: validate the params + if (ksu_set_app_profile(&profile, true)) { + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("prctl reply error, cmd: %lu\n", arg2); + } + } + return 0; + } + + return 0; +} + +static bool is_appuid(kuid_t uid) +{ +#define PER_USER_RANGE 100000 +#define FIRST_APPLICATION_UID 10000 +#define LAST_APPLICATION_UID 19999 + + uid_t appid = uid.val % PER_USER_RANGE; + return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID; +} + +static bool should_umount(struct path *path) +{ + if (!path) { + return false; + } + + if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) { + pr_info("ignore global mnt namespace process: %d\n", + current_uid().val); + return false; + } + + if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) { + const char *fstype = path->mnt->mnt_sb->s_type->name; + return strcmp(fstype, "overlay") == 0; + } + return false; +} + +static int ksu_umount_mnt(struct path *path, int flags) +{ +#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 +} + +static void try_umount(const char *mnt, bool check_mnt, int flags) +{ + struct path path; + int err = kern_path(mnt, 0, &path); + if (err) { + return; + } + + if (path.dentry != path.mnt->mnt_root) { + // it is not root mountpoint, maybe umounted by others already. + return; + } + + // we are only interest in some specific mounts + if (check_mnt && !should_umount(&path)) { + return; + } + + err = ksu_umount_mnt(&path, flags); + if (err) { + pr_warn("umount %s failed: %d\n", mnt, err); + } +} + +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! + if (!ksu_module_mounted) { + return 0; + } + + if (!new || !old) { + return 0; + } + + kuid_t new_uid = new->uid; + kuid_t old_uid = old->uid; + + if (0 != old_uid.val) { + // old process is not root, ignore it. + return 0; + } + + 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; + } + + if (ksu_is_allow_uid(new_uid.val)) { + // pr_info("handle setuid ignore allowed application: %d\n", new_uid.val); + return 0; + } + + if (!ksu_uid_should_umount(new_uid.val)) { + return 0; + } else { +#ifdef CONFIG_KSU_DEBUG + pr_info("uid: %d should not umount!\n", current_uid().val); +#endif + } + + // 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); + 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 + + // 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); + try_umount("/vendor", true, 0); + try_umount("/product", true, 0); + try_umount("/data/adb/modules", false, MNT_DETACH); + + // try umount ksu temp path + try_umount("/debug_ramdisk", false, MNT_DETACH); + try_umount("/sbin", false, MNT_DETACH); + + return 0; +} + +// Init functons + +static int handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_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); +} + +static struct kprobe prctl_kp = { + .symbol_name = PRCTL_SYMBOL, + .pre_handler = handler_pre, +}; + +static int renameat_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) + // https://elixir.bootlin.com/linux/v5.12-rc1/source/include/linux/fs.h + struct renamedata *rd = PT_REGS_PARM1(regs); + struct dentry *old_entry = rd->old_dentry; + struct dentry *new_entry = rd->new_dentry; +#else + struct dentry *old_entry = (struct dentry *)PT_REGS_PARM2(regs); + struct dentry *new_entry = (struct dentry *)PT_REGS_CCALL_PARM4(regs); +#endif + + return ksu_handle_rename(old_entry, new_entry); +} + +static struct kprobe renameat_kp = { + .symbol_name = "vfs_rename", + .pre_handler = renameat_handler_pre, +}; + +__maybe_unused int ksu_kprobe_init(void) +{ + int rc = 0; + rc = register_kprobe(&prctl_kp); + + if (rc) { + pr_info("prctl kprobe failed: %d.\n", rc); + return rc; + } + + rc = register_kprobe(&renameat_kp); + pr_info("renameat kp: %d\n", rc); + + return rc; +} + +__maybe_unused int ksu_kprobe_exit(void) +{ + unregister_kprobe(&prctl_kp); + unregister_kprobe(&renameat_kp); + return 0; +} + +static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5) +{ + ksu_handle_prctl(option, arg2, arg3, arg4, arg5); + 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) +{ + return ksu_handle_rename(old_dentry, new_dentry); +} + +static int ksu_task_fix_setuid(struct cred *new, const struct cred *old, + int flags) +{ + return ksu_handle_setuid(new, old); +} + +#ifndef MODULE +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 +static int override_security_head(void *head, const void *new_head, size_t len) +{ + unsigned long base = (unsigned long)head & PAGE_MASK; + unsigned long offset = offset_in_page(head); + + // this is impossible for our case because the page alignment + // but be careful for other cases! + BUG_ON(offset + len > PAGE_SIZE); + struct page *page = phys_to_page(__pa(base)); + if (!page) { + return -EFAULT; + } + + void *addr = vmap(&page, 1, VM_MAP, PAGE_KERNEL); + if (!addr) { + return -ENOMEM; + } + local_irq_disable(); + memcpy(addr + offset, new_head, len); + local_irq_enable(); + vunmap(addr); + return 0; +} + +static void free_security_hook_list(struct hlist_head *head) +{ + struct hlist_node *temp; + struct security_hook_list *entry; + + if (!head) + return; + + hlist_for_each_entry_safe (entry, temp, head, list) { + hlist_del(&entry->list); + kfree(entry); + } + + kfree(head); +} + +struct hlist_head *copy_security_hlist(struct hlist_head *orig) +{ + struct hlist_head *new_head = kmalloc(sizeof(*new_head), GFP_KERNEL); + if (!new_head) + return NULL; + + INIT_HLIST_HEAD(new_head); + + struct security_hook_list *entry; + struct security_hook_list *new_entry; + + hlist_for_each_entry (entry, orig, list) { + new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); + if (!new_entry) { + free_security_hook_list(new_head); + return NULL; + } + + *new_entry = *entry; + + hlist_add_tail_rcu(&new_entry->list, new_head); + } + + return new_head; +} + +#define LSM_SEARCH_MAX 180 // This should be enough to iterate +static void *find_head_addr(void *security_ptr, int *index) +{ + if (!security_ptr) { + return NULL; + } + struct hlist_head *head_start = + (struct hlist_head *)&security_hook_heads; + + for (int i = 0; i < LSM_SEARCH_MAX; i++) { + struct hlist_head *head = head_start + i; + struct security_hook_list *pos; + hlist_for_each_entry (pos, head, list) { + if (pos->hook.capget == security_ptr) { + if (index) { + *index = i; + } + return head; + } + } + } + + return NULL; +} + +#define GET_SYMBOL_ADDR(sym) \ + ({ \ + void *addr = kallsyms_lookup_name(#sym ".cfi_jt"); \ + if (!addr) { \ + addr = kallsyms_lookup_name(#sym); \ + } \ + addr; \ + }) + +#define KSU_LSM_HOOK_HACK_INIT(head_ptr, name, func) \ + do { \ + static struct security_hook_list hook = { \ + .hook = { .name = func } \ + }; \ + hook.head = head_ptr; \ + hook.lsm = "ksu"; \ + struct hlist_head *new_head = copy_security_hlist(hook.head); \ + if (!new_head) { \ + pr_err("Failed to copy security list: %s\n", #name); \ + break; \ + } \ + hlist_add_tail_rcu(&hook.list, new_head); \ + if (override_security_head(hook.head, new_head, \ + sizeof(*new_head))) { \ + free_security_hook_list(new_head); \ + pr_err("Failed to hack lsm for: %s\n", #name); \ + } \ + } while (0) + +void __init ksu_lsm_hook_init(void) +{ + void *cap_prctl = GET_SYMBOL_ADDR(cap_task_prctl); + void *prctl_head = find_head_addr(cap_prctl, NULL); + if (prctl_head) { + if (prctl_head != &security_hook_heads.task_prctl) { + pr_warn("prctl's address has shifted!\n"); + } + KSU_LSM_HOOK_HACK_INIT(prctl_head, task_prctl, ksu_task_prctl); + } else { + pr_warn("Failed to find task_prctl!\n"); + } + + int inode_killpriv_index = -1; + void *cap_killpriv = GET_SYMBOL_ADDR(cap_inode_killpriv); + find_head_addr(cap_killpriv, &inode_killpriv_index); + if (inode_killpriv_index < 0) { + pr_warn("Failed to find inode_rename, use kprobe instead!\n"); + register_kprobe(&renameat_kp); + } else { + int inode_rename_index = inode_killpriv_index + + &security_hook_heads.inode_rename - + &security_hook_heads.inode_killpriv; + struct hlist_head *head_start = + (struct hlist_head *)&security_hook_heads; + void *inode_rename_head = head_start + inode_rename_index; + if (inode_rename_head != &security_hook_heads.inode_rename) { + pr_warn("inode_rename's address has shifted!\n"); + } + KSU_LSM_HOOK_HACK_INIT(inode_rename_head, inode_rename, + ksu_inode_rename); + } + void *cap_setuid = GET_SYMBOL_ADDR(cap_task_fix_setuid); + void *setuid_head = find_head_addr(cap_setuid, NULL); + if (setuid_head) { + if (setuid_head != &security_hook_heads.task_fix_setuid) { + pr_warn("setuid's address has shifted!\n"); + } + KSU_LSM_HOOK_HACK_INIT(setuid_head, task_fix_setuid, + ksu_task_fix_setuid); + } else { + pr_warn("Failed to find task_fix_setuid!\n"); + } + smp_mb(); +} +#endif + +void __init ksu_core_init(void) +{ + ksu_lsm_hook_init(); +} + +void ksu_core_exit(void) +{ +#ifdef CONFIG_KPROBES + pr_info("ksu_core_kprobe_exit\n"); + // we dont use this now + // ksu_kprobe_exit(); +#endif +} diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c index b242bc637398..0ec3df624f94 100644 --- a/kernel/kernel_compat.c +++ b/kernel/kernel_compat.c @@ -77,6 +77,16 @@ 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) diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h index ba9981857fd1..34050e73d790 100644 --- a/kernel/kernel_compat.h +++ b/kernel/kernel_compat.h @@ -29,6 +29,7 @@ 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 3639edc21503..4a9cfaba9f4f 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); diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index 1ba6d853f2a9..7b24203ce0ad 100644 --- a/kernel/selinux/rules.c +++ b/kernel/selinux/rules.c @@ -134,6 +134,11 @@ 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"); +#endif + rcu_read_unlock(); }