ksud: migrate ksud execution to security_bprm_check (tiann#2653)
This migrates ksud execution decision-making to bprm_check_security. This requires passing proper argv and envp to a modified _ksud handler aptly named 'ksu_handle_bprm_ksud'. Introduces: int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const char *envp, size_t envp_len) which is adapted from: int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, struct user_arg_ptr *argv, struct user_arg_ptr *envp, int *flags) ksu_handle_bprm_ksud handles all the decision making, it decides when it is time to apply_kernelsu_rules depending if it sees "second_stage". For LSM hook, turns out we can pull out argv and envp from mm_struct. The code in here explains itself on how to do it. whole blob exists on arg_start to arg_end, so we just pull it out and grab next array after the first null terminator. as for envp, we pass the pointer then hunt for it when needed My reasoning on adding a fallback on usercopy is that on some devices a fault happens, and it copies garbled data. On my creation of this, I actually had to lock that _nofault copy on a spinlock as a way to mimic preempt_disable/enable without actually doing it. As per user reports, no failed _nofault copies anyway but we have-to-have a fallback for resilience. References: - old version16efcd8193e- old version237d5938e66- bad usercopy #21 This now provides a small helper function, ksu_copy_from_user_retry, which explains itself. First we attempt a _nofault copy, if that fails, we try plain. With that, It also provides an inlined copy_from_user_nofault for < 5.8. While using strncpy_from_user_nofault was considered, this wont do, this will only copy up to the first \0. devlog:16e5dce9e7...16c1f5f52128642e60d7...728de0c571References: https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429 https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/lsm_hooks.h Stale: https://github.com/tiann/KernelSU/pull/2653 Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
This commit is contained in:
@@ -20,6 +20,7 @@
|
|||||||
#include <linux/uidgid.h>
|
#include <linux/uidgid.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include <linux/mount.h>
|
#include <linux/mount.h>
|
||||||
|
#include <linux/binfmts.h>
|
||||||
|
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/namei.h>
|
#include <linux/namei.h>
|
||||||
@@ -1424,6 +1425,20 @@ static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
int ksu_bprm_check(struct linux_binprm *bprm)
|
||||||
|
{
|
||||||
|
char *filename = (char *)bprm->filename;
|
||||||
|
|
||||||
|
if (likely(!ksu_execveat_hook))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
ksu_handle_pre_ksud(filename);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
|
static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
|
||||||
struct inode *new_inode, struct dentry *new_dentry)
|
struct inode *new_inode, struct dentry *new_dentry)
|
||||||
{
|
{
|
||||||
@@ -1445,6 +1460,8 @@ static struct security_hook_list ksu_hooks[] = {
|
|||||||
defined(CONFIG_IS_HW_HISI) || \
|
defined(CONFIG_IS_HW_HISI) || \
|
||||||
defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
LSM_HOOK_INIT(key_permission, ksu_key_permission)
|
LSM_HOOK_INIT(key_permission, ksu_key_permission)
|
||||||
|
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||||
|
LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check),
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -70,8 +70,10 @@ void ksu_android_ns_fs_check(void)
|
|||||||
if (current->nsproxy && current->fs &&
|
if (current->nsproxy && current->fs &&
|
||||||
current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) {
|
current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) {
|
||||||
android_context_saved_enabled = true;
|
android_context_saved_enabled = true;
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n",
|
pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n",
|
||||||
current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns);
|
current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns);
|
||||||
|
#endif
|
||||||
ksu_save_ns_fs(&android_context_saved);
|
ksu_save_ns_fs(&android_context_saved);
|
||||||
} else {
|
} else {
|
||||||
pr_info("android context saved disabled\n");
|
pr_info("android context saved disabled\n");
|
||||||
@@ -93,7 +95,9 @@ struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
|||||||
// switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns
|
// switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns
|
||||||
struct ksu_ns_fs_saved saved;
|
struct ksu_ns_fs_saved saved;
|
||||||
if (android_context_saved_enabled) {
|
if (android_context_saved_enabled) {
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_info("start switch current nsproxy and fs to android context\n");
|
pr_info("start switch current nsproxy and fs to android context\n");
|
||||||
|
#endif
|
||||||
task_lock(current);
|
task_lock(current);
|
||||||
ksu_save_ns_fs(&saved);
|
ksu_save_ns_fs(&saved);
|
||||||
ksu_load_ns_fs(&android_context_saved);
|
ksu_load_ns_fs(&android_context_saved);
|
||||||
@@ -104,7 +108,9 @@ struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
|||||||
task_lock(current);
|
task_lock(current);
|
||||||
ksu_load_ns_fs(&saved);
|
ksu_load_ns_fs(&saved);
|
||||||
task_unlock(current);
|
task_unlock(current);
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_info("switch current nsproxy and fs back to saved successfully\n");
|
pr_info("switch current nsproxy and fs back to saved successfully\n");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return fp;
|
return fp;
|
||||||
}
|
}
|
||||||
@@ -208,3 +214,27 @@ long ksu_strncpy_from_user_retry(char *dst, const void __user *unsafe_addr,
|
|||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||||
|
return copy_from_user_nofault(dst, src, size);
|
||||||
|
#else
|
||||||
|
// https://elixir.bootlin.com/linux/v5.8/source/mm/maccess.c#L205
|
||||||
|
long ret = -EFAULT;
|
||||||
|
mm_segment_t old_fs = get_fs();
|
||||||
|
|
||||||
|
set_fs(USER_DS);
|
||||||
|
// tweaked to use ksu_access_ok
|
||||||
|
if (ksu_access_ok(src, size)) {
|
||||||
|
pagefault_disable();
|
||||||
|
ret = __copy_from_user_inatomic(dst, src, size);
|
||||||
|
pagefault_enable();
|
||||||
|
}
|
||||||
|
set_fs(old_fs);
|
||||||
|
|
||||||
|
if (ret)
|
||||||
|
return -EFAULT;
|
||||||
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@@ -77,6 +77,23 @@ extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
|||||||
loff_t *pos);
|
loff_t *pos);
|
||||||
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
|
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
|
||||||
size_t count, loff_t *pos);
|
size_t count, loff_t *pos);
|
||||||
|
extern long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size);
|
||||||
|
/*
|
||||||
|
* ksu_copy_from_user_retry
|
||||||
|
* try nofault copy first, if it fails, try with plain
|
||||||
|
* paramters are the same as copy_from_user
|
||||||
|
* 0 = success
|
||||||
|
*/
|
||||||
|
static long ksu_copy_from_user_retry(void *to,
|
||||||
|
const void __user *from, unsigned long count)
|
||||||
|
{
|
||||||
|
long ret = ksu_copy_from_user_nofault(to, from, count);
|
||||||
|
if (likely(!ret))
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
// we faulted! fallback to slow path
|
||||||
|
return copy_from_user(to, from, count);
|
||||||
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
||||||
#define ksu_access_ok(addr, size) access_ok(addr, size)
|
#define ksu_access_ok(addr, size) access_ok(addr, size)
|
||||||
|
|||||||
345
kernel/ksud.c
345
kernel/ksud.c
@@ -58,13 +58,13 @@ static void stop_input_hook(void);
|
|||||||
|
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||||
static struct work_struct stop_vfs_read_work;
|
static struct work_struct stop_vfs_read_work;
|
||||||
static struct work_struct stop_execve_hook_work;
|
static struct work_struct stop_bprm_check_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;
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_SUSFS_SUS_SU
|
#ifdef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
bool susfs_is_sus_su_ready = false;
|
bool susfs_is_sus_su_ready = false;
|
||||||
@@ -95,7 +95,6 @@ void on_post_fs_data(void)
|
|||||||
is_boot_phase = false;
|
is_boot_phase = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_ARG_STRINGS 0x7FFFFFFF
|
|
||||||
struct user_arg_ptr {
|
struct user_arg_ptr {
|
||||||
#ifdef CONFIG_COMPAT
|
#ifdef CONFIG_COMPAT
|
||||||
bool is_compat;
|
bool is_compat;
|
||||||
@@ -108,75 +107,11 @@ struct user_arg_ptr {
|
|||||||
} ptr;
|
} ptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
|
// 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)
|
||||||
{
|
{
|
||||||
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);
|
|
||||||
|
|
||||||
ksu_is_compat = true;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 CONFIG_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;
|
||||||
|
|
||||||
@@ -186,113 +121,148 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
|||||||
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;
|
||||||
|
|
||||||
if (!filename_ptr)
|
// return early when disabled
|
||||||
|
if (!ksu_execveat_hook)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
filename = *filename_ptr;
|
if (!filename)
|
||||||
if (IS_ERR(filename)) {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
|
||||||
|
|
||||||
if (unlikely(!memcmp(filename->name, system_bin_init,
|
// debug! remove me!
|
||||||
sizeof(system_bin_init) - 1) &&
|
pr_info("%s: filename: %s argv1: %s envp_len: %zu\n", __func__, filename, argv1, envp_len);
|
||||||
argv)) {
|
|
||||||
// /system/bin/init executed
|
if (init_second_stage_executed)
|
||||||
int argc = count(*argv, MAX_ARG_STRINGS);
|
goto first_app_process;
|
||||||
pr_info("/system/bin/init argc: %d\n", argc);
|
|
||||||
if (argc > 1 && !init_second_stage_executed) {
|
// /system/bin/init with argv1
|
||||||
const char __user *p = get_user_arg_ptr(*argv, 1);
|
if (!init_second_stage_executed
|
||||||
if (p && !IS_ERR(p)) {
|
&& (!memcmp(filename, system_bin_init, sizeof(system_bin_init) - 1))) {
|
||||||
char first_arg[16];
|
if (argv1 && !strcmp(argv1, "second_stage")) {
|
||||||
ksu_strncpy_from_user_retry(
|
pr_info("%s: /system/bin/init second_stage executed\n", __func__);
|
||||||
first_arg, p, sizeof(first_arg));
|
apply_kernelsu_rules();
|
||||||
pr_info("/system/bin/init first arg: %s\n",
|
init_second_stage_executed = true;
|
||||||
first_arg);
|
ksu_android_ns_fs_check();
|
||||||
if (!strcmp(first_arg, "second_stage")) {
|
|
||||||
pr_info("/system/bin/init second_stage executed\n");
|
|
||||||
apply_kernelsu_rules();
|
|
||||||
init_second_stage_executed = true;
|
|
||||||
ksu_android_ns_fs_check();
|
|
||||||
}
|
|
||||||
} 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];
|
|
||||||
ksu_strncpy_from_user_retry(
|
|
||||||
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;
|
|
||||||
ksu_android_ns_fs_check();
|
|
||||||
}
|
|
||||||
} 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 (ksu_strncpy_from_user_retry(
|
|
||||||
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;
|
|
||||||
ksu_android_ns_fs_check();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(first_app_process && !memcmp(filename->name, app_process,
|
// /init with argv1
|
||||||
sizeof(app_process) - 1))) {
|
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;
|
||||||
|
ksu_android_ns_fs_check();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// /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;
|
||||||
|
ksu_android_ns_fs_check();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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("exec app_process, /data prepared, second_stage: %d\n",
|
pr_info("%s: exec app_process, /data prepared, second_stage: %d\n", __func__, init_second_stage_executed);
|
||||||
init_second_stage_executed);
|
on_post_fs_data();
|
||||||
on_post_fs_data(); // we keep this for old ksud
|
|
||||||
stop_execve_hook();
|
stop_execve_hook();
|
||||||
}
|
}
|
||||||
|
|
||||||
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';
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
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 + envp_copy_len);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 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;
|
||||||
@@ -504,28 +474,13 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|||||||
return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL);
|
return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
extern int ksu_bprm_check(struct linux_binprm *bprm);
|
||||||
|
static int bprm_check_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
struct linux_binprm *bprm_local = (struct linux_binprm *)PT_REGS_PARM1(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 ksu_bprm_check(bprm_local);
|
||||||
return 0;
|
};
|
||||||
|
|
||||||
memset(path, 0, sizeof(path));
|
|
||||||
ksu_strncpy_from_user_nofault(path, *filename_user, 32);
|
|
||||||
filename_in.name = path;
|
|
||||||
|
|
||||||
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)
|
static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
@@ -546,9 +501,9 @@ static int input_handle_event_handler_pre(struct kprobe *p,
|
|||||||
return ksu_handle_input_handle_event(type, code, value);
|
return ksu_handle_input_handle_event(type, code, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct kprobe execve_kp = {
|
static struct kprobe bprm_check_kp = {
|
||||||
.symbol_name = SYS_EXECVE_SYMBOL,
|
.symbol_name = "security_bprm_check",
|
||||||
.pre_handler = sys_execve_handler_pre,
|
.pre_handler = bprm_check_handler_pre,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct kprobe vfs_read_kp = {
|
static struct kprobe vfs_read_kp = {
|
||||||
@@ -566,9 +521,9 @@ static void do_stop_vfs_read_hook(struct work_struct *work)
|
|||||||
unregister_kprobe(&vfs_read_kp);
|
unregister_kprobe(&vfs_read_kp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_stop_execve_hook(struct work_struct *work)
|
static void do_stop_bprm_check_hook(struct work_struct *work)
|
||||||
{
|
{
|
||||||
unregister_kprobe(&execve_kp);
|
unregister_kprobe(&bprm_check_kp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_stop_input_hook(struct work_struct *work)
|
static void do_stop_input_hook(struct work_struct *work)
|
||||||
@@ -636,11 +591,11 @@ static void stop_vfs_read_hook(void)
|
|||||||
static void stop_execve_hook(void)
|
static void stop_execve_hook(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||||
bool ret = schedule_work(&stop_execve_hook_work);
|
bool ret = schedule_work(&stop_bprm_check_work);
|
||||||
pr_info("unregister execve kprobe: %d!\n", ret);
|
pr_info("unregister execve kprobe: %d!\n", ret);
|
||||||
#else
|
#else
|
||||||
ksu_execveat_hook = false;
|
|
||||||
pr_info("stop execve_hook\n");
|
pr_info("stop execve_hook\n");
|
||||||
|
ksu_execveat_hook = false;
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_KSU_SUSFS_SUS_SU
|
#ifdef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
susfs_is_sus_su_ready = true;
|
susfs_is_sus_su_ready = true;
|
||||||
@@ -671,8 +626,8 @@ void ksu_ksud_init(void)
|
|||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = register_kprobe(&execve_kp);
|
ret = register_kprobe(&bprm_check_kp);
|
||||||
pr_info("ksud: execve_kp: %d\n", ret);
|
pr_info("ksud: bprm_check_kp: %d\n", ret);
|
||||||
|
|
||||||
ret = register_kprobe(&vfs_read_kp);
|
ret = register_kprobe(&vfs_read_kp);
|
||||||
pr_info("ksud: vfs_read_kp: %d\n", ret);
|
pr_info("ksud: vfs_read_kp: %d\n", ret);
|
||||||
@@ -681,7 +636,7 @@ void ksu_ksud_init(void)
|
|||||||
pr_info("ksud: input_event_kp: %d\n", ret);
|
pr_info("ksud: input_event_kp: %d\n", ret);
|
||||||
|
|
||||||
INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook);
|
INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook);
|
||||||
INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook);
|
INIT_WORK(&stop_bprm_check_work, do_stop_bprm_check_hook);
|
||||||
INIT_WORK(&stop_input_hook_work, do_stop_input_hook);
|
INIT_WORK(&stop_input_hook_work, do_stop_input_hook);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -689,7 +644,7 @@ void ksu_ksud_init(void)
|
|||||||
void ksu_ksud_exit(void)
|
void ksu_ksud_exit(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||||
unregister_kprobe(&execve_kp);
|
unregister_kprobe(&bprm_check_kp);
|
||||||
// this should be done before unregister vfs_read_kp
|
// this should be done before unregister vfs_read_kp
|
||||||
// unregister_kprobe(&vfs_read_kp);
|
// unregister_kprobe(&vfs_read_kp);
|
||||||
unregister_kprobe(&input_event_kp);
|
unregister_kprobe(&input_event_kp);
|
||||||
|
|||||||
@@ -11,4 +11,7 @@ bool ksu_is_safe_mode(void);
|
|||||||
|
|
||||||
extern u32 ksu_devpts_sid;
|
extern u32 ksu_devpts_sid;
|
||||||
|
|
||||||
|
extern bool ksu_execveat_hook __read_mostly;
|
||||||
|
extern int ksu_handle_pre_ksud(const char *filename);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user