kernel: Undo some changes

This commit is contained in:
ShirkNeko
2025-11-09 16:09:59 +08:00
parent a0ceda008d
commit 1c86944142
6 changed files with 296 additions and 549 deletions

View File

@@ -1,4 +1,3 @@
#include <linux/rcupdate.h> #include <linux/rcupdate.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/task_work.h> #include <linux/task_work.h>
@@ -33,8 +32,6 @@
#include "arch.h" #include "arch.h"
#include "klog.h" // IWYU pragma: keep #include "klog.h" // IWYU pragma: keep
#include "ksud.h" #include "ksud.h"
#include "kernel_compat.h"
#include "selinux/selinux.h" #include "selinux/selinux.h"
#include "syscall_hook_manager.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; static struct work_struct stop_input_hook_work;
#else #else
bool ksu_vfs_read_hook __read_mostly = true; bool ksu_vfs_read_hook __read_mostly = true;
bool ksu_execveat_hook __read_mostly = true;
bool ksu_input_hook __read_mostly = true; bool ksu_input_hook __read_mostly = true;
#endif #endif
bool ksu_execveat_hook __read_mostly = true;
u32 ksu_file_sid; u32 ksu_file_sid;
@@ -87,11 +84,11 @@ void on_post_fs_data(void)
{ {
static bool done = false; static bool done = false;
if (done) { if (done) {
pr_info("%s already done\n", __func__); pr_info("on_post_fs_data already done\n");
return; return;
} }
done = true; done = true;
pr_info("%s!\n", __func__); pr_info("on_post_fs_data!\n");
ksu_load_allow_list(); ksu_load_allow_list();
pr_info("mark tif for running process\n"); pr_info("mark tif for running process\n");
ksu_mark_running_process(); ksu_mark_running_process();
@@ -106,18 +103,6 @@ void on_post_fs_data(void)
pr_info("ksu_file sid: %d\n", ksu_file_sid); 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); extern void ext4_unregister_sysfs(struct super_block *sb);
static void nuke_ext4_sysfs(void) static void nuke_ext4_sysfs(void)
{ {
@@ -157,18 +142,95 @@ void on_boot_completed(void){
ksu_mark_running_process(); 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) static void on_post_fs_data_cbfun(struct callback_head *cb)
{ {
on_post_fs_data(); 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 // IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version
// this can probably work int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
// adapted from ksu_handle_execveat_ksud struct user_arg_ptr *argv,
static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const char *envp, size_t envp_len) 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 const char app_process[] = "/system/bin/app_process";
static bool first_app_process = true; 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 const char old_system_init[] = "/init";
static bool init_second_stage_executed = false; static bool init_second_stage_executed = false;
// return early when disabled if (!filename_ptr)
if (!ksu_execveat_hook)
return 0; return 0;
if (!filename) filename = *filename_ptr;
if (IS_ERR(filename)) {
return 0; return 0;
}
// debug! remove me! if (unlikely(!memcmp(filename->name, system_bin_init,
pr_info("%s: filename: %s argv1: %s envp_len: %zu\n", __func__, filename, argv1, envp_len); sizeof(system_bin_init) - 1) &&
argv)) {
#ifdef CONFIG_KSU_DEBUG // /system/bin/init executed
const char *envp_n = envp; int argc = count(*argv, MAX_ARG_STRINGS);
unsigned int envc = 1; pr_info("/system/bin/init argc: %d\n", argc);
do { if (argc > 1 && !init_second_stage_executed) {
pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n); const char __user *p = get_user_arg_ptr(*argv, 1);
envp_n += strlen(envp_n) + 1; if (p && !IS_ERR(p)) {
envc++; char first_arg[16];
} while (envp_n < envp + 256); strncpy_from_user_nofault(first_arg, p, sizeof(first_arg));
#endif pr_info("/system/bin/init first arg: %s\n", first_arg);
if (!strcmp(first_arg, "second_stage")) {
if (init_second_stage_executed) pr_info("/system/bin/init second_stage executed\n");
goto first_app_process; apply_kernelsu_rules();
init_second_stage_executed = true;
// /system/bin/init with argv1 }
if (!init_second_stage_executed } else {
&& (!memcmp(filename, system_bin_init, sizeof(system_bin_init) - 1))) { pr_err("/system/bin/init parse args err!\n");
if (argv1 && !strcmp(argv1, "second_stage")) { }
pr_info("%s: /system/bin/init second_stage executed\n", __func__); }
} 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(); apply_kernelsu_rules();
init_second_stage_executed = true; 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) if (unlikely(first_app_process && !memcmp(filename->name, app_process,
goto first_app_process; sizeof(app_process) - 1))) {
// /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)) {
first_app_process = false; 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; struct task_struct *init_task;
rcu_read_lock(); rcu_read_lock();
init_task = rcu_dereference(current->parent); init_task = rcu_dereference(current->real_parent);
if (init_task) { if (init_task) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
task_work_add(init_task, &on_post_fs_data_cb, TWA_RESUME); 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(); rcu_read_unlock();
@@ -270,68 +344,6 @@ first_app_process:
return 0; 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)(struct file *, char __user *, size_t, loff_t *);
static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *); static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *);
static struct file_operations fops_proxy; static struct file_operations fops_proxy;
@@ -343,8 +355,7 @@ static ssize_t read_proxy(struct file *file, char __user *buf, size_t count,
bool first_read = file->f_pos == 0; bool first_read = file->f_pos == 0;
ssize_t ret = orig_read(file, buf, count, pos); ssize_t ret = orig_read(file, buf, count, pos);
if (first_read) { if (first_read) {
pr_info("read_proxy append %ld + %ld\n", ret, pr_info("read_proxy append %ld + %ld\n", ret, read_count_append);
read_count_append);
ret += read_count_append; ret += read_count_append;
} }
return ret; return ret;
@@ -355,14 +366,13 @@ static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to)
bool first_read = iocb->ki_pos == 0; bool first_read = iocb->ki_pos == 0;
ssize_t ret = orig_read_iter(iocb, to); ssize_t ret = orig_read_iter(iocb, to);
if (first_read) { if (first_read) {
pr_info("read_iter_proxy append %ld + %ld\n", ret, pr_info("read_iter_proxy append %ld + %ld\n", ret, read_count_append);
read_count_append);
ret += read_count_append; ret += read_count_append;
} }
return ret; return ret;
} }
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, static 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_KPROBES_HOOK
@@ -456,7 +466,7 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
return 0; return 0;
} }
int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, static 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); struct file *file = fget(fd);
@@ -521,123 +531,27 @@ bool ksu_is_safe_mode()
} }
#ifdef KSU_KPROBES_HOOK #ifdef KSU_KPROBES_HOOK
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) 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); 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 **filename_user =
const char __user *const __user *__argv = (const char __user *const __user *)PT_REGS_PARM2(real_regs); (const char **)&PT_REGS_PARM1(real_regs);
const char __user *const __user *__envp = (const char __user *const __user *)PT_REGS_PARM3(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]; char path[32];
if (!filename_user) if (!filename_user)
return 0; return 0;
// filename stage memset(path, 0, sizeof(path));
if (ksu_copy_from_user_retry(path, filename_user, sizeof(path))) strncpy_from_user_nofault(path, *filename_user, 32);
return 0; filename_in.name = path;
path[sizeof(path) - 1] = '\0'; filename_p = &filename_in;
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, NULL);
// 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);
} }
static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs) static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
@@ -688,51 +602,6 @@ static void do_stop_input_hook(struct work_struct *work)
{ {
unregister_kprobe(&input_event_kp); 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 #endif
static void stop_vfs_read_hook(void) 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); bool ret = schedule_work(&stop_execve_hook_work);
pr_info("unregister execve kprobe: %d!\n", ret); pr_info("unregister execve kprobe: %d!\n", ret);
#else #else
pr_info("stop execve_hook\n");
ksu_execveat_hook = false; ksu_execveat_hook = false;
pr_info("stop execve_hook\n");
#endif #endif
} }
static void stop_input_hook(void) static void stop_input_hook(void)
{ {
#ifdef KSU_KPROBES_HOOK
static bool input_hook_stopped = false; static bool input_hook_stopped = false;
if (input_hook_stopped) { if (input_hook_stopped) {
return; return;
} }
input_hook_stopped = true; input_hook_stopped = true;
#ifdef KSU_KPROBES_HOOK
bool ret = schedule_work(&stop_input_hook_work); bool ret = schedule_work(&stop_input_hook_work);
pr_info("unregister input kprobe: %d!\n", ret); pr_info("unregister input kprobe: %d!\n", ret);
#else #else
if (!ksu_input_hook) { return; }
ksu_input_hook = false; ksu_input_hook = false;
pr_info("stop input_hook\n"); pr_info("stop input_hook\n");
#endif #endif

View File

@@ -18,7 +18,4 @@ extern u32 ksu_file_sid;
extern bool ksu_module_mounted; extern bool ksu_module_mounted;
extern bool ksu_boot_completed; extern bool ksu_boot_completed;
extern bool ksu_execveat_hook __read_mostly;
extern int ksu_handle_pre_ksud(const char *filename);
#endif #endif

View File

@@ -6,7 +6,7 @@
#include "selinux.h" #include "selinux.h"
#include "sepolicy.h" #include "sepolicy.h"
#include "ss/services.h" #include "ss/services.h"
#include "linux/lsm_audit.h" // IWYU pragma: keep #include "linux/lsm_audit.h"
#include "xfrm.h" #include "xfrm.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
@@ -157,39 +157,16 @@ void apply_kernelsu_rules(void)
#define CMD_TYPE_CHANGE 8 #define CMD_TYPE_CHANGE 8
#define CMD_GENFSCON 9 #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 { struct sepol_data {
u32 cmd; uint32_t cmd;
u32 subcmd; uint32_t subcmd;
usize field_sepol1; uint64_t sepol1;
usize field_sepol2; uint64_t sepol2;
usize field_sepol3; uint64_t sepol3;
usize field_sepol4; uint64_t sepol4;
usize field_sepol5; uint64_t sepol5;
usize field_sepol6; uint64_t sepol6;
usize field_sepol7; uint64_t 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;
}; };
static int get_object(char *buf, char __user *user_object, size_t buf_sz, static int get_object(char *buf, char __user *user_object, size_t buf_sz,
@@ -208,11 +185,14 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz,
return 0; 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); extern int avc_ss_reset(u32 seqno);
#else #else
extern int avc_ss_reset(struct selinux_avc *avc, u32 seqno); extern int avc_ss_reset(struct selinux_avc *avc, u32 seqno);
#endif #endif
// reset avc cache table, otherwise the new rules will not take effect if already denied // reset avc cache table, otherwise the new rules will not take effect if already denied
static void reset_avc_cache(void) static void reset_avc_cache(void)
{ {
@@ -242,76 +222,55 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
pr_info("SELinux permissive or disabled when handle policy!\n"); pr_info("SELinux permissive or disabled when handle policy!\n");
} }
u32 cmd, subcmd; struct sepol_data data = { 0 };
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))) { if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
pr_err("sepol: copy sepol_data failed.\n"); pr_err("sepol: copy sepol_data failed.\n");
return -1; return -EINVAL;
}
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;
} }
u32 cmd = data.cmd;
u32 subcmd = data.subcmd;
mutex_lock(&ksu_rules); mutex_lock(&ksu_rules);
db = get_policydb(); db = get_policydb();
int ret = -EINVAL; int ret = -EINVAL;
if (cmd == CMD_NORMAL_PERM) {
switch (cmd) {
case CMD_NORMAL_PERM: {
char src_buf[MAX_SEPOL_LEN]; char src_buf[MAX_SEPOL_LEN];
char tgt_buf[MAX_SEPOL_LEN]; char tgt_buf[MAX_SEPOL_LEN];
char cls_buf[MAX_SEPOL_LEN]; char cls_buf[MAX_SEPOL_LEN];
char perm_buf[MAX_SEPOL_LEN]; char perm_buf[MAX_SEPOL_LEN];
char *s, *t, *c, *p; 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"); pr_err("sepol: copy src failed.\n");
goto exit; 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"); pr_err("sepol: copy tgt failed.\n");
goto exit; 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"); pr_err("sepol: copy cls failed.\n");
goto exit; goto exit;
} }
if (get_object(perm_buf, sepol4, sizeof(perm_buf), &p) < if (get_object(perm_buf, (void __user *)data.sepol4,
0) { sizeof(perm_buf), &p) < 0) {
pr_err("sepol: copy perm failed.\n"); pr_err("sepol: copy perm failed.\n");
goto exit; goto exit;
} }
bool success = false; bool success = false;
if (subcmd == 1) { if (subcmd == 1) {
success = ksu_allow(db, s, t, c, p); success = ksu_allow(db, s, t, c, p);
} else if (subcmd == 2) { } 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); pr_err("sepol: unknown subcmd: %d\n", subcmd);
} }
ret = success ? 0 : -EINVAL; ret = success ? 0 : -EINVAL;
break;
} else if (cmd == CMD_XPERM) { }
case CMD_XPERM: {
char src_buf[MAX_SEPOL_LEN]; char src_buf[MAX_SEPOL_LEN];
char tgt_buf[MAX_SEPOL_LEN]; char tgt_buf[MAX_SEPOL_LEN];
char cls_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 perm_set[MAX_SEPOL_LEN];
char *s, *t, *c; 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"); pr_err("sepol: copy src failed.\n");
goto exit; 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"); pr_err("sepol: copy tgt failed.\n");
goto exit; 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"); pr_err("sepol: copy cls failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(operation, sepol4, if (strncpy_from_user(operation, (void __user *)data.sepol4,
sizeof(operation)) < 0) { sizeof(operation)) < 0) {
pr_err("sepol: copy operation failed.\n"); pr_err("sepol: copy operation failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(perm_set, sepol5, sizeof(perm_set)) < if (strncpy_from_user(perm_set, (void __user *)data.sepol5,
0) { sizeof(perm_set)) < 0) {
pr_err("sepol: copy perm_set failed.\n"); pr_err("sepol: copy perm_set failed.\n");
goto exit; goto exit;
} }
@@ -369,10 +332,13 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
pr_err("sepol: unknown subcmd: %d\n", subcmd); pr_err("sepol: unknown subcmd: %d\n", subcmd);
} }
ret = success ? 0 : -EINVAL; ret = success ? 0 : -EINVAL;
} else if (cmd == CMD_TYPE_STATE) { break;
}
case CMD_TYPE_STATE: {
char src[MAX_SEPOL_LEN]; 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"); pr_err("sepol: copy src failed.\n");
goto exit; goto exit;
} }
@@ -387,16 +353,20 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
} }
if (success) if (success)
ret = 0; ret = 0;
break;
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) { }
case CMD_TYPE:
case CMD_TYPE_ATTR: {
char type[MAX_SEPOL_LEN]; char type[MAX_SEPOL_LEN];
char attr[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"); pr_err("sepol: copy type failed.\n");
goto exit; 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"); pr_err("sepol: copy attr failed.\n");
goto exit; goto exit;
} }
@@ -412,11 +382,13 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
goto exit; goto exit;
} }
ret = 0; ret = 0;
break;
} else if (cmd == CMD_ATTR) { }
case CMD_ATTR: {
char attr[MAX_SEPOL_LEN]; 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"); pr_err("sepol: copy attr failed.\n");
goto exit; goto exit;
} }
@@ -425,36 +397,41 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
goto exit; goto exit;
} }
ret = 0; ret = 0;
break;
} else if (cmd == CMD_TYPE_TRANSITION) { }
case CMD_TYPE_TRANSITION: {
char src[MAX_SEPOL_LEN]; char src[MAX_SEPOL_LEN];
char tgt[MAX_SEPOL_LEN]; char tgt[MAX_SEPOL_LEN];
char cls[MAX_SEPOL_LEN]; char cls[MAX_SEPOL_LEN];
char default_type[MAX_SEPOL_LEN]; char default_type[MAX_SEPOL_LEN];
char object[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"); pr_err("sepol: copy src failed.\n");
goto exit; 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"); pr_err("sepol: copy tgt failed.\n");
goto exit; 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"); pr_err("sepol: copy cls failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(default_type, sepol4, if (strncpy_from_user(default_type, (void __user *)data.sepol4,
sizeof(default_type)) < 0) { sizeof(default_type)) < 0) {
pr_err("sepol: copy default_type failed.\n"); pr_err("sepol: copy default_type failed.\n");
goto exit; goto exit;
} }
char *real_object; char *real_object;
if (sepol5 == NULL) { if ((void __user *)data.sepol5 == NULL) {
real_object = NULL; real_object = NULL;
} else { } else {
if (strncpy_from_user(object, sepol5, if (strncpy_from_user(object,
(void __user *)data.sepol5,
sizeof(object)) < 0) { sizeof(object)) < 0) {
pr_err("sepol: copy object failed.\n"); pr_err("sepol: copy object failed.\n");
goto exit; goto exit;
@@ -466,26 +443,30 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
default_type, real_object); default_type, real_object);
if (success) if (success)
ret = 0; ret = 0;
break;
} else if (cmd == CMD_TYPE_CHANGE) { }
case CMD_TYPE_CHANGE: {
char src[MAX_SEPOL_LEN]; char src[MAX_SEPOL_LEN];
char tgt[MAX_SEPOL_LEN]; char tgt[MAX_SEPOL_LEN];
char cls[MAX_SEPOL_LEN]; char cls[MAX_SEPOL_LEN];
char default_type[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"); pr_err("sepol: copy src failed.\n");
goto exit; 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"); pr_err("sepol: copy tgt failed.\n");
goto exit; 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"); pr_err("sepol: copy cls failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(default_type, sepol4, if (strncpy_from_user(default_type, (void __user *)data.sepol4,
sizeof(default_type)) < 0) { sizeof(default_type)) < 0) {
pr_err("sepol: copy default_type failed.\n"); pr_err("sepol: copy default_type failed.\n");
goto exit; goto exit;
@@ -502,20 +483,24 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
} }
if (success) if (success)
ret = 0; ret = 0;
} else if (cmd == CMD_GENFSCON) { break;
}
case CMD_GENFSCON: {
char name[MAX_SEPOL_LEN]; char name[MAX_SEPOL_LEN];
char path[MAX_SEPOL_LEN]; char path[MAX_SEPOL_LEN];
char context[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"); pr_err("sepol: copy name failed.\n");
goto exit; 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"); pr_err("sepol: copy path failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(context, sepol3, sizeof(context)) < if (strncpy_from_user(context, (void __user *)data.sepol3,
0) { sizeof(context)) < 0) {
pr_err("sepol: copy context failed.\n"); pr_err("sepol: copy context failed.\n");
goto exit; goto exit;
} }
@@ -525,8 +510,12 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
goto exit; goto exit;
} }
ret = 0; ret = 0;
} else { break;
}
default: {
pr_err("sepol: unknown cmd: %d\n", cmd); pr_err("sepol: unknown cmd: %d\n", cmd);
break;
}
} }
exit: exit:

View File

@@ -150,60 +150,6 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
return 0; 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, int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
void *__never_use_argv, void *__never_use_envp, void *__never_use_argv, void *__never_use_envp,
int *__never_use_flags) int *__never_use_flags)

View File

@@ -209,7 +209,6 @@ static inline bool check_syscall_fastpath(int nr)
case __NR_execve: case __NR_execve:
case __NR_setresuid: case __NR_setresuid:
case __NR_faccessat2: case __NR_faccessat2:
case __NR_execveat:
case __NR_clone: case __NR_clone:
case __NR_clone3: case __NR_clone3:
return true; return true;
@@ -242,10 +241,6 @@ int ksu_handle_init_mark_tracker(int *fd, const char __user **filename_user,
#include "manual_su.h" #include "manual_su.h"
#endif #endif
#ifdef CONFIG_COMPAT
bool ksu_is_compat __read_mostly = false;
#endif
#ifndef LOOKUP_FOLLOW #ifndef LOOKUP_FOLLOW
#define LOOKUP_FOLLOW 0x0001 #define LOOKUP_FOLLOW 0x0001
#endif #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) static inline void ksu_handle_task_alloc(struct pt_regs *regs)
{ {
#ifdef CONFIG_KSU_MANUAL_SU #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) if (id == __NR_faccessat || id == __NR_faccessat2)
return ksu_handle_inode_permission(regs); 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 #ifdef CONFIG_KSU_MANUAL_SU
// Handle task_alloc via clone/fork // Handle task_alloc via clone/fork
if (id == __NR_clone || id == __NR_clone3) if (id == __NR_clone || id == __NR_clone3)

View File

@@ -15,8 +15,6 @@
#define DEVPTS_SUPER_MAGIC 0x1cd1 #define DEVPTS_SUPER_MAGIC 0x1cd1
#endif #endif
extern bool ksu_is_compat __read_mostly;
extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c
// Hook manager initialization and cleanup // Hook manager initialization and cleanup