diff --git a/kernel/ksud.c b/kernel/ksud.c index 1d18132b..469dc376 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -1,4 +1,3 @@ - #include #include #include @@ -33,8 +32,6 @@ #include "arch.h" #include "klog.h" // IWYU pragma: keep #include "ksud.h" - -#include "kernel_compat.h" #include "selinux/selinux.h" #include "syscall_hook_manager.h" @@ -74,9 +71,9 @@ static struct work_struct stop_execve_hook_work; static struct work_struct stop_input_hook_work; #else bool ksu_vfs_read_hook __read_mostly = true; +bool ksu_execveat_hook __read_mostly = true; bool ksu_input_hook __read_mostly = true; #endif -bool ksu_execveat_hook __read_mostly = true; u32 ksu_file_sid; @@ -87,11 +84,11 @@ 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(); pr_info("mark tif for running process\n"); ksu_mark_running_process(); @@ -106,18 +103,6 @@ void on_post_fs_data(void) pr_info("ksu_file sid: %d\n", ksu_file_sid); } -struct user_arg_ptr { -#ifdef CONFIG_COMPAT - bool is_compat; -#endif - union { - const char __user *const __user *native; -#ifdef CONFIG_COMPAT - const compat_uptr_t __user *compat; -#endif - } ptr; -}; - extern void ext4_unregister_sysfs(struct super_block *sb); static void nuke_ext4_sysfs(void) { @@ -157,18 +142,95 @@ void on_boot_completed(void){ ksu_mark_running_process(); } +#define MAX_ARG_STRINGS 0x7FFFFFFF +struct user_arg_ptr { +#ifdef CONFIG_COMPAT + bool is_compat; +#endif + union { + const char __user *const __user *native; +#ifdef CONFIG_COMPAT + const compat_uptr_t __user *compat; +#endif + } ptr; +}; + +static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr) +{ + const char __user *native; + +#ifdef CONFIG_COMPAT + if (unlikely(argv.is_compat)) { + compat_uptr_t compat; + + if (get_user(compat, argv.ptr.compat + nr)) + return ERR_PTR(-EFAULT); + + return compat_ptr(compat); + } +#endif + + if (get_user(native, argv.ptr.native + nr)) + return ERR_PTR(-EFAULT); + + return native; +} + +/* + * count() counts the number of strings in array ARGV. + */ + +/* + * Make sure old GCC compiler can use __maybe_unused, + * Test passed in 4.4.x ~ 4.9.x when use GCC. + */ + +static int __maybe_unused count(struct user_arg_ptr argv, int max) +{ + int i = 0; + + if (argv.ptr.native != NULL) { + for (;;) { + const char __user *p = get_user_arg_ptr(argv, i); + + if (!p) + break; + + if (IS_ERR(p)) + return -EFAULT; + + if (i >= max) + return -E2BIG; + ++i; + + if (fatal_signal_pending(current)) + return -ERESTARTNOHAND; + cond_resched(); + } + } + return i; +} + 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 -// adapted from ksu_handle_execveat_ksud -static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const char *envp, size_t envp_len) +// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version +int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, + struct user_arg_ptr *argv, + struct user_arg_ptr *envp, int *flags) { +#ifndef KSU_KPROBES_HOOK + if (!ksu_execveat_hook) { + return 0; + } +#endif + struct filename *filename; + static const char app_process[] = "/system/bin/app_process"; static bool first_app_process = true; @@ -178,89 +240,101 @@ static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const c static const char old_system_init[] = "/init"; static bool init_second_stage_executed = false; - // return early when disabled - if (!ksu_execveat_hook) + if (!filename_ptr) return 0; - if (!filename) + filename = *filename_ptr; + if (IS_ERR(filename)) { return 0; + } - // debug! remove me! - pr_info("%s: filename: %s argv1: %s envp_len: %zu\n", __func__, filename, argv1, envp_len); - -#ifdef CONFIG_KSU_DEBUG - const char *envp_n = envp; - unsigned int envc = 1; - do { - pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n); - envp_n += strlen(envp_n) + 1; - envc++; - } while (envp_n < envp + 256); -#endif - - if (init_second_stage_executed) - goto first_app_process; - - // /system/bin/init with argv1 - if (!init_second_stage_executed - && (!memcmp(filename, system_bin_init, sizeof(system_bin_init) - 1))) { - if (argv1 && !strcmp(argv1, "second_stage")) { - pr_info("%s: /system/bin/init second_stage executed\n", __func__); - apply_kernelsu_rules(); - init_second_stage_executed = true; + if (unlikely(!memcmp(filename->name, system_bin_init, + sizeof(system_bin_init) - 1) && + argv)) { + // /system/bin/init executed + int argc = count(*argv, MAX_ARG_STRINGS); + pr_info("/system/bin/init argc: %d\n", argc); + if (argc > 1 && !init_second_stage_executed) { + const char __user *p = get_user_arg_ptr(*argv, 1); + if (p && !IS_ERR(p)) { + char first_arg[16]; + strncpy_from_user_nofault(first_arg, p, sizeof(first_arg)); + pr_info("/system/bin/init first arg: %s\n", first_arg); + if (!strcmp(first_arg, "second_stage")) { + pr_info("/system/bin/init second_stage executed\n"); + apply_kernelsu_rules(); + init_second_stage_executed = true; + } + } else { + pr_err("/system/bin/init parse args err!\n"); + } + } + } else if (unlikely(!memcmp(filename->name, old_system_init, + sizeof(old_system_init) - 1) && + argv)) { + // /init executed + int argc = count(*argv, MAX_ARG_STRINGS); + pr_info("/init argc: %d\n", argc); + if (argc > 1 && !init_second_stage_executed) { + /* This applies to versions between Android 6 ~ 7 */ + const char __user *p = get_user_arg_ptr(*argv, 1); + if (p && !IS_ERR(p)) { + char first_arg[16]; + strncpy_from_user_nofault(first_arg, p, sizeof(first_arg)); + pr_info("/init first arg: %s\n", first_arg); + if (!strcmp(first_arg, "--second-stage")) { + pr_info("/init second_stage executed\n"); + apply_kernelsu_rules(); + init_second_stage_executed = true; + } + } else { + pr_err("/init parse args err!\n"); + } + } else if (argc == 1 && !init_second_stage_executed && envp) { + /* This applies to versions between Android 8 ~ 9 */ + int envc = count(*envp, MAX_ARG_STRINGS); + if (envc > 0) { + int n; + for (n = 1; n <= envc; n++) { + const char __user *p = get_user_arg_ptr(*envp, n); + if (!p || IS_ERR(p)) { + continue; + } + char env[256]; + // Reading environment variable strings from user space + if (strncpy_from_user_nofault(env, p, sizeof(env)) < 0) + continue; + // Parsing environment variable names and values + char *env_name = env; + char *env_value = strchr(env, '='); + if (env_value == NULL) + continue; + // Replace equal sign with string terminator + *env_value = '\0'; + env_value++; + // Check if the environment variable name and value are matching + if (!strcmp(env_name, "INIT_SECOND_STAGE") && + (!strcmp(env_value, "1") || + !strcmp(env_value, "true"))) { + pr_info("/init second_stage executed\n"); + apply_kernelsu_rules(); + init_second_stage_executed = true; + } + } + } } } - // /init with argv1 - if (!init_second_stage_executed - && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) { - if (argv1 && !strcmp(argv1, "--second-stage")) { - pr_info("%s: /init --second-stage executed\n", __func__); - apply_kernelsu_rules(); - init_second_stage_executed = true; - } - } - - if (!envp || !envp_len) - goto first_app_process; - - // /init without argv1/useless-argv1 but usable envp - // untested! TODO: test and debug me! - if (!init_second_stage_executed && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) { - - // we hunt for "INIT_SECOND_STAGE" - const char *envp_n = envp; - unsigned int envc = 1; - do { - if (strstarts(envp_n, "INIT_SECOND_STAGE")) - break; - envp_n += strlen(envp_n) + 1; - envc++; - } while (envp_n < envp + envp_len); - pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n); - - if (!strcmp(envp_n, "INIT_SECOND_STAGE=1") - || !strcmp(envp_n, "INIT_SECOND_STAGE=true") ) { - pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__); - apply_kernelsu_rules(); - init_second_stage_executed = true; - } - } - -first_app_process: - if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) { + if (unlikely(first_app_process && !memcmp(filename->name, app_process, + sizeof(app_process) - 1))) { first_app_process = false; - pr_info("%s: exec app_process, /data prepared, second_stage: %d\n", __func__, init_second_stage_executed); - + pr_info("exec app_process, /data prepared, second_stage: %d\n", + init_second_stage_executed); struct task_struct *init_task; rcu_read_lock(); - init_task = rcu_dereference(current->parent); + init_task = rcu_dereference(current->real_parent); if (init_task) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) 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 } rcu_read_unlock(); @@ -270,81 +344,18 @@ first_app_process: return 0; } -int ksu_handle_pre_ksud(const char *filename) -{ - if (likely(!ksu_execveat_hook)) - return 0; - - // not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy) - // return 0; - if (likely(strcmp(filename, "/system/bin/init") && strcmp(filename, "/init") - && !strstarts(filename, "/system/bin/app_process") )) - return 0; - - if (!current || !current->mm) - return 0; - - // https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429 - // unsigned long arg_start, arg_end, env_start, env_end; - unsigned long arg_start = current->mm->arg_start; - unsigned long arg_end = current->mm->arg_end; - unsigned long env_start = current->mm->env_start; - unsigned long env_end = current->mm->env_end; - - size_t arg_len = arg_end - arg_start; - size_t envp_len = env_end - env_start; - - if (arg_len <= 0 || envp_len <= 0) // this wont make sense, filter it - return 0; - - #define ARGV_MAX 32 // this is enough for argv1 - #define ENVP_MAX 256 // this is enough for INIT_SECOND_STAGE - char args[ARGV_MAX]; - size_t argv_copy_len = (arg_len > ARGV_MAX) ? ARGV_MAX : arg_len; - char envp[ENVP_MAX]; - size_t envp_copy_len = (envp_len > ENVP_MAX) ? ENVP_MAX : envp_len; - - // we cant use strncpy on here, else it will truncate once it sees \0 - if (ksu_copy_from_user_retry(args, (void __user *)arg_start, argv_copy_len)) - return 0; - - if (ksu_copy_from_user_retry(envp, (void __user *)env_start, envp_copy_len)) - return 0; - - args[ARGV_MAX - 1] = '\0'; - envp[ENVP_MAX - 1] = '\0'; - - // we only need argv1 ! - // abuse strlen here since it only gets length up to \0 - char *argv1 = args + strlen(args) + 1; - if (argv1 >= args + argv_copy_len) // out of bounds! - argv1 = ""; - - return ksu_handle_bprm_ksud(filename, argv1, envp, envp_copy_len); -} - -int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, - struct user_arg_ptr *argv, struct user_arg_ptr *envp, - int *flags) -{ - // this is now handled via security_bprm_check - // we only keep this for the sake of old hooks. - return 0; -} - static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *); static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *); static struct file_operations fops_proxy; static ssize_t read_count_append = 0; static ssize_t read_proxy(struct file *file, char __user *buf, size_t count, - loff_t *pos) + loff_t *pos) { bool first_read = file->f_pos == 0; ssize_t ret = orig_read(file, buf, count, pos); if (first_read) { - pr_info("read_proxy append %ld + %ld\n", ret, - read_count_append); + pr_info("read_proxy append %ld + %ld\n", ret, read_count_append); ret += read_count_append; } return ret; @@ -355,15 +366,14 @@ static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to) bool first_read = iocb->ki_pos == 0; ssize_t ret = orig_read_iter(iocb, to); if (first_read) { - pr_info("read_iter_proxy append %ld + %ld\n", ret, - read_count_append); + pr_info("read_iter_proxy append %ld + %ld\n", ret, read_count_append); ret += read_count_append; } return ret; } -int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, - size_t *count_ptr, loff_t **pos) +static int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, + size_t *count_ptr, loff_t **pos) { #ifndef KSU_KPROBES_HOOK if (!ksu_vfs_read_hook) { @@ -421,7 +431,7 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, size_t rc_count = strlen(KERNEL_SU_RC); pr_info("vfs_read: %s, comm: %s, count: %zu, rc_count: %zu\n", dpath, - current->comm, count, rc_count); + current->comm, count, rc_count); if (count < rc_count) { pr_err("count: %zu < rc_count: %zu\n", count, rc_count); @@ -456,8 +466,8 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, return 0; } -int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, - size_t *count_ptr) +static int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, + size_t *count_ptr) { struct file *file = fget(fd); if (!file) { @@ -476,7 +486,7 @@ static bool is_volumedown_enough(unsigned int count) } int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, - int *value) + int *value) { #ifndef KSU_KPROBES_HOOK if (!ksu_input_hook) { @@ -521,123 +531,27 @@ bool ksu_is_safe_mode() } #ifdef KSU_KPROBES_HOOK + static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) { - /* - asmlinkage int sys_execve(const char __user *filenamei, - const char __user *const __user *argv, - const char __user *const __user *envp, struct pt_regs *regs) - */ struct pt_regs *real_regs = PT_REAL_REGS(regs); - const char __user *filename_user = (const char __user *)PT_REGS_PARM1(real_regs); - const char __user *const __user *__argv = (const char __user *const __user *)PT_REGS_PARM2(real_regs); - const char __user *const __user *__envp = (const char __user *const __user *)PT_REGS_PARM3(real_regs); + const char __user **filename_user = + (const char **)&PT_REGS_PARM1(real_regs); + const char __user *const __user *__argv = + (const char __user *const __user *)PT_REGS_PARM2(real_regs); + struct user_arg_ptr argv = { .ptr.native = __argv }; + struct filename filename_in, *filename_p; char path[32]; if (!filename_user) return 0; -// filename stage - if (ksu_copy_from_user_retry(path, filename_user, sizeof(path))) - return 0; + memset(path, 0, sizeof(path)); + strncpy_from_user_nofault(path, *filename_user, 32); + filename_in.name = path; - path[sizeof(path) - 1] = '\0'; - - // not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy) - // we dont care !! - if (likely(strcmp(path, "/system/bin/init") && strcmp(path, "/init") - && !strstarts(path, "/system/bin/app_process") )) - return 0; - -// argv stage - char argv1[32] = {0}; - // memzero_explicit(argv1, 32); - if (__argv) { - const char __user *arg1_user = NULL; - // grab argv[1] pointer - // this looks like - /* - * 0x1000 ./program << this is __argv - * 0x1001 -o - * 0x1002 arg - */ - if (ksu_copy_from_user_retry(&arg1_user, __argv + 1, sizeof(arg1_user))) - goto no_argv1; // copy argv[1] pointer fail, probably no argv1 !! - - if (arg1_user) - ksu_copy_from_user_retry(argv1, arg1_user, sizeof(argv1)); - } - -no_argv1: - argv1[sizeof(argv1) - 1] = '\0'; - -// envp stage - #define ENVP_MAX 256 - char envp[ENVP_MAX] = {0}; - char *dst = envp; - size_t envp_len = 0; - int i = 0; // to track user pointer offset from __envp - - // memzero_explicit(envp, ENVP_MAX); - - if (__envp) { - do { - const char __user *env_entry_user = NULL; - // this is also like argv above - /* - * 0x1001 PATH=/bin - * 0x1002 VARIABLE=value - * 0x1002 some_more_env_var=1 - */ - - // check if pointer exists - if (ksu_copy_from_user_retry(&env_entry_user, __envp + i, sizeof(env_entry_user))) - break; - - // check if no more env entry - if (!env_entry_user) - break; - - // probably redundant to while condition but ok - if (envp_len >= ENVP_MAX - 1) - break; - - // copy strings from env_entry_user pointer that we collected - // also break if failed - if (ksu_copy_from_user_retry(dst, env_entry_user, ENVP_MAX - envp_len)) - break; - - // get the length of that new copy above - // get lngth of dst as far as ENVP_MAX - current collected envp_len - size_t len = strnlen(dst, ENVP_MAX - envp_len); - if (envp_len + len + 1 > ENVP_MAX) - break; // if more than 255 bytes, bail - - dst[len] = '\0'; - // collect total number of copied strings - envp_len = envp_len + len + 1; - // increment dst address since we need to put something on next iter - dst = dst + len + 1; - // pointer walk, __envp + i - i++; - } while (envp_len < ENVP_MAX); - } - - /* - at this point, we shoul've collected envp from - * 0x1001 PATH=/bin - * 0x1002 VARIABLE=value - * 0x1002 some_more_env_var=1 - to - * 0x1234 PATH=/bin\0VARIABLE=value\0some_more_env_var=1\0\0\0\0 - */ - - envp[ENVP_MAX - 1] = '\0'; - -#ifdef CONFIG_KSU_DEBUG - pr_info("%s: filename: %s argv[1]:%s envp_len: %zu\n", __func__, path, argv1, envp_len); -#endif - return ksu_handle_bprm_ksud(path, argv1, envp, envp_len); + filename_p = &filename_in; + return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, NULL); } static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs) @@ -651,7 +565,7 @@ static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs) } static int input_handle_event_handler_pre(struct kprobe *p, - struct pt_regs *regs) + struct pt_regs *regs) { unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs); unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs); @@ -688,51 +602,6 @@ static void do_stop_input_hook(struct work_struct *work) { unregister_kprobe(&input_event_kp); } -#else -static int ksu_execve_ksud_common(const char __user *filename_user, - struct user_arg_ptr *argv) -{ - struct filename filename_in, *filename_p; - char path[32]; - long len; - - // return early if disabled. - if (!ksu_execveat_hook) { - return 0; - } - - if (!filename_user) - return 0; - - len = ksu_strncpy_from_user_nofault(path, filename_user, 32); - if (len <= 0) - return 0; - - path[sizeof(path) - 1] = '\0'; - - // this is because ksu_handle_execveat_ksud calls it filename->name - filename_in.name = path; - filename_p = &filename_in; - - return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, argv, NULL, NULL); -} - -int __maybe_unused ksu_handle_execve_ksud(const char __user *filename_user, - const char __user *const __user *__argv) -{ - struct user_arg_ptr argv = { .ptr.native = __argv }; - return ksu_execve_ksud_common(filename_user, &argv); -} - -#if defined(CONFIG_COMPAT) && defined(CONFIG_64BIT) -int __maybe_unused ksu_handle_compat_execve_ksud(const char __user *filename_user, - const compat_uptr_t __user *__argv) -{ - struct user_arg_ptr argv = { .ptr.compat = __argv }; - return ksu_execve_ksud_common(filename_user, &argv); -} -#endif /* COMPAT & 64BIT */ - #endif static void stop_vfs_read_hook(void) @@ -752,23 +621,22 @@ static void stop_execve_hook(void) bool ret = schedule_work(&stop_execve_hook_work); pr_info("unregister execve kprobe: %d!\n", ret); #else - pr_info("stop execve_hook\n"); ksu_execveat_hook = false; + pr_info("stop execve_hook\n"); #endif } static void stop_input_hook(void) { -#ifdef KSU_KPROBES_HOOK static bool input_hook_stopped = false; if (input_hook_stopped) { return; } input_hook_stopped = true; +#ifdef KSU_KPROBES_HOOK bool ret = schedule_work(&stop_input_hook_work); pr_info("unregister input kprobe: %d!\n", ret); #else - if (!ksu_input_hook) { return; } ksu_input_hook = false; pr_info("stop input_hook\n"); #endif @@ -805,4 +673,4 @@ void ksu_ksud_exit(void) #endif is_boot_phase = false; volumedown_pressed_count = 0; -} +} \ No newline at end of file diff --git a/kernel/ksud.h b/kernel/ksud.h index 4c9f6bc8..140969e3 100644 --- a/kernel/ksud.h +++ b/kernel/ksud.h @@ -18,7 +18,4 @@ 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); - #endif diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index 4661c997..7d74ce87 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" // IWYU pragma: keep +#include "linux/lsm_audit.h" #include "xfrm.h" #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) @@ -141,7 +141,7 @@ void apply_kernelsu_rules(void) // https://android-review.googlesource.com/c/platform/system/logging/+/3725346 ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr"); - + mutex_unlock(&ksu_rules); } @@ -157,39 +157,16 @@ void apply_kernelsu_rules(void) #define CMD_TYPE_CHANGE 8 #define CMD_GENFSCON 9 -// keep it! -extern bool ksu_is_compat __read_mostly; - -// armv7l kernel compat -#ifdef CONFIG_64BIT -#define usize u64 -#else -#define usize u32 -#endif - struct sepol_data { - u32 cmd; - u32 subcmd; - usize field_sepol1; - usize field_sepol2; - usize field_sepol3; - usize field_sepol4; - usize field_sepol5; - usize field_sepol6; - usize field_sepol7; -}; - -// ksud 32-bit on arm64 kernel -struct __maybe_unused sepol_data_compat { - u32 cmd; - u32 subcmd; - u32 field_sepol1; - u32 field_sepol2; - u32 field_sepol3; - u32 field_sepol4; - u32 field_sepol5; - u32 field_sepol6; - u32 field_sepol7; + 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; }; static int get_object(char *buf, char __user *user_object, size_t buf_sz, @@ -208,15 +185,18 @@ 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)) + +#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) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \ !defined(KSU_COMPAT_USE_SELINUX_STATE) avc_ss_reset(0); selnl_notify_policyload(0); @@ -242,76 +222,55 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) pr_info("SELinux permissive or disabled when handle policy!\n"); } - u32 cmd, subcmd; - char __user *sepol1, *sepol2, *sepol3, *sepol4, *sepol5, *sepol6, *sepol7; - - if (unlikely(ksu_is_compat)) { - struct sepol_data_compat data_compat; - if (copy_from_user(&data_compat, arg4, sizeof(struct sepol_data_compat))) { - pr_err("sepol: copy sepol_data failed.\n"); - return -1; - } - pr_info("sepol: running in compat mode!\n"); - sepol1 = compat_ptr(data_compat.field_sepol1); - sepol2 = compat_ptr(data_compat.field_sepol2); - sepol3 = compat_ptr(data_compat.field_sepol3); - sepol4 = compat_ptr(data_compat.field_sepol4); - sepol5 = compat_ptr(data_compat.field_sepol5); - sepol6 = compat_ptr(data_compat.field_sepol6); - sepol7 = compat_ptr(data_compat.field_sepol7); - cmd = data_compat.cmd; - subcmd = data_compat.subcmd; - } else { - struct sepol_data data; - if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) { - pr_err("sepol: copy sepol_data failed.\n"); - return -1; - } - sepol1 = data.field_sepol1; - sepol2 = data.field_sepol2; - sepol3 = data.field_sepol3; - sepol4 = data.field_sepol4; - sepol5 = data.field_sepol5; - sepol6 = data.field_sepol6; - sepol7 = data.field_sepol7; - cmd = data.cmd; - subcmd = data.subcmd; + struct sepol_data data = { 0 }; + if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) { + pr_err("sepol: copy sepol_data failed.\n"); + return -EINVAL; } + u32 cmd = data.cmd; + u32 subcmd = data.subcmd; + mutex_lock(&ksu_rules); db = get_policydb(); int ret = -EINVAL; - if (cmd == CMD_NORMAL_PERM) { + + switch (cmd) { + case CMD_NORMAL_PERM: { char src_buf[MAX_SEPOL_LEN]; char tgt_buf[MAX_SEPOL_LEN]; char cls_buf[MAX_SEPOL_LEN]; char perm_buf[MAX_SEPOL_LEN]; char *s, *t, *c, *p; - if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) { + if (get_object(src_buf, (void __user *)data.sepol1, + sizeof(src_buf), &s) < 0) { pr_err("sepol: copy src failed.\n"); goto exit; } - if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) { + if (get_object(tgt_buf, (void __user *)data.sepol2, + sizeof(tgt_buf), &t) < 0) { pr_err("sepol: copy tgt failed.\n"); goto exit; } - if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) { + if (get_object(cls_buf, (void __user *)data.sepol3, + sizeof(cls_buf), &c) < 0) { pr_err("sepol: copy cls failed.\n"); goto exit; } - if (get_object(perm_buf, sepol4, sizeof(perm_buf), &p) < - 0) { + if (get_object(perm_buf, (void __user *)data.sepol4, + sizeof(perm_buf), &p) < 0) { pr_err("sepol: copy perm failed.\n"); goto exit; } bool success = false; + if (subcmd == 1) { success = ksu_allow(db, s, t, c, p); } else if (subcmd == 2) { @@ -324,8 +283,9 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) pr_err("sepol: unknown subcmd: %d\n", subcmd); } ret = success ? 0 : -EINVAL; - - } else if (cmd == CMD_XPERM) { + break; + } + case CMD_XPERM: { char src_buf[MAX_SEPOL_LEN]; char tgt_buf[MAX_SEPOL_LEN]; char cls_buf[MAX_SEPOL_LEN]; @@ -335,25 +295,28 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) char perm_set[MAX_SEPOL_LEN]; char *s, *t, *c; - if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) { + if (get_object(src_buf, (void __user *)data.sepol1, + sizeof(src_buf), &s) < 0) { pr_err("sepol: copy src failed.\n"); goto exit; } - if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) { + if (get_object(tgt_buf, (void __user *)data.sepol2, + sizeof(tgt_buf), &t) < 0) { pr_err("sepol: copy tgt failed.\n"); goto exit; } - if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) { + if (get_object(cls_buf, (void __user *)data.sepol3, + sizeof(cls_buf), &c) < 0) { pr_err("sepol: copy cls failed.\n"); goto exit; } - if (strncpy_from_user(operation, sepol4, + if (strncpy_from_user(operation, (void __user *)data.sepol4, sizeof(operation)) < 0) { pr_err("sepol: copy operation failed.\n"); goto exit; } - if (strncpy_from_user(perm_set, sepol5, sizeof(perm_set)) < - 0) { + if (strncpy_from_user(perm_set, (void __user *)data.sepol5, + sizeof(perm_set)) < 0) { pr_err("sepol: copy perm_set failed.\n"); goto exit; } @@ -369,10 +332,13 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) pr_err("sepol: unknown subcmd: %d\n", subcmd); } ret = success ? 0 : -EINVAL; - } else if (cmd == CMD_TYPE_STATE) { + break; + } + case CMD_TYPE_STATE: { char src[MAX_SEPOL_LEN]; - if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) { + if (strncpy_from_user(src, (void __user *)data.sepol1, + sizeof(src)) < 0) { pr_err("sepol: copy src failed.\n"); goto exit; } @@ -387,16 +353,20 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) } if (success) ret = 0; - - } else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) { + break; + } + case CMD_TYPE: + case CMD_TYPE_ATTR: { char type[MAX_SEPOL_LEN]; char attr[MAX_SEPOL_LEN]; - if (strncpy_from_user(type, sepol1, sizeof(type)) < 0) { + if (strncpy_from_user(type, (void __user *)data.sepol1, + sizeof(type)) < 0) { pr_err("sepol: copy type failed.\n"); goto exit; } - if (strncpy_from_user(attr, sepol2, sizeof(attr)) < 0) { + if (strncpy_from_user(attr, (void __user *)data.sepol2, + sizeof(attr)) < 0) { pr_err("sepol: copy attr failed.\n"); goto exit; } @@ -412,11 +382,13 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) goto exit; } ret = 0; - - } else if (cmd == CMD_ATTR) { + break; + } + case CMD_ATTR: { char attr[MAX_SEPOL_LEN]; - if (strncpy_from_user(attr, sepol1, sizeof(attr)) < 0) { + if (strncpy_from_user(attr, (void __user *)data.sepol1, + sizeof(attr)) < 0) { pr_err("sepol: copy attr failed.\n"); goto exit; } @@ -425,36 +397,41 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) goto exit; } ret = 0; - - } else if (cmd == CMD_TYPE_TRANSITION) { + break; + } + case CMD_TYPE_TRANSITION: { char src[MAX_SEPOL_LEN]; char tgt[MAX_SEPOL_LEN]; char cls[MAX_SEPOL_LEN]; char default_type[MAX_SEPOL_LEN]; char object[MAX_SEPOL_LEN]; - if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) { + if (strncpy_from_user(src, (void __user *)data.sepol1, + sizeof(src)) < 0) { pr_err("sepol: copy src failed.\n"); goto exit; } - if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) { + if (strncpy_from_user(tgt, (void __user *)data.sepol2, + sizeof(tgt)) < 0) { pr_err("sepol: copy tgt failed.\n"); goto exit; } - if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) { + if (strncpy_from_user(cls, (void __user *)data.sepol3, + sizeof(cls)) < 0) { pr_err("sepol: copy cls failed.\n"); goto exit; } - if (strncpy_from_user(default_type, sepol4, + if (strncpy_from_user(default_type, (void __user *)data.sepol4, sizeof(default_type)) < 0) { pr_err("sepol: copy default_type failed.\n"); goto exit; } char *real_object; - if (sepol5 == NULL) { + if ((void __user *)data.sepol5 == NULL) { real_object = NULL; } else { - if (strncpy_from_user(object, sepol5, + if (strncpy_from_user(object, + (void __user *)data.sepol5, sizeof(object)) < 0) { pr_err("sepol: copy object failed.\n"); goto exit; @@ -466,26 +443,30 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) default_type, real_object); if (success) ret = 0; - - } else if (cmd == CMD_TYPE_CHANGE) { + break; + } + case CMD_TYPE_CHANGE: { char src[MAX_SEPOL_LEN]; char tgt[MAX_SEPOL_LEN]; char cls[MAX_SEPOL_LEN]; char default_type[MAX_SEPOL_LEN]; - if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) { + if (strncpy_from_user(src, (void __user *)data.sepol1, + sizeof(src)) < 0) { pr_err("sepol: copy src failed.\n"); goto exit; } - if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) { + if (strncpy_from_user(tgt, (void __user *)data.sepol2, + sizeof(tgt)) < 0) { pr_err("sepol: copy tgt failed.\n"); goto exit; } - if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) { + if (strncpy_from_user(cls, (void __user *)data.sepol3, + sizeof(cls)) < 0) { pr_err("sepol: copy cls failed.\n"); goto exit; } - if (strncpy_from_user(default_type, sepol4, + if (strncpy_from_user(default_type, (void __user *)data.sepol4, sizeof(default_type)) < 0) { pr_err("sepol: copy default_type failed.\n"); goto exit; @@ -502,20 +483,24 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) } if (success) ret = 0; - } else if (cmd == CMD_GENFSCON) { + break; + } + case CMD_GENFSCON: { char name[MAX_SEPOL_LEN]; char path[MAX_SEPOL_LEN]; char context[MAX_SEPOL_LEN]; - if (strncpy_from_user(name, sepol1, sizeof(name)) < 0) { + if (strncpy_from_user(name, (void __user *)data.sepol1, + sizeof(name)) < 0) { pr_err("sepol: copy name failed.\n"); goto exit; } - if (strncpy_from_user(path, sepol2, sizeof(path)) < 0) { + if (strncpy_from_user(path, (void __user *)data.sepol2, + sizeof(path)) < 0) { pr_err("sepol: copy path failed.\n"); goto exit; } - if (strncpy_from_user(context, sepol3, sizeof(context)) < - 0) { + if (strncpy_from_user(context, (void __user *)data.sepol3, + sizeof(context)) < 0) { pr_err("sepol: copy context failed.\n"); goto exit; } @@ -525,8 +510,12 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4) goto exit; } ret = 0; - } else { + break; + } + default: { pr_err("sepol: unknown cmd: %d\n", cmd); + break; + } } exit: @@ -537,4 +526,4 @@ exit: reset_avc_cache(); return ret; -} +} \ No newline at end of file diff --git a/kernel/sucompat.c b/kernel/sucompat.c index e9386f43..0c100e0f 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -150,60 +150,6 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags) return 0; } -// the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code -int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, - void *__never_use_argv, void *__never_use_envp, - int *__never_use_flags) -{ - struct filename *filename; - const char sh[] = KSUD_PATH; - const char su[] = SU_PATH; - -#ifdef KSU_MANUAL_HOOK - if (!ksu_su_compat_enabled) { - return 0; - } -#endif - if (unlikely(!filename_ptr)) - return 0; - - filename = *filename_ptr; - if (IS_ERR(filename)) { - return 0; - } - - if (likely(memcmp(filename->name, su, sizeof(su)))) - return 0; - -#if __SULOG_GATE - bool is_allowed = ksu_is_allow_uid_for_current(current_uid().val); - ksu_sulog_report_syscall(current_uid().val, NULL, "execve", filename->name); - - if (!is_allowed) { - return 0; - } - - ksu_sulog_report_su_attempt(current_uid().val, NULL, filename->name, is_allowed); -#else - if (!ksu_is_allow_uid_for_current(current_uid().val)) { - return 0; - } -#endif - - pr_info("do_execveat_common su found\n"); - memcpy((void *)filename->name, sh, sizeof(sh)); - - escape_with_root_profile(); - - return 0; -} - -int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, - void *envp, int *flags) -{ - return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, 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) diff --git a/kernel/syscall_hook_manager.c b/kernel/syscall_hook_manager.c index 79df3804..fa196126 100644 --- a/kernel/syscall_hook_manager.c +++ b/kernel/syscall_hook_manager.c @@ -209,7 +209,6 @@ static inline bool check_syscall_fastpath(int nr) case __NR_execve: case __NR_setresuid: case __NR_faccessat2: - case __NR_execveat: case __NR_clone: case __NR_clone3: return true; @@ -242,10 +241,6 @@ int ksu_handle_init_mark_tracker(int *fd, const char __user **filename_user, #include "manual_su.h" #endif -#ifdef CONFIG_COMPAT -bool ksu_is_compat __read_mostly = false; -#endif - #ifndef LOOKUP_FOLLOW #define LOOKUP_FOLLOW 0x0001 #endif @@ -266,48 +261,6 @@ static inline void ksu_handle_inode_permission(struct pt_regs *regs) } } -static inline void ksu_handle_bprm_check_security(struct pt_regs *regs, long id) -{ - const char __user *filename; - char path_buf[256]; - - if (id == __NR_execve) - filename = (const char __user *)PT_REGS_PARM1(regs); - else /* __NR_execveat */ - filename = (const char __user *)PT_REGS_PARM2(regs); - - if (!ksu_execveat_hook) - return; - - memset(path_buf, 0, sizeof(path_buf)); - strncpy_from_user_nofault(path_buf, filename, sizeof(path_buf)); - -#ifdef CONFIG_COMPAT - static bool compat_check_done __read_mostly = false; - if (unlikely(!compat_check_done) && - unlikely(!strcmp(path_buf, "/data/adb/ksud"))) { - char buf[4]; - struct file *file = filp_open(path_buf, O_RDONLY, 0); - if (!IS_ERR(file)) { - loff_t pos = 0; - kernel_read(file, buf, 4, &pos); - if (!memcmp(buf, "\x7f\x45\x4c\x46", 4)) { - char elf_class; - pos = 4; - kernel_read(file, &elf_class, 1, &pos); - if (elf_class == 0x01) - ksu_is_compat = true; - pr_info("%s: %s ELF magic found! ksu_is_compat: %d\n", - __func__, path_buf, ksu_is_compat); - compat_check_done = true; - } - filp_close(file, NULL); - } - } -#endif - ksu_handle_pre_ksud(path_buf); -} - static inline void ksu_handle_task_alloc(struct pt_regs *regs) { #ifdef CONFIG_KSU_MANUAL_SU @@ -371,10 +324,6 @@ static void ksu_sys_enter_handler(void *data, struct pt_regs *regs, long id) if (id == __NR_faccessat || id == __NR_faccessat2) return ksu_handle_inode_permission(regs); - // Handle bprm_check_security via execve/execveat - if (id == __NR_execve || id == __NR_execveat) - return ksu_handle_bprm_check_security(regs, id); - #ifdef CONFIG_KSU_MANUAL_SU // Handle task_alloc via clone/fork if (id == __NR_clone || id == __NR_clone3) diff --git a/kernel/syscall_hook_manager.h b/kernel/syscall_hook_manager.h index 19b4e24f..b19d617b 100644 --- a/kernel/syscall_hook_manager.h +++ b/kernel/syscall_hook_manager.h @@ -15,8 +15,6 @@ #define DEVPTS_SUPER_MAGIC 0x1cd1 #endif -extern bool ksu_is_compat __read_mostly; - extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c // Hook manager initialization and cleanup