diff --git a/kernel/Kconfig b/kernel/Kconfig index 99e1d136..839c7d41 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -48,4 +48,131 @@ config KSU_MANUAL_HOOK help If enabled, Hook required KernelSU syscalls with manually-patched function. +menu "KernelSU - SUSFS" + +config KSU_SUSFS + bool "KernelSU addon - SUSFS" + depends on KSU + depends on THREAD_INFO_IN_TASK + 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 n + help + - Allow hiding the user-defined path and all its sub-paths from various system calls. + - Includes temp fix for the leaks of app path in /sdcard/Android/data directory. + - Effective only on zygote spawned user app process. + - 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 n + help + - Allow spoofing the kstat of user-defined file/directory. + - Effective only on zygote spawned user app process. + +config KSU_SUSFS_TRY_UMOUNT + bool "Enable to use ksu's try_umount" + depends on KSU_SUSFS + default y + help + - Allow using try_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 n + 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_CMDLINE_OR_BOOTCONFIG + bool "Enable to spoof /proc/bootconfig (gki) or /proc/cmdline (non-gki)" + depends on KSU_SUSFS + default y + help + - Spoof the output of /proc/bootconfig (gki) or /proc/cmdline (non-gki) 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 n + 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_MAP + bool "Enable to hide some mmapped real file from different proc maps interfaces" + depends on KSU_SUSFS + default y + help + - Allow hiding mmapped real file from /proc//[maps|smaps|smaps_rollup|map_files|mem|pagemap] + - It does NOT support hiding for anon memory. + - It does NOT hide any inline hooks or plt hooks cause by the injected library itself. + - It may not be able to evade detections by apps that implement a good injection detection. + - Effective only on zygote spawned umounted user app process. + +endmenu + endmenu diff --git a/kernel/Makefile b/kernel/Makefile index 08df7c94..74a1d654 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -18,6 +18,7 @@ kernelsu-objs += seccomp_cache.o kernelsu-objs += file_wrapper.o kernelsu-objs += throne_comm.o kernelsu-objs += sulog.o +kernelsu-objs += lsm_hook.o ifeq ($(CONFIG_KSU_MANUAL_SU), y) ccflags-y += -DCONFIG_KSU_MANUAL_SU @@ -183,4 +184,14 @@ $(info -- Supported Unofficial Manager: 5ec1cff (GKI) rsuntk (Non-GKI) ShirkNeko ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat ccflags-y += -Wno-declaration-after-statement -Wno-unused-function -Wno-unused-variable -# Keep a new line here!! Because someone may append config +## For susfs stuff ## +ifeq ($(shell test -e $(srctree)/fs/susfs.c; echo $$?),0) +$(eval SUSFS_VERSION=$(shell cat $(srctree)/include/linux/susfs.h | grep -E '^#define SUSFS_VERSION' | cut -d' ' -f3 | sed 's/"//g')) +$(info ) +$(info -- SUSFS_VERSION: $(SUSFS_VERSION)) +else +$(info -- You have not integrated susfs in your kernel yet.) +$(info -- Read: https://gitlab.com/simonpunk/susfs4ksu) +endif + +# Keep a new line here!! Because someone may append config \ No newline at end of file diff --git a/kernel/allowlist.c b/kernel/allowlist.c index ce2a435c..fdf6485f 100644 --- a/kernel/allowlist.c +++ b/kernel/allowlist.c @@ -21,7 +21,9 @@ #include "selinux/selinux.h" #include "allowlist.h" #include "manager.h" +#ifndef CONFIG_KSU_SUSFS #include "syscall_hook_manager.h" +#endif // #ifndef CONFIG_KSU_SUSFS #define FILE_MAGIC 0x7f4b5355 // ' KSU', u32 #define FILE_FORMAT_VERSION 3 // u32 @@ -262,8 +264,10 @@ out: if (persist) { persistent_allow_list(); +#ifndef CONFIG_KSU_SUSFS // FIXME: use a new flag ksu_mark_running_process(); +#endif // #ifndef CONFIG_KSU_SUSFS } return result; diff --git a/kernel/app_profile.c b/kernel/app_profile.c index 9eec4ede..0542bd77 100644 --- a/kernel/app_profile.c +++ b/kernel/app_profile.c @@ -12,7 +12,9 @@ #include "app_profile.h" #include "klog.h" // IWYU pragma: keep #include "selinux/selinux.h" +#ifndef CONFIG_KSU_SUSFS #include "syscall_hook_manager.h" +#endif #include "sucompat.h" #include "sulog.h" @@ -88,8 +90,10 @@ void disable_seccomp(void) void escape_with_root_profile(void) { struct cred *cred; +#ifndef CONFIG_KSU_SUSFS struct task_struct *p = current; struct task_struct *t; +#endif cred = prepare_creds(); if (!cred) { @@ -149,9 +153,11 @@ void escape_with_root_profile(void) ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root"); #endif +#ifndef CONFIG_KSU_SUSFS for_each_thread (p, t) { ksu_set_task_tracepoint_flag(t); } +#endif } #ifdef CONFIG_KSU_MANUAL_SU @@ -214,9 +220,10 @@ void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid) { struct cred *newcreds; struct task_struct *target_task; - unsigned long flags; +#ifndef CONFIG_KSU_SUSFS struct task_struct *p = current; struct task_struct *t; +#endif pr_info("cmd_su: escape_to_root_for_cmd_su called for UID: %d, PID: %d\n", target_uid, target_pid); @@ -298,9 +305,11 @@ void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid) #if __SULOG_GATE ksu_sulog_report_su_grant(target_uid, "cmd_su", "manual_escalation"); #endif +#ifndef CONFIG_KSU_SUSFS for_each_thread (p, t) { ksu_set_task_tracepoint_flag(t); } +#endif pr_info("cmd_su: privilege escalation completed for UID: %d, PID: %d\n", target_uid, target_pid); } #endif diff --git a/kernel/kernel_umount.c b/kernel/kernel_umount.c index f784e648..547e900e 100644 --- a/kernel/kernel_umount.c +++ b/kernel/kernel_umount.c @@ -21,7 +21,11 @@ #include "sulog.h" +#ifndef CONFIG_KSU_SUSFS static bool ksu_kernel_umount_enabled = true; +#else +bool ksu_kernel_umount_enabled = true; +#endif static int kernel_umount_feature_get(u64 *value) { @@ -44,11 +48,25 @@ static const struct ksu_feature_handler kernel_umount_handler = { .set_handler = kernel_umount_feature_set, }; +#ifdef CONFIG_KSU_SUSFS +extern bool susfs_is_mnt_devname_ksu(struct path *path); + +#if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG) +extern bool susfs_is_log_enabled; +#endif // #if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG) +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT +extern void susfs_try_umount(void); +#endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT +#endif // #ifdef CONFIG_KSU_SUSFS + static bool should_umount(struct path *path) { if (!path) { return false; } +#ifdef CONFIG_KSU_SUSFS + return susfs_is_mnt_devname_ksu(path); +#else if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) { pr_info("ignore global mnt namespace process: %d\n", current_uid().val); @@ -60,6 +78,7 @@ static bool should_umount(struct path *path) return strcmp(fstype, "overlay") == 0; } return false; +#endif // #ifdef CONFIG_KSU_SUSFS } #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || defined(KSU_HAS_PATH_UMOUNT) @@ -97,7 +116,11 @@ static int ksu_sys_umount(const char *mnt, int flags) }) #endif +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT 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 // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT { struct path path; int ret; @@ -118,6 +141,12 @@ void try_umount(const char *mnt, bool check_mnt, int flags) return; } +#if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG) + if (susfs_is_log_enabled) { + pr_info("susfs: umounting '%s'\n", mnt); + } +#endif // #if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG) + ret = ksu_umount_mnt(mnt, &path, flags); if (ret) { #ifdef CONFIG_KSU_DEBUG @@ -126,6 +155,20 @@ void try_umount(const char *mnt, bool check_mnt, int flags) } } +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT +void susfs_try_umount_all(void) { + susfs_try_umount(); + try_umount("/odm", true, 0); + try_umount("/system", true, 0); + try_umount("/vendor", true, 0); + try_umount("/product", true, 0); + try_umount("/system_ext", true, 0); + try_umount("/data/adb/modules", false, MNT_DETACH); + try_umount("/debug_ramdisk", true, MNT_DETACH); +} +#endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT + +#ifndef CONFIG_KSU_SUSFS struct umount_tw { struct callback_head cb; const struct cred *old_cred; @@ -147,15 +190,8 @@ static void umount_tw_func(struct callback_head *cb) try_umount("/product", true, 0); try_umount("/system_ext", true, 0); try_umount("/data/adb/modules", false, MNT_DETACH); - try_umount("/data/adb/kpm", false, MNT_DETACH); // try umount ksu temp path try_umount("/debug_ramdisk", false, MNT_DETACH); - try_umount("/sbin", false, MNT_DETACH); - - try_umount("/system/etc/hosts", false, MNT_DETACH); - // try umount lsposed dex2oat bins - try_umount("/apex/com.android.art/bin/dex2oat64", false, MNT_DETACH); - try_umount("/apex/com.android.art/bin/dex2oat32", false, MNT_DETACH); if (saved) revert_creds(saved); @@ -224,6 +260,7 @@ int ksu_handle_umount(uid_t old_uid, uid_t new_uid) return 0; } +#endif // #ifndef CONFIG_KSU_SUSFS void ksu_kernel_umount_init(void) { diff --git a/kernel/kernel_umount.h b/kernel/kernel_umount.h index 4c7a158c..9ca23c5b 100644 --- a/kernel/kernel_umount.h +++ b/kernel/kernel_umount.h @@ -6,7 +6,9 @@ void ksu_kernel_umount_init(void); void ksu_kernel_umount_exit(void); +#ifndef CONFIG_KSU_SUSFS // Handler function to be called from setresuid hook int ksu_handle_umount(uid_t old_uid, uid_t new_uid); +#endif // #ifndef CONFIG_KSU_SUSFS #endif \ No newline at end of file diff --git a/kernel/ksu.c b/kernel/ksu.c index efd98a84..5b2868ad 100644 --- a/kernel/ksu.c +++ b/kernel/ksu.c @@ -7,11 +7,18 @@ #include #include /* LINUX_VERSION_CODE, KERNEL_VERSION macros */ +#ifdef CONFIG_KSU_SUSFS +#include +#endif + #include "allowlist.h" +#include "ksu.h" #include "feature.h" #include "klog.h" // IWYU pragma: keep #include "throne_tracker.h" +#ifndef CONFIG_KSU_SUSFS #include "syscall_hook_manager.h" +#endif #include "ksud.h" #include "supercalls.h" @@ -57,11 +64,15 @@ int __init kernelsu_init(void) ksu_feature_init(); + ksu_lsm_hook_init(); + ksu_supercalls_init(); sukisu_custom_config_init(); +#if defined(CONFIG_KPROBES) && !defined(CONFIG_KSU_SUSFS) ksu_syscall_hook_manager_init(); +#endif ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0); @@ -69,7 +80,13 @@ int __init kernelsu_init(void) ksu_throne_tracker_init(); +#ifdef CONFIG_KSU_SUSFS + susfs_init(); +#endif + +#if defined(CONFIG_KPROBES) && !defined(CONFIG_KSU_SUSFS) ksu_ksud_init(); +#endif #ifdef MODULE #ifndef CONFIG_KSU_DEBUG @@ -90,9 +107,11 @@ void kernelsu_exit(void) destroy_workqueue(ksu_workqueue); +#if defined(CONFIG_KPROBES) && !defined(CONFIG_KSU_SUSFS) ksu_ksud_exit(); ksu_syscall_hook_manager_exit(); +#endif sukisu_custom_config_exit(); diff --git a/kernel/ksu.h b/kernel/ksu.h index cf43fbe8..d3140509 100644 --- a/kernel/ksu.h +++ b/kernel/ksu.h @@ -43,6 +43,8 @@ struct manager_list_info { bool ksu_queue_work(struct work_struct *work); +void ksu_lsm_hook_init(void); + #if 0 static inline int startswith(char *s, char *prefix) { diff --git a/kernel/ksud.c b/kernel/ksud.c index d23160ee..4f2f03d7 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -33,7 +33,9 @@ #include "klog.h" // IWYU pragma: keep #include "ksud.h" #include "selinux/selinux.h" +#ifndef CONFIG_KSU_SUSFS #include "syscall_hook_manager.h" +#endif // #ifndef CONFIG_KSU_SUSFS bool ksu_module_mounted __read_mostly = false; bool ksu_boot_completed __read_mostly = false; @@ -65,7 +67,7 @@ static void stop_vfs_read_hook(void); static void stop_execve_hook(void); static void stop_input_hook(void); -#ifdef KSU_KPROBES_HOOK +#if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS) static struct work_struct stop_vfs_read_work; static struct work_struct stop_execve_hook_work; static struct work_struct stop_input_hook_work; @@ -90,8 +92,10 @@ void on_post_fs_data(void) done = true; pr_info("on_post_fs_data!\n"); ksu_load_allow_list(); +#ifndef CONFIG_KSU_SUSFS pr_info("mark tif for running process\n"); ksu_mark_running_process(); +#endif // #ifndef CONFIG_KSU_SUSFS ksu_observer_init(); // sanity check, this may influence the performance stop_input_hook(); @@ -136,11 +140,15 @@ void on_module_mounted(void){ void on_boot_completed(void){ ksu_boot_completed = true; pr_info("on_boot_completed!\n"); +#ifndef CONFIG_KSU_SUSFS // remark process, we don't want to mark other init // forked process excepte zygote and adbd + ksu_unmark_all_process(); ksu_mark_running_process(); +#endif // #ifndef CONFIG_KSU_SUSFS } +#ifndef CONFIG_KSU_SUSFS #define MAX_ARG_STRINGS 0x7FFFFFFF struct user_arg_ptr { #ifdef CONFIG_COMPAT @@ -153,6 +161,7 @@ struct user_arg_ptr { #endif } ptr; }; +#endif // #ifndef CONFIG_KSU_SUSFS static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr) { @@ -335,7 +344,9 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, task_work_add(init_task, &on_post_fs_data_cb, TWA_RESUME); } rcu_read_unlock(); +#ifndef CONFIG_KSU_SUSFS ksu_set_task_tracepoint_flag(current); // we are zygote! +#endif stop_execve_hook(); } @@ -529,7 +540,7 @@ bool ksu_is_safe_mode() return false; } -#ifdef KSU_KPROBES_HOOK +#if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS) static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) { @@ -605,7 +616,7 @@ static void do_stop_input_hook(struct work_struct *work) static void stop_vfs_read_hook(void) { -#ifdef KSU_KPROBES_HOOK +#if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS) bool ret = schedule_work(&stop_vfs_read_work); pr_info("unregister vfs_read kprobe: %d!\n", ret); #else @@ -616,7 +627,7 @@ static void stop_vfs_read_hook(void) static void stop_execve_hook(void) { -#ifdef KSU_KPROBES_HOOK +#if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS) bool ret = schedule_work(&stop_execve_hook_work); pr_info("unregister execve kprobe: %d!\n", ret); #else @@ -632,7 +643,7 @@ static void stop_input_hook(void) return; } input_hook_stopped = true; -#ifdef KSU_KPROBES_HOOK +#if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS) bool ret = schedule_work(&stop_input_hook_work); pr_info("unregister input kprobe: %d!\n", ret); #else @@ -644,7 +655,7 @@ static void stop_input_hook(void) // ksud: module support void ksu_ksud_init(void) { -#ifdef KSU_KPROBES_HOOK +#if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS) int ret; ret = register_kprobe(&execve_kp); @@ -664,7 +675,7 @@ void ksu_ksud_init(void) void ksu_ksud_exit(void) { -#ifdef KSU_KPROBES_HOOK +#if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS) unregister_kprobe(&execve_kp); // this should be done before unregister vfs_read_kp // unregister_kprobe(&vfs_read_kp); diff --git a/kernel/ksud.h b/kernel/ksud.h index 140969e3..5eb7296a 100644 --- a/kernel/ksud.h +++ b/kernel/ksud.h @@ -18,4 +18,23 @@ extern u32 ksu_file_sid; extern bool ksu_module_mounted; extern bool ksu_boot_completed; +#ifdef CONFIG_KSU_SUSFS +#define MAX_ARG_STRINGS 0x7FFFFFFF +struct user_arg_ptr { +#ifdef CONFIG_COMPAT + bool is_compat; +#endif + union { + const char __user *const __user *native; +#ifdef CONFIG_COMPAT + const compat_uptr_t __user *compat; +#endif + } ptr; +}; + +int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, + struct user_arg_ptr *argv, + struct user_arg_ptr *envp, int *flags); +#endif // #ifdef CONFIG_KSU_SUSFS + #endif diff --git a/kernel/lsm_hook.c b/kernel/lsm_hook.c new file mode 100644 index 00000000..931cf26a --- /dev/null +++ b/kernel/lsm_hook.c @@ -0,0 +1,74 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kernel_compat.h" +#include "ksu.h" + +#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0) && defined(CONFIG_KSU_MANUAL_SU) +#include "manual_su.h" + +static int ksu_task_alloc(struct task_struct *task, + unsigned long clone_flags) +{ + ksu_try_escalate_for_uid(task_uid(task).val); + return 0; +} +#endif + +// kernel 4.4 and 4.9 +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ + defined(CONFIG_IS_HW_HISI) || \ + defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) +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 struct security_hook_list ksu_hooks[] = { +#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0) && defined(CONFIG_KSU_MANUAL_SU) + LSM_HOOK_INIT(task_alloc, ksu_task_alloc), +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ + defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) + LSM_HOOK_INIT(key_permission, ksu_key_permission) +#endif +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) +const struct lsm_id ksu_lsmid = { + .name = "ksu", + .id = 912, +}; +#endif + +void __init ksu_lsm_hook_init(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + // https://elixir.bootlin.com/linux/v6.8/source/include/linux/lsm_hooks.h#L120 + security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), &ksu_lsmid); +#elif 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 +} diff --git a/kernel/manager.h b/kernel/manager.h index 4c9e9570..3cef7976 100644 --- a/kernel/manager.h +++ b/kernel/manager.h @@ -18,21 +18,36 @@ static inline bool ksu_is_manager_uid_valid(void) return ksu_manager_uid != KSU_INVALID_UID; } +#ifndef CONFIG_KSU_SUSFS static inline bool is_manager(void) { return unlikely(ksu_is_any_manager(current_uid().val) || (ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val)); } +#else +static inline bool is_manager() +{ + return unlikely((ksu_manager_uid == current_uid().val % 100000) || + (ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val % 100000)); +} +#endif static inline uid_t ksu_get_manager_uid(void) { return ksu_manager_uid; } +#ifndef CONFIG_KSU_SUSFS static inline void ksu_set_manager_uid(uid_t uid) { ksu_manager_uid = uid; } +#else +static inline void ksu_set_manager_uid(uid_t uid) +{ + ksu_manager_uid = uid % 100000; +} +#endif static inline void ksu_invalidate_manager_uid(void) { diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index 7d74ce87..9d9132f4 100644 --- a/kernel/selinux/rules.c +++ b/kernel/selinux/rules.c @@ -142,6 +142,15 @@ void apply_kernelsu_rules(void) // https://android-review.googlesource.com/c/platform/system/logging/+/3725346 ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr"); +#ifdef CONFIG_KSU_SUSFS + // Allow umount in zygote process without installing zygisk + ksu_allow(db, "zygote", "labeledfs", "filesystem", "unmount"); + susfs_set_kernel_sid(); + susfs_set_init_sid(); + susfs_set_ksu_sid(); + susfs_set_zygote_sid(); +#endif + mutex_unlock(&ksu_rules); } diff --git a/kernel/selinux/selinux.c b/kernel/selinux/selinux.c index baf200c2..303884f0 100644 --- a/kernel/selinux/selinux.c +++ b/kernel/selinux/selinux.c @@ -166,3 +166,96 @@ u32 ksu_get_ksu_file_sid() } return ksu_file_sid; } + +#ifdef CONFIG_KSU_SUSFS +#define KERNEL_INIT_DOMAIN "u:r:init:s0" +#define KERNEL_ZYGOTE_DOMAIN "u:r:zygote:s0" +#define KERNEL_KERNEL_DOMAIN "u:r:kernel:s0" +#ifndef KERNEL_SU_DOMAIN +#define KERNEL_SU_DOMAIN "u:r:su:s0" +#endif // #ifndef KERNEL_SU_DOMAIN +u32 susfs_ksu_sid = 0; +u32 susfs_init_sid = 0; +u32 susfs_zygote_sid = 0; +u32 susfs_kernel_sid = 0; + +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); +} + +void susfs_set_kernel_sid(void) +{ + susfs_set_sid(KERNEL_KERNEL_DOMAIN, &susfs_kernel_sid); +} +#endif \ No newline at end of file diff --git a/kernel/selinux/selinux.h b/kernel/selinux/selinux.h index 6ed2d002..5e8eb793 100644 --- a/kernel/selinux/selinux.h +++ b/kernel/selinux/selinux.h @@ -29,4 +29,17 @@ u32 ksu_get_ksu_file_sid(void); int handle_sepolicy(unsigned long arg3, void __user *arg4); +#ifdef CONFIG_KSU_SUSFS +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); +void susfs_set_kernel_sid(void); +#endif // #ifdef CONFIG_KSU_SUSFS + #endif diff --git a/kernel/setuid_hook.c b/kernel/setuid_hook.c index 8ca37f7d..fd2a525e 100644 --- a/kernel/setuid_hook.c +++ b/kernel/setuid_hook.c @@ -29,10 +29,13 @@ #include #include #include -#include #include #include +#ifdef CONFIG_KSU_SUSFS +#include +#endif // #ifdef CONFIG_KSU_SUSFS + #ifndef KSU_HAS_PATH_UMOUNT #include // sys_umount (<4.17) & ksys_umount (4.17+) #endif @@ -54,12 +57,48 @@ #include "selinux/selinux.h" #include "seccomp_cache.h" #include "supercalls.h" +#ifndef CONFIG_KSU_SUSFS #include "syscall_hook_manager.h" +#endif #include "kernel_umount.h" #include "app_profile.h" #include "sulog.h" +#ifdef CONFIG_KSU_SUSFS +static inline bool is_some_system_uid(uid_t uid) +{ + uid %= 100000; + return (uid >= 1000 && uid < 10000); +} + +static inline bool is_zygote_isolated_service_uid(uid_t uid) +{ + uid %= 100000; + return (uid >= 90000 && uid < 100000); +} + +static inline bool is_zygote_normal_app_uid(uid_t uid) +{ + uid %= 100000; + return (uid >= 10000 && uid < 19999); +} + +bool susfs_is_umount_for_zygote_system_process_enabled = false; + +extern bool susfs_is_umount_for_zygote_iso_service_enabled; +extern u32 susfs_zygote_sid; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +extern void susfs_run_sus_path_loop(uid_t uid); +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_PATH +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +extern void susfs_reorder_mnt_id(void); +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT +extern void susfs_try_umount_all(void); +#endif +#endif // #ifdef CONFIG_KSU_SUSFS + static bool ksu_enhanced_security_enabled = false; static int enhanced_security_feature_get(u64 *value) @@ -92,6 +131,7 @@ static inline bool is_allow_su(void) return ksu_is_allow_uid_for_current(current_uid().val); } +#ifndef CONFIG_KSU_SUSFS int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid) { uid_t new_uid = ruid; @@ -171,59 +211,114 @@ int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid) return 0; } +#else +extern bool ksu_kernel_umount_enabled; +extern bool ksu_module_mounted; +int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid){ + // we rely on the fact that zygote always call setresuid(3) with same uids + uid_t new_uid = ruid; + uid_t old_uid = current_uid().val; -// kernel 4.4 and 4.9 -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ - defined(CONFIG_IS_HW_HISI) || \ - defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) -static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred, - unsigned perm) -{ - if (init_session_keyring != NULL) { + // if old process is root, ignore it. + if (old_uid != 0 && ksu_enhanced_security_enabled) { + // disallow any non-ksu domain escalation from non-root to root! + // euid is what we care about here as it controls permission + if (unlikely(euid == 0)) { + if (!is_ksu_domain()) { + pr_warn("find suspicious EoP: %d %s, from %d to %d\n", + current->pid, current->comm, old_uid, new_uid); + force_sig(SIGKILL); + return 0; + } + } + // disallow appuid decrease to any other uid if it is not allowed to su + if (is_appuid(old_uid)) { + if (euid < current_euid().val && !ksu_is_allow_uid_for_current(old_uid)) { + pr_warn("find suspicious EoP: %d %s, from %d to %d\n", + current->pid, current->comm, old_uid, new_uid); + force_sig(SIGKILL); + return 0; + } + } return 0; } - if (strcmp(current->comm, "init")) { - // we are only interested in `init` process + + // We only interest in process spwaned by zygote + if (!susfs_is_sid_equal(current_cred()->security, susfs_zygote_sid)) { return 0; } - init_session_keyring = cred->session_keyring; - pr_info("kernel_compat: got init_session_keyring\n"); + + // Check if spawned process is isolated service first, and force to do umount if so + if (is_zygote_isolated_service_uid(new_uid) && susfs_is_umount_for_zygote_iso_service_enabled) { + goto do_umount; + } + + // - Since ksu maanger app uid is excluded in allow_list_arr, so ksu_uid_should_umount(manager_uid) + // will always return true, that's why we need to explicitly check if new_uid belongs to + // ksu manager + if (ksu_get_manager_uid() == new_uid % 100000) { + pr_info("install fd for manager: %d\n", new_uid); + ksu_install_fd(); + spin_lock_irq(¤t->sighand->siglock); + ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot); + spin_unlock_irq(¤t->sighand->siglock); + return 0; + } + + // Check if spawned process is normal user app and needs to be umounted + if (likely(is_zygote_normal_app_uid(new_uid) && ksu_uid_should_umount(new_uid))) { + goto do_umount; + } + + // Lastly, Check if spawned process is some system process and needs to be umounted + if (unlikely(is_some_system_uid(new_uid) && susfs_is_umount_for_zygote_system_process_enabled)) { + goto do_umount; + } + + if (ksu_is_allow_uid_for_current(new_uid)) { + if (current->seccomp.mode == SECCOMP_MODE_FILTER && + current->seccomp.filter) { + spin_lock_irq(¤t->sighand->siglock); + ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot); + spin_unlock_irq(¤t->sighand->siglock); + } + } + + return 0; + +do_umount: +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT + if (!ksu_kernel_umount_enabled || !ksu_module_mounted) { + goto skip_try_umount; + + } + + pr_info("susfs: running susfs_try_umount_all() for uid: %u\n", new_uid); + susfs_try_umount_all(); + +skip_try_umount: +#endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT + + get_task_struct(current); + +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + // We can reorder the mnt_id now after all sus mounts are umounted + susfs_reorder_mnt_id(); +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + + susfs_set_current_proc_umounted(); + + put_task_struct(current); + +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + susfs_run_sus_path_loop(new_uid); +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_PATH return 0; } -#endif - -#ifndef MODULE -static struct security_hook_list ksu_hooks[] = { -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ - defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) - LSM_HOOK_INIT(key_permission, ksu_key_permission) -#endif -}; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) -const struct lsm_id ksu_lsmid = { - .name = "ksu", - .id = 912, -}; -#endif - -void __init ksu_lsm_hook_init(void) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) - // https://elixir.bootlin.com/linux/v6.8/source/include/linux/lsm_hooks.h#L120 - security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), &ksu_lsmid); -#elif 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 -} -#endif +#endif // #ifndef CONFIG_KSU_SUSFS void ksu_setuid_hook_init(void) { - ksu_lsm_hook_init(); ksu_kernel_umount_init(); if (ksu_register_feature_handler(&enhanced_security_handler)) { pr_err("Failed to register enhanced security feature handler\n"); diff --git a/kernel/sucompat.c b/kernel/sucompat.c index 028454a1..bc1c8e91 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -13,13 +13,20 @@ #include #endif +#ifdef CONFIG_KSU_SUSFS +#include +#include "objsec.h" +#endif // #ifdef CONFIG_KSU_SUSFS + #include "allowlist.h" #include "feature.h" #include "klog.h" // IWYU pragma: keep #include "ksud.h" #include "sucompat.h" #include "app_profile.h" +#ifndef CONFIG_KSU_SUSFS #include "syscall_hook_manager.h" +#endif // #ifndef CONFIG_KSU_SUSFS #include "sulog.h" @@ -72,6 +79,7 @@ static char __user *ksud_user_path(void) return userspace_stack_buffer(ksud_path, sizeof(ksud_path)); } +#ifndef CONFIG_KSU_SUSFS int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *__unused_flags) { @@ -192,6 +200,142 @@ int ksu_handle_execve_sucompat(const char __user **filename_user, return 0; } +#else +static const char sh_path[] = SH_PATH; +static const char su_path[] = SU_PATH; +static const char ksud_path[] = KSUD_PATH; + +extern bool ksu_kernel_umount_enabled; + +// the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code +int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, + void *__never_use_argv, void *__never_use_envp, + int *__never_use_flags) +{ + struct filename *filename; + + if (unlikely(!filename_ptr)) + return 0; + + filename = *filename_ptr; + if (IS_ERR(filename)) { + return 0; + } + + if (likely(memcmp(filename->name, su_path, sizeof(su_path)))) + return 0; + + pr_info("do_execveat_common su found\n"); + memcpy((void *)filename->name, ksud_path, sizeof(ksud_path)); + + escape_with_root_profile(); + + return 0; +} + +int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, + void *envp, int *flags) +{ + if (ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags)) { + return 0; + } + return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, + flags); +} + +int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, + int *__unused_flags) +{ + char path[sizeof(su_path) + 1] = {0}; + + strncpy_from_user_nofault(path, *filename_user, sizeof(path)); + + if (unlikely(!memcmp(path, su_path, sizeof(su_path)))) { + pr_info("faccessat su->sh!\n"); + *filename_user = sh_user_path(); + } + + return 0; + + return 0; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) +int ksu_handle_stat(int *dfd, struct filename **filename, int *flags) { + if (unlikely(IS_ERR(*filename) || (*filename)->name == NULL)) { + return 0; + } + + if (likely(memcmp((*filename)->name, su_path, sizeof(su_path)))) { + return 0; + } + + pr_info("ksu_handle_stat: su->sh!\n"); + memcpy((void *)((*filename)->name), sh_path, sizeof(sh_path)); + return 0; +} +#else +int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags) +{ + if (unlikely(!filename_user)) { + return 0; + } + + char path[sizeof(su_path) + 1] = {0}; + +// Remove this later!! we use syscall hook, so this will never happen!!!!! +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0 + // it becomes a `struct filename *` after 5.18 + // https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216 +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) + struct filename *filename = *((struct filename **)filename_user); +#endif + + if (IS_ERR(filename)) { + return 0; + } + if (likely(memcmp(filename->name, su_path, sizeof(su_path)))) + return 0; + pr_info("ksu_handle_stat: su->sh!\n"); + memcpy((void *)filename->name, sh_path, sizeof(sh_path)); +#else + strncpy_from_user_nofault(path, *filename_user, sizeof(path)); + + if (unlikely(!memcmp(path, su_path, sizeof(su_path)))) { + pr_info("ksu_handle_stat: su->sh!\n"); + *filename_user = sh_user_path(); + } +#endif + + return 0; +} +#endif // #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + +int ksu_handle_devpts(struct inode *inode) +{ + if (!current->mm) { + return 0; + } + + uid_t uid = current_uid().val; + if (uid % 100000 < 10000) { + // not untrusted_app, ignore it + return 0; + } + + if (!__ksu_is_allow_uid_for_current(uid)) + return 0; + + if (ksu_file_sid) { + struct inode_security_struct *sec = selinux_inode(inode); + if (sec) { + sec->sid = ksu_file_sid; + } + } + + return 0; +} +#endif // #ifndef CONFIG_KSU_SUSFS // sucompat: permitted process can execute 'su' to gain root access. void ksu_sucompat_init() diff --git a/kernel/sucompat.h b/kernel/sucompat.h index 82161f7f..7f66e3ea 100644 --- a/kernel/sucompat.h +++ b/kernel/sucompat.h @@ -10,7 +10,11 @@ void ksu_sucompat_exit(void); // Handler functions exported for hook_manager int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *__unused_flags); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) && defined(CONFIG_KSU_SUSFS) +int ksu_handle_stat(int *dfd, struct filename **filename, int *flags); +#else int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags); +#endif // #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) && defined(CONFIG_KSU_SUSFS) int ksu_handle_execve_sucompat(const char __user **filename_user, void *__never_use_argv, void *__never_use_envp, int *__never_use_flags); diff --git a/kernel/supercalls.c b/kernel/supercalls.c index 95d5f84c..cf8904ce 100644 --- a/kernel/supercalls.c +++ b/kernel/supercalls.c @@ -24,7 +24,9 @@ #include "selinux/selinux.h" #include "objsec.h" #include "file_wrapper.h" +#ifndef CONFIG_KSU_SUSFS #include "syscall_hook_manager.h" +#endif #include "throne_comm.h" #include "dynamic_manager.h" @@ -32,6 +34,56 @@ #include "manual_su.h" #endif +#ifdef CONFIG_KSU_SUSFS +#include +#include + +bool susfs_is_boot_completed_triggered = false; + +extern bool susfs_is_umount_for_zygote_system_process_enabled; +#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT +extern bool susfs_is_auto_add_sus_bind_mount_enabled; +#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT +#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT +extern bool susfs_is_auto_add_sus_ksu_default_mount_enabled; +#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT +#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT +extern bool susfs_is_auto_add_try_umount_for_bind_mount_enabled; +#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT + +static void susfs_on_post_fs_data(void) { + struct path path; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (!kern_path(DATA_ADB_UMOUNT_FOR_ZYGOTE_SYSTEM_PROCESS, 0, &path)) { + susfs_is_umount_for_zygote_system_process_enabled = true; + path_put(&path); + } + pr_info("susfs_is_umount_for_zygote_system_process_enabled: %d\n", susfs_is_umount_for_zygote_system_process_enabled); +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT + if (!kern_path(DATA_ADB_NO_AUTO_ADD_SUS_BIND_MOUNT, 0, &path)) { + susfs_is_auto_add_sus_bind_mount_enabled = false; + path_put(&path); + } + pr_info("susfs_is_auto_add_sus_bind_mount_enabled: %d\n", susfs_is_auto_add_sus_bind_mount_enabled); +#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT +#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT + if (!kern_path(DATA_ADB_NO_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT, 0, &path)) { + susfs_is_auto_add_sus_ksu_default_mount_enabled = false; + path_put(&path); + } + pr_info("susfs_is_auto_add_sus_ksu_default_mount_enabled: %d\n", susfs_is_auto_add_sus_ksu_default_mount_enabled); +#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT +#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT + if (!kern_path(DATA_ADB_NO_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT, 0, &path)) { + susfs_is_auto_add_try_umount_for_bind_mount_enabled = false; + path_put(&path); + } + pr_info("susfs_is_auto_add_try_umount_for_bind_mount_enabled: %d\n", susfs_is_auto_add_try_umount_for_bind_mount_enabled); +#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT +} +#endif // #ifdef CONFIG_KSU_SUSFS + bool ksu_uid_scanner_enabled = false; // Permission check functions @@ -121,6 +173,10 @@ static int do_report_event(void __user *arg) if (!post_fs_data_lock) { post_fs_data_lock = true; pr_info("post-fs-data triggered\n"); +#ifdef CONFIG_KSU_SUSFS + susfs_on_post_fs_data(); + pr_info("susfs_on_post_fs_data triggered\n"); +#endif on_post_fs_data(); init_uid_scanner(); #if __SULOG_GATE @@ -136,6 +192,9 @@ static int do_report_event(void __user *arg) boot_complete_lock = true; pr_info("boot_complete triggered\n"); on_boot_completed(); +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + susfs_is_boot_completed_triggered = true; +#endif } break; } @@ -422,7 +481,9 @@ put_orig_file: static int do_manage_mark(void __user *arg) { struct ksu_manage_mark_cmd cmd; +#ifndef CONFIG_KSU_SUSFS int ret = 0; +#endif if (copy_from_user(&cmd, arg, sizeof(cmd))) { pr_err("manage_mark: copy_from_user failed\n"); @@ -431,6 +492,7 @@ static int do_manage_mark(void __user *arg) switch (cmd.operation) { case KSU_MARK_GET: { +#ifndef CONFIG_KSU_SUSFS // Get task mark status ret = ksu_get_task_mark(cmd.pid); if (ret < 0) { @@ -439,8 +501,12 @@ static int do_manage_mark(void __user *arg) } cmd.result = (u32)ret; break; +#else + return -EINVAL; +#endif } case KSU_MARK_MARK: { +#ifndef CONFIG_KSU_SUSFS if (cmd.pid == 0) { ksu_mark_all_process(); } else { @@ -451,9 +517,13 @@ static int do_manage_mark(void __user *arg) return ret; } } +#else + pr_info("susfs: cmd: KSU_MARK_MARK => do nothing\n"); +#endif break; } case KSU_MARK_UNMARK: { +#ifndef CONFIG_KSU_SUSFS if (cmd.pid == 0) { ksu_unmark_all_process(); } else { @@ -464,11 +534,18 @@ static int do_manage_mark(void __user *arg) return ret; } } +#else + pr_info("susfs: cmd: KSU_MARK_UNMARK => do nothing\n"); +#endif break; } case KSU_MARK_REFRESH: { +#ifndef CONFIG_KSU_SUSFS ksu_mark_running_process(); pr_info("manage_mark: refreshed running processes\n"); +#else + pr_info("susfs: cmd: KSU_MARK_REFRESH: do nothing\n"); +#endif break; } default: { @@ -508,9 +585,11 @@ static int do_get_hook_type(void __user *arg) { struct ksu_hook_type_cmd cmd = {0}; const char *type = "Tracepoint"; - + #if defined(KSU_MANUAL_HOOK) type = "Manual"; +#elif defined(CONFIG_KSU_SUSFS) + type = "Inline"; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) @@ -717,6 +796,7 @@ static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = { { .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL} // Sentine }; +#ifndef CONFIG_KSU_SUSFS struct ksu_install_fd_tw { struct callback_head cb; int __user *outp; @@ -792,6 +872,142 @@ static struct kprobe reboot_kp = { .pre_handler = reboot_handler_pre, }; #endif +#else +int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd, void __user **arg) +{ + if (magic1 != KSU_INSTALL_MAGIC1) { + return -EINVAL; + } + + // If magic2 is susfs and current process is root + if (magic2 == SUSFS_MAGIC && current_uid().val == 0) { +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (cmd == CMD_SUSFS_ADD_SUS_PATH) { + susfs_add_sus_path(arg); + return 0; + } + if (cmd == CMD_SUSFS_ADD_SUS_PATH_LOOP) { + susfs_add_sus_path_loop(arg); + return 0; + } + if (cmd == CMD_SUSFS_SET_ANDROID_DATA_ROOT_PATH) { + susfs_set_i_state_on_external_dir(arg); + return 0; + } + if (cmd == CMD_SUSFS_SET_SDCARD_ROOT_PATH) { + susfs_set_i_state_on_external_dir(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_PATH +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (cmd == CMD_SUSFS_ADD_SUS_MOUNT) { + susfs_add_sus_mount(arg); + return 0; + } + if (cmd == CMD_SUSFS_HIDE_SUS_MNTS_FOR_ALL_PROCS) { + susfs_set_hide_sus_mnts_for_all_procs(arg); + return 0; + } + if (cmd == CMD_SUSFS_UMOUNT_FOR_ZYGOTE_ISO_SERVICE) { + susfs_set_umount_for_zygote_iso_service(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + if (cmd == CMD_SUSFS_ADD_SUS_KSTAT) { + susfs_add_sus_kstat(arg); + return 0; + } + if (cmd == CMD_SUSFS_UPDATE_SUS_KSTAT) { + susfs_update_sus_kstat(arg); + return 0; + } + if (cmd == CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY) { + susfs_add_sus_kstat(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT + if (cmd == CMD_SUSFS_ADD_TRY_UMOUNT) { + susfs_add_try_umount(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT +#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME + if (cmd == CMD_SUSFS_SET_UNAME) { + susfs_set_uname(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME +#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG + if (cmd == CMD_SUSFS_ENABLE_LOG) { + susfs_enable_log(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG +#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG + if (cmd == CMD_SUSFS_SET_CMDLINE_OR_BOOTCONFIG) { + susfs_set_cmdline_or_bootconfig(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + if (cmd == CMD_SUSFS_ADD_OPEN_REDIRECT) { + susfs_add_open_redirect(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +#ifdef CONFIG_KSU_SUSFS_SUS_SU + if (cmd == CMD_SUSFS_SHOW_SUS_SU_WORKING_MODE) { + susfs_get_sus_su_working_mode(arg); + return 0; + } + if (cmd == CMD_SUSFS_IS_SUS_SU_READY) { + susfs_is_sus_su_ready(arg); + return 0; + } + if (cmd == CMD_SUSFS_SUS_SU) { + susfs_sus_su(arg); + return 0; + } +#endif //#ifdef CONFIG_KSU_SUSFS_SUS_SU +#ifdef CONFIG_KSU_SUSFS_SUS_MAP + if (cmd == CMD_SUSFS_ADD_SUS_MAP) { + susfs_add_sus_map(arg); + return 0; + } +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MAP + if (cmd == CMD_SUSFS_ENABLE_AVC_LOG_SPOOFING) { + susfs_set_avc_log_spoofing(arg); + return 0; + } + if (cmd == CMD_SUSFS_SHOW_ENABLED_FEATURES) { + susfs_get_enabled_features(arg); + return 0; + } + if (cmd == CMD_SUSFS_SHOW_VARIANT) { + susfs_show_variant(arg); + return 0; + } + if (cmd == CMD_SUSFS_SHOW_VERSION) { + susfs_show_version(arg); + return 0; + } + return 0; + } + + // Check if this is a request to install KSU fd + if (magic2 == KSU_INSTALL_MAGIC2) { + int fd = ksu_install_fd(); + pr_info("[%d] install ksu fd: %d\n", current->pid, fd); + if (copy_to_user((int *)*arg, &fd, sizeof(fd))) { + pr_err("install ksu fd reply err\n"); + return 0; + } + } + return 0; +} +#endif // #ifndef CONFIG_KSU_SUSFS void ksu_supercalls_init(void) { @@ -801,6 +1017,7 @@ void ksu_supercalls_init(void) for (i = 0; ksu_ioctl_handlers[i].handler; i++) { pr_info(" %-18s = 0x%08x\n", ksu_ioctl_handlers[i].name, ksu_ioctl_handlers[i].cmd); } +#ifndef CONFIG_KSU_SUSFS #ifdef KSU_KPROBES_HOOK int rc = register_kprobe(&reboot_kp); if (rc) { @@ -809,13 +1026,18 @@ void ksu_supercalls_init(void) pr_info("reboot kprobe registered successfully\n"); } #endif +#endif } void ksu_supercalls_exit(void) { +#ifndef CONFIG_KSU_SUSFS #ifdef KSU_KPROBES_HOOK unregister_kprobe(&reboot_kp); #endif +#else + pr_info("susfs: do nothing\n"); +#endif } static inline void ksu_ioctl_audit(unsigned int cmd, const char *cmd_name, uid_t uid, int ret) @@ -906,4 +1128,4 @@ int ksu_install_fd(void) pr_info("ksu fd installed: %d for pid %d\n", fd, current->pid); return fd; -} \ No newline at end of file +} diff --git a/kernel/syscall_hook_manager.c b/kernel/syscall_hook_manager.c index 65430600..dc87b4e6 100644 --- a/kernel/syscall_hook_manager.c +++ b/kernel/syscall_hook_manager.c @@ -259,28 +259,28 @@ int ksu_handle_init_mark_tracker(const char __user **filename_user) return 0; } -#ifdef CONFIG_KSU_MANUAL_SU -#include "manual_su.h" -static inline void ksu_handle_task_alloc(struct pt_regs *regs) -{ - ksu_try_escalate_for_uid(current_uid().val); -} -#endif #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS // Generic sys_enter handler that dispatches to specific handlers static void ksu_sys_enter_handler(void *data, struct pt_regs *regs, long id) { if (unlikely(check_syscall_fastpath(id))) { +#ifndef CONFIG_KSU_SUSFS #ifdef KSU_TP_HOOK if (ksu_su_compat_enabled) { // Handle newfstatat if (id == __NR_newfstatat) { int *dfd = (int *)&PT_REGS_PARM1(regs); - const char __user **filename_user = - (const char __user **)&PT_REGS_PARM2(regs); int *flags = (int *)&PT_REGS_SYSCALL_PARM4(regs); - ksu_handle_stat(dfd, filename_user, flags); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) && defined(CONFIG_KSU_SUSFS) + // Kernel 6.1+ with SUSFS uses struct filename ** + struct filename **filename_ptr = (struct filename **)&PT_REGS_PARM2(regs); + ksu_handle_stat(dfd, filename_ptr, flags); +#else + // Older kernel or no SUSFS: use const char __user ** + const char __user **filename_user = (const char __user **)&PT_REGS_PARM2(regs); + ksu_handle_stat(dfd, filename_user, flags); +#endif return; } @@ -316,11 +316,6 @@ static void ksu_sys_enter_handler(void *data, struct pt_regs *regs, long id) ksu_handle_setresuid(ruid, euid, suid); return; } - -#ifdef CONFIG_KSU_MANUAL_SU - // Handle task_alloc via clone/fork - if (id == __NR_clone || id == __NR_clone3) - return ksu_handle_task_alloc(regs); #endif } }