diff --git a/README.md b/README.md index 0cdf4cc2966a..abe525e59f85 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ A Kernel based root solution for Android devices. ## Compatibility State -KernelSU officially supports Android GKI 2.0 devices(with kernel 5.10+), old kernels(4.14+) is also compatiable, but you need to build kernel yourself. +KernelSU officially supports Android GKI 2.0 devices(with kernel 5.10+), old kernels(4.4+) is also compatiable, but you need to build kernel yourself. WSA and containter-based Android should also work with KernelSU integrated. diff --git a/README_CN.md b/README_CN.md index 6bf7fd3acf4e..cecc7d455fcc 100644 --- a/README_CN.md +++ b/README_CN.md @@ -11,7 +11,7 @@ ## 兼容状态 -KernelSU 官方支持 GKI 2.0 的设备(内核版本5.10以上);旧内核也是兼容的(最低4.14+),不过需要自己编译内核。 +KernelSU 官方支持 GKI 2.0 的设备(内核版本5.10以上);旧内核也是兼容的(最低4.4+),不过需要自己编译内核。 WSA 和运行在容器上的 Android 也可以与 KernelSU 一起工作。 diff --git a/kernel/allowlist.c b/kernel/allowlist.c index 1c4a3756ab6e..86894fb6b486 100644 --- a/kernel/allowlist.c +++ b/kernel/allowlist.c @@ -22,15 +22,14 @@ struct perm_data { static struct list_head allow_list; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) #define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist" -#else -// filp_open return error if under encryption dir on Kernel4.4 -#define KERNEL_SU_ALLOWLIST "/data/user_de/.ksu_allowlist" -#endif static struct work_struct ksu_save_work; static struct work_struct ksu_load_work; +#ifndef FILP_OPEN_WORKS_IN_WORKER +static struct file *fp; +static DEFINE_MUTEX(fp_mutex); +#endif bool persistent_allow_list(void); @@ -125,6 +124,7 @@ void do_persistent_allow_list(struct work_struct *work) struct list_head *pos = NULL; loff_t off = 0; +#ifdef FILP_OPEN_WORKS_IN_WORKER struct file *fp = filp_open(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT, 0644); @@ -132,6 +132,13 @@ void do_persistent_allow_list(struct work_struct *work) pr_err("save_allow_list creat file failed: %d\n", PTR_ERR(fp)); return; } +#else + mutex_lock(&fp_mutex); + if (!fp) { + pr_err("save_allow_list fp wasn't opened successfully\n"); + goto exit; + } +#endif // store magic and version if (kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) { @@ -154,17 +161,24 @@ void do_persistent_allow_list(struct work_struct *work) } exit: +#ifdef FILP_OPEN_WORKS_IN_WORKER filp_close(fp, 0); +#else + mutex_unlock(&fp_mutex); +#endif } void do_load_allow_list(struct work_struct *work) { loff_t off = 0; ssize_t ret = 0; +#ifdef FILP_OPEN_WORKS_IN_WORKER struct file *fp = NULL; +#endif u32 magic; u32 version; +#ifdef FILP_OPEN_WORKS_IN_WORKER fp = filp_open("/data/adb", O_RDONLY, 0); if (IS_ERR(fp)) { int errno = PTR_ERR(fp); @@ -198,6 +212,13 @@ void do_load_allow_list(struct work_struct *work) #endif return; } +#else + mutex_lock(&fp_mutex); + if (!fp) { + pr_err("load_allow_list fp wasn't opened successfully\n"); + goto unlock; + } +#endif /* FILP_OPEN_WORKS_IN_WORKER */ // verify magic if (kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) || @@ -231,7 +252,12 @@ void do_load_allow_list(struct work_struct *work) exit: ksu_show_allow_list(); +#ifdef FILP_OPEN_WORKS_IN_WORKER filp_close(fp, 0); +#else +unlock: + mutex_unlock(&fp_mutex); +#endif } void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data) @@ -261,11 +287,47 @@ void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data) // make sure allow list works cross boot bool persistent_allow_list(void) { +#ifndef FILP_OPEN_WORKS_IN_WORKER + mutex_lock(&fp_mutex); + if (fp) + filp_close(fp, 0); + fp = filp_open(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT, 0644); + + if (IS_ERR(fp)) { + pr_err("persistent_allow_list creat file failed: %d\n", PTR_ERR(fp)); + fp = NULL; + mutex_unlock(&fp_mutex); + return false; + } + mutex_unlock(&fp_mutex); +#endif return ksu_queue_work(&ksu_save_work); } bool ksu_load_allow_list(void) { +#ifndef FILP_OPEN_WORKS_IN_WORKER + mutex_lock(&fp_mutex); + if (fp) + filp_close(fp, 0); + + // load allowlist now! + fp = filp_open(KERNEL_SU_ALLOWLIST, O_RDONLY, 0); + + if (IS_ERR(fp)) { + int errno = PTR_ERR(fp); + fp = NULL; + mutex_unlock(&fp_mutex); +#ifdef CONFIG_KSU_DEBUG + if (errno == -ENOENT) + ksu_allow_uid(2000, true, + true); // allow adb shell by default +#endif + pr_err("ksu_load_allow_list open file failed: %d\n", errno); + return false; + } + mutex_unlock(&fp_mutex); +#endif /* FILP_OPEN_WORKS_IN_WORKER */ return ksu_queue_work(&ksu_load_work); } @@ -291,4 +353,13 @@ void ksu_allowlist_exit(void) kfree(np); } mutex_unlock(&allowlist_mutex); -} \ No newline at end of file + +#ifndef FILP_OPEN_WORKS_IN_WORKER + mutex_lock(&fp_mutex); + if (fp) { + filp_close(fp, 0); + fp = NULL; + } + mutex_unlock(&fp_mutex); +#endif +} diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 64f371d1c286..16f1c9708ce5 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -21,7 +21,9 @@ #include "ksud.h" #include "manager.h" #include "selinux/selinux.h" +#include "selinux/kernel_compat.h" #include "uid_observer.h" +#include "kernel_compat.h" extern int handle_sepolicy(unsigned long arg3, void __user *arg4); @@ -253,12 +255,33 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, bool allow = arg2 == CMD_ALLOW_SU; bool success = false; uid_t uid = (uid_t)arg3; +#ifndef FILP_OPEN_WORKS_IN_WORKER + struct ksu_cred_t old_cred; + if (!ksu_save_cred(&old_cred)) { + pr_err("failed to save manager cred\n"); + goto show; + } + if(!ksu_tmp_root_begin()) { + pr_err("cannot switch to root\n"); + goto show; + } +#endif success = ksu_allow_uid(uid, allow, true); +#ifndef FILP_OPEN_WORKS_IN_WORKER + ksu_tmp_root_end(); + if (!ksu_restore_cred(&old_cred)) { + pr_err("failed to restore manager cred\n"); + BUG(); + } +#endif if (success) { if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { pr_err("prctl reply error, cmd: %d\n", arg2); } } +#ifndef FILP_OPEN_WORKS_IN_WORKER +show: +#endif ksu_show_allow_list(); } else if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) { u32 array[128]; @@ -395,4 +418,4 @@ void ksu_core_exit(void) pr_info("ksu_kprobe_exit\n"); ksu_kprobe_exit(); #endif -} \ No newline at end of file +} diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h index 703586bbefdb..60b94cc66146 100644 --- a/kernel/kernel_compat.h +++ b/kernel/kernel_compat.h @@ -1,3 +1,15 @@ +#ifndef __KSU_H_KERNEL_COMPAT +#define __KSU_H_KERNEL_COMPAT + #include "linux/fs.h" +#include "linux/version.h" + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) +#define FILP_OPEN_WORKS_IN_WORKER +#elif defined(MODULE) +#warning building as module may not work due to Kprobes bugs on old kernels +#endif + extern ssize_t kernel_read_compat(struct file *p, void* buf, size_t count, loff_t *pos); -extern ssize_t kernel_write_compat(struct file *p, const void *buf, size_t count, loff_t *pos); \ No newline at end of file +extern ssize_t kernel_write_compat(struct file *p, const void *buf, size_t count, loff_t *pos); +#endif diff --git a/kernel/ksu.c b/kernel/ksu.c index a507665b6ab4..6c896e512290 100644 --- a/kernel/ksu.c +++ b/kernel/ksu.c @@ -1,6 +1,7 @@ #include "linux/fs.h" #include "linux/module.h" #include "linux/workqueue.h" +#include "linux/version.h" #include "allowlist.h" #include "arch.h" @@ -32,6 +33,9 @@ int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, extern void ksu_enable_sucompat(); extern void ksu_enable_ksud(); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +extern void ksu_enable_selinux_compat(); +#endif int __init kernelsu_init(void) { @@ -56,6 +60,9 @@ int __init kernelsu_init(void) #ifdef CONFIG_KPROBES ksu_enable_sucompat(); ksu_enable_ksud(); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) + ksu_enable_selinux_compat(); +#endif #else #warning("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html") #endif diff --git a/kernel/ksud.c b/kernel/ksud.c index e84cf4939549..297ad9df8462 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -16,6 +16,10 @@ #include "ksud.h" #include "selinux/selinux.h" +#if defined(CONFIG_KPROBES) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +#define APPLY_RULES_IN_WORKER +#endif + static const char KERNEL_SU_RC[] = "\n" @@ -40,6 +44,9 @@ static const char KERNEL_SU_RC[] = static void stop_vfs_read_hook(); static void stop_execve_hook(); +#ifdef APPLY_RULES_IN_WORKER +static void ksu_apply_rules(); +#endif #ifdef CONFIG_KPROBES static struct work_struct stop_vfs_read_work; @@ -49,6 +56,10 @@ static bool vfs_read_hook = true; static bool execveat_hook = true; #endif +#ifdef APPLY_RULES_IN_WORKER +static struct work_struct apply_rules_work; +#endif + void on_post_fs_data(void) { static bool done = false; @@ -91,7 +102,11 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, // 1: /system/bin/init selinux_setup // 2: /system/bin/init second_stage pr_info("/system/bin/init second_stage executed\n"); +#ifdef APPLY_RULES_IN_WORKER + ksu_apply_rules(); +#else apply_kernelsu_rules(); +#endif } } @@ -99,7 +114,9 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, !memcmp(filename->name, app_process, sizeof(app_process) - 1)) { first_app_process = false; pr_info("exec app_process, /data prepared!\n"); +#ifdef FILP_OPEN_WORKS_IN_WORKER on_post_fs_data(); // we keep this for old ksud +#endif stop_execve_hook(); } @@ -212,11 +229,9 @@ static int read_handler_pre(struct kprobe *p, struct pt_regs *regs) static struct kprobe execve_kp = { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) .symbol_name = "do_execveat_common", -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) && \ - LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) .symbol_name = "__do_execve_file", -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) && \ - LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) .symbol_name = "do_execveat_common", #endif .pre_handler = execve_handler_pre, @@ -236,6 +251,13 @@ static void do_stop_execve_hook(struct work_struct *work) { unregister_kprobe(&execve_kp); } + +#ifdef APPLY_RULES_IN_WORKER +static void do_apply_kernelsu_rules(struct work_struct *work) +{ + apply_kernelsu_rules(); +} +#endif #endif static void stop_vfs_read_hook() @@ -258,6 +280,14 @@ static void stop_execve_hook() #endif } +#ifdef APPLY_RULES_IN_WORKER +static void ksu_apply_rules() +{ + bool ret = schedule_work(&apply_rules_work); + pr_info("apply kernelsu rules: %d!\n", ret); +} +#endif + // ksud: module support void ksu_enable_ksud() { @@ -272,5 +302,8 @@ void ksu_enable_ksud() INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook); INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook); +#ifdef APPLY_RULES_IN_WORKER + INIT_WORK(&apply_rules_work, do_apply_kernelsu_rules); +#endif #endif } diff --git a/kernel/selinux/Makefile b/kernel/selinux/Makefile index ae1609d73ceb..199bd2336b1f 100644 --- a/kernel/selinux/Makefile +++ b/kernel/selinux/Makefile @@ -1,9 +1,10 @@ obj-y += selinux.o obj-y += sepolicy.o obj-y += rules.o +obj-y += kernel_compat.o ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion ccflags-y += -Wno-macro-redefined -Wno-declaration-after-statement -Wno-unused-function ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include -ccflags-y += -I$(objtree)/security/selinux \ No newline at end of file +ccflags-y += -I$(objtree)/security/selinux diff --git a/kernel/selinux/kernel_compat.c b/kernel/selinux/kernel_compat.c new file mode 100644 index 000000000000..799691b9a267 --- /dev/null +++ b/kernel/selinux/kernel_compat.c @@ -0,0 +1,224 @@ +#include "linux/types.h" +#ifdef CONFIG_KPROBES +#include "linux/kprobes.h" +#endif +#include "linux/version.h" +#include "avc_ss.h" + +#include "kernel_compat.h" +#include "selinux.h" +#include "../klog.h" // IWYU pragma: keep +#include "../arch.h" +#include "objsec.h" +#include "../kernel_compat.h" + +#ifndef FILP_OPEN_WORKS_IN_WORKER +static struct group_info root_groups = { .usage = ATOMIC_INIT(2) }; + +bool ksu_save_cred(struct ksu_cred_t *ksu_cred) { + struct cred *cred; + struct task_security_struct *tsec; + + cred = (struct cred *)__task_cred(current); + + tsec = cred->security; + if (!tsec) { + pr_err("tsec == NULL!\n"); + return false; + } + + ksu_cred->uid = cred->uid; + ksu_cred->gid = cred->gid; + ksu_cred->suid = cred->suid; + ksu_cred->euid = cred->euid; + ksu_cred->egid = cred->egid; + ksu_cred->fsuid = cred->fsuid; + ksu_cred->fsgid = cred->fsgid; + ksu_cred->cap_inheritable = cred->cap_inheritable; + ksu_cred->cap_permitted = cred->cap_permitted; + ksu_cred->cap_effective = cred->cap_effective; + ksu_cred->cap_bset = cred->cap_bset; + ksu_cred->cap_ambient = cred->cap_ambient; + +#if defined(CONFIG_GENERIC_ENTRY) && \ + LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + ksu_cred->thread_info.flags = current_thread_info()->syscall_work; +#else + ksu_cred->thread_info.flags = current_thread_info()->flags; +#endif + ksu_cred->seccomp.mode = current->seccomp.mode; + ksu_cred->seccomp.filter = current->seccomp.filter; + + ksu_cred->group_info = cred->group_info; + + ksu_cred->tsec.sid = tsec->sid; + ksu_cred->tsec.create_sid = tsec->create_sid; + ksu_cred->tsec.keycreate_sid = tsec->keycreate_sid; + ksu_cred->tsec.sockcreate_sid = tsec->sockcreate_sid; + + return true; +} + +bool ksu_restore_cred(struct ksu_cred_t *ksu_cred) { + struct cred *cred; + struct task_security_struct *tsec; + + cred = (struct cred *)__task_cred(current); + + tsec = cred->security; + if (!tsec) { + pr_err("tsec == NULL!\n"); + return false; + } + + cred->uid = ksu_cred->uid; + cred->gid = ksu_cred->gid; + cred->suid = ksu_cred->suid; + cred->euid = ksu_cred->euid; + cred->egid = ksu_cred->egid; + cred->fsuid = ksu_cred->fsuid; + cred->fsgid = ksu_cred->fsgid; + cred->cap_inheritable = ksu_cred->cap_inheritable; + cred->cap_permitted = ksu_cred->cap_permitted; + cred->cap_effective = ksu_cred->cap_effective; + cred->cap_bset = ksu_cred->cap_bset; + cred->cap_ambient = ksu_cred->cap_ambient; + +#if defined(CONFIG_GENERIC_ENTRY) && \ + LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + current_thread_info()->syscall_work = ksu_cred->thread_info.flags; +#else + current_thread_info()->flags = ksu_cred->thread_info.flags; +#endif + current->seccomp.mode = ksu_cred->seccomp.mode; + current->seccomp.filter = ksu_cred->seccomp.filter; + + cred->group_info = ksu_cred->group_info; + + tsec->sid = ksu_cred->tsec.sid; + tsec->create_sid = ksu_cred->tsec.create_sid; + tsec->keycreate_sid = ksu_cred->tsec.keycreate_sid; + tsec->sockcreate_sid = ksu_cred->tsec.sockcreate_sid; + + return true; +} + +bool ksu_tmp_root_begin(void) +{ + struct ksu_cred_t ksu_cred; + struct cred *cred; + int error; + u32 sid; + + cred = (struct cred *)__task_cred(current); + + const char *su_domain = KERNEL_SU_DOMAIN; + + error = security_secctx_to_secid(su_domain, strlen(su_domain), &sid); + pr_info("error: %d, sid: %d\n", error, sid); + + if (error) + return false; + + memset(&ksu_cred.uid, 0, sizeof(ksu_cred.uid)); + memset(&ksu_cred.gid, 0, sizeof(ksu_cred.gid)); + memset(&ksu_cred.suid, 0, sizeof(ksu_cred.suid)); + memset(&ksu_cred.euid, 0, sizeof(ksu_cred.euid)); + memset(&ksu_cred.egid, 0, sizeof(ksu_cred.egid)); + memset(&ksu_cred.fsuid, 0, sizeof(ksu_cred.fsuid)); + memset(&ksu_cred.fsgid, 0, sizeof(ksu_cred.fsgid)); + memset(&ksu_cred.cap_inheritable, 0xff, sizeof(ksu_cred.cap_inheritable)); + memset(&ksu_cred.cap_permitted, 0xff, sizeof(ksu_cred.cap_permitted)); + memset(&ksu_cred.cap_effective, 0xff, sizeof(ksu_cred.cap_effective)); + memset(&ksu_cred.cap_bset, 0xff, sizeof(ksu_cred.cap_bset)); + memset(&ksu_cred.cap_ambient, 0xff, sizeof(ksu_cred.cap_ambient)); + + // disable seccomp +#if defined(CONFIG_GENERIC_ENTRY) && \ + LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + ksu_cred.thread_info.flags = current_thread_info()->syscall_work & ~SYSCALL_WORK_SECCOMP; +#else + ksu_cred.thread_info.flags = current_thread_info()->flags & ~(TIF_SECCOMP | _TIF_SECCOMP); +#endif + ksu_cred.seccomp.mode = 0; + ksu_cred.seccomp.filter = NULL; + + // setgroup to root + ksu_cred.group_info = get_group_info(&root_groups); + + ksu_cred.tsec.sid = sid; + ksu_cred.tsec.create_sid = 0; + ksu_cred.tsec.keycreate_sid = 0; + ksu_cred.tsec.sockcreate_sid = 0; + + return ksu_restore_cred(&ksu_cred); +} + +void ksu_tmp_root_end(void) +{ + struct cred *cred; + + cred = (struct cred *)__task_cred(current); + + if (cred->group_info) + put_group_info(cred->group_info); +} +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +int ksu_handle_security_bounded_transition(u32 *old_sid, u32 *new_sid) { + u32 init_sid, su_sid; + int error; + + if (!ss_initialized) + return 0; + + /* domain unchanged */ + if (*old_sid == *new_sid) + return 0; + + const char *init_domain = INIT_DOMAIN; + const char *su_domain = KERNEL_SU_DOMAIN; + + error = security_secctx_to_secid(init_domain, strlen(init_domain), &init_sid); + if (error) { + pr_warn("cannot get sid of init context, err %d\n", error); + return 0; + } + + error = security_secctx_to_secid(su_domain, strlen(su_domain), &su_sid); + if (error) { + pr_warn("cannot get sid of su context, err %d\n", error); + return 0; + } + + if (*old_sid == init_sid && *new_sid == su_sid) { + pr_info("init to su transition found\n"); + *old_sid = *new_sid; // make the original func return 0 + } + + return 0; +} + +#ifdef CONFIG_KPROBES +static int handler_pre(struct kprobe *p, struct pt_regs *regs) { + u32 *old_sid = (u32 *)&PT_REGS_PARM1(regs); + u32 *new_sid = (u32 *)&PT_REGS_PARM2(regs); + + return ksu_handle_security_bounded_transition(old_sid, new_sid); +} + +static struct kprobe kp = { + .symbol_name = "security_bounded_transition", + .pre_handler = handler_pre, +}; + +// selinux_compat: make ksud init trigger work for kernel < 4.14 +void ksu_enable_selinux_compat() { + int ret; + + ret = register_kprobe(&kp); + pr_info("selinux_compat: kp: %d\n", ret); +} +#endif +#endif diff --git a/kernel/selinux/kernel_compat.h b/kernel/selinux/kernel_compat.h new file mode 100644 index 000000000000..cdab5445adee --- /dev/null +++ b/kernel/selinux/kernel_compat.h @@ -0,0 +1,45 @@ +#ifndef __KSU_H_SELINUX_KERNEL_COMPAT +#define __KSU_H_SELINUX_KERNEL_COMPAT + +#include "linux/cred.h" +#include "linux/seccomp.h" + +#include "../kernel_compat.h" + +#ifndef FILP_OPEN_WORKS_IN_WORKER +struct ksu_cred_t { + kuid_t uid; + kgid_t gid; + kuid_t suid; + kuid_t euid; + kgid_t egid; + kuid_t fsuid; + kgid_t fsgid; + kernel_cap_t cap_inheritable; + kernel_cap_t cap_permitted; + kernel_cap_t cap_effective; + kernel_cap_t cap_bset; + kernel_cap_t cap_ambient; + struct { + unsigned long flags; + } thread_info; + struct { + int mode; + struct seccomp_filter *filter; + } seccomp; + struct group_info *group_info; + struct { + u32 sid; + u32 create_sid; + u32 keycreate_sid; + u32 sockcreate_sid; + } tsec; +}; + +bool ksu_save_cred(struct ksu_cred_t *ksu_cred); +bool ksu_restore_cred(struct ksu_cred_t *ksu_cred); + +bool ksu_tmp_root_begin(void); +void ksu_tmp_root_end(void); +#endif +#endif diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index 758950db5848..72fedd06ea28 100644 --- a/kernel/selinux/rules.c +++ b/kernel/selinux/rules.c @@ -69,6 +69,7 @@ void apply_kernelsu_rules() ksu_allow(db, "kernel", "adb_data_file", "file", ALL); // we may need to do mount on shell ksu_allow(db, "kernel", "shell_data_file", "file", ALL); + ksu_allow(db, "kernel", "system_server", "fd", "use"); // we need to read /data/system/packages.list ksu_allow(db, "kernel", "kernel", "capability", "dac_override"); // Android 10+: @@ -441,4 +442,4 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) rcu_read_unlock(); return ret; -} \ No newline at end of file +} diff --git a/kernel/selinux/selinux.c b/kernel/selinux/selinux.c index be8a35c0f0b7..92b42de4ea76 100644 --- a/kernel/selinux/selinux.c +++ b/kernel/selinux/selinux.c @@ -3,8 +3,6 @@ #include "linux/version.h" #include "../klog.h" // IWYU pragma: keep -#define KERNEL_SU_DOMAIN "u:r:su:s0" - static u32 ksu_sid; static int transive_to_domain(const char *domain) @@ -53,36 +51,51 @@ if (!is_domain_permissive) { void setenforce(bool enforce) { -#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 14, 0) #ifdef CONFIG_SECURITY_SELINUX_DEVELOP +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) selinux_state.enforcing = enforce; -#endif #else - selinux_enabled = enforce; + selinux_enabled = enforce; +#endif #endif } bool getenforce() { -#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 14, 0) #ifdef CONFIG_SECURITY_SELINUX_DISABLE +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) if (selinux_state.disabled) { +#else + if (selinux_disabled) { +#endif return false; } #endif #ifdef CONFIG_SECURITY_SELINUX_DEVELOP +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) return selinux_state.enforcing; #else - return false; + return selinux_enabled; #endif #else - return selinux_enabled; + return true; #endif +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) +/* + * get the subjective security ID of the current task + */ +static inline u32 current_sid(void) +{ + const struct task_security_struct *tsec = current_security(); + return tsec->sid; } +#endif bool is_ksu_domain() { return ksu_sid && current_sid() == ksu_sid; -} \ No newline at end of file +} diff --git a/kernel/selinux/selinux.h b/kernel/selinux/selinux.h index 2ecdd09c220f..63b5b0b6b5a0 100644 --- a/kernel/selinux/selinux.h +++ b/kernel/selinux/selinux.h @@ -3,6 +3,9 @@ #include "linux/types.h" +#define KERNEL_SU_DOMAIN "u:r:su:s0" +#define INIT_DOMAIN "u:r:init:s0" + void setup_selinux(); void setenforce(bool); @@ -13,4 +16,4 @@ bool is_ksu_domain(); void apply_kernelsu_rules(); -#endif \ No newline at end of file +#endif diff --git a/kernel/sucompat.c b/kernel/sucompat.c index 22c9fd494ad5..ef294a54529a 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -134,16 +134,15 @@ static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs) { -// static int vfs_statx(int dfd, const char __user *filename, int flags,struct kstat *stat, u32 request_mask) int *dfd = (int *)&PT_REGS_PARM1(regs); const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs); -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +// static int vfs_statx(int dfd, const char __user *filename, int flags, struct kstat *stat, u32 request_mask) int *flags = (int *)&PT_REGS_PARM3(regs); #else // int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,int flag) int *flags = (int *)&PT_REGS_PARM4(regs); #endif - return ksu_handle_stat(dfd, filename_user, flags); } @@ -172,10 +171,10 @@ static struct kprobe faccessat_kp = { }; static struct kprobe newfstatat_kp = { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) .symbol_name = "vfs_statx", #else - .symbol_name = "vfs_fstatat", + .symbol_name = "vfs_fstatat", #endif .pre_handler = newfstatat_handler_pre, }; @@ -183,14 +182,9 @@ static struct kprobe newfstatat_kp = { static struct kprobe execve_kp = { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) .symbol_name = "do_execveat_common", -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) && \ - LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) .symbol_name = "__do_execve_file", -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) && \ - LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) - .symbol_name = "do_execveat_common", -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) && \ - LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) .symbol_name = "do_execveat_common", #endif .pre_handler = execve_handler_pre, diff --git a/kernel/uid_observer.c b/kernel/uid_observer.c index 7b9fcfd77b43..c66a46d6ed66 100644 --- a/kernel/uid_observer.c +++ b/kernel/uid_observer.c @@ -16,6 +16,10 @@ #define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list" static struct work_struct ksu_update_uid_work; +#ifndef FILP_OPEN_WORKS_IN_WORKER +static struct file *fp; +static DEFINE_MUTEX(fp_mutex); +#endif struct uid_data { struct list_head list; @@ -39,6 +43,7 @@ static bool is_uid_exist(uid_t uid, void *data) static void do_update_uid(struct work_struct *work) { +#ifdef FILP_OPEN_WORKS_IN_WORKER struct file *fp = filp_open(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0); if (IS_ERR(fp)) { pr_err("do_update_uid, open " SYSTEM_PACKAGES_LIST_PATH @@ -46,6 +51,13 @@ static void do_update_uid(struct work_struct *work) ERR_PTR(fp)); return; } +#else + mutex_lock(&fp_mutex); + if (!fp) { + pr_err("do_update_uid fp wasn't opened successfully\n"); + goto unlock; + } +#endif struct list_head uid_list; INIT_LIST_HEAD(&uid_list); @@ -115,11 +127,32 @@ static void do_update_uid(struct work_struct *work) list_del(&np->list); kfree(np); } +#ifdef FILP_OPEN_WORKS_IN_WORKER filp_close(fp, 0); +#else +unlock: + mutex_unlock(&fp_mutex); +#endif } void update_uid() { +#ifndef FILP_OPEN_WORKS_IN_WORKER + mutex_lock(&fp_mutex); + if (fp) + filp_close(fp, 0); + fp = filp_open(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0); + + if (IS_ERR(fp)) { + pr_err("update_uid, open " SYSTEM_PACKAGES_LIST_PATH + " failed: %d\n", + ERR_PTR(fp)); + fp = NULL; + mutex_unlock(&fp_mutex); + return; + } + mutex_unlock(&fp_mutex); +#endif ksu_queue_work(&ksu_update_uid_work); } @@ -131,5 +164,13 @@ int ksu_uid_observer_init() int ksu_uid_observer_exit() { +#ifndef FILP_OPEN_WORKS_IN_WORKER + mutex_lock(&fp_mutex); + if (fp) { + filp_close(fp, 0); + fp = NULL; + } + mutex_unlock(&fp_mutex); +#endif return 0; -} \ No newline at end of file +} diff --git a/website/docs/guide/faq.md b/website/docs/guide/faq.md index e6e3938b9da9..6a61fcb4a2bf 100644 --- a/website/docs/guide/faq.md +++ b/website/docs/guide/faq.md @@ -41,7 +41,7 @@ It is device's kernel that affect KernelSU's compatability and it has nothing to ## Can KernelSU support old kernel? -It is possible, KernelSU is backported to kernel 4.14 now, for older kernel, you need to backport it manully and PRs welcome! +It is possible, KernelSU is backported to kernel 4.4 now, for older kernel, you need to backport it manully and PRs welcome! ## How to integrate KernelSU for old kernel? diff --git a/website/docs/guide/how-to-integrate-for-non-gki.md b/website/docs/guide/how-to-integrate-for-non-gki.md index 3e29ce479c99..f2c81c20b198 100644 --- a/website/docs/guide/how-to-integrate-for-non-gki.md +++ b/website/docs/guide/how-to-integrate-for-non-gki.md @@ -1,6 +1,6 @@ # How to integrate KernelSU for non GKI kernel? -KernelSU can be integrate to non GKI kernel, and it is backported to 4.14 now, it is also possible to run on kernel below 4.14. +KernelSU can be integrate to non GKI kernel, and it is backported to 4.4 now, it is also possible to run on kernel below 4.4. Since the fragmentization of non GKI kernels, we don't have a uniform way to build it, so we can not provide non GKI boot images. But you can build the kernel yourself with KernelSU integrated. @@ -142,4 +142,57 @@ You should found the four functions in kernel source: 3. vfs_read, usually in `fs/read_write.c` 4. vfs_statx, usually in `fs/stat.c` +If your kernel does not have the `vfs_statx`, use `vfs_fstatat` instead: + +```diff +diff --git a/fs/stat.c b/fs/stat.c +index 068fdbcc9e26..5348b7bb9db2 100644 +--- a/fs/stat.c ++++ b/fs/stat.c +@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat) + } + EXPORT_SYMBOL(vfs_fstat); + ++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags); ++ + int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, + int flag) + { +@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, + int error = -EINVAL; + unsigned int lookup_flags = 0; + ++ ksu_handle_stat(&dfd, &filename, &flag); ++ + if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | + AT_EMPTY_PATH)) != 0) + goto out; +``` + +For kernels eariler than 4.14, you may need one extra call in `security/selinux/ss/services.c`: + +```diff +diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c +index bfc4ffa1fa1a..ffaf7b51a6ff 100644 +--- a/security/selinux/ss/services.c ++++ b/security/selinux/ss/services.c +@@ -838,6 +838,8 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, + orig_tclass, false); + } + ++extern int ksu_handle_security_bounded_transition(u32 *old_sid, u32 *new_sid); ++ + /* + * security_bounded_transition - check whether the given + * transition is directed to bounded, or not. +@@ -854,6 +856,8 @@ int security_bounded_transition(u32 old_sid, u32 new_sid) + int index; + int rc; + ++ ksu_handle_security_bounded_transition(&old_sid, &new_sid); ++ + if (!ss_initialized) + return 0; +``` + Finally, build your kernel again, KernelSU should works well. diff --git a/website/docs/id_ID/guide/faq.md b/website/docs/id_ID/guide/faq.md index 935d08eb1227..5a8c5e66d08a 100644 --- a/website/docs/id_ID/guide/faq.md +++ b/website/docs/id_ID/guide/faq.md @@ -41,7 +41,7 @@ Kernel perangkatlah yang mempengaruhi kompatibilitas KernelSU dan tidak ada hubu ## Dapatkah KernelSU mendukung kernel lama? -Ada kemungkinan, KernelSU sudah di-backport ke kernel 4.14 sekarang, untuk kernel yang lebih lama, Anda harus melakukan backport secara manual dan menyambut baik PR darimu! +Ada kemungkinan, KernelSU sudah di-backport ke kernel 4.4 sekarang, untuk kernel yang lebih lama, Anda harus melakukan backport secara manual dan menyambut baik PR darimu! ## Cara mengintegrasikan KernelSU untuk kernel lama? diff --git a/website/docs/id_ID/guide/how-to-integrate-for-non-gki.md b/website/docs/id_ID/guide/how-to-integrate-for-non-gki.md index 27b91cbfe01a..5eaf45269f13 100644 --- a/website/docs/id_ID/guide/how-to-integrate-for-non-gki.md +++ b/website/docs/id_ID/guide/how-to-integrate-for-non-gki.md @@ -1,6 +1,6 @@ # Bagaimana Caranya untuk mengintegrasikan KernelSU ke kernel non GKI? -KernelSU dapat diintegrasikan ke kernel non GKI, dan saat ini sudah di-backport ke 4.14, dan juga dapat dijalankan pada kernel di bawah 4.14. +KernelSU dapat diintegrasikan ke kernel non GKI, dan saat ini sudah di-backport ke 4.4, dan juga dapat dijalankan pada kernel di bawah 4.4. Karena fragmentasi kernel non GKI, kami tidak memiliki cara yang seragam untuk membangunnya, sehingga kami tidak dapat menyediakan gambar boot non GKI. Tetapi Anda dapat membangun kernel sendiri dengan KernelSU yang terintegrasi. diff --git a/website/docs/vi_VN/guide/faq.md b/website/docs/vi_VN/guide/faq.md index e87c25541d54..cb127a80c65e 100644 --- a/website/docs/vi_VN/guide/faq.md +++ b/website/docs/vi_VN/guide/faq.md @@ -41,8 +41,8 @@ Kernel của thiết bị ảnh hưởng đến khả năng tương thích của ## KernelSU có thể hỗ trợ kernel cũ không? -Có thể, KernelSU hiện đã được backport xuống kernel 4.14, đối với kernel cũ hơn, bạn cần backport một cách thủ công và các Pull-Requests luôn được hoan nghênh! +Có thể, KernelSU hiện đã được backport xuống kernel 4.4, đối với kernel cũ hơn, bạn cần backport một cách thủ công và các Pull-Requests luôn được hoan nghênh! ## Làm cách nào để tích hợp KernelSU cho kernel cũ? -Vui lòng tham khảo [hướng dẫn này](how-to-integrate-for-non-gki) \ No newline at end of file +Vui lòng tham khảo [hướng dẫn này](how-to-integrate-for-non-gki) diff --git a/website/docs/vi_VN/guide/how-to-integrate-for-non-gki.md b/website/docs/vi_VN/guide/how-to-integrate-for-non-gki.md index 3784048755df..eff2672601f1 100644 --- a/website/docs/vi_VN/guide/how-to-integrate-for-non-gki.md +++ b/website/docs/vi_VN/guide/how-to-integrate-for-non-gki.md @@ -1,6 +1,6 @@ # Làm thế nào để tích hợp KernelSU vào thiết bị không sử dụng GKI ? -KernelSU có thể được tích hợp vào kernel không phải GKI và hiện tại nó đã được backport xuống 4.14, thậm chí cũng có thể chạy trên kernel thấp hơn 4.14. +KernelSU có thể được tích hợp vào kernel không phải GKI và hiện tại nó đã được backport xuống 4.4, thậm chí cũng có thể chạy trên kernel thấp hơn 4.4. Do các kernel không phải GKI bị phân mảnh nên chúng tôi không có cách build thống nhất, vì vậy chúng tôi không thể cung cấp các boot image không phải GKI. Nhưng bạn có thể tự build kernel với KernelSU được tích hợp vào. diff --git a/website/docs/zh_CN/guide/faq.md b/website/docs/zh_CN/guide/faq.md index 644d9554c6d1..abc71ffd699f 100644 --- a/website/docs/zh_CN/guide/faq.md +++ b/website/docs/zh_CN/guide/faq.md @@ -41,7 +41,7 @@ KernelSU 的模块系统与 Magisk 的 magic mount 有冲突,如果 KernelSU ## KernelSU 可以支持旧内核吗? -可以,目前最低支持到 4.14;更低的版本你需要手动移植它,欢迎 PR ! +可以,目前最低支持到 4.4;更低的版本你需要手动移植它,欢迎 PR ! ## 如何为旧内核集成 KernelSU? diff --git a/website/docs/zh_CN/guide/how-to-integrate-for-non-gki.md b/website/docs/zh_CN/guide/how-to-integrate-for-non-gki.md index 5660bb5adb48..28aa4237b106 100644 --- a/website/docs/zh_CN/guide/how-to-integrate-for-non-gki.md +++ b/website/docs/zh_CN/guide/how-to-integrate-for-non-gki.md @@ -1,6 +1,6 @@ # 如何为非 GKI 内核集成 KernelSU -KernelSU 可以被集成到非 GKI 内核中,现在它最低支持到内核 4.14 版本;理论上也可以支持更低的版本。 +KernelSU 可以被集成到非 GKI 内核中,现在它最低支持到内核 4.4 版本;理论上也可以支持更低的版本。 由于非 GKI 内核的碎片化极其严重,因此通常没有统一的方法来编译它,所以我们也无法为非 GKI 设备提供 boot 镜像。但你完全可以自己集成 KernelSU 然后编译内核使用。 @@ -142,4 +142,57 @@ index 376543199b5a..82adcef03ecc 100644 3. vfs_read,通常位于 `fs/read_write.c` 4. vfs_statx,通常位于 `fs/stat.c` +如果你的内核没有 `vfs_statx`, 使用 `vfs_fstatat` 来代替它: + +```diff +diff --git a/fs/stat.c b/fs/stat.c +index 068fdbcc9e26..5348b7bb9db2 100644 +--- a/fs/stat.c ++++ b/fs/stat.c +@@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat) + } + EXPORT_SYMBOL(vfs_fstat); + ++extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags); ++ + int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, + int flag) + { +@@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat, + int error = -EINVAL; + unsigned int lookup_flags = 0; + ++ ksu_handle_stat(&dfd, &filename, &flag); ++ + if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | + AT_EMPTY_PATH)) != 0) + goto out; +``` + +对于早于 4.14 的内核,你可能需要在 `security/selinux/ss/services.c` 中做一个额外的修改: + +```diff +diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c +index bfc4ffa1fa1a..ffaf7b51a6ff 100644 +--- a/security/selinux/ss/services.c ++++ b/security/selinux/ss/services.c +@@ -838,6 +838,8 @@ int security_validate_transition(u32 oldsid, u32 newsid, u32 tasksid, + orig_tclass, false); + } + ++extern int ksu_handle_security_bounded_transition(u32 *old_sid, u32 *new_sid); ++ + /* + * security_bounded_transition - check whether the given + * transition is directed to bounded, or not. +@@ -854,6 +856,8 @@ int security_bounded_transition(u32 old_sid, u32 new_sid) + int index; + int rc; + ++ ksu_handle_security_bounded_transition(&old_sid, &new_sid); ++ + if (!ss_initialized) + return 0; +``` + 改完之后重新编译内核即可。