diff --git a/kernel/Kconfig b/kernel/Kconfig index 3c3df97e..c5fe9ee2 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -8,6 +8,11 @@ config KSU To compile as a module, choose M here: the module will be called kernelsu. +# For easier extern ifdef handling +config RKSU + bool "RKSU compat, do not modify" + default y + config KSU_DEBUG bool "KernelSU debug mode" depends on KSU @@ -16,12 +21,11 @@ config KSU_DEBUG Enable KernelSU debug mode. config KSU_ALLOWLIST_WORKAROUND - bool "KernelSU Session init keyring workaround" + bool "KernelSU allowlist workaround" depends on KSU default n help - Enable session keyring init workaround for problematic devices. - Useful for situations where the SU allowlist is not kept after a reboot. + Enable workaround for broken allowlist save config KPM bool "Enable SukiSU KPM" @@ -35,9 +39,21 @@ config KPM select KALLSYMS_ALL config KSU_MANUAL_HOOK - bool "Hook KernelSU manually" - depends on KSU != m - help - If enabled, Hook required KernelSU syscalls with manually-patched function. + bool "KernelSU manual hook mode." + depends on KSU && KSU != m + default y if !KPROBES + default n + help + Enable manual hook support. + +config KSU_SHOULD_USE_NEW_TP + bool "KernelSU tracepoint+kretprobe hook" + depends on KSU && !KSU_MANUAL_HOOK + depends on KRETPROBES && KPROBES && HAVE_SYSCALL_TRACEPOINTS + default y if KPROBES && KRETPROBES && HAVE_SYSCALL_TRACEPOINTS + default n + help + Enable KPROBES, KRETPROBES and TRACEPOINT hook for KernelSU core. + This should not be used on kernel below 5.10. endmenu diff --git a/kernel/Makefile b/kernel/Makefile index a0f36e23..52391a13 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -1,17 +1,22 @@ kernelsu-objs := ksu.o kernelsu-objs += allowlist.o kernelsu-objs += dynamic_manager.o +kernelsu-objs += app_profile.o kernelsu-objs += apk_sign.o kernelsu-objs += sucompat.o +kernelsu-objs += syscall_hook_manager.o kernelsu-objs += throne_tracker.o kernelsu-objs += pkg_observer.o -kernelsu-objs += core_hook.o +kernelsu-objs += setuid_hook.o +kernelsu-objs += lsm_hooks.o +kernelsu-objs += kernel_compat.o +kernelsu-objs += kernel_umount.o kernelsu-objs += supercalls.o kernelsu-objs += feature.o kernelsu-objs += throne_tracker.o kernelsu-objs += ksud.o kernelsu-objs += embed_ksud.o -kernelsu-objs += kernel_compat.o +kernelsu-objs += seccomp_cache.o kernelsu-objs += file_wrapper.o kernelsu-objs += throne_comm.o @@ -84,14 +89,33 @@ endif ccflags-y += -DKSU_VERSION=$(KSU_VERSION) ccflags-y += -DKSU_VERSION_FULL=\"$(KSU_VERSION_FULL)\" +# RKSU: checks for available hook +## Logic flipped for HAVE_KSU_HOOK: 0 is success, 1 is failure, but not with KSU_DRY_RUN +HAVE_KSU_HOOK ?= 1 +KSU_DRY_RUN ?= 0 + # Checks hooks state +ifeq ($(CONFIG_KSU_SHOULD_USE_NEW_TP), y) +$(info -- KernelSU: SHOULD_USE_NEW_TP) +ccflags-y += -DKSU_SHOULD_USE_NEW_TP +# Let's make it 0, so it would pass. +HAVE_KSU_HOOK := 0 +endif + ifeq ($(CONFIG_KSU_MANUAL_HOOK), y) -ccflags-y += -DKSU_MANUAL_HOOK -$(info -- SukiSU: KSU_MANUAL_HOOK) +HAVE_KSU_HOOK := $(shell grep -q "ksu_handle_faccessat" $(srctree)/fs/open.c; echo $$?) +ifeq ($(HAVE_KSU_HOOK),0) +$(info -- KernelSU: CONFIG_KSU_MANUAL_HOOK) +endif +endif + +ifeq ($(KSU_DRY_RUN),0) +ifneq ($(HAVE_KSU_HOOK),0) +$(error -- KernelSU: No hooks were defined, bail!) +endif else -ccflags-y += -DKSU_HAVE_SYSCALL_TRACEPOINTS_HOOK -ccflags-y += -DKSU_KPROBES_HOOK -$(info -- SukiSU: KSU_TRACEPOINT_HOOK) +$(info -- KernelSU in dry run mode, skip hook checks) + endif # SELinux drivers check @@ -122,16 +146,12 @@ ifeq ($(shell grep -q "task_security_struct\s\+\*selinux_cred" $(srctree)/securi ccflags-y += -DKSU_OPTIONAL_SELINUX_CRED endif -# Check if __poll_t exists in linux or uapi types headers (handles backports) -ifneq ($(shell grep -q "__poll_t" \ - $(srctree)/include/linux/types.h \ - $(srctree)/include/uapi/linux/types.h; echo $$?),0) -ccflags-y += -DKSU_NO___POLL_T -endif - ifeq ($(shell grep -q "anon_inode_getfd_secure" $(srctree)/fs/anon_inodes.c; echo $$?),0) ccflags-y += -DKSU_HAS_GETFD_SECURE endif +ifeq ($(shell grep -A1 "^int vfs_getattr" $(srctree)/fs/stat.c | grep -q "query_flags"; echo $$?),0) +ccflags-y += -DKSU_HAS_NEW_VFS_GETATTR +endif # Checks Samsung ifeq ($(shell grep -q "CONFIG_KDP_CRED" $(srctree)/kernel/cred.c; echo $$?),0) diff --git a/kernel/allowlist.c b/kernel/allowlist.c index 8d2ad8e2..16b2c72d 100644 --- a/kernel/allowlist.c +++ b/kernel/allowlist.c @@ -19,12 +19,13 @@ #include #endif -#include "ksu.h" #include "klog.h" // IWYU pragma: keep +#include "ksud.h" #include "selinux/selinux.h" -#include "kernel_compat.h" #include "allowlist.h" #include "manager.h" +#include "kernel_compat.h" +#include "syscall_hook_manager.h" #define FILE_MAGIC 0x7f4b5355 // ' KSU', u32 #define FILE_FORMAT_VERSION 3 // u32 @@ -50,7 +51,7 @@ static void remove_uid_from_arr(uid_t uid) if (allow_list_pointer == 0) return; - temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL); + temp_arr = kzalloc(sizeof(allow_list_arr), GFP_KERNEL); if (temp_arr == NULL) { pr_err("%s: unable to allocate memory\n", __func__); return; @@ -205,7 +206,7 @@ bool ksu_set_app_profile(struct app_profile *profile, bool persist) } // not found, alloc a new node! - p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL); + p = (struct perm_data *)kzalloc(sizeof(struct perm_data), GFP_KERNEL); if (!p) { pr_err("ksu_set_app_profile alloc failed\n"); return false; @@ -264,8 +265,11 @@ out: sizeof(default_root_profile)); } - if (persist) + if (persist) { persistent_allow_list(); + // FIXME: use a new flag + ksu_mark_running_process(); + } return result; } @@ -432,12 +436,7 @@ void persistent_allow_list(void) goto put_task; } cb->func = do_persistent_allow_list; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 8) - task_work_add(tsk, cb, TWA_RESUME); -#else - task_work_add(tsk, cb, true); -#endif + ksu_task_work_add(tsk, cb, TWA_RESUME); put_task: put_task_struct(tsk); @@ -506,6 +505,11 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), struct perm_data *np = NULL; struct perm_data *n = NULL; + if (!ksu_boot_completed) { + pr_info("boot not completed, skip prune\n"); + return; + } + bool modified = false; // TODO: use RCU! mutex_lock(&allowlist_mutex); @@ -554,8 +558,6 @@ void ksu_allowlist_exit(void) struct perm_data *np = NULL; struct perm_data *n = NULL; - persistent_allow_list(); - // free allowlist mutex_lock(&allowlist_mutex); list_for_each_entry_safe (np, n, &allow_list, list) { diff --git a/kernel/allowlist.h b/kernel/allowlist.h index b9d845f1..8d9906f7 100644 --- a/kernel/allowlist.h +++ b/kernel/allowlist.h @@ -2,7 +2,12 @@ #define __KSU_H_ALLOWLIST #include -#include "ksu.h" +#include +#include "app_profile.h" + +#define PER_USER_RANGE 100000 +#define FIRST_APPLICATION_UID 10000 +#define LAST_APPLICATION_UID 19999 void ksu_allowlist_init(void); @@ -31,4 +36,10 @@ bool ksu_set_app_profile(struct app_profile *, bool persist); bool ksu_uid_should_umount(uid_t uid); struct root_profile *ksu_get_root_profile(uid_t uid); + +static inline bool is_appuid(uid_t uid) +{ + uid_t appid = uid % PER_USER_RANGE; + return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID; +} #endif diff --git a/kernel/apk_sign.c b/kernel/apk_sign.c index 88a48a08..4c105e51 100644 --- a/kernel/apk_sign.c +++ b/kernel/apk_sign.c @@ -38,7 +38,7 @@ static struct sdesc *init_sdesc(struct crypto_shash *alg) int size; size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); - sdesc = kmalloc(size, GFP_KERNEL); + sdesc = kzalloc(size, GFP_KERNEL); if (!sdesc) return ERR_PTR(-ENOMEM); sdesc->shash.tfm = alg; diff --git a/kernel/app_profile.c b/kernel/app_profile.c new file mode 100644 index 00000000..bf1471d1 --- /dev/null +++ b/kernel/app_profile.c @@ -0,0 +1,309 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) +#include // signal_struct +#include +#endif +#include +#include +#include +#include +#include + +#include "allowlist.h" +#include "app_profile.h" +#include "arch.h" +#include "kernel_compat.h" +#include "klog.h" // IWYU pragma: keep +#include "selinux/selinux.h" +#include "syscall_hook_manager.h" + +static struct group_info root_groups = { .usage = ATOMIC_INIT(2) }; + +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); + put_group_info(group_info); +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) +extern long SYS_SETNS_SYMBOL(const struct pt_regs *regs); +static long ksu_sys_setns(int fd, int flags) +{ + struct pt_regs regs; + memset(®s, 0, sizeof(regs)); + + PT_REGS_PARM1(®s) = fd; + PT_REGS_PARM2(®s) = flags; + +#if (defined(__aarch64__) || defined(__x86_64__)) + return SYS_SETNS_SYMBOL(®s); +#else + return -ENOSYS; +#endif +} +#else +static long ksu_sys_setns(int fd, int flags) +{ + return sys_setns(fd, flags); +} + +__weak int ksys_unshare(unsigned long unshare_flags) +{ + return sys_unshare(unshare_flags); +} +#endif + +static void setup_mount_namespace(int32_t ns_mode) +{ + pr_info("setup mount namespace for pid: %d\n", current->pid); + + if (ns_mode == 0) { + pr_info("mount namespace mode: inherit\n"); + // do nothing + return; + } + + if (ns_mode > 2) { + pr_warn("unknown mount namespace mode: %d\n", ns_mode); + return; + } + + const struct cred *old_cred = NULL; + struct cred *new_cred = NULL; + if (!(capable(CAP_SYS_ADMIN) && capable(CAP_SYS_CHROOT))) { + pr_info("process dont have CAP_SYS_ADMIN or CAP_SYS_CHROOT, adding it temporarily.\n"); + new_cred = prepare_creds(); + if (!new_cred) { + pr_warn("failed to prepare new credentials\n"); + return; + } + cap_raise(new_cred->cap_effective, CAP_SYS_ADMIN); + cap_raise(new_cred->cap_effective, CAP_SYS_CHROOT); + old_cred = override_creds(new_cred); + } + + if (ns_mode == 1) { + pr_info("mount namespace mode: global\n"); + struct file *ns_file; + struct path ns_path; + struct task_struct *pid1_task = NULL; + struct pid *pid_struct = NULL; + rcu_read_lock(); + // find init + pid_struct = find_pid_ns(1, &init_pid_ns); + if (unlikely(!pid_struct)) { + rcu_read_unlock(); + pr_warn("failed to find pid_struct for PID 1\n"); + goto try_drop_caps; + } + + pid1_task = get_pid_task(pid_struct, PIDTYPE_PID); + rcu_read_unlock(); + if (unlikely(!pid1_task)) { + pr_warn("failed to get task_struct for PID 1\n"); + goto try_drop_caps; + } + // mabe you can use &init_task for first stage init? + long ret = ns_get_path(&ns_path, pid1_task, &mntns_operations); + put_task_struct(pid1_task); + if (ret) { + pr_warn("failed to get path for init's mount namespace: %ld\n", + ret); + goto try_drop_caps; + } + ns_file = dentry_open(&ns_path, O_RDONLY, current_cred()); + + path_put(&ns_path); + if (IS_ERR(ns_file)) { + pr_warn("failed to open file for init's mount namespace: %ld\n", + PTR_ERR(ns_file)); + goto try_drop_caps; + } + + int fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + pr_warn("failed to get an unused fd: %d\n", fd); + fput(ns_file); + goto try_drop_caps; + } + + fd_install(fd, ns_file); + pr_info("calling sys_setns with fd : %d\n", fd); + + ret = ksu_sys_setns(fd, CLONE_NEWNS); + if (ret) { + pr_warn("sys_setns failed: %ld\n", ret); + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + close_fd(fd); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) + ksys_close(fd); +#else + sys_close(fd); +#endif + } + + if (ns_mode == 2) { + long ret; + pr_info("mount namespace mode: independent\n"); + + ret = ksys_unshare(CLONE_NEWNS); + if (ret) { + pr_warn("call ksys_unshare failed: %ld\n", ret); + } + } + +try_drop_caps: + if (old_cred) { + pr_info("dropping temporarily capability.\n"); + revert_creds(old_cred); + put_cred(new_cred); + } + return; +} + +// RKSU: Use it wisely, not static. +void disable_seccomp(struct task_struct *tsk) +{ + if (unlikely(!tsk)) + return; + + assert_spin_locked(&tsk->sighand->siglock); + + // disable seccomp +#if defined(CONFIG_GENERIC_ENTRY) && \ + LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + clear_syscall_work(SECCOMP); +#else + clear_thread_flag(TIF_SECCOMP); +#endif + +#ifdef CONFIG_SECCOMP + tsk->seccomp.mode = 0; + if (tsk->seccomp.filter) { + // 5.9+ have filter_count and use seccomp_filter_release +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + seccomp_filter_release(tsk); + atomic_set(&tsk->seccomp.filter_count, 0); +#else + // for 6.11+ kernel support? +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) + put_seccomp_filter(tsk); +#endif + tsk->seccomp.filter = NULL; +#endif + } +#endif +} + +void escape_with_root_profile(void) +{ + struct cred *cred; + // a bit useless, but we just want less ifdefs + struct task_struct *p = current; + + if (current_euid().val == 0) { + pr_warn("Already root, don't escape!\n"); + return; + } + + cred = prepare_creds(); + if (!cred) { + pr_warn("prepare_creds failed!\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; + cred->securebits = 0; + + 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_permitted, &profile->capabilities.effective, + sizeof(cred->cap_permitted)); + memcpy(&cred->cap_bset, &profile->capabilities.effective, + sizeof(cred->cap_bset)); + + setup_groups(profile, cred); + + commit_creds(cred); + + // Refer to kernel/seccomp.c: seccomp_set_mode_strict + // When disabling Seccomp, ensure that current->sighand->siglock is held during the operation. + spin_lock_irq(&p->sighand->siglock); + disable_seccomp(p); + spin_unlock_irq(&p->sighand->siglock); + + setup_selinux(profile->selinux_domain); + setup_mount_namespace(profile->namespaces); + +#ifdef KSU_SHOULD_USE_NEW_TP + struct task_struct *t; + for_each_thread (p, t) { + ksu_set_task_tracepoint_flag(t); + } +#endif +} diff --git a/kernel/app_profile.h b/kernel/app_profile.h new file mode 100644 index 00000000..047ed9f9 --- /dev/null +++ b/kernel/app_profile.h @@ -0,0 +1,66 @@ +#ifndef __KSU_H_APP_PROFILE +#define __KSU_H_APP_PROFILE + +#include + +// Forward declarations +struct cred; + +#define KSU_APP_PROFILE_VER 2 +#define KSU_MAX_PACKAGE_NAME 256 +// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups. +#define KSU_MAX_GROUPS 32 +#define KSU_SELINUX_DOMAIN 64 + +struct root_profile { + int32_t uid; + int32_t gid; + + int32_t groups_count; + int32_t groups[KSU_MAX_GROUPS]; + + // kernel_cap_t is u32[2] for capabilities v3 + struct { + u64 effective; + u64 permitted; + u64 inheritable; + } capabilities; + + char selinux_domain[KSU_SELINUX_DOMAIN]; + + int32_t namespaces; +}; + +struct non_root_profile { + bool umount_modules; +}; + +struct app_profile { + // It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this. + u32 version; + + // this is usually the package of the app, but can be other value for special apps + char key[KSU_MAX_PACKAGE_NAME]; + int32_t current_uid; + bool allow_su; + + union { + struct { + bool use_default; + char template_name[KSU_MAX_PACKAGE_NAME]; + + struct root_profile profile; + } rp_config; + + struct { + bool use_default; + + struct non_root_profile profile; + } nrp_config; + }; +}; + +// Escalate current process to root with the appropriate profile +void escape_with_root_profile(void); + +#endif diff --git a/kernel/arch.h b/kernel/arch.h index 761647d8..08be1ecf 100644 --- a/kernel/arch.h +++ b/kernel/arch.h @@ -21,19 +21,13 @@ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) #define REBOOT_SYMBOL "__arm64_sys_reboot" #define SYS_READ_SYMBOL "__arm64_sys_read" -#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat" -#define SYS_FSTATAT64_SYMBOL "__arm64_sys_fstatat64" -#define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat" #define SYS_EXECVE_SYMBOL "__arm64_sys_execve" -#define SYS_EXECVE_COMPAT_SYMBOL "__arm64_compat_sys_execve" +#define SYS_SETNS_SYMBOL __arm64_sys_setns #else #define REBOOT_SYMBOL "sys_reboot" #define SYS_READ_SYMBOL "sys_read" -#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat" -#define SYS_FSTATAT64_SYMBOL "sys_fstatat64" -#define SYS_FACCESSAT_SYMBOL "sys_faccessat" #define SYS_EXECVE_SYMBOL "sys_execve" -#define SYS_EXECVE_COMPAT_SYMBOL "compat_sys_execve" +#define SYS_SETNS_SYMBOL sys_setns #endif #elif defined(__x86_64__) @@ -51,26 +45,21 @@ #define __PT_RC_REG ax #define __PT_SP_REG sp #define __PT_IP_REG ip + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) #define REBOOT_SYMBOL "__x64_sys_reboot" #define SYS_READ_SYMBOL "__x64_sys_read" -#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat" -#define SYS_FSTATAT64_SYMBOL "__x64_sys_fstatat64" -#define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat" #define SYS_EXECVE_SYMBOL "__x64_sys_execve" -#define SYS_EXECVE_COMPAT_SYMBOL "__x64_compat_sys_execve" +#define SYS_SETNS_SYMBOL __x64_sys_setns #else -#define PRCTL_SYMBOL "sys_reboot" +#define REBOOT_SYMBOL "sys_reboot" #define SYS_READ_SYMBOL "sys_read" -#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat" -#define SYS_FSTATAT64_SYMBOL "sys_fstatat64" -#define SYS_FACCESSAT_SYMBOL "sys_faccessat" #define SYS_EXECVE_SYMBOL "sys_execve" -#define SYS_EXECVE_COMPAT_SYMBOL "compat_sys_execve" +#define SYS_SETNS_SYMBOL sys_setns #endif #else -#ifdef KSU_KPROBES_HOOK +#ifdef KSU_SHOULD_USE_NEW_TP #error "Unsupported arch" #endif #endif @@ -99,4 +88,4 @@ #define PT_REAL_REGS(regs) ((regs)) #endif -#endif \ No newline at end of file +#endif diff --git a/kernel/core_hook.c b/kernel/core_hook.c deleted file mode 100644 index cedbfe58..00000000 --- a/kernel/core_hook.c +++ /dev/null @@ -1,835 +0,0 @@ -#include -#include -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) -#include -#else -#include -#endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifndef KSU_HAS_PATH_UMOUNT -#include // sys_umount (<4.17) & ksys_umount (4.17+) -#endif -#include -#include - -#include "allowlist.h" -#include "arch.h" -#include "core_hook.h" -#include "feature.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_comm.h" -#include "kernel_compat.h" -#include "supercalls.h" -#include "sucompat.h" - -bool ksu_module_mounted __read_mostly = false; - -#ifndef DEVPTS_SUPER_MAGIC -#define DEVPTS_SUPER_MAGIC 0x1cd1 -#endif - -extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c - -#ifdef CONFIG_COMPAT -bool ksu_is_compat __read_mostly = false; -#endif - -static bool ksu_kernel_umount_enabled = true; -static bool ksu_enhanced_security_enabled = false; - -static int kernel_umount_feature_get(u64 *value) -{ - *value = ksu_kernel_umount_enabled ? 1 : 0; - return 0; -} - -static int kernel_umount_feature_set(u64 value) -{ - bool enable = value != 0; - ksu_kernel_umount_enabled = enable; - pr_info("kernel_umount: set to %d\n", enable); - return 0; -} - -static const struct ksu_feature_handler kernel_umount_handler = { - .feature_id = KSU_FEATURE_KERNEL_UMOUNT, - .name = "kernel_umount", - .get_handler = kernel_umount_feature_get, - .set_handler = kernel_umount_feature_set, -}; - -static int enhanced_security_feature_get(u64 *value) -{ - *value = ksu_enhanced_security_enabled ? 1 : 0; - return 0; -} - -static int enhanced_security_feature_set(u64 value) -{ - bool enable = value != 0; - ksu_enhanced_security_enabled = enable; - pr_info("enhanced_security: set to %d\n", enable); - return 0; -} - -static const struct ksu_feature_handler enhanced_security_handler = { - .feature_id = KSU_FEATURE_ENHANCED_SECURITY, - .name = "enhanced_security", - .get_handler = enhanced_security_feature_get, - .set_handler = enhanced_security_feature_set, -}; - -static inline bool is_allow_su(void) -{ - if (is_manager()) { - // we are manager, allow! - return true; - } - return ksu_is_allow_uid_for_current(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; -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0) -static struct group_info root_groups = { - .usage = REFCOUNT_INIT(2), -}; -#else -static struct group_info root_groups = { .usage = ATOMIC_INIT(2) }; -#endif - -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); - put_group_info(group_info); -} - -static void disable_seccomp(struct task_struct *tsk) -{ - assert_spin_locked(&tsk->sighand->siglock); - - // disable seccomp -#if defined(CONFIG_GENERIC_ENTRY) && \ - LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) - clear_syscall_work(SECCOMP); -#else - clear_thread_flag(TIF_SECCOMP); -#endif - -#ifdef CONFIG_SECCOMP - tsk->seccomp.mode = 0; - if (tsk->seccomp.filter) { - // 5.9+ have filter_count and use seccomp_filter_release -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) - seccomp_filter_release(tsk); - atomic_set(&tsk->seccomp.filter_count, 0); -#else - // for 6.11+ kernel support? -#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) - put_seccomp_filter(tsk); -#endif - tsk->seccomp.filter = NULL; -#endif - } -#endif -} - -void escape_to_root(void) -{ - struct cred *cred; -#ifdef KSU_SHOULD_USE_NEW_TP - struct task_struct *p = current; - struct task_struct *t; -#endif - - cred = prepare_creds(); - if (!cred) { - pr_warn("prepare_creds failed!\n"); - return; - } - - if (cred->euid.val == 0) { - pr_warn("Already root, don't escape!\n"); - abort_creds(cred); - 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; - cred->securebits = 0; - - 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_permitted, &profile->capabilities.effective, - sizeof(cred->cap_permitted)); - memcpy(&cred->cap_bset, &profile->capabilities.effective, - sizeof(cred->cap_bset)); - - setup_groups(profile, cred); - - commit_creds(cred); - - spin_lock_irq(¤t->sighand->siglock); - disable_seccomp(current); - spin_unlock_irq(¤t->sighand->siglock); - - setup_selinux(profile->selinux_domain); - -#ifdef KSU_SHOULD_USE_NEW_TP - for_each_thread (p, t) { - ksu_set_task_tracepoint_flag(t); - } -#endif -} - -extern void ext4_unregister_sysfs(struct super_block *sb); -void nuke_ext4_sysfs(void) -{ -#ifdef CONFIG_EXT4_FS - struct path path; - int err = kern_path("/data/adb/modules", 0, &path); - if (err) { - pr_err("%s: failed to get path, err %d\n", __func__, err); - return; - } - - struct super_block *sb = path.dentry->d_inode->i_sb; - const char *name = sb->s_type->name; - if (strcmp(name, "ext4") != 0) { - pr_info("nuke_module: skipping s_type: %s\n", name); - path_put(&path); - return; - } - - ext4_unregister_sysfs(sb); - pr_info("nuke_module: ext4 sysfs unregistered.\n"); - path_put(&path); -#endif -} - -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; -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || \ - defined(KSU_HAS_PATH_UMOUNT) -extern int path_umount(struct path *path, int flags); -#define ksu_umount_mnt(__unused, path, flags) (path_umount(path, flags)) -#else -static int ksu_sys_umount(const char *mnt, int flags) -{ - char __user *usermnt = (char __user *)mnt; - mm_segment_t old_fs; - int ret; // although asmlinkage long - - old_fs = get_fs(); - set_fs(KERNEL_DS); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) - ret = ksys_umount(usermnt, flags); -#else - ret = sys_umount(usermnt, flags); // cuz asmlinkage long sys##name -#endif - set_fs(old_fs); - return ret; -} - -#define ksu_umount_mnt(mnt, __unused, flags) \ - ({ \ - int ret; \ - path_put(__unused); \ - ret = ksu_sys_umount(mnt, flags); \ - ret; \ - }) - -#endif - -static void try_umount(const char *mnt, bool check_mnt, int flags) -{ - struct path path; - int ret; - 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. - path_put(&path); - return; - } - - // we are only interest in some specific mounts - if (check_mnt && !should_umount(&path)) { - path_put(&path); - return; - } - - ret = ksu_umount_mnt(mnt, &path, flags); - if (ret) { -#ifdef CONFIG_KSU_DEBUG - pr_info("%s: path: %s, ret: %d\n", __func__, mnt, ret); -#endif - } -} - -static void ksu_do_umount_lists(void) -{ - // fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and - // filter the mountpoint whose target is `/data/adb` - 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", false, MNT_DETACH); - try_umount("/sbin", false, MNT_DETACH); -} - -#if defined(MODULE) || defined(KSU_KPROBES_HOOK) -struct umount_tw { - struct callback_head cb; - const struct cred *old_cred; -}; - -static void umount_tw_func(struct callback_head *cb) -{ - struct umount_tw *tw = container_of(cb, struct umount_tw, cb); - const struct cred *saved = NULL; - if (tw->old_cred) { - saved = override_creds(tw->old_cred); - } - - ksu_do_umount_lists(); - - if (saved) - revert_creds(saved); - - if (tw->old_cred) - put_cred(tw->old_cred); - - kfree(tw); -} -#endif - -// force_sig kcompat, TODO: move it out of core_hook.c -// https://elixir.bootlin.com/linux/v5.3-rc1/source/kernel/signal.c#L1613 -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) -#define __force_sig(sig) force_sig(sig) -#else -#define __force_sig(sig) force_sig(sig, current) -#endif - -int ksu_handle_setuid(struct cred *new, const struct cred *old) -{ - if (!new || !old) { - return 0; - } - - kuid_t new_uid = new->uid; - kuid_t old_uid = old->uid; - // pr_info("handle_setuid from %d to %d\n", old_uid.val, new_uid.val); - - if (0 != old_uid.val) { - // old process is not root, ignore it. - if (ksu_enhanced_security_enabled) { - // disallow any non-ksu domain escalation from non-root to root! - if (unlikely(new_uid.val) == 0) { - if (!is_ksu_domain()) { - pr_warn("find suspicious EoP: %d %s, from %d to %d\n", - current->pid, current->comm, - old_uid.val, new_uid.val); - __force_sig(SIGKILL); - return 0; - } - } - // disallow appuid decrease to any other uid if it is allowed to su - if (is_appuid(old_uid)) { - if (new_uid.val < old_uid.val && - !ksu_is_allow_uid_for_current( - old_uid.val)) { - pr_warn("find suspicious EoP: %d %s, from %d to %d\n", - current->pid, current->comm, - old_uid.val, new_uid.val); - __force_sig(SIGKILL); - return 0; - } - } - } - return 0; - } - -#ifdef KSU_SHOULD_USE_NEW_TP - if (new_uid.val == 2000 && ksu_su_compat_enabled) { - ksu_set_task_tracepoint_flag(current); - } -#endif - - if (!is_appuid(new_uid) || is_unsupported_uid(new_uid.val)) { - // pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid.val); - return 0; - } - - // if on private space, see if its possibly the manager - if (new_uid.val > 100000 && - new_uid.val % 100000 == ksu_get_manager_uid()) { - ksu_set_manager_uid(new_uid.val); - } - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) - if (ksu_get_manager_uid() == new_uid.val) { - pr_info("install fd for ksu manager(uid=%d)\n", new_uid.val); - ksu_install_fd(); - spin_lock_irq(¤t->sighand->siglock); - ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot); - spin_unlock_irq(¤t->sighand->siglock); - ksu_set_task_tracepoint_flag(current); - return 0; - } - - if (ksu_is_allow_uid_for_current(new_uid.val)) { - 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); - } - if (ksu_su_compat_enabled) { - ksu_set_task_tracepoint_flag(current); - } - } else { - if (ksu_su_compat_enabled) { - // Disable syscall tracepoint sucompat for non-allowed processes - ksu_clear_task_tracepoint_flag(current); - } - } -#else - if (ksu_is_allow_uid_for_current(new_uid.val)) { - spin_lock_irq(¤t->sighand->siglock); - disable_seccomp(current); - spin_unlock_irq(¤t->sighand->siglock); - - if (ksu_get_manager_uid() == new_uid.val) { - pr_info("install fd for ksu manager(uid=%d)\n", - new_uid.val); - ksu_install_fd(); - } - - return 0; - } -#endif - - // 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 (!ksu_kernel_umount_enabled) { - 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! - if (!is_zygote(old)) { - 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 - -#if defined(MODULE) || defined(KSU_KPROBES_HOOK) - struct umount_tw *tw; - tw = kmalloc(sizeof(*tw), GFP_ATOMIC); - if (!tw) - return 0; - - tw->old_cred = get_current_cred(); - tw->cb.func = umount_tw_func; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 8) - int err = task_work_add(current, &tw->cb, TWA_RESUME); -#else - int err = task_work_add(current, &tw->cb, true); -#endif - - if (err) { - if (tw->old_cred) { - put_cred(tw->old_cred); - } - kfree(tw); - pr_warn("unmount add task_work failed\n"); - } -#else - ksu_do_umount_lists(); -#endif - - return 0; -} - -int ksu_inode_permission(struct inode *inode, int mask) -{ - if (inode && inode->i_sb - && unlikely(inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) { - //pr_info("%s: handling devpts for: %s \n", __func__, current->comm); - __ksu_handle_devpts(inode); - } - return 0; -} - -int ksu_bprm_check(struct linux_binprm *bprm) -{ - char *filename = (char *)bprm->filename; - - if (likely(!ksu_execveat_hook)) - return 0; - -#ifdef CONFIG_COMPAT - static bool compat_check_done __read_mostly = false; - if ( unlikely(!compat_check_done) && unlikely(!strcmp(filename, "/data/adb/ksud")) - && !memcmp(bprm->buf, "\x7f\x45\x4c\x46", 4) ) { - if (bprm->buf[4] == 0x01 ) - ksu_is_compat = true; - - pr_info("%s: %s ELF magic found! ksu_is_compat: %d \n", __func__, filename, ksu_is_compat); - compat_check_done = true; - } -#endif - - ksu_handle_pre_ksud(filename); - - return 0; - -} - -int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd, - void __user **arg) -{ - if (magic1 != KSU_INSTALL_MAGIC1) - return 0; - -#ifdef CONFIG_KSU_DEBUG - pr_info("sys_reboot: intercepted call! magic: 0x%x id: %d\n", magic1, - magic2); -#endif - - // Check if this is a request to install KSU fd - if (magic2 == KSU_INSTALL_MAGIC2) { - int fd = ksu_install_fd(); - // downstream: dereference all arg usage! - if (copy_to_user((void __user *)*arg, &fd, sizeof(fd))) { - pr_err("install ksu fd reply err\n"); - } - return 0; - } - - // extensions - - return 0; -} - -// -- For old kernel compat? -#if !defined(MODULE) && !defined(KSU_KPROBE_HOOK) -static int ksu_task_fix_setuid(struct cred *new, const struct cred *old, - int flags) -{ - return ksu_handle_setuid(new, old); -} - -// 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 - -#include -static struct security_hook_list ksu_hooks[] = { - LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid), - LSM_HOOK_INIT(inode_permission, ksu_inode_permission), - #ifndef KSU_KPROBES_HOOK - LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check), -#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) -static const struct lsm_id ksu_lsmid = { - .name = "ksu", - .id = 912, -}; -#endif - -static void ksu_lsm_hook_init(void) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) - 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 - pr_info("LSM hooks initialized.\n"); -} -#else -static void ksu_lsm_hook_init(void) -{ -} -#endif - -// -- For KPROBE and LKM handler -#if defined(MODULE) || defined(KSU_KPROBES_HOOK) -static int reboot_handler_pre(struct kprobe *p, struct pt_regs *regs) -{ - struct pt_regs *real_regs = PT_REAL_REGS(regs); - int magic1 = (int)PT_REGS_PARM1(real_regs); - int magic2 = (int)PT_REGS_PARM2(real_regs); - int cmd = (int)PT_REGS_PARM3(real_regs); - void __user **arg = (void __user **)&PT_REGS_SYSCALL_PARM4(real_regs); - - return ksu_handle_sys_reboot(magic1, magic2, cmd, arg); -} - -static struct kprobe reboot_kp = { - .symbol_name = REBOOT_SYMBOL, - .pre_handler = reboot_handler_pre, -}; - -// 2. cap_task_fix_setuid hook for handling setuid -static int cap_task_fix_setuid_handler_pre(struct kprobe *p, - struct pt_regs *regs) -{ - struct cred *new = (struct cred *)PT_REGS_PARM1(regs); - const struct cred *old = (const struct cred *)PT_REGS_PARM2(regs); - - ksu_handle_setuid(new, old); - - return 0; -} - -static struct kprobe cap_task_fix_setuid_kp = { - .symbol_name = "cap_task_fix_setuid", - .pre_handler = cap_task_fix_setuid_handler_pre, -}; - -static int ksu_kprobe_init(void) -{ - int rc = 0; - - // Register reboot kprobe - rc = register_kprobe(&reboot_kp); - if (rc) { - pr_err("reboot kprobe failed: %d\n", rc); - return rc; - } - pr_info("reboot kprobe registered successfully\n"); - - // Register cap_task_fix_setuid kprobe - rc = register_kprobe(&cap_task_fix_setuid_kp); - if (rc) { - pr_err("cap_task_fix_setuid kprobe failed: %d\n", rc); - unregister_kprobe(&reboot_kp); - return rc; - } - pr_info("cap_task_fix_setuid kprobe registered successfully\n"); - - return 0; -} - -static void ksu_kprobe_exit(void) -{ - unregister_kprobe(&cap_task_fix_setuid_kp); - unregister_kprobe(&reboot_kp); -} - -void __init ksu_core_init(void) -{ - int rc = ksu_kprobe_init(); - if (rc) { - pr_err("ksu_kprobe_init failed: %d\n", rc); - } - - if (ksu_register_feature_handler(&kernel_umount_handler)) { - pr_err("Failed to register umount feature handler\n"); - } - - if (ksu_register_feature_handler(&enhanced_security_handler)) { - pr_err("Failed to register enhanced security feature handler\n"); - } -} - -void ksu_core_exit(void) -{ - pr_info("ksu_core_exit\n"); - ksu_kprobe_exit(); - ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT); -} -#else - -void __init ksu_core_init(void) -{ - ksu_lsm_hook_init(); - - if (ksu_register_feature_handler(&kernel_umount_handler)) { - pr_err("Failed to register umount feature handler\n"); - } - - if (ksu_register_feature_handler(&enhanced_security_handler)) { - pr_err("Failed to register enhanced security feature handler\n"); - } -} - -void ksu_core_exit(void) -{ - ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT); - - ksu_uid_exit(); - ksu_throne_comm_exit(); - - ksu_unregister_feature_handler(KSU_FEATURE_ENHANCED_SECURITY); -} - -#endif diff --git a/kernel/core_hook.h b/kernel/core_hook.h deleted file mode 100644 index 47405fa9..00000000 --- a/kernel/core_hook.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __KSU_H_KSU_CORE -#define __KSU_H_KSU_CORE - -#include -#include "apk_sign.h" - -void __init ksu_core_init(void); -void ksu_core_exit(void); - -void escape_to_root(void); - -void nuke_ext4_sysfs(void); - -extern bool ksu_module_mounted; - -#endif diff --git a/kernel/feature.c b/kernel/feature.c index 2df5c3a4..a1017aaf 100644 --- a/kernel/feature.c +++ b/kernel/feature.c @@ -157,7 +157,7 @@ void ksu_feature_init(void) feature_handlers[i] = NULL; } - pr_info("%s: feature management initialized\n", __func__); + pr_info("feature: feature management initialized\n"); } void ksu_feature_exit(void) @@ -172,5 +172,5 @@ void ksu_feature_exit(void) mutex_unlock(&feature_mutex); - pr_info("%s: feature management cleaned up\n", __func__); + pr_info("feature: feature management cleaned up\n"); } diff --git a/kernel/file_wrapper.c b/kernel/file_wrapper.c index 5be9e41f..70e9b508 100644 --- a/kernel/file_wrapper.c +++ b/kernel/file_wrapper.c @@ -1,188 +1,160 @@ -#include "linux/export.h" +#include #include +#include // kernel 3.18 #include #include #include #include #include -#include +#include #include -#include #include #include -#include "allowlist.h" #include "klog.h" // IWYU pragma: keep -#include "ksu.h" -#include "ksud.h" -#include "manager.h" #include "selinux/selinux.h" -#include "core_hook.h" -#include "objsec.h" #include "file_wrapper.h" -#ifdef KSU_NO___POLL_T -typedef unsigned int ksu_poll_t; -#else -typedef __poll_t ksu_poll_t; -#endif - -static loff_t ksu_wrapper_llseek(struct file *fp, loff_t off, int flags) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static loff_t ksu_wrapper_llseek(struct file *fp, loff_t off, int flags) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; return orig->f_op->llseek(data->orig, off, flags); } -static ssize_t ksu_wrapper_read(struct file *fp, char __user *ptr, size_t sz, - loff_t *off) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static ssize_t ksu_wrapper_read(struct file *fp, char __user *ptr, size_t sz, loff_t *off) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; return orig->f_op->read(orig, ptr, sz, off); } -static ssize_t ksu_wrapper_write(struct file *fp, const char __user *ptr, - size_t sz, loff_t *off) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static ssize_t ksu_wrapper_write(struct file *fp, const char __user *ptr, size_t sz, loff_t *off) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; return orig->f_op->write(orig, ptr, sz, off); } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) -static ssize_t ksu_wrapper_read_iter(struct kiocb *iocb, struct iov_iter *iovi) -{ - struct ksu_file_wrapper *data = iocb->ki_filp->private_data; - struct file *orig = data->orig; +static ssize_t ksu_wrapper_read_iter(struct kiocb *iocb, struct iov_iter *iovi) { + struct ksu_file_wrapper* data = iocb->ki_filp->private_data; + struct file* orig = data->orig; iocb->ki_filp = orig; return orig->f_op->read_iter(iocb, iovi); } -static ssize_t ksu_wrapper_write_iter(struct kiocb *iocb, - struct iov_iter *iovi) -{ - struct ksu_file_wrapper *data = iocb->ki_filp->private_data; - struct file *orig = data->orig; +static ssize_t ksu_wrapper_write_iter(struct kiocb *iocb, struct iov_iter *iovi) { + struct ksu_file_wrapper* data = iocb->ki_filp->private_data; + struct file* orig = data->orig; iocb->ki_filp = orig; return orig->f_op->write_iter(iocb, iovi); } #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) -static int ksu_wrapper_iopoll(struct kiocb *kiocb, struct io_comp_batch *icb, - unsigned int v) -{ - struct ksu_file_wrapper *data = kiocb->ki_filp->private_data; - struct file *orig = data->orig; +static int ksu_wrapper_iopoll(struct kiocb *kiocb, struct io_comp_batch* icb, unsigned int v) { + struct ksu_file_wrapper* data = kiocb->ki_filp->private_data; + struct file* orig = data->orig; kiocb->ki_filp = orig; return orig->f_op->iopoll(kiocb, icb, v); } #elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) -static int ksu_wrapper_iopoll(struct kiocb *kiocb, bool spin) -{ - struct ksu_file_wrapper *data = kiocb->ki_filp->private_data; - struct file *orig = data->orig; +static int ksu_wrapper_iopoll(struct kiocb *kiocb, bool spin) { + struct ksu_file_wrapper* data = kiocb->ki_filp->private_data; + struct file* orig = data->orig; kiocb->ki_filp = orig; return orig->f_op->iopoll(kiocb, spin); } #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) -static int ksu_wrapper_iterate(struct file *fp, struct dir_context *dc) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) && (LINUX_VERSION_CODE > KERNEL_VERSION(3, 11, 0) || defined(KSU_HAS_ITERATE_DIR)) +static int ksu_wrapper_iterate (struct file *fp, struct dir_context *dc) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; return orig->f_op->iterate(orig, dc); } +#endif + +// int (*readdir) (struct file *, void *, filldir_t); +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) && !defined(KSU_HAS_ITERATE_DIR) +static int ksu_wrapper_readdir(struct file *fp, void *ptr, filldir_t filler) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; + return orig->f_op->readdir(orig, ptr, filler); +} #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) -static int ksu_wrapper_iterate_shared(struct file *fp, struct dir_context *dc) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static int ksu_wrapper_iterate_shared(struct file *fp, struct dir_context *dc) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; return orig->f_op->iterate_shared(orig, dc); } #endif -static ksu_poll_t ksu_wrapper_poll(struct file *fp, - struct poll_table_struct *pts) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +// typedef unsigned __bitwise __poll_t; +static unsigned __bitwise ksu_wrapper_poll(struct file *fp, struct poll_table_struct *pts) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; return orig->f_op->poll(orig, pts); } -static long ksu_wrapper_unlocked_ioctl(struct file *fp, unsigned int cmd, - unsigned long arg) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static long ksu_wrapper_unlocked_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; return orig->f_op->unlocked_ioctl(orig, cmd, arg); } -static long ksu_wrapper_compat_ioctl(struct file *fp, unsigned int cmd, - unsigned long arg) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static long ksu_wrapper_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; return orig->f_op->compat_ioctl(orig, cmd, arg); } -static int ksu_wrapper_mmap(struct file *fp, struct vm_area_struct *vma) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static int ksu_wrapper_mmap(struct file *fp, struct vm_area_struct * vma) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; return orig->f_op->mmap(orig, vma); } // static unsigned long mmap_supported_flags {} -static int ksu_wrapper_open(struct inode *ino, struct file *fp) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static int ksu_wrapper_open(struct inode *ino, struct file *fp) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; struct inode *orig_ino = file_inode(orig); return orig->f_op->open(orig_ino, orig); } -static int ksu_wrapper_flush(struct file *fp, fl_owner_t id) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static int ksu_wrapper_flush(struct file *fp, fl_owner_t id) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; return orig->f_op->flush(orig, id); } -static int ksu_wrapper_fsync(struct file *fp, loff_t off1, loff_t off2, - int datasync) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; + +static int ksu_wrapper_fsync(struct file *fp, loff_t off1, loff_t off2, int datasync) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; return orig->f_op->fsync(orig, off1, off2, datasync); } -static int ksu_wrapper_fasync(int arg, struct file *fp, int arg2) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static int ksu_wrapper_fasync(int arg, struct file *fp, int arg2) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; return orig->f_op->fasync(arg, orig, arg2); } -static int ksu_wrapper_lock(struct file *fp, int arg1, struct file_lock *fl) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static int ksu_wrapper_lock(struct file *fp, int arg1, struct file_lock *fl) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; return orig->f_op->lock(orig, arg1, fl); } + #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) -static ssize_t ksu_wrapper_sendpage(struct file *fp, struct page *pg, int arg1, - size_t sz, loff_t *off, int arg2) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static ssize_t ksu_wrapper_sendpage(struct file *fp, struct page *pg, int arg1, size_t sz, loff_t *off, int arg2) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; if (orig->f_op->sendpage) { return orig->f_op->sendpage(orig, pg, arg1, sz, off, arg2); } @@ -190,51 +162,38 @@ static ssize_t ksu_wrapper_sendpage(struct file *fp, struct page *pg, int arg1, } #endif -static unsigned long ksu_wrapper_get_unmapped_area(struct file *fp, - unsigned long arg1, - unsigned long arg2, - unsigned long arg3, - unsigned long arg4) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static unsigned long ksu_wrapper_get_unmapped_area(struct file *fp, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; if (orig->f_op->get_unmapped_area) { - return orig->f_op->get_unmapped_area(orig, arg1, arg2, arg3, - arg4); + return orig->f_op->get_unmapped_area(orig, arg1, arg2, arg3, arg4); } return -EINVAL; } // static int ksu_wrapper_check_flags(int arg) {} -static int ksu_wrapper_flock(struct file *fp, int arg1, struct file_lock *fl) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static int ksu_wrapper_flock(struct file *fp, int arg1, struct file_lock *fl) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; if (orig->f_op->flock) { return orig->f_op->flock(orig, arg1, fl); } return -EINVAL; } -static ssize_t ksu_wrapper_splice_write(struct pipe_inode_info *pii, - struct file *fp, loff_t *off, - size_t sz, unsigned int arg1) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static ssize_t ksu_wrapper_splice_write(struct pipe_inode_info * pii, struct file *fp, loff_t *off, size_t sz, unsigned int arg1) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; if (orig->f_op->splice_write) { return orig->f_op->splice_write(pii, orig, off, sz, arg1); } return -EINVAL; } -static ssize_t ksu_wrapper_splice_read(struct file *fp, loff_t *off, - struct pipe_inode_info *pii, size_t sz, - unsigned int arg1) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static ssize_t ksu_wrapper_splice_read(struct file *fp, loff_t *off, struct pipe_inode_info *pii, size_t sz, unsigned int arg1) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; if (orig->f_op->splice_read) { return orig->f_op->splice_read(orig, off, pii, sz, arg1); } @@ -242,10 +201,9 @@ static ssize_t ksu_wrapper_splice_read(struct file *fp, loff_t *off, } #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) -void ksu_wrapper_splice_eof(struct file *fp) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +void ksu_wrapper_splice_eof(struct file *fp) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; if (orig->f_op->splice_eof) { return orig->f_op->splice_eof(orig); } @@ -253,45 +211,36 @@ void ksu_wrapper_splice_eof(struct file *fp) #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) -static int ksu_wrapper_setlease(struct file *fp, int arg1, - struct file_lease **fl, void **p) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lease **fl, void **p) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; if (orig->f_op->setlease) { return orig->f_op->setlease(orig, arg1, fl, p); } return -EINVAL; } #elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) -static int ksu_wrapper_setlease(struct file *fp, int arg1, - struct file_lock **fl, void **p) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lock **fl, void **p) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; if (orig->f_op->setlease) { return orig->f_op->setlease(orig, arg1, fl, p); } return -EINVAL; } -// int (*setlease)(struct file *, long, struct file_lock **, void **); -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) -static int ksu_wrapper_setlease(struct file *fp, long arg1, - struct file_lock **fl, void **p) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) // int (*setlease)(struct file *, long, struct file_lock **, void **); +static int ksu_wrapper_setlease(struct file *fp, long arg1, struct file_lock **fl, void **p) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; if (orig->f_op->setlease) { return orig->f_op->setlease(orig, arg1, fl, p); } return -EINVAL; } #else // int (*setlease)(struct file *, long, struct file_lock **); -static int ksu_wrapper_setlease(struct file *fp, long arg1, - struct file_lock **fl) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static int ksu_wrapper_setlease(struct file *fp, long arg1, struct file_lock **fl) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; if (orig->f_op->setlease) { return orig->f_op->setlease(orig, arg1, fl); } @@ -299,11 +248,9 @@ static int ksu_wrapper_setlease(struct file *fp, long arg1, } #endif -static long ksu_wrapper_fallocate(struct file *fp, int mode, loff_t offset, - loff_t len) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static long ksu_wrapper_fallocate(struct file *fp, int mode, loff_t offset, loff_t len) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; if (orig->f_op->fallocate) { return orig->f_op->fallocate(orig, mode, offset, len); } @@ -311,20 +258,17 @@ static long ksu_wrapper_fallocate(struct file *fp, int mode, loff_t offset, } #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) -static void ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) -{ - struct ksu_file_wrapper *data = m->file->private_data; - struct file *orig = data->orig; +static void ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) { + struct ksu_file_wrapper* data = f->private_data; + struct file* orig = data->orig; if (orig->f_op->show_fdinfo) { orig->f_op->show_fdinfo(m, orig); } } - -#else -static int ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) -{ - struct ksu_file_wrapper *data = m->file->private_data; - struct file *orig = data->orig; +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) +static int ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) { + struct ksu_file_wrapper* data = f->private_data; + struct file* orig = data->orig; if (orig->f_op->show_fdinfo) { orig->f_op->show_fdinfo(m, orig); } @@ -333,44 +277,40 @@ static int ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) -static ssize_t ksu_wrapper_copy_file_range(struct file *f1, loff_t off1, - struct file *f2, loff_t off2, - size_t sz, unsigned int flags) -{ - // TODO: determine which file to use - struct ksu_file_wrapper *data = f1->private_data; - struct file *orig = data->orig; - if (orig->f_op->copy_file_range) { - return orig->f_op->copy_file_range(orig, off1, f2, off2, sz, - flags); - } - return -EINVAL; +// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/read_write.c;l=1593-1606;drc=398da7defe218d3e51b0f3bdff75147e28125b60 +static ssize_t ksu_wrapper_copy_file_range(struct file *file_in, loff_t pos_in, struct file *file_out, + loff_t pos_out, size_t len, unsigned int flags) { + struct ksu_file_wrapper* data = file_out->private_data; + struct file* orig = data->orig; + return orig->f_op->copy_file_range(file_in, pos_in, orig, pos_out, len, flags); } #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) +// no REMAP_FILE_DEDUP: use file_in +// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/read_write.c;l=1598-1599;drc=398da7defe218d3e51b0f3bdff75147e28125b60 +// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/remap_range.c;l=403-404;drc=398da7defe218d3e51b0f3bdff75147e28125b60 +// REMAP_FILE_DEDUP: use file_out +// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/remap_range.c;l=483-484;drc=398da7defe218d3e51b0f3bdff75147e28125b60 static loff_t ksu_wrapper_remap_file_range(struct file *file_in, loff_t pos_in, - struct file *file_out, - loff_t pos_out, loff_t len, - unsigned int remap_flags) -{ - // TODO: determine which file to use - struct ksu_file_wrapper *data = file_in->private_data; - struct file *orig = data->orig; - if (orig->f_op->remap_file_range) { - return orig->f_op->remap_file_range(orig, pos_in, file_out, - pos_out, len, remap_flags); + struct file *file_out, loff_t pos_out, + loff_t len, unsigned int remap_flags) { + if (remap_flags & REMAP_FILE_DEDUP) { + struct ksu_file_wrapper* data = file_out->private_data; + struct file* orig = data->orig; + return orig->f_op->remap_file_range(file_in, pos_in, orig, pos_out, len, remap_flags); + } else { + struct ksu_file_wrapper* data = file_in->private_data; + struct file* orig = data->orig; + return orig->f_op->remap_file_range(orig, pos_in, file_out, pos_out, len, remap_flags); } - return -EINVAL; } #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) -static int ksu_wrapper_fadvise(struct file *fp, loff_t off1, loff_t off2, - int flags) -{ - struct ksu_file_wrapper *data = fp->private_data; - struct file *orig = data->orig; +static int ksu_wrapper_fadvise(struct file *fp, loff_t off1, loff_t off2, int flags) { + struct ksu_file_wrapper* data = fp->private_data; + struct file* orig = data->orig; if (orig->f_op->fadvise) { return orig->f_op->fadvise(orig, off1, off2, flags); } @@ -378,16 +318,13 @@ static int ksu_wrapper_fadvise(struct file *fp, loff_t off1, loff_t off2, } #endif -static int ksu_wrapper_release(struct inode *inode, struct file *filp) -{ +static int ksu_wrapper_release(struct inode *inode, struct file *filp) { ksu_delete_file_wrapper(filp->private_data); - return 0; + return 0; } -struct ksu_file_wrapper *ksu_create_file_wrapper(struct file *fp) -{ - struct ksu_file_wrapper *p = - kcalloc(sizeof(struct ksu_file_wrapper), 1, GFP_KERNEL); +struct ksu_file_wrapper* ksu_create_file_wrapper(struct file* fp) { + struct ksu_file_wrapper* p = kcalloc(sizeof(struct ksu_file_wrapper), 1, GFP_KERNEL); if (!p) { return NULL; } @@ -401,24 +338,23 @@ struct ksu_file_wrapper *ksu_create_file_wrapper(struct file *fp) p->ops.write = fp->f_op->write ? ksu_wrapper_write : NULL; #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0) p->ops.read_iter = fp->f_op->read_iter ? ksu_wrapper_read_iter : NULL; - p->ops.write_iter = - fp->f_op->write_iter ? ksu_wrapper_write_iter : NULL; + p->ops.write_iter = fp->f_op->write_iter ? ksu_wrapper_write_iter : NULL; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) p->ops.iopoll = fp->f_op->iopoll ? ksu_wrapper_iopoll : NULL; #endif -#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) && (LINUX_VERSION_CODE > KERNEL_VERSION(3, 11, 0) || defined(KSU_HAS_ITERATE_DIR)) p->ops.iterate = fp->f_op->iterate ? ksu_wrapper_iterate : NULL; #endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) && !defined(KSU_HAS_ITERATE_DIR) + p->ops.readdir = fp->f_op->readdir ? ksu_wrapper_readdir : NULL; +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) - p->ops.iterate_shared = - fp->f_op->iterate_shared ? ksu_wrapper_iterate_shared : NULL; + p->ops.iterate_shared = fp->f_op->iterate_shared ? ksu_wrapper_iterate_shared : NULL; #endif p->ops.poll = fp->f_op->poll ? ksu_wrapper_poll : NULL; - p->ops.unlocked_ioctl = - fp->f_op->unlocked_ioctl ? ksu_wrapper_unlocked_ioctl : NULL; - p->ops.compat_ioctl = - fp->f_op->compat_ioctl ? ksu_wrapper_compat_ioctl : NULL; + p->ops.unlocked_ioctl = fp->f_op->unlocked_ioctl ? ksu_wrapper_unlocked_ioctl : NULL; + p->ops.compat_ioctl = fp->f_op->compat_ioctl ? ksu_wrapper_compat_ioctl : NULL; p->ops.mmap = fp->f_op->mmap ? ksu_wrapper_mmap : NULL; #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) p->ops.fop_flags = fp->f_op->fop_flags; @@ -434,42 +370,33 @@ struct ksu_file_wrapper *ksu_create_file_wrapper(struct file *fp) #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) p->ops.sendpage = fp->f_op->sendpage ? ksu_wrapper_sendpage : NULL; #endif - p->ops.get_unmapped_area = fp->f_op->get_unmapped_area ? - ksu_wrapper_get_unmapped_area : - NULL; + p->ops.get_unmapped_area = fp->f_op->get_unmapped_area ? ksu_wrapper_get_unmapped_area : NULL; p->ops.check_flags = fp->f_op->check_flags; p->ops.flock = fp->f_op->flock ? ksu_wrapper_flock : NULL; - p->ops.splice_write = - fp->f_op->splice_write ? ksu_wrapper_splice_write : NULL; - p->ops.splice_read = - fp->f_op->splice_read ? ksu_wrapper_splice_read : NULL; + p->ops.splice_write = fp->f_op->splice_write ? ksu_wrapper_splice_write : NULL; + p->ops.splice_read = fp->f_op->splice_read ? ksu_wrapper_splice_read : NULL; p->ops.setlease = fp->f_op->setlease ? ksu_wrapper_setlease : NULL; p->ops.fallocate = fp->f_op->fallocate ? ksu_wrapper_fallocate : NULL; - p->ops.show_fdinfo = - fp->f_op->show_fdinfo ? ksu_wrapper_show_fdinfo : NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0) + p->ops.show_fdinfo = fp->f_op->show_fdinfo ? ksu_wrapper_show_fdinfo : NULL; +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0) - p->ops.copy_file_range = - fp->f_op->copy_file_range ? ksu_wrapper_copy_file_range : NULL; + p->ops.copy_file_range = fp->f_op->copy_file_range ? ksu_wrapper_copy_file_range : NULL; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) - p->ops.remap_file_range = fp->f_op->remap_file_range ? - ksu_wrapper_remap_file_range : - NULL; + p->ops.remap_file_range = fp->f_op->remap_file_range ? ksu_wrapper_remap_file_range : NULL; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) p->ops.fadvise = fp->f_op->fadvise ? ksu_wrapper_fadvise : NULL; #endif - #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) - p->ops.splice_eof = - fp->f_op->splice_eof ? ksu_wrapper_splice_eof : NULL; + p->ops.splice_eof = fp->f_op->splice_eof ? ksu_wrapper_splice_eof : NULL; #endif return p; } -void ksu_delete_file_wrapper(struct ksu_file_wrapper *data) -{ - fput((struct file *)data->orig); +void ksu_delete_file_wrapper(struct ksu_file_wrapper* data) { + fput((struct file*) data->orig); kfree(data); } \ No newline at end of file diff --git a/kernel/file_wrapper.h b/kernel/file_wrapper.h index a3db299c..cf643ef3 100644 --- a/kernel/file_wrapper.h +++ b/kernel/file_wrapper.h @@ -1,5 +1,5 @@ -#ifndef __KSU_H_FILE_WRAPPER -#define __KSU_H_FILE_WRAPPER +#ifndef KSU_FILE_WRAPPER_H +#define KSU_FILE_WRAPPER_H #include #include @@ -12,4 +12,4 @@ struct ksu_file_wrapper { struct ksu_file_wrapper *ksu_create_file_wrapper(struct file *fp); void ksu_delete_file_wrapper(struct ksu_file_wrapper *data); -#endif \ No newline at end of file +#endif // KSU_FILE_WRAPPER_H diff --git a/kernel/include/ksu_hook.h b/kernel/include/ksu_hook.h deleted file mode 100644 index 8d748cc3..00000000 --- a/kernel/include/ksu_hook.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef __KSU_H_KSHOOK -#define __KSU_H_KSHOOK - -#include -#include - -// For sucompat - -int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, - int *flags); - -int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags); - -// For ksud - -int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, - size_t *count_ptr, loff_t **pos); - -// For ksud and sucompat - -int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, - void *envp, int *flags); - -// For volume button -int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, - int *value); - -#endif diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c index 1020581c..a93b3308 100644 --- a/kernel/kernel_compat.c +++ b/kernel/kernel_compat.c @@ -1,14 +1,11 @@ #include #include -#include #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) #include #else #include #endif #include -#include -#include #include "klog.h" // IWYU pragma: keep #include "kernel_compat.h" @@ -17,10 +14,12 @@ #include #include #include +#include extern int install_session_keyring_to_cred(struct cred *, struct key *); struct key *init_session_keyring = NULL; -static inline int install_session_keyring(struct key *keyring) + +static int install_session_keyring(struct key *keyring) { struct cred *new; int ret; @@ -124,117 +123,3 @@ long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, return ret; } #endif - -long ksu_strncpy_from_user_retry(char *dst, const void __user *unsafe_addr, - long count) -{ - long ret; - - ret = ksu_strncpy_from_user_nofault(dst, unsafe_addr, count); - if (likely(ret >= 0)) - return ret; - - // we faulted! fallback to slow path - if (unlikely(!ksu_access_ok(unsafe_addr, count))) { -#ifdef CONFIG_KSU_DEBUG - pr_err("%s: faulted!\n", __func__); -#endif - return -EFAULT; - } - - // why we don't do like how strncpy_from_user_nofault? - ret = strncpy_from_user(dst, unsafe_addr, count); - - if (ret >= count) { - ret = count; - dst[ret - 1] = '\0'; - } else if (likely(ret >= 0)) { - ret++; - } - - return ret; -} - -long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) - return copy_from_user_nofault(dst, src, size); -#else - // https://elixir.bootlin.com/linux/v5.8/source/mm/maccess.c#L205 - long ret = -EFAULT; - mm_segment_t old_fs = get_fs(); - - set_fs(USER_DS); - // tweaked to use ksu_access_ok - if (ksu_access_ok(src, size)) { - pagefault_disable(); - ret = __copy_from_user_inatomic(dst, src, size); - pagefault_enable(); - } - set_fs(old_fs); - - if (ret) - return -EFAULT; - return 0; -#endif -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) - -struct action_cache { - DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR); -#ifdef SECCOMP_ARCH_COMPAT - DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR); -#endif -}; - -struct seccomp_filter { - refcount_t refs; - refcount_t users; - bool log; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) - bool wait_killable_recv; -#endif - struct action_cache cache; - struct seccomp_filter *prev; - struct bpf_prog *prog; - struct notification *notif; - struct mutex notify_lock; - wait_queue_head_t wqh; -}; - -void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr) -{ - if (!filter) { - return; - } - - if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) { - clear_bit(nr, filter->cache.allow_native); - } - -#ifdef SECCOMP_ARCH_COMPAT - if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) { - clear_bit(nr, filter->cache.allow_compat); - } -#endif -} - -void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr) -{ - if (!filter) { - return; - } - - if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) { - set_bit(nr, filter->cache.allow_native); - } - -#ifdef SECCOMP_ARCH_COMPAT - if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) { - set_bit(nr, filter->cache.allow_compat); - } -#endif -} - -#endif diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h index 8b6033dc..9c29b1ac 100644 --- a/kernel/kernel_compat.h +++ b/kernel/kernel_compat.h @@ -3,6 +3,7 @@ #include #include +#include #include "ss/policydb.h" #include "linux/key.h" @@ -27,21 +28,9 @@ #endif #endif -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) && defined(KSU_KPROBE_HOOK) -#define KSU_SHOULD_USE_NEW_TP -#endif - extern long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, long count); -extern long ksu_strncpy_from_user_retry(char *dst, - const void __user *unsafe_addr, - long count); - -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ - defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) -extern struct key *init_session_keyring; -#endif extern struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode); @@ -67,9 +56,9 @@ static long ksu_copy_from_user_retry(void *to, return copy_from_user(to, from, count); } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) -extern void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr); -extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ + defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) +extern struct key *init_session_keyring; #endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) @@ -78,4 +67,18 @@ extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr); #define ksu_access_ok(addr, size) access_ok(VERIFY_READ, addr, size) #endif +// Linux >= 5.7 +// task_work_add (struct, struct, enum) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) +#define ksu_task_work_add(tsk, cb, notify) task_work_add(tsk, cb, notify) +#else +// Linux pre-5.7 +// task_work_add (struct, struct, bool) +#define ksu_task_work_add(tsk, cb, notify) task_work_add(tsk, cb, notify) +// Decoy, so it wouldn't complain. +#ifndef TWA_RESUME +#define TWA_RESUME true +#endif +#endif + #endif diff --git a/kernel/kernel_umount.c b/kernel/kernel_umount.c new file mode 100644 index 00000000..eb8a83ba --- /dev/null +++ b/kernel/kernel_umount.c @@ -0,0 +1,227 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef KSU_HAS_PATH_UMOUNT +#include +#endif + +#include "kernel_umount.h" +#include "klog.h" // IWYU pragma: keep +#include "allowlist.h" +#include "kernel_compat.h" +#include "selinux/selinux.h" +#include "feature.h" +#include "ksud.h" + +static bool ksu_kernel_umount_enabled = true; + +static int kernel_umount_feature_get(u64 *value) +{ + *value = ksu_kernel_umount_enabled ? 1 : 0; + return 0; +} + +static int kernel_umount_feature_set(u64 value) +{ + bool enable = value != 0; + ksu_kernel_umount_enabled = enable; + pr_info("kernel_umount: set to %d\n", enable); + return 0; +} + +static const struct ksu_feature_handler kernel_umount_handler = { + .feature_id = KSU_FEATURE_KERNEL_UMOUNT, + .name = "kernel_umount", + .get_handler = kernel_umount_feature_get, + .set_handler = kernel_umount_feature_set, +}; + +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; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || defined(KSU_HAS_PATH_UMOUNT) +extern int path_umount(struct path *path, int flags); +static void ksu_umount_mnt(const char *__never_use_mnt, struct path *path, int flags) +{ + int err = path_umount(path, flags); + if (err) { + pr_info("umount %s failed: %d\n", path->dentry->d_iname, err); + } +} +#else +static void ksu_sys_umount(const char *mnt, int flags) +{ + char __user *usermnt = (char __user *)mnt; + mm_segment_t old_fs; + + old_fs = get_fs(); + set_fs(KERNEL_DS); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) + ksys_umount(usermnt, flags); +#else + sys_umount(usermnt, flags); // cuz asmlinkage long sys##name +#endif + set_fs(old_fs); +} + +#define ksu_umount_mnt(mnt, __unused, flags) \ + ({ \ + path_put(__unused); \ + ksu_sys_umount(mnt, flags); \ + }) + +#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. + path_put(&path); + return; + } + + // we are only interest in some specific mounts + if (check_mnt && !should_umount(&path)) { + path_put(&path); + return; + } + + ksu_umount_mnt(mnt, &path, flags); +} + +static inline void do_ksu_umount_lists(void) +{ + // fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and + // filter the mountpoint whose target is `/data/adb` + 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); +} + +#ifdef KSU_SHOULD_USE_NEW_TP +struct umount_tw { + struct callback_head cb; + const struct cred *old_cred; +}; + +static void umount_tw_func(struct callback_head *cb) +{ + struct umount_tw *tw = container_of(cb, struct umount_tw, cb); + const struct cred *saved = NULL; + if (tw->old_cred) { + saved = override_creds(tw->old_cred); + } + + do_ksu_umount_lists(); + + if (saved) + revert_creds(saved); + + if (tw->old_cred) + put_cred(tw->old_cred); + + kfree(tw); +} +#endif + +int ksu_handle_umount(uid_t old_uid, uid_t new_uid) +{ + // 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 (!ksu_kernel_umount_enabled) { + return 0; + } + + // FIXME: isolated process which directly forks from zygote is not handled + if (!is_appuid(new_uid)) { + return 0; + } + + if (!ksu_uid_should_umount(new_uid)) { + return 0; + } + + // 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(get_current_cred()); + if (!is_zygote_child) { + pr_info("handle umount ignore non zygote child: %d\n", + current->pid); + return 0; + } + // umount the target mnt + pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid); + +#ifdef KSU_SHOULD_USE_NEW_TP + struct umount_tw *tw; + tw = kzalloc(sizeof(*tw), GFP_ATOMIC); + if (!tw) + return 0; + + tw->old_cred = get_current_cred(); + tw->cb.func = umount_tw_func; + + int err = ksu_task_work_add(current, &tw->cb, TWA_RESUME); + if (err) { + if (tw->old_cred) { + put_cred(tw->old_cred); + } + kfree(tw); + pr_warn("unmount add task_work failed\n"); + } +#else + // Using task work for non-kp context is expansive? + do_ksu_umount_lists(); +#endif + + return 0; +} + +void ksu_kernel_umount_init(void) +{ + if (ksu_register_feature_handler(&kernel_umount_handler)) { + pr_err("Failed to register kernel_umount feature handler\n"); + } +} + +void ksu_kernel_umount_exit(void) +{ + ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT); +} diff --git a/kernel/kernel_umount.h b/kernel/kernel_umount.h new file mode 100644 index 00000000..3f7a426d --- /dev/null +++ b/kernel/kernel_umount.h @@ -0,0 +1,12 @@ +#ifndef __KSU_H_KERNEL_UMOUNT +#define __KSU_H_KERNEL_UMOUNT + +#include + +void ksu_kernel_umount_init(void); +void ksu_kernel_umount_exit(void); + +// Handler function to be called from setresuid hook +int ksu_handle_umount(uid_t old_uid, uid_t new_uid); + +#endif diff --git a/kernel/ksu.c b/kernel/ksu.c index 72bf586c..342a0c18 100644 --- a/kernel/ksu.c +++ b/kernel/ksu.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -9,15 +10,17 @@ #include "allowlist.h" #include "arch.h" -#include "core_hook.h" #include "feature.h" #include "klog.h" // IWYU pragma: keep #include "ksu.h" #include "throne_tracker.h" -#include "sucompat.h" +#include "syscall_hook_manager.h" #include "ksud.h" #include "supercalls.h" +#include "throne_comm.h" +#include "dynamic_manager.h" + static struct workqueue_struct *ksu_workqueue; bool ksu_queue_work(struct work_struct *work) @@ -25,6 +28,17 @@ bool ksu_queue_work(struct work_struct *work) return queue_work(ksu_workqueue, work); } +void sukisu_custom_config_init(void) +{ +} + +void sukisu_custom_config_exit(void) +{ + ksu_uid_exit(); + ksu_throne_comm_exit(); + ksu_dynamic_manager_exit(); +} + extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags); extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, @@ -39,8 +53,10 @@ int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, int __init kernelsu_init(void) { +#ifndef DDK_ENV pr_info("Initialized on: %s (%s) with driver version: %u\n", UTS_RELEASE, UTS_MACHINE, KSU_VERSION); +#endif #ifdef CONFIG_KSU_DEBUG pr_alert("*************************************************************"); @@ -56,7 +72,9 @@ int __init kernelsu_init(void) ksu_supercalls_init(); - ksu_core_init(); + sukisu_custom_config_init(); + + ksu_syscall_hook_manager_init(); ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0); @@ -64,8 +82,6 @@ int __init kernelsu_init(void) ksu_throne_tracker_init(); - ksu_sucompat_init(); - ksu_ksud_init(); #ifdef MODULE @@ -89,10 +105,12 @@ void kernelsu_exit(void) ksu_ksud_exit(); - ksu_sucompat_exit(); + ksu_syscall_hook_manager_exit(); + + sukisu_custom_config_exit(); + + ksu_supercalls_exit(); - ksu_core_exit(); - ksu_feature_exit(); } diff --git a/kernel/ksu.h b/kernel/ksu.h index db14bf92..cf43fbe8 100644 --- a/kernel/ksu.h +++ b/kernel/ksu.h @@ -13,12 +13,6 @@ extern bool ksu_uid_scanner_enabled; #define EVENT_BOOT_COMPLETED 2 #define EVENT_MODULE_MOUNTED 3 -#define KSU_APP_PROFILE_VER 2 -#define KSU_MAX_PACKAGE_NAME 256 -// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups. -#define KSU_MAX_GROUPS 32 -#define KSU_SELINUX_DOMAIN 64 - // SukiSU Ultra kernel su version full strings #ifndef KSU_VERSION_FULL #define KSU_VERSION_FULL "v3.x-00000000@unknown" @@ -34,83 +28,35 @@ extern bool ksu_uid_scanner_enabled; #define UID_SCANNER_OP_CLEAR_ENV 2 struct dynamic_manager_user_config { - unsigned int operation; - unsigned int size; - char hash[65]; + unsigned int operation; + unsigned int size; + char hash[65]; }; struct manager_list_info { - int count; - struct { - uid_t uid; - int signature_index; - } managers[2]; + int count; + struct { + uid_t uid; + int signature_index; + } managers[2]; }; -struct root_profile { - int32_t uid; - int32_t gid; - - int32_t groups_count; - int32_t groups[KSU_MAX_GROUPS]; - - // kernel_cap_t is u32[2] for capabilities v3 - struct { - u64 effective; - u64 permitted; - u64 inheritable; - } capabilities; - - char selinux_domain[KSU_SELINUX_DOMAIN]; - - int32_t namespaces; -}; - -struct non_root_profile { - bool umount_modules; -}; - -struct app_profile { - // It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this. - u32 version; - - // this is usually the package of the app, but can be other value for special apps - char key[KSU_MAX_PACKAGE_NAME]; - int32_t current_uid; - bool allow_su; - - union { - struct { - bool use_default; - char template_name[KSU_MAX_PACKAGE_NAME]; - - struct root_profile profile; - } rp_config; - - struct { - bool use_default; - - struct non_root_profile profile; - } nrp_config; - }; -}; +bool ksu_queue_work(struct work_struct *work); #if 0 static inline int startswith(char *s, char *prefix) { - return strncmp(s, prefix, strlen(prefix)); + return strncmp(s, prefix, strlen(prefix)); } static inline int endswith(const char *s, const char *t) { - size_t slen = strlen(s); - size_t tlen = strlen(t); - if (tlen > slen) - return 1; - return strcmp(s + slen - tlen, t); + size_t slen = strlen(s); + size_t tlen = strlen(t); + if (tlen > slen) + return 1; + return strcmp(s + slen - tlen, t); } #endif -bool ksu_queue_work(struct work_struct *work); - #endif \ No newline at end of file diff --git a/kernel/ksud.c b/kernel/ksud.c index 359b9648..bc7fc7d9 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -21,16 +21,25 @@ #include #include #include +#include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include +#else +#include +#endif +#include "manager.h" #include "allowlist.h" #include "arch.h" +#include "kernel_compat.h" #include "klog.h" // IWYU pragma: keep #include "ksud.h" -#include "kernel_compat.h" #include "selinux/selinux.h" -#include "manager.h" -#include "sucompat.h" +#include "throne_tracker.h" + +bool ksu_module_mounted __read_mostly = false; +bool ksu_boot_completed __read_mostly = false; static const char KERNEL_SU_RC[] = "\n" @@ -59,7 +68,7 @@ static void stop_vfs_read_hook(void); static void stop_execve_hook(void); static void stop_input_hook(void); -#ifdef KSU_KPROBES_HOOK +#ifdef KSU_SHOULD_USE_NEW_TP static struct work_struct stop_vfs_read_work; static struct work_struct stop_execve_hook_work; static struct work_struct stop_input_hook_work; @@ -78,13 +87,12 @@ void on_post_fs_data(void) { static bool done = false; if (done) { - pr_info("%s already done\n", __func__); + pr_info("on_post_fs_data already done\n"); return; } done = true; - pr_info("%s!\n", __func__); + pr_info("on_post_fs_data!\n"); ksu_load_allow_list(); - ksu_mark_running_process(); ksu_observer_init(); // sanity check, this may influence the performance stop_input_hook(); @@ -96,6 +104,43 @@ void on_post_fs_data(void) pr_info("devpts sid: %d\n", ksu_file_sid); } +extern void ext4_unregister_sysfs(struct super_block *sb); +static void nuke_ext4_sysfs(void) +{ + struct path path; + int err = kern_path("/data/adb/modules", 0, &path); + if (err) { + pr_err("nuke path err: %d\n", err); + return; + } + + struct super_block *sb = path.dentry->d_inode->i_sb; + const char *name = sb->s_type->name; + if (strcmp(name, "ext4") != 0) { + pr_info("nuke but module aren't mounted\n"); + path_put(&path); + return; + } + + ext4_unregister_sysfs(sb); + path_put(&path); +} + +void on_module_mounted(void) +{ + pr_info("on_module_mounted!\n"); + ksu_module_mounted = true; + nuke_ext4_sysfs(); +} + +void on_boot_completed(void) +{ + ksu_boot_completed = true; + pr_info("on_boot_completed!\n"); + track_throne(true); +} + +#define MAX_ARG_STRINGS 0x7FFFFFFF struct user_arg_ptr { #ifdef CONFIG_COMPAT bool is_compat; @@ -113,7 +158,9 @@ static void on_post_fs_data_cbfun(struct callback_head *cb) on_post_fs_data(); } -static struct callback_head on_post_fs_data_cb = { .func = on_post_fs_data_cbfun }; +static struct callback_head on_post_fs_data_cb = { + .func = on_post_fs_data_cbfun +}; // since _ksud handler only uses argv and envp for comparisons // this can probably work @@ -207,13 +254,11 @@ first_app_process: rcu_read_lock(); init_task = rcu_dereference(current->real_parent); if (init_task) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 8) - task_work_add(init_task, &on_post_fs_data_cb, TWA_RESUME); -#else - task_work_add(init_task, &on_post_fs_data_cb, true); -#endif + ksu_task_work_add(init_task, &on_post_fs_data_cb, + TWA_RESUME); } rcu_read_unlock(); + stop_execve_hook(); } @@ -313,9 +358,9 @@ static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to) } int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, - size_t *count_ptr, loff_t **pos) + size_t *count_ptr, loff_t **pos) { -#ifndef KSU_KPROBES_HOOK +#ifndef KSU_SHOULD_USE_NEW_TP if (!ksu_vfs_read_hook) { return 0; } @@ -407,7 +452,7 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, } int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, - size_t *count_ptr) + size_t *count_ptr) { struct file *file = fget(fd); if (!file) { @@ -428,7 +473,7 @@ static bool is_volumedown_enough(unsigned int count) int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value) { -#ifndef KSU_KPROBES_HOOK +#ifndef KSU_SHOULD_USE_NEW_TP if (!ksu_input_hook) { return 0; } @@ -470,7 +515,8 @@ bool ksu_is_safe_mode(void) return false; } -#ifdef KSU_KPROBES_HOOK +#ifdef KSU_SHOULD_USE_NEW_TP + static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) { /* @@ -689,7 +735,7 @@ int __maybe_unused ksu_handle_compat_execve_ksud( static void stop_vfs_read_hook(void) { -#ifdef KSU_KPROBES_HOOK +#ifdef KSU_SHOULD_USE_NEW_TP bool ret = schedule_work(&stop_vfs_read_work); pr_info("unregister vfs_read kprobe: %d!\n", ret); #else @@ -700,7 +746,7 @@ static void stop_vfs_read_hook(void) static void stop_execve_hook(void) { -#ifdef KSU_KPROBES_HOOK +#ifdef KSU_SHOULD_USE_NEW_TP bool ret = schedule_work(&stop_execve_hook_work); pr_info("unregister execve kprobe: %d!\n", ret); #else @@ -711,7 +757,7 @@ static void stop_execve_hook(void) static void stop_input_hook(void) { -#ifdef KSU_KPROBES_HOOK +#ifdef KSU_SHOULD_USE_NEW_TP static bool input_hook_stopped = false; if (input_hook_stopped) { return; @@ -731,7 +777,7 @@ static void stop_input_hook(void) // ksud: module support void ksu_ksud_init(void) { -#ifdef KSU_KPROBES_HOOK +#ifdef KSU_SHOULD_USE_NEW_TP int ret; ret = register_kprobe(&execve_kp); @@ -751,7 +797,7 @@ void ksu_ksud_init(void) void ksu_ksud_exit(void) { -#ifdef KSU_KPROBES_HOOK +#ifdef KSU_SHOULD_USE_NEW_TP 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 9a184114..d78bc969 100644 --- a/kernel/ksud.h +++ b/kernel/ksud.h @@ -5,14 +5,18 @@ #define KSUD_PATH "/data/adb/ksud" -void ksu_ksud_init(); -void ksu_ksud_exit(); +void ksu_ksud_init(void); +void ksu_ksud_exit(void); void on_post_fs_data(void); +void on_module_mounted(void); +void on_boot_completed(void); bool ksu_is_safe_mode(void); extern u32 ksu_file_sid; +extern bool ksu_module_mounted; +extern bool ksu_boot_completed; extern bool ksu_execveat_hook __read_mostly; extern int ksu_handle_pre_ksud(const char *filename); diff --git a/kernel/lsm_hooks.c b/kernel/lsm_hooks.c new file mode 100644 index 00000000..44a0131a --- /dev/null +++ b/kernel/lsm_hooks.c @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include + +#include "klog.h" // IWYU pragma: keep +#include "kernel_compat.h" +#include "setuid_hook.h" + +#ifndef KSU_SHOULD_USE_NEW_TP + +#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 int ksu_task_fix_setuid(struct cred *new, const struct cred *old, + int flags) +{ + kuid_t new_uid = new->uid; + kuid_t new_euid = new->euid; + + return ksu_handle_setresuid((uid_t)new_uid.val, (uid_t)new_euid.val, + (uid_t)new_uid.val); +} + +#ifndef DEVPTS_SUPER_MAGIC +#define DEVPTS_SUPER_MAGIC 0x1cd1 +#endif + +extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c + +#ifdef CONFIG_COMPAT +bool ksu_is_compat __read_mostly = false; +#endif + +int ksu_inode_permission(struct inode *inode, int mask) +{ + if (inode && inode->i_sb + && unlikely(inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) { + //pr_info("%s: handling devpts for: %s \n", __func__, current->comm); + __ksu_handle_devpts(inode); + } + return 0; +} + +int ksu_bprm_check(struct linux_binprm *bprm) +{ + char *filename = (char *)bprm->filename; + + if (likely(!ksu_execveat_hook)) + return 0; + +#ifdef CONFIG_COMPAT + static bool compat_check_done __read_mostly = false; + if ( unlikely(!compat_check_done) && unlikely(!strcmp(filename, "/data/adb/ksud")) + && !memcmp(bprm->buf, "\x7f\x45\x4c\x46", 4) ) { + if (bprm->buf[4] == 0x01 ) + ksu_is_compat = true; + + pr_info("%s: %s ELF magic found! ksu_is_compat: %d \n", __func__, filename, ksu_is_compat); + compat_check_done = true; + } +#endif + + ksu_handle_pre_ksud(filename); + + return 0; + +} + +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 + LSM_HOOK_INIT(inode_permission, ksu_inode_permission), +#ifndef KSU_SHOULD_USE_NEW_TP + LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check), +#endif + LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid) +}; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) +static const struct lsm_id ksu_lsmid = { + .name = "ksu", + .id = 912, +}; +#endif + +void ksu_lsm_hook_init(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) + 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 + pr_info("LSM hooks initialized.\n"); +} +#else +void ksu_lsm_hook_init(void) +{ + return; +} +#endif diff --git a/kernel/pkg_observer.c b/kernel/pkg_observer.c index e78d7ba7..1ee6d241 100644 --- a/kernel/pkg_observer.c +++ b/kernel/pkg_observer.c @@ -31,7 +31,7 @@ static KSU_DECL_FSNOTIFY_OPS(ksu_handle_generic_event) if (ksu_fname_len(file_name) == 13 && !memcmp(ksu_fname_arg(file_name), "packages.list", 13)) { pr_info("packages.list detected (mask=%d)\n", mask); - track_throne(); + track_throne(false); } return 0; } diff --git a/kernel/seccomp_cache.c b/kernel/seccomp_cache.c new file mode 100644 index 00000000..04cc55a6 --- /dev/null +++ b/kernel/seccomp_cache.c @@ -0,0 +1,67 @@ +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +#include +#include +#include +#include +#include +#include "klog.h" // IWYU pragma: keep +#include "seccomp_cache.h" + +struct action_cache { + DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR); +#ifdef SECCOMP_ARCH_COMPAT + DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR); +#endif +}; + +struct seccomp_filter { + refcount_t refs; + refcount_t users; + bool log; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) + bool wait_killable_recv; +#endif + struct action_cache cache; + struct seccomp_filter *prev; + struct bpf_prog *prog; + struct notification *notif; + struct mutex notify_lock; + wait_queue_head_t wqh; +}; + +void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr) +{ + if (!filter) { + return; + } + + if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) { + clear_bit(nr, filter->cache.allow_native); + } + +#ifdef SECCOMP_ARCH_COMPAT + if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) { + clear_bit(nr, filter->cache.allow_compat); + } +#endif +} + +void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr) +{ + if (!filter) { + return; + } + + if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) { + set_bit(nr, filter->cache.allow_native); + } + +#ifdef SECCOMP_ARCH_COMPAT + if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) { + set_bit(nr, filter->cache.allow_compat); + } +#endif +} +#endif diff --git a/kernel/seccomp_cache.h b/kernel/seccomp_cache.h new file mode 100644 index 00000000..74a1eb99 --- /dev/null +++ b/kernel/seccomp_cache.h @@ -0,0 +1,12 @@ +#ifndef __KSU_H_SECCOMP_CACHE +#define __KSU_H_SECCOMP_CACHE + +#include +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) +extern void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr); +extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr); +#endif + +#endif diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index b855f7f0..5e79c225 100644 --- a/kernel/selinux/rules.c +++ b/kernel/selinux/rules.c @@ -6,7 +6,7 @@ #include "selinux.h" #include "sepolicy.h" #include "ss/services.h" -#include "linux/lsm_audit.h" +#include "linux/lsm_audit.h" // IWYU pragma: keep #include "xfrm.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) @@ -37,6 +37,7 @@ static struct policydb *get_policydb(void) } static DEFINE_MUTEX(ksu_rules); + void apply_kernelsu_rules(void) { struct policydb *db; @@ -95,7 +96,6 @@ void apply_kernelsu_rules(void) ksu_allow(db, "init", "adb_data_file", "file", ALL); ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289 ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL); - // we need to umount modules in zygote ksu_allow(db, "zygote", "adb_data_file", "dir", "search"); @@ -139,9 +139,6 @@ void apply_kernelsu_rules(void) ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid"); ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill"); - // https://android-review.googlesource.com/c/platform/system/logging/+/3725346 - ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr"); - mutex_unlock(&ksu_rules); } @@ -158,15 +155,15 @@ void apply_kernelsu_rules(void) #define CMD_GENFSCON 9 struct sepol_data { - uint32_t cmd; - uint32_t subcmd; - uint64_t sepol1; - uint64_t sepol2; - uint64_t sepol3; - uint64_t sepol4; - uint64_t sepol5; - uint64_t sepol6; - uint64_t sepol7; + u32 cmd; + u32 subcmd; + u64 sepol1; + u64 sepol2; + u64 sepol3; + u64 sepol4; + u64 sepol5; + u64 sepol6; + u64 sepol7; }; static int get_object(char *buf, char __user *user_object, size_t buf_sz, @@ -185,14 +182,12 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz, return 0; } - #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \ !defined(KSU_COMPAT_USE_SELINUX_STATE) extern int avc_ss_reset(u32 seqno); #else extern int avc_ss_reset(struct selinux_avc *avc, u32 seqno); #endif - // reset avc cache table, otherwise the new rules will not take effect if already denied static void reset_avc_cache(void) { @@ -222,7 +217,7 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) pr_info("SELinux permissive or disabled when handle policy!\n"); } - struct sepol_data data = { 0 }; + struct sepol_data data; if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) { pr_err("sepol: copy sepol_data failed.\n"); return -EINVAL; @@ -236,7 +231,6 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) db = get_policydb(); int ret = -EINVAL; - switch (cmd) { case CMD_NORMAL_PERM: { char src_buf[MAX_SEPOL_LEN]; diff --git a/kernel/selinux/selinux.c b/kernel/selinux/selinux.c index f34e1093..f1db3b42 100644 --- a/kernel/selinux/selinux.c +++ b/kernel/selinux/selinux.c @@ -59,6 +59,7 @@ is_ksu_transition(const struct task_security_struct *old_tsec, } #endif + void setup_selinux(const char *domain) { if (transive_to_domain(domain)) { @@ -106,7 +107,7 @@ static int __security_secid_to_secctx(u32 secid, struct lsm_context *cp) } static void __security_release_secctx(struct lsm_context *cp) { - return security_release_secctx(cp->context, cp->len); + security_release_secctx(cp->context, cp->len); } #else #define __security_secid_to_secctx security_secid_to_secctx @@ -139,7 +140,7 @@ bool is_ksu_domain(void) return is_task_ksu_domain(current_cred()); } -bool is_zygote(const struct cred *cred) +bool is_context(const struct cred *cred, const char *context) { if (!cred) { return false; @@ -154,11 +155,21 @@ bool is_zygote(const struct cred *cred) if (err) { return false; } - result = strncmp("u:r:zygote:s0", ctx.context, ctx.len) == 0; + result = strncmp(context, ctx.context, ctx.len) == 0; __security_release_secctx(&ctx); return result; } +bool is_zygote(const struct cred *cred) +{ + return is_context(cred, "u:r:zygote:s0"); +} + +bool is_init(const struct cred *cred) +{ + return is_context(cred, "u:r:init:s0"); +} + #define KSU_FILE_DOMAIN "u:object_r:ksu_file:s0" u32 ksu_get_ksu_file_sid(void) diff --git a/kernel/selinux/selinux.h b/kernel/selinux/selinux.h index 1384147a..c816575d 100644 --- a/kernel/selinux/selinux.h +++ b/kernel/selinux/selinux.h @@ -3,7 +3,7 @@ #include "linux/types.h" #include "linux/version.h" -#include "linux/sched.h" +#include "linux/cred.h" #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || \ defined(KSU_COMPAT_HAS_SELINUX_STATE) @@ -14,14 +14,16 @@ void setup_selinux(const char *); void setenforce(bool); -bool is_task_ksu_domain(const struct cred *cred); - bool getenforce(void); +bool is_task_ksu_domain(const struct cred *cred); + bool is_ksu_domain(void); bool is_zygote(const struct cred *cred); +bool is_init(const struct cred *cred); + void apply_kernelsu_rules(void); u32 ksu_get_ksu_file_sid(void); diff --git a/kernel/selinux/sepolicy.c b/kernel/selinux/sepolicy.c index acdc45ad..d58fdffd 100644 --- a/kernel/selinux/sepolicy.c +++ b/kernel/selinux/sepolicy.c @@ -355,7 +355,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src, if (datum->u.xperms == NULL) { datum->u.xperms = - (struct avtab_extended_perms *)(kmalloc( + (struct avtab_extended_perms *)(kzalloc( sizeof(xperms), GFP_KERNEL)); if (!datum->u.xperms) { pr_err("alloc xperms failed\n"); @@ -555,7 +555,7 @@ static bool add_filename_trans(struct policydb *db, const char *s, trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans), 1, GFP_ATOMIC); struct filename_trans_key *new_key = - (struct filename_trans_key *)kmalloc(sizeof(*new_key), + (struct filename_trans_key *)kzalloc(sizeof(*new_key), GFP_ATOMIC); *new_key = key; new_key->name = kstrdup(key.name, GFP_ATOMIC); @@ -585,7 +585,7 @@ static bool add_filename_trans(struct policydb *db, const char *s, return false; } struct filename_trans *new_key = - (struct filename_trans *)kmalloc(sizeof(*new_key), + (struct filename_trans *)kzalloc(sizeof(*new_key), GFP_ATOMIC); if (!new_key) { pr_err("add_filename_trans: Failed to alloc new_key\n"); diff --git a/kernel/setuid_hook.c b/kernel/setuid_hook.c new file mode 100644 index 00000000..a0fef06b --- /dev/null +++ b/kernel/setuid_hook.c @@ -0,0 +1,186 @@ +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) +#include +#endif +#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 + +#include "allowlist.h" +#include "setuid_hook.h" +#include "feature.h" +#include "klog.h" // IWYU pragma: keep +#include "manager.h" +#include "selinux/selinux.h" +#include "seccomp_cache.h" +#include "supercalls.h" +#include "syscall_hook_manager.h" +#include "kernel_umount.h" + +static bool ksu_enhanced_security_enabled = false; + +static int enhanced_security_feature_get(u64 *value) +{ + *value = ksu_enhanced_security_enabled ? 1 : 0; + return 0; +} + +static int enhanced_security_feature_set(u64 value) +{ + bool enable = value != 0; + ksu_enhanced_security_enabled = enable; + pr_info("enhanced_security: set to %d\n", enable); + return 0; +} + +static const struct ksu_feature_handler enhanced_security_handler = { + .feature_id = KSU_FEATURE_ENHANCED_SECURITY, + .name = "enhanced_security", + .get_handler = enhanced_security_feature_get, + .set_handler = enhanced_security_feature_set, +}; + +static inline bool is_allow_su(void) +{ + if (is_manager()) { + // we are manager, allow! + return true; + } + return ksu_is_allow_uid_for_current(current_uid().val); +} + +// force_sig kcompat, TODO: move it out of core_hook.c +// https://elixir.bootlin.com/linux/v5.3-rc1/source/kernel/signal.c#L1613 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) +#define __force_sig(sig) force_sig(sig) +#else +#define __force_sig(sig) force_sig(sig, current) +#endif + +extern void disable_seccomp(struct task_struct *tsk); +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; + + if (old_uid != new_uid) + pr_info("handle_setresuid from %d to %d\n", old_uid, new_uid); + + // 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 on private space, see if its possibly the manager + if (new_uid > PER_USER_RANGE && + new_uid % PER_USER_RANGE == ksu_get_manager_uid()) { + ksu_set_manager_uid(new_uid); + } + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) + if (ksu_get_manager_uid() == new_uid) { + pr_info("install fd for ksu manager(uid=%d)\n", new_uid); + ksu_install_fd(); + spin_lock_irq(¤t->sighand->siglock); + ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot); + ksu_set_task_tracepoint_flag(current); + spin_unlock_irq(¤t->sighand->siglock); + return 0; + } + + 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); + } + ksu_set_task_tracepoint_flag(current); + } else { + ksu_clear_task_tracepoint_flag_if_needed(current); + } +#else + if (ksu_is_allow_uid_for_current(new_uid)) { + spin_lock_irq(¤t->sighand->siglock); + disable_seccomp(current); + spin_unlock_irq(¤t->sighand->siglock); + + if (ksu_get_manager_uid() == new_uid) { + pr_info("install fd for ksu manager(uid=%d)\n", + new_uid); + ksu_install_fd(); + } + + return 0; + } +#endif + + // Handle kernel umount + ksu_handle_umount(old_uid, new_uid); + + return 0; +} + +extern void ksu_lsm_hook_init(void); +void ksu_setuid_hook_init(void) +{ + ksu_kernel_umount_init(); + ksu_lsm_hook_init(); // <4.11 + if (ksu_register_feature_handler(&enhanced_security_handler)) { + pr_err("Failed to register enhanced security feature handler\n"); + } +} + +void ksu_setuid_hook_exit(void) +{ + pr_info("ksu_core_exit\n"); + ksu_kernel_umount_exit(); + ksu_unregister_feature_handler(KSU_FEATURE_ENHANCED_SECURITY); +} diff --git a/kernel/setuid_hook.h b/kernel/setuid_hook.h new file mode 100644 index 00000000..0d8f883d --- /dev/null +++ b/kernel/setuid_hook.h @@ -0,0 +1,13 @@ +#ifndef __KSU_H_KSU_SETUID_HOOK +#define __KSU_H_KSU_SETUID_HOOK + +#include +#include + +void ksu_setuid_hook_init(void); +void ksu_setuid_hook_exit(void); + +// Handler functions for hook_manager +int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid); + +#endif diff --git a/kernel/sucompat.c b/kernel/sucompat.c index 90514624..2bbecfc9 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -1,109 +1,35 @@ -#include -#include #include #include #include #include -#include #include #include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) +#include +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) #include #else #include #endif -#include "objsec.h" #include "allowlist.h" -#include "arch.h" #include "feature.h" #include "klog.h" // IWYU pragma: keep #include "ksud.h" -#include "kernel_compat.h" #include "sucompat.h" -#include "core_hook.h" +#include "app_profile.h" +#include "syscall_hook_manager.h" #define SU_PATH "/system/bin/su" #define SH_PATH "/system/bin/sh" -static const char su[] = SU_PATH; -static const char ksud_path[] = KSUD_PATH; bool ksu_su_compat_enabled __read_mostly = true; -#ifdef KSU_SHOULD_USE_NEW_TP - -#include "linux/compiler.h" -#include "linux/printk.h" -#include "selinux/selinux.h" - -#include -#include -#include -#include - -void ksu_mark_running_process(void) -{ - struct task_struct *p, *t; - read_lock(&tasklist_lock); - for_each_process_thread (p, t) { - if (!t->mm) { // only user processes - continue; - } - int uid = task_uid(t).val; - bool ksu_root_process = - uid == 0 && is_task_ksu_domain(get_task_cred(t)); - if (ksu_root_process || ksu_is_allow_uid(uid)) { - ksu_set_task_tracepoint_flag(t); - pr_info("sucompat: mark process: pid:%d, uid: %d, comm:%s\n", - t->pid, uid, t->comm); - } - } - read_unlock(&tasklist_lock); -} - -static void handle_process_mark(bool mark) -{ - struct task_struct *p, *t; - read_lock(&tasklist_lock); - for_each_process_thread (p, t) { - if (mark) - ksu_set_task_tracepoint_flag(t); - else - ksu_clear_task_tracepoint_flag(t); - } - read_unlock(&tasklist_lock); -} - -static void mark_all_process(void) -{ - handle_process_mark(true); - pr_info("sucompat: mark all user process done!\n"); -} - -static void unmark_all_process(void) -{ - handle_process_mark(false); - pr_info("sucompat: unmark all user process done!\n"); -} -#else -void ksu_mark_running_process(void) -{ -} - -static void handle_process_mark(bool mark) -{ -} - -static void mark_all_process(void) -{ -} - -static void unmark_all_process(void) -{ -} -#endif +static const char su[] = SU_PATH; +static const char ksud_path[] = KSUD_PATH; static int su_compat_feature_get(u64 *value) { @@ -114,21 +40,8 @@ static int su_compat_feature_get(u64 *value) static int su_compat_feature_set(u64 value) { bool enable = value != 0; - - if (enable == ksu_su_compat_enabled) { - pr_info("su_compat: no need to change\n"); - return 0; - } - - if (enable) { - ksu_sucompat_enable(); - } else { - ksu_sucompat_disable(); - } - ksu_su_compat_enabled = enable; pr_info("su_compat: set to %d\n", enable); - return 0; } @@ -139,29 +52,30 @@ static const struct ksu_feature_handler su_compat_handler = { .set_handler = su_compat_feature_set, }; -static inline void __user *userspace_stack_buffer(const void *d, size_t len) +static void __user *userspace_stack_buffer(const void *d, size_t len) { - /* To avoid having to mmap a page in userspace, just write below the stack - * pointer. */ + // To avoid having to mmap a page in userspace, just write below the stack + // pointer. char __user *p = (void __user *)current_user_stack_pointer() - len; return copy_to_user(p, d, len) ? NULL : p; } -static inline char __user *sh_user_path(void) +static char __user *sh_user_path(void) { - const char sh_path[] = SH_PATH; + static const char sh_path[] = "/system/bin/sh"; + return userspace_stack_buffer(sh_path, sizeof(sh_path)); } -static inline char __user *ksud_user_path(void) +static char __user *ksud_user_path(void) { return userspace_stack_buffer(ksud_path, sizeof(ksud_path)); } static inline bool __is_su_allowed(const void *ptr_to_check) { -#ifdef KSU_KPROBES_HOOK +#ifndef KSU_SHOULD_USE_NEW_TP if (!ksu_su_compat_enabled) return false; #endif @@ -182,7 +96,7 @@ static int ksu_sucompat_user_common(const char __user **filename_user, char path[sizeof(su)]; // sizeof includes nullterm already! memset(path, 0, sizeof(path)); - ksu_strncpy_from_user_retry(path, *filename_user, sizeof(path)); + ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path)); if (memcmp(path, su, sizeof(su))) return 0; @@ -190,7 +104,7 @@ static int ksu_sucompat_user_common(const char __user **filename_user, if (escalate) { pr_info("%s su found\n", syscall_name); *filename_user = ksud_user_path(); - escape_to_root(); // escalate !! + escape_with_root_profile(); // escalate !! } else { pr_info("%s su->sh!\n", syscall_name); *filename_user = sh_user_path(); @@ -245,14 +159,14 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, pr_info("do_execveat_common su found\n"); memcpy((void *)filename->name, ksud_path, sizeof(ksud_path)); - escape_to_root(); + escape_with_root_profile(); return 0; } int __ksu_handle_devpts(struct inode *inode) { -#ifndef KSU_KPROBES_HOOK +#ifndef KSU_SHOULD_USE_NEW_TP if (!ksu_su_compat_enabled) return 0; #endif @@ -281,193 +195,15 @@ int __ksu_handle_devpts(struct inode *inode) return 0; } -#ifdef KSU_SHOULD_USE_NEW_TP -#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS - -// Tracepoint probe for sys_enter -static void sucompat_sys_enter_handler(void *data, struct pt_regs *regs, - long id) -{ - // Handle newfstatat - if (unlikely(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); - return; - } - - // Handle faccessat - if (unlikely(id == __NR_faccessat)) { - int *dfd = (int *)&PT_REGS_PARM1(regs); - const char __user **filename_user = - (const char __user **)&PT_REGS_PARM2(regs); - int *mode = (int *)&PT_REGS_PARM3(regs); - ksu_handle_faccessat(dfd, filename_user, mode, NULL); - return; - } - - // Handle execve - if (unlikely(id == __NR_execve)) { - const char __user **filename_user = - (const char __user **)&PT_REGS_PARM1(regs); - ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL, - NULL); - return; - } -} - -#endif // CONFIG_HAVE_SYSCALL_TRACEPOINTS - -#ifdef CONFIG_KRETPROBES - -static struct kretprobe *init_kretprobe(const char *name, - kretprobe_handler_t handler) -{ - struct kretprobe *rp = kzalloc(sizeof(struct kretprobe), GFP_KERNEL); - if (!rp) - return NULL; - rp->kp.symbol_name = name; - rp->handler = handler; - rp->data_size = 0; - rp->maxactive = 0; - - int ret = register_kretprobe(rp); - pr_info("sucompat: register_%s kretprobe: %d\n", name, ret); - if (ret) { - kfree(rp); - return NULL; - } - - return rp; -} - -static void destroy_kretprobe(struct kretprobe **rp_ptr) -{ - struct kretprobe *rp = *rp_ptr; - if (!rp) - return; - unregister_kretprobe(rp); - synchronize_rcu(); - kfree(rp); - *rp_ptr = NULL; -} - -static int tracepoint_reg_count = 0; -static DEFINE_SPINLOCK(tracepoint_reg_lock); - -static int syscall_regfunc_handler(struct kretprobe_instance *ri, - struct pt_regs *regs) -{ - unsigned long flags; - spin_lock_irqsave(&tracepoint_reg_lock, flags); - if (tracepoint_reg_count < 1) { - // while install our tracepoint, mark our processes - unmark_all_process(); - ksu_mark_running_process(); - } else { - // while installing other tracepoint, mark all processes - mark_all_process(); - } - tracepoint_reg_count++; - spin_unlock_irqrestore(&tracepoint_reg_lock, flags); - return 0; -} - -static int syscall_unregfunc_handler(struct kretprobe_instance *ri, - struct pt_regs *regs) -{ - unsigned long flags; - spin_lock_irqsave(&tracepoint_reg_lock, flags); - if (tracepoint_reg_count <= 1) { - // while uninstall our tracepoint, unmark all processes - unmark_all_process(); - } else { - // while uninstalling other tracepoint, mark our processes - unmark_all_process(); - ksu_mark_running_process(); - } - tracepoint_reg_count--; - spin_unlock_irqrestore(&tracepoint_reg_lock, flags); - return 0; -} - -static struct kretprobe *syscall_regfunc_rp = NULL; -static struct kretprobe *syscall_unregfunc_rp = NULL; -#endif -#endif - -void ksu_sucompat_enable(void) -{ -#ifdef KSU_SHOULD_USE_NEW_TP - int ret; - pr_info("sucompat: ksu_sucompat_enable called\n"); - -#ifdef CONFIG_KRETPROBES - // Register kretprobe for syscall_regfunc - syscall_regfunc_rp = - init_kretprobe("syscall_regfunc", syscall_regfunc_handler); - // Register kretprobe for syscall_unregfunc - syscall_unregfunc_rp = - init_kretprobe("syscall_unregfunc", syscall_unregfunc_handler); -#endif - -#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS - ret = register_trace_sys_enter(sucompat_sys_enter_handler, NULL); -#ifndef CONFIG_KRETPROBES - unmark_all_process(); - ksu_mark_running_process(); -#endif - if (ret) { - pr_err("sucompat: failed to register sys_enter tracepoint: %d\n", - ret); - } else { - pr_info("sucompat: sys_enter tracepoint registered\n"); - } -#endif -#else - ksu_su_compat_enabled = true; - pr_info("init sucompat\n"); -#endif -} - -void ksu_sucompat_disable(void) -{ -#ifdef KSU_SHOULD_USE_NEW_TP - pr_info("sucompat: ksu_sucompat_disable called\n"); -#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS - unregister_trace_sys_enter(sucompat_sys_enter_handler, NULL); - tracepoint_synchronize_unregister(); - pr_info("sucompat: sys_enter tracepoint unregistered\n"); -#endif - -#ifdef CONFIG_KRETPROBES - destroy_kretprobe(&syscall_regfunc_rp); - destroy_kretprobe(&syscall_unregfunc_rp); -#endif -#else - ksu_su_compat_enabled = false; - pr_info("deinit sucompat\n"); -#endif -} - -// sucompat: permited process can execute 'su' to gain root access. +// sucompat: permitted process can execute 'su' to gain root access. void ksu_sucompat_init(void) { if (ksu_register_feature_handler(&su_compat_handler)) { pr_err("Failed to register su_compat feature handler\n"); } - - if (ksu_su_compat_enabled) { - ksu_sucompat_enable(); - } } void ksu_sucompat_exit(void) { - if (ksu_su_compat_enabled) { - ksu_sucompat_disable(); - } ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT); } diff --git a/kernel/sucompat.h b/kernel/sucompat.h index 149aab7a..27e39854 100644 --- a/kernel/sucompat.h +++ b/kernel/sucompat.h @@ -1,49 +1,18 @@ #ifndef __KSU_H_SUCOMPAT #define __KSU_H_SUCOMPAT -#include -#include +#include extern bool ksu_su_compat_enabled; void ksu_sucompat_init(void); void ksu_sucompat_exit(void); -void ksu_sucompat_enable(void); -void ksu_sucompat_disable(void); - -void ksu_mark_running_process(void); - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) && defined(KSU_KPROBE_HOOK) - -#include - -static inline void ksu_set_task_tracepoint_flag(struct task_struct *t) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) - set_task_syscall_work(t, SYSCALL_TRACEPOINT); -#else - set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT); -#endif -} - -static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t) -{ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) - clear_task_syscall_work(t, SYSCALL_TRACEPOINT); -#else - clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT); -#endif -} -#else -static inline void ksu_set_task_tracepoint_flag(struct task_struct *t) -{ - return; -} - -static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t) -{ - return; -} -#endif +// Handler functions exported for hook_manager +int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, + int *__unused_flags); +int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags); +int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user, + void *__never_use_argv, void *__never_use_envp, + int *__never_use_flags); #endif diff --git a/kernel/supercalls.c b/kernel/supercalls.c index e62b8788..b56d9297 100644 --- a/kernel/supercalls.c +++ b/kernel/supercalls.c @@ -4,23 +4,26 @@ #include #include #include +#include #include #include #include +#include +#include +#include #include #include +#include "arch.h" #include "allowlist.h" #include "feature.h" #include "klog.h" // IWYU pragma: keep -#include "ksu.h" #include "ksud.h" #include "manager.h" #include "selinux/selinux.h" -#include "core_hook.h" #include "objsec.h" #include "file_wrapper.h" -#include "kernel_compat.h" +#include "syscall_hook_manager.h" #include "throne_comm.h" #include "dynamic_manager.h" @@ -30,328 +33,325 @@ bool ksu_uid_scanner_enabled = false; // Permission check functions bool only_manager(void) { - return is_manager(); + return is_manager(); } bool only_root(void) { - return current_uid().val == 0; + return current_uid().val == 0; } bool manager_or_root(void) { - return current_uid().val == 0 || is_manager(); + return current_uid().val == 0 || is_manager(); } bool always_allow(void) { - return true; // No permission check + return true; // No permission check } bool allowed_for_su(void) { - bool is_allowed = - is_manager() || ksu_is_allow_uid_for_current(current_uid().val); + bool is_allowed = is_manager() || ksu_is_allow_uid_for_current(current_uid().val); return is_allowed; } static void init_uid_scanner(void) { - ksu_uid_init(); - do_load_throne_state(NULL); - - if (ksu_uid_scanner_enabled) { - int ret = ksu_throne_comm_init(); - if (ret != 0) { - pr_err("Failed to initialize throne communication: %d\n", ret); - } - } + ksu_uid_init(); + do_load_throne_state(NULL); + + if (ksu_uid_scanner_enabled) { + int ret = ksu_throne_comm_init(); + if (ret != 0) { + pr_err("Failed to initialize throne communication: %d\n", ret); + } + } } static int do_grant_root(void __user *arg) { - // we already check uid above on allowed_for_su() + // we already check uid above on allowed_for_su() - pr_info("allow root for: %d\n", current_uid().val); - escape_to_root(); + pr_info("allow root for: %d\n", current_uid().val); + escape_with_root_profile(); - return 0; + return 0; } static int do_get_info(void __user *arg) { - struct ksu_get_info_cmd cmd = {.version = KERNEL_SU_VERSION, .flags = 0}; + struct ksu_get_info_cmd cmd = {.version = KERNEL_SU_VERSION, .flags = 0}; #ifdef MODULE - cmd.flags |= 0x1; + cmd.flags |= 0x1; #endif - if (is_manager()) { - cmd.flags |= 0x2; - } - cmd.features = KSU_FEATURE_MAX; + if (is_manager()) { + cmd.flags |= 0x2; + } + cmd.features = KSU_FEATURE_MAX; - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("get_version: copy_to_user failed\n"); - return -EFAULT; - } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_version: copy_to_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } static int do_report_event(void __user *arg) { - struct ksu_report_event_cmd cmd; + struct ksu_report_event_cmd cmd; - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - return -EFAULT; - } + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } - switch (cmd.event) { - 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(); - init_uid_scanner(); - ksu_dynamic_manager_init(); - } - 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"); - nuke_ext4_sysfs(); - break; - } - default: - break; - } + switch (cmd.event) { + 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(); + init_uid_scanner(); + ksu_dynamic_manager_init(); + } + 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"); + on_boot_completed(); + } + break; + } + case EVENT_MODULE_MOUNTED: { + pr_info("module mounted!\n"); + on_module_mounted(); + break; + } + default: + break; + } - return 0; + return 0; } static int do_set_sepolicy(void __user *arg) { - struct ksu_set_sepolicy_cmd cmd; + struct ksu_set_sepolicy_cmd cmd; - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - return -EFAULT; - } + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } - return handle_sepolicy(cmd.cmd, (void __user *)cmd.arg); + return handle_sepolicy(cmd.cmd, (void __user *)cmd.arg); } static int do_check_safemode(void __user *arg) { - struct ksu_check_safemode_cmd cmd; + struct ksu_check_safemode_cmd cmd; - cmd.in_safe_mode = ksu_is_safe_mode(); + cmd.in_safe_mode = ksu_is_safe_mode(); - if (cmd.in_safe_mode) { - pr_warn("safemode enabled!\n"); - } + if (cmd.in_safe_mode) { + pr_warn("safemode enabled!\n"); + } - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("check_safemode: copy_to_user failed\n"); - return -EFAULT; - } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("check_safemode: copy_to_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } static int do_get_allow_list(void __user *arg) { - struct ksu_get_allow_list_cmd cmd; + struct ksu_get_allow_list_cmd cmd; - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - return -EFAULT; - } + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } - bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, true); + bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, true); - if (!success) { - return -EFAULT; - } + if (!success) { + return -EFAULT; + } - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("get_allow_list: copy_to_user failed\n"); - return -EFAULT; - } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_allow_list: copy_to_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } static int do_get_deny_list(void __user *arg) { - struct ksu_get_allow_list_cmd cmd; + struct ksu_get_allow_list_cmd cmd; - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - return -EFAULT; - } + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } - bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, false); + bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, false); - if (!success) { - return -EFAULT; - } + if (!success) { + return -EFAULT; + } - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("get_deny_list: copy_to_user failed\n"); - return -EFAULT; - } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_deny_list: copy_to_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } static int do_uid_granted_root(void __user *arg) { - struct ksu_uid_granted_root_cmd cmd; + struct ksu_uid_granted_root_cmd cmd; - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - return -EFAULT; - } + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } cmd.granted = ksu_is_allow_uid_for_current(cmd.uid); - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("uid_granted_root: copy_to_user failed\n"); - return -EFAULT; - } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("uid_granted_root: copy_to_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } static int do_uid_should_umount(void __user *arg) { - struct ksu_uid_should_umount_cmd cmd; + struct ksu_uid_should_umount_cmd cmd; - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - return -EFAULT; - } + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + return -EFAULT; + } - cmd.should_umount = ksu_uid_should_umount(cmd.uid); + cmd.should_umount = ksu_uid_should_umount(cmd.uid); - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("uid_should_umount: copy_to_user failed\n"); - return -EFAULT; - } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("uid_should_umount: copy_to_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } static int do_get_manager_uid(void __user *arg) { - struct ksu_get_manager_uid_cmd cmd; + struct ksu_get_manager_uid_cmd cmd; - cmd.uid = ksu_get_manager_uid(); + cmd.uid = ksu_get_manager_uid(); - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("get_manager_uid: copy_to_user failed\n"); - return -EFAULT; - } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_manager_uid: copy_to_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } static int do_get_app_profile(void __user *arg) { - struct ksu_get_app_profile_cmd cmd; + struct ksu_get_app_profile_cmd cmd; - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - pr_err("get_app_profile: copy_from_user failed\n"); - return -EFAULT; - } + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("get_app_profile: copy_from_user failed\n"); + return -EFAULT; + } - if (!ksu_get_app_profile(&cmd.profile)) { - return -ENOENT; - } + if (!ksu_get_app_profile(&cmd.profile)) { + return -ENOENT; + } - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("get_app_profile: copy_to_user failed\n"); - return -EFAULT; - } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_app_profile: copy_to_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } static int do_set_app_profile(void __user *arg) { - struct ksu_set_app_profile_cmd cmd; + struct ksu_set_app_profile_cmd cmd; - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - pr_err("set_app_profile: copy_from_user failed\n"); - return -EFAULT; - } + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("set_app_profile: copy_from_user failed\n"); + return -EFAULT; + } - if (!ksu_set_app_profile(&cmd.profile, true)) { - return -EFAULT; - } + if (!ksu_set_app_profile(&cmd.profile, true)) { + return -EFAULT; + } - return 0; + return 0; } static int do_get_feature(void __user *arg) { - struct ksu_get_feature_cmd cmd; - bool supported; - int ret; + struct ksu_get_feature_cmd cmd; + bool supported; + int ret; - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - pr_err("get_feature: copy_from_user failed\n"); - return -EFAULT; - } + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("get_feature: copy_from_user failed\n"); + return -EFAULT; + } - ret = ksu_get_feature(cmd.feature_id, &cmd.value, &supported); - cmd.supported = supported ? 1 : 0; + ret = ksu_get_feature(cmd.feature_id, &cmd.value, &supported); + cmd.supported = supported ? 1 : 0; - if (ret && supported) { - pr_err("get_feature: failed for feature %u: %d\n", cmd.feature_id, ret); - return ret; - } + if (ret && supported) { + pr_err("get_feature: failed for feature %u: %d\n", cmd.feature_id, ret); + return ret; + } - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("get_feature: copy_to_user failed\n"); - return -EFAULT; - } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_feature: copy_to_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } static int do_set_feature(void __user *arg) { - struct ksu_set_feature_cmd cmd; - int ret; + struct ksu_set_feature_cmd cmd; + int ret; - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - pr_err("set_feature: copy_from_user failed\n"); - return -EFAULT; - } + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("set_feature: copy_from_user failed\n"); + return -EFAULT; + } - ret = ksu_set_feature(cmd.feature_id, cmd.value); - if (ret) { - pr_err("set_feature: failed for feature %u: %d\n", cmd.feature_id, ret); - return ret; - } + ret = ksu_set_feature(cmd.feature_id, cmd.value); + if (ret) { + pr_err("set_feature: failed for feature %u: %d\n", cmd.feature_id, ret); + return ret; + } - return 0; + return 0; } -static int do_get_wrapper_fd(void __user *arg) -{ +static int do_get_wrapper_fd(void __user *arg) { if (!ksu_file_sid) { return -EINVAL; } - const char *anon_name = "[ksu_fdwrapper]"; struct ksu_get_wrapper_fd_cmd cmd; int ret; @@ -360,7 +360,7 @@ static int do_get_wrapper_fd(void __user *arg) return -EFAULT; } - struct file *f = fget(cmd.fd); + struct file* f = fget(cmd.fd); if (!f) { return -EBADF; } @@ -373,37 +373,26 @@ static int do_get_wrapper_fd(void __user *arg) #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) #define getfd_secure anon_inode_create_getfd -#elif defined(KSU_HAS_GETFD_SECURE) +#else #define getfd_secure anon_inode_getfd_secure #endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) || defined(KSU_HAS_GETFD_SECURE) - ret = getfd_secure(anon_name, &data->ops, data, f->f_flags, NULL); -#else - ret = anon_inode_getfd(anon_name, &data->ops, data, f->f_flags); -#endif - + ret = getfd_secure("[ksu_fdwrapper]", &data->ops, data, f->f_flags, NULL); if (ret < 0) { pr_err("ksu_fdwrapper: getfd failed: %d\n", ret); goto put_wrapper_data; } + struct file* pf = fget(ret); - struct file *pf = fget(ret); - struct inode *wrapper_inode = file_inode(pf); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || \ - defined(KSU_OPTIONAL_SELINUX_INODE) + struct inode* wrapper_inode = file_inode(pf); + // copy original inode mode + wrapper_inode->i_mode = file_inode(f)->i_mode; struct inode_security_struct *sec = selinux_inode(wrapper_inode); -#else - struct inode_security_struct *sec = - (struct inode_security_struct *)wrapper_inode->i_security; -#endif if (sec) { sec->sid = ksu_file_sid; } fput(pf); goto put_orig_file; - put_wrapper_data: ksu_delete_file_wrapper(data); put_orig_file: @@ -412,273 +401,449 @@ put_orig_file: return ret; } +static int do_manage_mark(void __user *arg) +{ + struct ksu_manage_mark_cmd cmd; + int ret = 0; + + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("manage_mark: copy_from_user failed\n"); + return -EFAULT; + } + + switch (cmd.operation) { + case KSU_MARK_GET: { + // Get task mark status + ret = ksu_get_task_mark(cmd.pid); + if (ret < 0) { + pr_err("manage_mark: get failed for pid %d: %d\n", cmd.pid, ret); + return ret; + } + cmd.result = (u32)ret; + break; + } + case KSU_MARK_MARK: { + if (cmd.pid == 0) { + ksu_mark_all_process(); + } else { + ret = ksu_set_task_mark(cmd.pid, true); + if (ret < 0) { + pr_err("manage_mark: set_mark failed for pid %d: %d\n", cmd.pid, + ret); + return ret; + } + } + break; + } + case KSU_MARK_UNMARK: { + if (cmd.pid == 0) { + ksu_unmark_all_process(); + } else { + ret = ksu_set_task_mark(cmd.pid, false); + if (ret < 0) { + pr_err("manage_mark: set_unmark failed for pid %d: %d\n", + cmd.pid, ret); + return ret; + } + } + break; + } + case KSU_MARK_REFRESH: { + ksu_mark_running_process(); + pr_info("manage_mark: refreshed running processes\n"); + break; + } + default: { + pr_err("manage_mark: invalid operation %u\n", cmd.operation); + return -EINVAL; + } + } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("manage_mark: copy_to_user failed\n"); + return -EFAULT; + } + + return 0; +} + // 100. GET_FULL_VERSION - Get full version string static int do_get_full_version(void __user *arg) { - struct ksu_get_full_version_cmd cmd = {0}; + struct ksu_get_full_version_cmd cmd = {0}; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) - strscpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full)); + strscpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full)); #else - strlcpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full)); + strlcpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full)); #endif - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("get_full_version: copy_to_user failed\n"); - return -EFAULT; - } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_full_version: copy_to_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } // 101. HOOK_TYPE - Get hook type static int do_get_hook_type(void __user *arg) { - struct ksu_hook_type_cmd cmd = {0}; - const char *type = "Unknown"; + struct ksu_hook_type_cmd cmd = {0}; + const char *type = "Tracepoint"; -#if defined(KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK) - type = "Tracepoint"; -#elif defined(KSU_MANUAL_HOOK) - type = "Manual"; +#if defined(CONFIG_KSU_MANUAL_HOOK) + type = "Manual"; #endif + #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) - strscpy(cmd.hook_type, type, sizeof(cmd.hook_type)); + strscpy(cmd.hook_type, type, sizeof(cmd.hook_type)); #else - strlcpy(cmd.hook_type, type, sizeof(cmd.hook_type)); + strlcpy(cmd.hook_type, type, sizeof(cmd.hook_type)); #endif - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("get_hook_type: copy_to_user failed\n"); - return -EFAULT; - } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_hook_type: copy_to_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } // 102. ENABLE_KPM - Check if KPM is enabled static int do_enable_kpm(void __user *arg) { - struct ksu_enable_kpm_cmd cmd; - - cmd.enabled = IS_ENABLED(CONFIG_KPM); + struct ksu_enable_kpm_cmd cmd; + + cmd.enabled = IS_ENABLED(CONFIG_KPM); - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("enable_kpm: copy_to_user failed\n"); - return -EFAULT; - } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("enable_kpm: copy_to_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } static int do_dynamic_manager(void __user *arg) { - struct ksu_dynamic_manager_cmd cmd; + struct ksu_dynamic_manager_cmd cmd; - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - pr_err("dynamic_manager: copy_from_user failed\n"); - return -EFAULT; - } + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("dynamic_manager: copy_from_user failed\n"); + return -EFAULT; + } - int ret = ksu_handle_dynamic_manager(&cmd.config); - if (ret) - return ret; + int ret = ksu_handle_dynamic_manager(&cmd.config); + if (ret) + return ret; - if (cmd.config.operation == DYNAMIC_MANAGER_OP_GET && - copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("dynamic_manager: copy_to_user failed\n"); - return -EFAULT; - } + if (cmd.config.operation == DYNAMIC_MANAGER_OP_GET && + copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("dynamic_manager: copy_to_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } static int do_get_managers(void __user *arg) { - struct ksu_get_managers_cmd cmd; + struct ksu_get_managers_cmd cmd; - int ret = ksu_get_active_managers(&cmd.manager_info); - if (ret) - return ret; + int ret = ksu_get_active_managers(&cmd.manager_info); + if (ret) + return ret; - if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("get_managers: copy_from_user failed\n"); - return -EFAULT; - } + if (copy_to_user(arg, &cmd, sizeof(cmd))) { + pr_err("get_managers: copy_from_user failed\n"); + return -EFAULT; + } - return 0; + return 0; } static int do_enable_uid_scanner(void __user *arg) { - struct ksu_enable_uid_scanner_cmd cmd; + struct ksu_enable_uid_scanner_cmd cmd; - if (copy_from_user(&cmd, arg, sizeof(cmd))) { - pr_err("enable_uid_scanner: copy_from_user failed\n"); - return -EFAULT; - } + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("enable_uid_scanner: copy_from_user failed\n"); + return -EFAULT; + } - switch (cmd.operation) { - case UID_SCANNER_OP_GET_STATUS: { - bool status = ksu_uid_scanner_enabled; - if (copy_to_user((void __user *)cmd.status_ptr, &status, sizeof(status))) { - pr_err("enable_uid_scanner: copy status failed\n"); - return -EFAULT; - } - break; - } - case UID_SCANNER_OP_TOGGLE: { - bool enabled = cmd.enabled; + switch (cmd.operation) { + case UID_SCANNER_OP_GET_STATUS: { + bool status = ksu_uid_scanner_enabled; + if (copy_to_user((void __user *)cmd.status_ptr, &status, sizeof(status))) { + pr_err("enable_uid_scanner: copy status failed\n"); + return -EFAULT; + } + break; + } + case UID_SCANNER_OP_TOGGLE: { + bool enabled = cmd.enabled; - if (enabled == ksu_uid_scanner_enabled) { - pr_info("enable_uid_scanner: no need to change, already %s\n", - enabled ? "enabled" : "disabled"); - break; - } + if (enabled == ksu_uid_scanner_enabled) { + pr_info("enable_uid_scanner: no need to change, already %s\n", + enabled ? "enabled" : "disabled"); + break; + } - if (enabled) { - // Enable UID scanner - int ret = ksu_throne_comm_init(); - if (ret != 0) { - pr_err("enable_uid_scanner: failed to initialize: %d\n", ret); - return -EFAULT; - } - pr_info("enable_uid_scanner: enabled\n"); - } else { - // Disable UID scanner - ksu_throne_comm_exit(); - pr_info("enable_uid_scanner: disabled\n"); - } + if (enabled) { + // Enable UID scanner + int ret = ksu_throne_comm_init(); + if (ret != 0) { + pr_err("enable_uid_scanner: failed to initialize: %d\n", ret); + return -EFAULT; + } + pr_info("enable_uid_scanner: enabled\n"); + } else { + // Disable UID scanner + ksu_throne_comm_exit(); + pr_info("enable_uid_scanner: disabled\n"); + } - ksu_uid_scanner_enabled = enabled; - ksu_throne_comm_save_state(); - break; - } - case UID_SCANNER_OP_CLEAR_ENV: { - // Clear environment (force exit) - ksu_throne_comm_exit(); - ksu_uid_scanner_enabled = false; - ksu_throne_comm_save_state(); - pr_info("enable_uid_scanner: environment cleared\n"); - break; - } - default: - pr_err("enable_uid_scanner: invalid operation\n"); - return -EINVAL; - } + ksu_uid_scanner_enabled = enabled; + ksu_throne_comm_save_state(); + break; + } + case UID_SCANNER_OP_CLEAR_ENV: { + // Clear environment (force exit) + ksu_throne_comm_exit(); + ksu_uid_scanner_enabled = false; + ksu_throne_comm_save_state(); + pr_info("enable_uid_scanner: environment cleared\n"); + break; + } + default: + pr_err("enable_uid_scanner: invalid operation\n"); + return -EINVAL; + } - return 0; + return 0; } // IOCTL handlers mapping table static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = { - { .cmd = KSU_IOCTL_GRANT_ROOT, .name = "GRANT_ROOT", .handler = do_grant_root, .perm_check = allowed_for_su }, - { .cmd = KSU_IOCTL_GET_INFO, .name = "GET_INFO", .handler = do_get_info, .perm_check = always_allow }, - { .cmd = KSU_IOCTL_REPORT_EVENT, .name = "REPORT_EVENT", .handler = do_report_event, .perm_check = only_root }, - { .cmd = KSU_IOCTL_SET_SEPOLICY, .name = "SET_SEPOLICY", .handler = do_set_sepolicy, .perm_check = only_root }, - { .cmd = KSU_IOCTL_CHECK_SAFEMODE, .name = "CHECK_SAFEMODE", .handler = do_check_safemode, .perm_check = always_allow }, - { .cmd = KSU_IOCTL_GET_ALLOW_LIST, .name = "GET_ALLOW_LIST", .handler = do_get_allow_list, .perm_check = manager_or_root }, - { .cmd = KSU_IOCTL_GET_DENY_LIST, .name = "GET_DENY_LIST", .handler = do_get_deny_list, .perm_check = manager_or_root }, - { .cmd = KSU_IOCTL_UID_GRANTED_ROOT, .name = "UID_GRANTED_ROOT", .handler = do_uid_granted_root, .perm_check = manager_or_root }, - { .cmd = KSU_IOCTL_UID_SHOULD_UMOUNT, .name = "UID_SHOULD_UMOUNT", .handler = do_uid_should_umount, .perm_check = manager_or_root }, - { .cmd = KSU_IOCTL_GET_MANAGER_UID, .name = "GET_MANAGER_UID", .handler = do_get_manager_uid, .perm_check = manager_or_root }, - { .cmd = KSU_IOCTL_GET_APP_PROFILE, .name = "GET_APP_PROFILE", .handler = do_get_app_profile, .perm_check = only_manager }, - { .cmd = KSU_IOCTL_SET_APP_PROFILE, .name = "SET_APP_PROFILE", .handler = do_set_app_profile, .perm_check = only_manager }, - { .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root }, - { .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root }, - { .cmd = KSU_IOCTL_PROXY_FILE, .name = "PROXY_FILE", .handler = do_get_wrapper_fd, .perm_check = manager_or_root }, - { .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow}, - { .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root}, - { .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root}, - { .cmd = KSU_IOCTL_DYNAMIC_MANAGER, .name = "SET_DYNAMIC_MANAGER", .handler = do_dynamic_manager, .perm_check = manager_or_root}, - { .cmd = KSU_IOCTL_GET_MANAGERS, .name = "GET_MANAGERS", .handler = do_get_managers, .perm_check = manager_or_root}, - { .cmd = KSU_IOCTL_ENABLE_UID_SCANNER, .name = "SET_ENABLE_UID_SCANNER", .handler = do_enable_uid_scanner, .perm_check = manager_or_root}, + { .cmd = KSU_IOCTL_GRANT_ROOT, .name = "GRANT_ROOT", .handler = do_grant_root, .perm_check = allowed_for_su }, + { .cmd = KSU_IOCTL_GET_INFO, .name = "GET_INFO", .handler = do_get_info, .perm_check = always_allow }, + { .cmd = KSU_IOCTL_REPORT_EVENT, .name = "REPORT_EVENT", .handler = do_report_event, .perm_check = only_root }, + { .cmd = KSU_IOCTL_SET_SEPOLICY, .name = "SET_SEPOLICY", .handler = do_set_sepolicy, .perm_check = only_root }, + { .cmd = KSU_IOCTL_CHECK_SAFEMODE, .name = "CHECK_SAFEMODE", .handler = do_check_safemode, .perm_check = always_allow }, + { .cmd = KSU_IOCTL_GET_ALLOW_LIST, .name = "GET_ALLOW_LIST", .handler = do_get_allow_list, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_DENY_LIST, .name = "GET_DENY_LIST", .handler = do_get_deny_list, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_UID_GRANTED_ROOT, .name = "UID_GRANTED_ROOT", .handler = do_uid_granted_root, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_UID_SHOULD_UMOUNT, .name = "UID_SHOULD_UMOUNT", .handler = do_uid_should_umount, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_MANAGER_UID, .name = "GET_MANAGER_UID", .handler = do_get_manager_uid, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_APP_PROFILE, .name = "GET_APP_PROFILE", .handler = do_get_app_profile, .perm_check = only_manager }, + { .cmd = KSU_IOCTL_SET_APP_PROFILE, .name = "SET_APP_PROFILE", .handler = do_set_app_profile, .perm_check = only_manager }, + { .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_WRAPPER_FD, .name = "GET_WRAPPER_FD", .handler = do_get_wrapper_fd, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_MANAGE_MARK, .name = "MANAGE_MARK", .handler = do_manage_mark, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow}, + { .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root}, + { .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root}, + { .cmd = KSU_IOCTL_DYNAMIC_MANAGER, .name = "SET_DYNAMIC_MANAGER", .handler = do_dynamic_manager, .perm_check = manager_or_root}, + { .cmd = KSU_IOCTL_GET_MANAGERS, .name = "GET_MANAGERS", .handler = do_get_managers, .perm_check = manager_or_root}, + { .cmd = KSU_IOCTL_ENABLE_UID_SCANNER, .name = "SET_ENABLE_UID_SCANNER", .handler = do_enable_uid_scanner, .perm_check = manager_or_root}, #ifdef CONFIG_KPM - { .cmd = KSU_IOCTL_KPM, .name = "KPM_OPERATION", .handler = do_kpm, .perm_check = manager_or_root}, + { .cmd = KSU_IOCTL_KPM, .name = "KPM_OPERATION", .handler = do_kpm, .perm_check = manager_or_root}, #endif - { .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL} // Sentine + { .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL} // Sentine }; +#ifdef KSU_SHOULD_USE_NEW_TP +struct ksu_install_fd_tw { + struct callback_head cb; + int __user *outp; +}; + +static void ksu_install_fd_tw_func(struct callback_head *cb) +{ + struct ksu_install_fd_tw *tw = + container_of(cb, struct ksu_install_fd_tw, cb); + int fd = ksu_install_fd(); + pr_info("[%d] install ksu fd: %d\n", current->pid, fd); + + if (copy_to_user(tw->outp, &fd, sizeof(fd))) { + pr_err("install ksu fd reply err\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + close_fd(fd); +#else + ksys_close(fd); +#endif + } + + kfree(tw); +} + +static int reboot_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + int magic1 = (int)PT_REGS_PARM1(real_regs); + int magic2 = (int)PT_REGS_PARM2(real_regs); + unsigned long arg4; + + // Check if this is a request to install KSU fd + if (magic1 == KSU_INSTALL_MAGIC1 && magic2 == KSU_INSTALL_MAGIC2) { + struct ksu_install_fd_tw *tw; + + arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs); + + tw = kzalloc(sizeof(*tw), GFP_ATOMIC); + if (!tw) + return 0; + + tw->outp = (int __user *)arg4; + tw->cb.func = ksu_install_fd_tw_func; + + if (ksu_task_work_add(current, &tw->cb, TWA_RESUME)) { + kfree(tw); + pr_warn("install fd add task_work failed\n"); + } + } + + return 0; +} + +static struct kprobe reboot_kp = { + .symbol_name = REBOOT_SYMBOL, + .pre_handler = reboot_handler_pre, +}; +#else +int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd, + void __user **arg) +{ + if (magic1 != KSU_INSTALL_MAGIC1) + return 0; + +#ifdef CONFIG_KSU_DEBUG + pr_info("sys_reboot: intercepted call! magic: 0x%x id: %d\n", magic1, + magic2); +#endif + + // Check if this is a request to install KSU fd + if (magic2 == KSU_INSTALL_MAGIC2) { + int fd = ksu_install_fd(); + // downstream: dereference all arg usage! + if (copy_to_user((void __user *)*arg, &fd, sizeof(fd))) { + pr_err("install ksu fd reply err\n"); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + close_fd(fd); +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) + ksys_close(fd); +#else + sys_close(fd); +#endif + } + return 0; + } + + return 0; +} +#endif + void ksu_supercalls_init(void) { - int i; + int i; - pr_info("KernelSU IOCTL Commands:\n"); - 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); - } + pr_info("KernelSU IOCTL Commands:\n"); + 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); + } +#ifdef KSU_SHOULD_USE_NEW_TP + int rc = register_kprobe(&reboot_kp); + if (rc) { + pr_err("reboot kprobe failed: %d\n", rc); + } else { + pr_info("reboot kprobe registered successfully\n"); + } +#endif +} + +void ksu_supercalls_exit(void) +{ +#ifdef KSU_SHOULD_USE_NEW_TP + unregister_kprobe(&reboot_kp); +#endif } // IOCTL dispatcher -static long anon_ksu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) +static long anon_ksu_ioctl(struct file *filp, unsigned int cmd, + unsigned long arg) { - void __user *argp = (void __user *)arg; - int i; + void __user *argp = (void __user *)arg; + int i; #ifdef CONFIG_KSU_DEBUG - pr_info("ksu ioctl: cmd=0x%x from uid=%d\n", cmd, current_uid().val); + pr_info("ksu ioctl: cmd=0x%x from uid=%d\n", cmd, current_uid().val); #endif - for (i = 0; ksu_ioctl_handlers[i].handler; i++) { - if (cmd == ksu_ioctl_handlers[i].cmd) { - // Check permission first - if (ksu_ioctl_handlers[i].perm_check && - !ksu_ioctl_handlers[i].perm_check()) { - pr_warn("ksu ioctl: permission denied for cmd=0x%x uid=%d\n", - cmd, current_uid().val); - return -EPERM; - } - // Execute handler - int ret = ksu_ioctl_handlers[i].handler(argp); - return ret; - } - } + for (i = 0; ksu_ioctl_handlers[i].handler; i++) { + if (cmd == ksu_ioctl_handlers[i].cmd) { + // Check permission first + if (ksu_ioctl_handlers[i].perm_check && + !ksu_ioctl_handlers[i].perm_check()) { + pr_warn("ksu ioctl: permission denied for cmd=0x%x uid=%d\n", + cmd, current_uid().val); + return -EPERM; + } + // Execute handler + return ksu_ioctl_handlers[i].handler(argp); + } + } - pr_warn("ksu ioctl: unsupported command 0x%x\n", cmd); - return -ENOTTY; + pr_warn("ksu ioctl: unsupported command 0x%x\n", cmd); + return -ENOTTY; } // File release handler static int anon_ksu_release(struct inode *inode, struct file *filp) { - pr_info("ksu fd released\n"); - return 0; +#ifdef CONFIG_KSU_DEBUG + pr_info("ksu fd released\n"); +#endif + return 0; } // File operations structure static const struct file_operations anon_ksu_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = anon_ksu_ioctl, - .compat_ioctl = anon_ksu_ioctl, - .release = anon_ksu_release, + .owner = THIS_MODULE, + .unlocked_ioctl = anon_ksu_ioctl, + .compat_ioctl = anon_ksu_ioctl, + .release = anon_ksu_release, }; // Install KSU fd to current process int ksu_install_fd(void) { - struct file *filp; - int fd; + struct file *filp; + int fd; - // Get unused fd - fd = get_unused_fd_flags(O_CLOEXEC); - if (fd < 0) { - pr_err("ksu_install_fd: failed to get unused fd\n"); - return fd; - } + // Get unused fd + fd = get_unused_fd_flags(O_CLOEXEC); + if (fd < 0) { + pr_err("ksu_install_fd: failed to get unused fd\n"); + return fd; + } - // Create anonymous inode file - filp = anon_inode_getfile("[ksu_driver]", &anon_ksu_fops, NULL, O_RDWR | O_CLOEXEC); - if (IS_ERR(filp)) { - pr_err("ksu_install_fd: failed to create anon inode file\n"); - put_unused_fd(fd); - return PTR_ERR(filp); - } + // Create anonymous inode file + filp = anon_inode_getfile("[ksu_driver]", &anon_ksu_fops, NULL, + O_RDWR | O_CLOEXEC); + if (IS_ERR(filp)) { + pr_err("ksu_install_fd: failed to create anon inode file\n"); + put_unused_fd(fd); + return PTR_ERR(filp); + } - // Install fd - fd_install(fd, filp); + // Install fd + fd_install(fd, filp); - pr_info("ksu fd[%d] installed for %s/%d\n", fd, current->comm, current->pid); + pr_info("ksu fd[%d] installed for %s/%d\n", fd, current->comm, + current->pid); - return fd; -} + return fd; +} \ No newline at end of file diff --git a/kernel/supercalls.h b/kernel/supercalls.h index c2e7c9aa..d68deda1 100644 --- a/kernel/supercalls.h +++ b/kernel/supercalls.h @@ -3,7 +3,7 @@ #include #include -#include "ksu.h" +#include "app_profile.h" #ifdef CONFIG_KPM #include "kpm/kpm.h" @@ -105,10 +105,21 @@ struct ksu_enable_uid_scanner_cmd { }; struct ksu_get_wrapper_fd_cmd { - __u32 fd; - __u32 flags; // CLOEXEC + __u32 fd; // Input: userspace fd + __u32 flags; // Input: flags of userspace fd }; +struct ksu_manage_mark_cmd { + __u32 operation; // Input: KSU_MARK_* + __s32 pid; // Input: target pid (0 for all processes) + __u32 result; // Output: for get operation - mark status or reg_count +}; + +#define KSU_MARK_GET 1 +#define KSU_MARK_MARK 2 +#define KSU_MARK_UNMARK 3 +#define KSU_MARK_REFRESH 4 + // IOCTL command definitions #define KSU_IOCTL_GRANT_ROOT _IOC(_IOC_NONE, 'K', 1, 0) #define KSU_IOCTL_GET_INFO _IOC(_IOC_READ, 'K', 2, 0) @@ -124,7 +135,8 @@ struct ksu_get_wrapper_fd_cmd { #define KSU_IOCTL_SET_APP_PROFILE _IOC(_IOC_WRITE, 'K', 12, 0) #define KSU_IOCTL_GET_FEATURE _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0) #define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0) -#define KSU_IOCTL_PROXY_FILE _IOC(_IOC_NONE, 'K', 15, 0) +#define KSU_IOCTL_GET_WRAPPER_FD _IOC(_IOC_WRITE, 'K', 15, 0) +#define KSU_IOCTL_MANAGE_MARK _IOC(_IOC_READ | _IOC_WRITE, 'K', 16, 0) // Other IOCTL command definitions #define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0) #define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0) @@ -148,6 +160,6 @@ struct ksu_ioctl_cmd_map { // Install KSU fd to current process int ksu_install_fd(void); -void ksu_supercalls_init(); - +void ksu_supercalls_init(void); +void ksu_supercalls_exit(void); #endif // __KSU_H_SUPERCALLS diff --git a/kernel/syscall_hook_manager.c b/kernel/syscall_hook_manager.c new file mode 100644 index 00000000..8fdd01eb --- /dev/null +++ b/kernel/syscall_hook_manager.c @@ -0,0 +1,381 @@ +#ifdef KSU_SHOULD_USE_NEW_TP +#include "linux/compiler.h" +#include "linux/cred.h" +#include "linux/printk.h" +#include "selinux/selinux.h" +#include +#include +#include +#include +#include +#include +#include + +#include "allowlist.h" +#include "arch.h" +#include "klog.h" // IWYU pragma: keep +#include "syscall_hook_manager.h" +#include "sucompat.h" +#include "setuid_hook.h" +#include "selinux/selinux.h" + +// Tracepoint registration count management +// == 1: just us +// > 1: someone else is also using syscall tracepoint e.g. ftrace +static int tracepoint_reg_count = 0; +static DEFINE_SPINLOCK(tracepoint_reg_lock); + +void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t) +{ + unsigned long flags; + spin_lock_irqsave(&tracepoint_reg_lock, flags); + if (tracepoint_reg_count <= 1) { + ksu_clear_task_tracepoint_flag(t); + } + spin_unlock_irqrestore(&tracepoint_reg_lock, flags); +} + +// Process marking management +static void handle_process_mark(bool mark) +{ + struct task_struct *p, *t; + read_lock(&tasklist_lock); + for_each_process_thread (p, t) { + if (mark) + ksu_set_task_tracepoint_flag(t); + else + ksu_clear_task_tracepoint_flag(t); + } + read_unlock(&tasklist_lock); +} + +void ksu_mark_all_process(void) +{ + handle_process_mark(true); + pr_info("hook_manager: mark all user process done!\n"); +} + +void ksu_unmark_all_process(void) +{ + handle_process_mark(false); + pr_info("hook_manager: unmark all user process done!\n"); +} + +static void ksu_mark_running_process_locked() +{ + struct task_struct *p, *t; + read_lock(&tasklist_lock); + for_each_process_thread (p, t) { + if (!t->mm) { // only user processes + continue; + } + int uid = task_uid(t).val; + const struct cred *cred = get_task_cred(t); + bool ksu_root_process = uid == 0 && is_task_ksu_domain(cred); + bool is_zygote_process = is_zygote(cred); + bool is_shell = uid == 2000; + // before boot completed, we shall mark init for marking zygote + bool is_init = t->pid == 1; + if (ksu_root_process || is_zygote_process || is_shell || + is_init || ksu_is_allow_uid(uid)) { + ksu_set_task_tracepoint_flag(t); + pr_info("hook_manager: mark process: pid:%d, uid: %d, comm:%s\n", + t->pid, uid, t->comm); + } else { + ksu_clear_task_tracepoint_flag(t); + pr_info("hook_manager: unmark process: pid:%d, uid: %d, comm:%s\n", + t->pid, uid, t->comm); + } + put_cred(cred); + } + read_unlock(&tasklist_lock); +} + +void ksu_mark_running_process() +{ + unsigned long flags; + spin_lock_irqsave(&tracepoint_reg_lock, flags); + if (tracepoint_reg_count <= 1) { + ksu_mark_running_process_locked(); + } else { + pr_info("hook_manager: not mark running process since syscall tracepoint is in use\n"); + } + spin_unlock_irqrestore(&tracepoint_reg_lock, flags); +} + +// Get task mark status +// Returns: 1 if marked, 0 if not marked, -ESRCH if task not found +int ksu_get_task_mark(pid_t pid) +{ + struct task_struct *task; + int marked = -ESRCH; + + rcu_read_lock(); + task = find_task_by_vpid(pid); + if (task) { + get_task_struct(task); + rcu_read_unlock(); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + marked = test_task_syscall_work(task, SYSCALL_TRACEPOINT) ? 1 : + 0; +#else + marked = test_tsk_thread_flag(task, TIF_SYSCALL_TRACEPOINT) ? + 1 : + 0; +#endif + put_task_struct(task); + } else { + rcu_read_unlock(); + } + + return marked; +} + +// Set task mark status +// Returns: 0 on success, -ESRCH if task not found +int ksu_set_task_mark(pid_t pid, bool mark) +{ + struct task_struct *task; + int ret = -ESRCH; + + rcu_read_lock(); + task = find_task_by_vpid(pid); + if (task) { + get_task_struct(task); + rcu_read_unlock(); + if (mark) { + ksu_set_task_tracepoint_flag(task); + pr_info("hook_manager: marked task pid=%d comm=%s\n", + pid, task->comm); + } else { + ksu_clear_task_tracepoint_flag(task); + pr_info("hook_manager: unmarked task pid=%d comm=%s\n", + pid, task->comm); + } + put_task_struct(task); + ret = 0; + } else { + rcu_read_unlock(); + } + + return ret; +} + +static struct kretprobe *init_kretprobe(const char *name, + kretprobe_handler_t handler) +{ + struct kretprobe *rp = kzalloc(sizeof(struct kretprobe), GFP_KERNEL); + if (!rp) + return NULL; + rp->kp.symbol_name = name; + rp->handler = handler; + rp->data_size = 0; + rp->maxactive = 0; + + int ret = register_kretprobe(rp); + pr_info("hook_manager: register_%s kretprobe: %d\n", name, ret); + if (ret) { + kfree(rp); + return NULL; + } + + return rp; +} + +static void destroy_kretprobe(struct kretprobe **rp_ptr) +{ + struct kretprobe *rp = *rp_ptr; + if (!rp) + return; + unregister_kretprobe(rp); + synchronize_rcu(); + kfree(rp); + *rp_ptr = NULL; +} + +static int syscall_regfunc_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + unsigned long flags; + spin_lock_irqsave(&tracepoint_reg_lock, flags); + if (tracepoint_reg_count < 1) { + // while install our tracepoint, mark our processes + ksu_mark_running_process_locked(); + } else if (tracepoint_reg_count == 1) { + // while other tracepoint first added, mark all processes + ksu_mark_all_process(); + } + tracepoint_reg_count++; + spin_unlock_irqrestore(&tracepoint_reg_lock, flags); + return 0; +} + +static int syscall_unregfunc_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + unsigned long flags; + spin_lock_irqsave(&tracepoint_reg_lock, flags); + tracepoint_reg_count--; + if (tracepoint_reg_count <= 0) { + // while no tracepoint left, unmark all processes + ksu_unmark_all_process(); + } else if (tracepoint_reg_count == 1) { + // while just our tracepoint left, unmark disallowed processes + ksu_mark_running_process_locked(); + } + spin_unlock_irqrestore(&tracepoint_reg_lock, flags); + return 0; +} + +static struct kretprobe *syscall_regfunc_rp = NULL; +static struct kretprobe *syscall_unregfunc_rp = NULL; + +static inline bool check_syscall_fastpath(int nr) +{ + switch (nr) { + case __NR_newfstatat: + case __NR_faccessat: + case __NR_execve: + case __NR_setresuid: + return true; + default: + return false; + } +} + +// Unmark init's child that are not zygote, adbd or ksud +int ksu_handle_init_mark_tracker(const char __user **filename_user) +{ + char path[64]; + + if (unlikely(!filename_user)) + return 0; + + memset(path, 0, sizeof(path)); + ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path)); + + if (likely(strstr(path, "/app_process") == NULL && + strstr(path, "/adbd") == NULL && + strstr(path, "/ksud") == NULL)) { + pr_info("hook_manager: unmark %d exec %s", current->pid, path); + ksu_clear_task_tracepoint_flag_if_needed(current); + } + + return 0; +} + +// 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))) { + 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); + return; + } + + // Handle faccessat + if (id == __NR_faccessat) { + int *dfd = (int *)&PT_REGS_PARM1(regs); + const char __user **filename_user = + (const char __user **)&PT_REGS_PARM2( + regs); + int *mode = (int *)&PT_REGS_PARM3(regs); + ksu_handle_faccessat(dfd, filename_user, mode, + NULL); + return; + } + + // Handle execve + if (id == __NR_execve) { + const char __user **filename_user = + (const char __user **)&PT_REGS_PARM1( + regs); + if (current->pid != 1 && + is_init(get_current_cred())) { + ksu_handle_init_mark_tracker( + filename_user); + } else { + ksu_handle_execve_sucompat( + NULL, filename_user, NULL, + NULL, NULL); + } + return; + } + } + + // Handle setresuid + if (id == __NR_setresuid) { + uid_t ruid = (uid_t)PT_REGS_PARM1(regs); + uid_t euid = (uid_t)PT_REGS_PARM2(regs); + uid_t suid = (uid_t)PT_REGS_PARM3(regs); + ksu_handle_setresuid(ruid, euid, suid); + return; + } + } +} + +void ksu_syscall_hook_manager_init(void) +{ + int ret; + pr_info("hook_manager: ksu_hook_manager_init called\n"); + + // Register kretprobe for syscall_regfunc + syscall_regfunc_rp = + init_kretprobe("syscall_regfunc", syscall_regfunc_handler); + // Register kretprobe for syscall_unregfunc + syscall_unregfunc_rp = + init_kretprobe("syscall_unregfunc", syscall_unregfunc_handler); + + ret = register_trace_sys_enter(ksu_sys_enter_handler, NULL); + if (ret) { + pr_err("hook_manager: failed to register sys_enter tracepoint: %d\n", + ret); + } else { + pr_info("hook_manager: sys_enter tracepoint registered\n"); + } + + ksu_setuid_hook_init(); + ksu_sucompat_init(); +} + +void ksu_syscall_hook_manager_exit(void) +{ + pr_info("hook_manager: ksu_hook_manager_exit called\n"); + unregister_trace_sys_enter(ksu_sys_enter_handler, NULL); + tracepoint_synchronize_unregister(); + pr_info("hook_manager: sys_enter tracepoint unregistered\n"); + + destroy_kretprobe(&syscall_regfunc_rp); + destroy_kretprobe(&syscall_unregfunc_rp); + + ksu_sucompat_exit(); + ksu_setuid_hook_exit(); +} +#else +#include "klog.h" // IWYU pragma: keep +#include "syscall_hook_manager.h" +#include "sucompat.h" +#include "setuid_hook.h" + +void ksu_syscall_hook_manager_init(void) +{ + pr_info("hook_manager: initializing..\n"); + ksu_setuid_hook_init(); + ksu_sucompat_init(); +} + +void ksu_syscall_hook_manager_exit(void) +{ + pr_info("hook_manager: exiting..\n"); + ksu_sucompat_exit(); + ksu_setuid_hook_exit(); +} +#endif diff --git a/kernel/syscall_hook_manager.h b/kernel/syscall_hook_manager.h new file mode 100644 index 00000000..ac2274fb --- /dev/null +++ b/kernel/syscall_hook_manager.h @@ -0,0 +1,85 @@ +#ifndef __KSU_H_HOOK_MANAGER +#define __KSU_H_HOOK_MANAGER + +#include +#include +#include +#include "kernel_compat.h" + +// Hook manager initialization and cleanup +void ksu_syscall_hook_manager_init(void); +void ksu_syscall_hook_manager_exit(void); + +#ifdef KSU_SHOULD_USE_NEW_TP +// Process marking for tracepoint +void ksu_mark_all_process(void); +void ksu_unmark_all_process(void); +void ksu_mark_running_process(void); + +// Per-task mark operations +int ksu_get_task_mark(pid_t pid); +int ksu_set_task_mark(pid_t pid, bool mark); + +static inline void ksu_set_task_tracepoint_flag(struct task_struct *t) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + set_task_syscall_work(t, SYSCALL_TRACEPOINT); +#else + set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT); +#endif +} + +static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + clear_task_syscall_work(t, SYSCALL_TRACEPOINT); +#else + clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT); +#endif +} + +void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t); +#else +// Process marking for tracepoint +static inline void ksu_mark_all_process(void) +{ + return; +} + +static inline void ksu_unmark_all_process(void) +{ + return; +} + +static inline void ksu_mark_running_process(void) +{ + return; +} + +// Per-task mark operations +static inline int ksu_get_task_mark(pid_t pid) +{ + return 0; +} + +static inline int ksu_set_task_mark(pid_t pid, bool mark) +{ + return 0; +} + +static inline void ksu_set_task_tracepoint_flag(struct task_struct *t) +{ + return; +} + +static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t) +{ + return; +} +static inline void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t) +{ + return; +} +#endif + +#endif diff --git a/kernel/throne_tracker.c b/kernel/throne_tracker.c index aec00a6a..9602b78a 100644 --- a/kernel/throne_tracker.c +++ b/kernel/throne_tracker.c @@ -10,11 +10,9 @@ #include "allowlist.h" #include "klog.h" // IWYU pragma: keep -#include "ksu.h" -#include "apk_sign.h" #include "manager.h" #include "throne_tracker.h" -#include "kernel_compat.h" +#include "apk_sign.h" #include "dynamic_manager.h" #include "throne_comm.h" @@ -40,7 +38,7 @@ static int uid_from_um_list(struct list_head *uid_list) ssize_t nr; int cnt = 0; - fp = ksu_filp_open_compat(KSU_UID_LIST_PATH, O_RDONLY, 0); + fp = filp_open(KSU_UID_LIST_PATH, O_RDONLY, 0); if (IS_ERR(fp)) return -ENOENT; @@ -57,7 +55,7 @@ static int uid_from_um_list(struct list_head *uid_list) return -ENOMEM; } - nr = ksu_kernel_read_compat(fp, buf, size, &pos); + nr = kernel_read(fp, buf, size, &pos); filp_close(fp, NULL); if (nr != size) { pr_err("uid_list: short read %zd/%lld\n", nr, size); @@ -155,7 +153,7 @@ static void crown_manager(const char *apk, struct list_head *uid_data, int signa // pkg is `/` if (strncmp(pkg, KSU_MANAGER_PACKAGE, sizeof(KSU_MANAGER_PACKAGE))) { pr_info("manager package is inconsistent with kernel build: %s\n", - KSU_MANAGER_PACKAGE); + KSU_MANAGER_PACKAGE); return; } #endif @@ -235,10 +233,9 @@ struct my_dir_context { #define FILLDIR_ACTOR_CONTINUE 0 #define FILLDIR_ACTOR_STOP -EINVAL #endif - FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name, - int namelen, loff_t off, u64 ino, - unsigned int d_type) + int namelen, loff_t off, u64 ino, + unsigned int d_type) { struct my_dir_context *my_ctx = container_of(ctx, struct my_dir_context, ctx); @@ -271,7 +268,7 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name, if (d_type == DT_DIR && my_ctx->depth > 0 && (my_ctx->stop && !*my_ctx->stop)) { - struct data_path *data = kmalloc(sizeof(struct data_path), GFP_ATOMIC); + struct data_path *data = kzalloc(sizeof(struct data_path), GFP_ATOMIC); if (!data) { pr_err("Failed to allocate memory for %s\n", dirpath); @@ -306,29 +303,24 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name, // Check for dynamic sign or multi-manager signatures if (is_multi_manager && (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2)) { crown_manager(dirpath, my_ctx->private_data, signature_index); - - struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC); - if (apk_data) { - apk_data->hash = hash; - apk_data->exists = true; - list_add_tail(&apk_data->list, &apk_path_hash_list); - } } else if (is_manager_apk(dirpath)) { crown_manager(dirpath, my_ctx->private_data, 0); *my_ctx->stop = 1; + } + struct apk_path_hash *apk_data = kzalloc(sizeof(*apk_data), GFP_ATOMIC); + if (apk_data) { + apk_data->hash = hash; + apk_data->exists = true; + list_add_tail(&apk_data->list, &apk_path_hash_list); + } + + if (is_manager_apk(dirpath)) { // Manager found, clear APK cache list list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) { list_del(&pos->list); kfree(pos); } - } else { - struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC); - if (apk_data) { - apk_data->hash = hash; - apk_data->exists = true; - list_add_tail(&apk_data->list, &apk_path_hash_list); - } } } } @@ -345,7 +337,7 @@ void search_manager(const char *path, int depth, struct list_head *uid_data) // Initialize APK cache list struct apk_path_hash *pos, *n; - list_for_each_entry(pos, &apk_path_hash_list, list) { + list_for_each_entry (pos, &apk_path_hash_list, list) { pos->exists = false; } @@ -358,36 +350,39 @@ void search_manager(const char *path, int depth, struct list_head *uid_data) for (i = depth; i >= 0; i--) { struct data_path *pos, *n; - list_for_each_entry_safe(pos, n, &data_path_list, list) { + list_for_each_entry_safe (pos, n, &data_path_list, list) { struct my_dir_context ctx = { .ctx.actor = my_actor, - .data_path_list = &data_path_list, - .parent_dir = pos->dirpath, - .private_data = uid_data, - .depth = pos->depth, - .stop = &stop }; + .data_path_list = &data_path_list, + .parent_dir = pos->dirpath, + .private_data = uid_data, + .depth = pos->depth, + .stop = &stop }; struct file *file; if (!stop) { - file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0); + file = filp_open(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0); if (IS_ERR(file)) { - pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file)); + pr_err("Failed to open directory: %s, err: %ld\n", + pos->dirpath, PTR_ERR(file)); goto skip_iterate; } - + // grab magic on first folder, which is /data/app if (!data_app_magic) { if (file->f_inode->i_sb->s_magic) { data_app_magic = file->f_inode->i_sb->s_magic; - pr_info("%s: dir: %s got magic! 0x%lx\n", __func__, pos->dirpath, data_app_magic); + pr_info("%s: dir: %s got magic! 0x%lx\n", __func__, + pos->dirpath, data_app_magic); } else { filp_close(file, NULL); goto skip_iterate; } } - + if (file->f_inode->i_sb->s_magic != data_app_magic) { - pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n", __func__, pos->dirpath, - file->f_inode->i_sb->s_magic, data_app_magic); + pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n", + __func__, pos->dirpath, + file->f_inode->i_sb->s_magic, data_app_magic); filp_close(file, NULL); goto skip_iterate; } @@ -395,7 +390,7 @@ void search_manager(const char *path, int depth, struct list_head *uid_data) iterate_dir(file, &ctx.ctx); filp_close(file, NULL); } -skip_iterate: + skip_iterate: list_del(&pos->list); if (pos != &data) kfree(pos); @@ -403,7 +398,7 @@ skip_iterate: } // Remove stale cached APK entries - list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) { + list_for_each_entry_safe (pos, n, &apk_path_hash_list, list) { if (!pos->exists) { list_del(&pos->list); kfree(pos); @@ -427,11 +422,11 @@ static bool is_uid_exist(uid_t uid, char *package, void *data) return exist; } -void track_throne(void) +void track_throne(bool prune_only) { struct list_head uid_list; struct uid_data *np, *n; - struct file *fp; + struct file *fp; char chr = 0; loff_t pos = 0; loff_t line_start = 0; @@ -456,7 +451,7 @@ void track_throne(void) } { - fp = ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0); + fp = filp_open(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0); if (IS_ERR(fp)) { pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n", __func__, PTR_ERR(fp)); return; @@ -464,13 +459,13 @@ void track_throne(void) for (;;) { ssize_t count = - ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos); + kernel_read(fp, &chr, sizeof(chr), &pos); if (count != sizeof(chr)) break; if (chr != '\n') continue; - count = ksu_kernel_read_compat(fp, buf, sizeof(buf), + count = kernel_read(fp, buf, sizeof(buf), &line_start); struct uid_data *data = kzalloc(sizeof(struct uid_data), GFP_ATOMIC); @@ -504,6 +499,9 @@ void track_throne(void) } uid_ready: + if (prune_only) + goto prune; + // first, check if manager_uid exist! list_for_each_entry(np, &uid_list, list) { if (np->uid == current_manager_uid) { @@ -547,6 +545,7 @@ uid_ready: pr_info("Manager search finished\n"); } +prune: // then prune the allowlist ksu_prune_allowlist(is_uid_exist, &uid_list); out: @@ -565,4 +564,4 @@ void ksu_throne_tracker_init(void) void ksu_throne_tracker_exit(void) { // nothing to do -} +} \ No newline at end of file diff --git a/kernel/throne_tracker.h b/kernel/throne_tracker.h index 7799a823..8bb3b9a2 100644 --- a/kernel/throne_tracker.h +++ b/kernel/throne_tracker.h @@ -5,6 +5,6 @@ void ksu_throne_tracker_init(void); void ksu_throne_tracker_exit(void); -void track_throne(void); +void track_throne(bool prune_only); #endif