Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions kernel/core/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ int __init kernelsu_init(void)
ksu_cred = prepare_creds();
if (!ksu_cred) {
pr_err("prepare cred failed!\n");
return -ENOSYS;
}

ksu_init_symbol_resolver();
Expand Down Expand Up @@ -211,9 +212,7 @@ void __exit kernelsu_exit(void)
ksu_sulog_exit();
ksu_feature_exit();

if (ksu_cred) {
put_cred(ksu_cred);
}
put_cred(ksu_cred);
}

#if NEED_OWN_STACKPROTECTOR
Expand Down
4 changes: 0 additions & 4 deletions kernel/feature/kernel_umount.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,6 @@ int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
return 0;
}

if (!ksu_cred) {
return 0;
}

// There are 6 scenarios:
// 1. Normal app: zygote -> appuid
// 2. Isolated process forked from zygote: zygote -> isolated_process
Expand Down
153 changes: 112 additions & 41 deletions kernel/feature/sucompat.c
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include "linux/file.h"
#include "linux/namei.h"
#include <linux/compiler_types.h>
#include <linux/preempt.h>
#include <linux/printk.h>
Expand All @@ -21,6 +23,8 @@
#include "policy/app_profile.h"
#include "hook/syscall_hook.h"
#include "sulog/event.h"
#include "ksu.h"
#include "util.h"

#define SU_PATH "/system/bin/su"
#define SH_PATH "/system/bin/sh"
Expand Down Expand Up @@ -57,74 +61,112 @@ static void __user *userspace_stack_buffer(const void *d, size_t len)
return copy_to_user(p, d, len) ? NULL : p;
}

static char __user *sh_user_path(void)
static char __user *ksud_user_path(void)
{
static const char sh_path[] = "/system/bin/sh";
static const char ksud_path[] = KSUD_PATH;

return userspace_stack_buffer(sh_path, sizeof(sh_path));
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
}

static char __user *ksud_user_path(void)
static char __user *empty_user_path(void)
{
static const char ksud_path[] = KSUD_PATH;
return userspace_stack_buffer("", sizeof(""));
}

return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
static const char su_path[] = SU_PATH;

static bool is_ksud_exists()
Comment thread
5ec1cff marked this conversation as resolved.
{
struct path path;

if (kern_path(KSUD_PATH, 0, &path) < 0) {
return false;
}
path_put(&path);
return true;
}

int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *__unused_flags)
long ksu_handle_faccessat_sucompat(int orig_nr, struct pt_regs *regs)
{
const char su[] = SU_PATH;
const char __user **filename_user, *orig_filename;
long ret;
const struct cred *old_cred;

if (!ksu_is_allow_uid_for_current(current_uid().val)) {
return 0;
goto do_orig_facessat;
}

char path[sizeof(su) + 1];
filename_user = (const char __user **)&PT_REGS_PARM2(regs);
Comment thread
5ec1cff marked this conversation as resolved.

char path[sizeof(su_path) + 1];
memset(path, 0, sizeof(path));
strncpy_from_user_nofault(path, *filename_user, sizeof(path));

if (unlikely(!memcmp(path, su, sizeof(su)))) {
pr_info("faccessat su->sh!\n");
*filename_user = sh_user_path();
if (unlikely(!memcmp(path, su_path, sizeof(su_path)))) {
old_cred = override_creds(ksu_cred);
if (is_ksud_exists()) {
pr_info("faccessat su->ksud!\n");
orig_filename = *filename_user;
*filename_user = ksud_user_path();
ret = ksu_syscall_table[orig_nr](regs);
revert_creds(old_cred);
*filename_user = orig_filename;
return ret;
} else {
revert_creds(old_cred);
}
}

return 0;
do_orig_facessat:
return ksu_syscall_table[orig_nr](regs);
}

int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
long ksu_handle_stat_sucompat(int orig_nr, struct pt_regs *regs)
{
// const char sh[] = SH_PATH;
const char su[] = SU_PATH;
const char __user **filename_user, *orig_filename;
long ret;
const struct cred *old_cred;

if (!ksu_is_allow_uid_for_current(current_uid().val)) {
return 0;
goto do_orig_stat;
}

if (unlikely(!filename_user)) {
return 0;
}
filename_user = (const char __user **)&PT_REGS_PARM2(regs);
Comment thread
5ec1cff marked this conversation as resolved.

char path[sizeof(su) + 1];
char path[sizeof(su_path) + 1];
memset(path, 0, sizeof(path));
strncpy_from_user_nofault(path, *filename_user, sizeof(path));

if (unlikely(!memcmp(path, su, sizeof(su)))) {
pr_info("newfstatat su->sh!\n");
*filename_user = sh_user_path();
if (unlikely(!memcmp(path, su_path, sizeof(su_path)))) {
old_cred = override_creds(ksu_cred);
if (is_ksud_exists()) {
pr_info("newfstatat su->ksud!\n");
orig_filename = *filename_user;
*filename_user = ksud_user_path();
ret = ksu_syscall_table[orig_nr](regs);
revert_creds(old_cred);
*filename_user = orig_filename;
return ret;
} else {
revert_creds(old_cred);
}
}

return 0;
do_orig_stat:
return ksu_syscall_table[orig_nr](regs);
}

long ksu_handle_execve_sucompat(const char __user **filename_user, int orig_nr, const struct pt_regs *regs)
long ksu_handle_execve_sucompat(const char __user **filename_user, int orig_nr, struct pt_regs *regs)
{
const char su[] = SU_PATH;
const char __user *fn;
const char __user *const __user *argv_user = (const char __user *const __user *)PT_REGS_PARM2(regs);
struct ksu_sulog_pending_event *pending_sucompat = NULL;
char path[sizeof(su) + 1];
long ret;
char path[sizeof(su_path) + 1];
long ret, orig_regs[5];
unsigned long addr;
int tmp_fd;
struct file *ksud_file;
const struct cred *old_cred;

if (unlikely(!filename_user))
goto do_orig_execve;
Expand All @@ -143,29 +185,58 @@ long ksu_handle_execve_sucompat(const char __user **filename_user, int orig_nr,
goto do_orig_execve;
}

if (likely(memcmp(path, su, sizeof(su))))
if (likely(memcmp(path, su_path, sizeof(su_path))))
goto do_orig_execve;

pr_info("sys_execve su found\n");

tmp_fd = get_unused_fd_flags(O_CLOEXEC);
if (tmp_fd < 0) {
pr_err("alloc tmp fd err: %d\n", tmp_fd);
goto do_orig_execve;
}

old_cred = override_creds(ksu_cred);
ksud_file = filp_open(KSUD_PATH, O_PATH, 0);
revert_creds(old_cred);
Comment thread
5ec1cff marked this conversation as resolved.
if (IS_ERR(ksud_file)) {
pr_err("open ksud err: %ld\n", PTR_ERR(ksud_file));
put_unused_fd(tmp_fd);
goto do_orig_execve;
}

fd_install(tmp_fd, ksud_file);

pending_sucompat = ksu_sulog_capture_sucompat(*filename_user, argv_user, GFP_KERNEL);
*filename_user = ksud_user_path();
// execve(file, argv, environ)
// execveat(fd, file, argv, environ, flags)
orig_regs[0] = regs->__PT_PARM1_REG;
orig_regs[1] = regs->__PT_PARM2_REG;
orig_regs[2] = regs->__PT_PARM3_REG;
orig_regs[3] = regs->__PT_SYSCALL_PARM4_REG;
orig_regs[4] = regs->__PT_PARM5_REG;
regs->__PT_PARM5_REG = AT_EMPTY_PATH;
regs->__PT_SYSCALL_PARM4_REG = regs->__PT_PARM3_REG;
regs->__PT_PARM3_REG = regs->__PT_PARM2_REG;
regs->__PT_PARM2_REG = empty_user_path();
regs->__PT_PARM1_REG = tmp_fd;

ret = escape_with_root_profile();
if (ret) {
pr_err("escape_with_root_profile failed: %ld\n", ret);
ksu_sulog_emit_pending(pending_sucompat, ret, GFP_KERNEL);
goto do_orig_execve;
}
ksu_sulog_emit_pending(pending_sucompat, ret, GFP_KERNEL);
Comment thread
5ec1cff marked this conversation as resolved.

ret = ksu_syscall_table[orig_nr](regs);
ret = ksu_syscall_table[__NR_execveat](regs);
if (ret < 0) {
pr_err("failed to execve ksud as su: %ld, fallback to sh\n", ret);
ksu_sulog_emit_pending(pending_sucompat, ret, GFP_KERNEL);
*filename_user = sh_user_path();
} else {
ksu_sulog_emit_pending(pending_sucompat, ret, GFP_KERNEL);
return ret;
ksu_close_fd(tmp_fd);
regs->__PT_PARM1_REG = orig_regs[0];
regs->__PT_PARM2_REG = orig_regs[1];
regs->__PT_PARM3_REG = orig_regs[2];
regs->__PT_SYSCALL_PARM4_REG = orig_regs[3];
regs->__PT_PARM5_REG = orig_regs[4];
}
return ret;

do_orig_execve:
return ksu_syscall_table[orig_nr](regs);
Expand Down
6 changes: 3 additions & 3 deletions kernel/feature/sucompat.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ void ksu_sucompat_init(void);
void ksu_sucompat_exit(void);

// Handler functions exported for hook_manager
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *__unused_flags);
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
long ksu_handle_execve_sucompat(const char __user **filename_user, int orig_nr, const struct pt_regs *regs);
long ksu_handle_faccessat_sucompat(int orig_nr, struct pt_regs *regs);
long ksu_handle_stat_sucompat(int orig_nr, struct pt_regs *regs);
long ksu_handle_execve_sucompat(const char __user **filename_user, int orig_nr, struct pt_regs *regs);

#endif
24 changes: 3 additions & 21 deletions kernel/hook/syscall_event_bridge.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,36 +49,18 @@ static int ksu_handle_init_mark_tracker(const char __user **filename_user)

long __nocfi ksu_hook_newfstatat(int orig_nr, const struct pt_regs *regs)
{
int *dfd;
const char __user **filename_user;
int *flags;

if (!ksu_su_compat_enabled)
return ksu_syscall_table[orig_nr](regs);

dfd = (int *)&PT_REGS_PARM1(regs);
filename_user = (const char __user **)&PT_REGS_PARM2(regs);
flags = (int *)&PT_REGS_SYSCALL_PARM4(regs);
ksu_handle_stat(dfd, filename_user, flags);

return ksu_syscall_table[orig_nr](regs);
return ksu_handle_stat_sucompat(orig_nr, (struct pt_regs *)regs);
}

long __nocfi ksu_hook_faccessat(int orig_nr, const struct pt_regs *regs)
{
int *dfd;
const char __user **filename_user;
int *mode;

if (!ksu_su_compat_enabled)
return ksu_syscall_table[orig_nr](regs);

dfd = (int *)&PT_REGS_PARM1(regs);
filename_user = (const char __user **)&PT_REGS_PARM2(regs);
mode = (int *)&PT_REGS_PARM3(regs);
ksu_handle_faccessat(dfd, filename_user, mode, NULL);

return ksu_syscall_table[orig_nr](regs);
return ksu_handle_faccessat_sucompat(orig_nr, (struct pt_regs *)regs);
}

DEFINE_STATIC_KEY_TRUE(ksud_execve_key);
Expand Down Expand Up @@ -109,7 +91,7 @@ long __nocfi ksu_hook_execve(int orig_nr, const struct pt_regs *regs)
pr_err("adb root failed: %ld\n", ret);
}
} else if (ksu_su_compat_enabled) {
ret = ksu_handle_execve_sucompat(filename_user, orig_nr, regs);
ret = ksu_handle_execve_sucompat(filename_user, orig_nr, (struct pt_regs *)regs);
ksu_sulog_emit_pending(pending_root_execve, ret, GFP_KERNEL);
return ret;
}
Expand Down
14 changes: 14 additions & 0 deletions kernel/include/util.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ifndef __KSU_H_UTIL
#define __KSU_H_UTIL

#include "linux/fdtable.h" // IWYU pragma: keep
#include <linux/version.h>
#include <linux/syscalls.h>

#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
#define ksu_close_fd close_fd
#else
#define ksu_close_fd ksys_close
#endif
Comment thread
5ec1cff marked this conversation as resolved.

#endif
12 changes: 2 additions & 10 deletions kernel/infra/su_mount_ns.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "klog.h" // IWYU pragma: keep
#include "ksu.h"
#include "infra/su_mount_ns.h"
#include "util.h"

extern int path_mount(const char *dev_name, struct path *path, const char *type_page, unsigned long flags,
void *data_page);
Expand Down Expand Up @@ -114,11 +115,7 @@ static void ksu_mnt_ns_global(void)
fd_install(fd, ns_file);
ret = ksu_sys_setns(fd, CLONE_NEWNS);

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 11, 0)
ksys_close(fd);
#else
close_fd(fd);
#endif
ksu_close_fd(fd);

if (ret) {
pr_warn("call setns failed: %ld\n", ret);
Expand Down Expand Up @@ -172,11 +169,6 @@ void setup_mount_ns(int32_t ns_mode)
return;
}

if (!ksu_cred) {
pr_err("no ksu cred! skip mnt_ns magic for pid: %d.\n", current->pid);
return;
}

const struct cred *old_cred = override_creds(ksu_cred);
if (ns_mode == KSU_NS_GLOBAL) {
ksu_mnt_ns_global();
Expand Down
6 changes: 1 addition & 5 deletions kernel/policy/app_profile.c
Original file line number Diff line number Diff line change
Expand Up @@ -171,11 +171,7 @@ int escape_with_root_profile(void)
}
#endif

// setup capabilities
// we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process
// we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec!
u64 cap_for_ksud = profile->capabilities.effective | CAP_DAC_READ_SEARCH;
memcpy(&cred->cap_effective, &cap_for_ksud, sizeof(cred->cap_effective));
memcpy(&cred->cap_effective, &profile->capabilities.effective, sizeof(cred->cap_effective));
memcpy(&cred->cap_permitted, &profile->capabilities.effective, sizeof(cred->cap_permitted));
memcpy(&cred->cap_bset, &profile->capabilities.effective, sizeof(cred->cap_bset));

Expand Down
5 changes: 2 additions & 3 deletions kernel/runtime/ksud_integration.c
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ static void load_module_rc_once(void)
return;
}

old_cred = ksu_cred ? override_creds(ksu_cred) : NULL;
old_cred = override_creds(ksu_cred);

f = open_module_rc(&path);
if (IS_ERR(f)) {
Expand Down Expand Up @@ -267,8 +267,7 @@ static void load_module_rc_once(void)
filp_close(f, NULL);

out_revert_creds:
if (old_cred)
revert_creds(old_cred);
revert_creds(old_cred);
}

static void free_module_rc(void)
Expand Down
Loading