kernel: Syncronize upstream changes (#198)
* Cherry-picked range: (kernel)
ebea31daa8..6915b62b9a
* Also merged unmerged pr:
https://github.com/tiann/KernelSU/pull/ 2909
Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: 5ec1cff <56485584+5ec1cff@users.noreply.github.com>
Co-authored-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
Co-authored-by: u9521 <63995396+u9521@users.noreply.github.com>
Co-authored-by: Wang Han <416810799@qq.com>
This commit is contained in:
@@ -8,6 +8,11 @@ config KSU
|
|||||||
To compile as a module, choose M here: the
|
To compile as a module, choose M here: the
|
||||||
module will be called kernelsu.
|
module will be called kernelsu.
|
||||||
|
|
||||||
|
# For easier extern ifdef handling
|
||||||
|
config RKSU
|
||||||
|
bool "RKSU compat, do not modify"
|
||||||
|
default y
|
||||||
|
|
||||||
config KSU_DEBUG
|
config KSU_DEBUG
|
||||||
bool "KernelSU debug mode"
|
bool "KernelSU debug mode"
|
||||||
depends on KSU
|
depends on KSU
|
||||||
@@ -16,12 +21,11 @@ config KSU_DEBUG
|
|||||||
Enable KernelSU debug mode.
|
Enable KernelSU debug mode.
|
||||||
|
|
||||||
config KSU_ALLOWLIST_WORKAROUND
|
config KSU_ALLOWLIST_WORKAROUND
|
||||||
bool "KernelSU Session init keyring workaround"
|
bool "KernelSU allowlist workaround"
|
||||||
depends on KSU
|
depends on KSU
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enable session keyring init workaround for problematic devices.
|
Enable workaround for broken allowlist save
|
||||||
Useful for situations where the SU allowlist is not kept after a reboot.
|
|
||||||
|
|
||||||
config KPM
|
config KPM
|
||||||
bool "Enable SukiSU KPM"
|
bool "Enable SukiSU KPM"
|
||||||
@@ -35,9 +39,21 @@ config KPM
|
|||||||
select KALLSYMS_ALL
|
select KALLSYMS_ALL
|
||||||
|
|
||||||
config KSU_MANUAL_HOOK
|
config KSU_MANUAL_HOOK
|
||||||
bool "Hook KernelSU manually"
|
bool "KernelSU manual hook mode."
|
||||||
depends on KSU != m
|
depends on KSU && KSU != m
|
||||||
|
default y if !KPROBES
|
||||||
|
default n
|
||||||
help
|
help
|
||||||
If enabled, Hook required KernelSU syscalls with manually-patched function.
|
Enable manual hook support.
|
||||||
|
|
||||||
|
config KSU_SHOULD_USE_NEW_TP
|
||||||
|
bool "KernelSU tracepoint+kretprobe hook"
|
||||||
|
depends on KSU && !KSU_MANUAL_HOOK
|
||||||
|
depends on KRETPROBES && KPROBES && HAVE_SYSCALL_TRACEPOINTS
|
||||||
|
default y if KPROBES && KRETPROBES && HAVE_SYSCALL_TRACEPOINTS
|
||||||
|
default n
|
||||||
|
help
|
||||||
|
Enable KPROBES, KRETPROBES and TRACEPOINT hook for KernelSU core.
|
||||||
|
This should not be used on kernel below 5.10.
|
||||||
|
|
||||||
endmenu
|
endmenu
|
||||||
|
|||||||
@@ -1,17 +1,22 @@
|
|||||||
kernelsu-objs := ksu.o
|
kernelsu-objs := ksu.o
|
||||||
kernelsu-objs += allowlist.o
|
kernelsu-objs += allowlist.o
|
||||||
kernelsu-objs += dynamic_manager.o
|
kernelsu-objs += dynamic_manager.o
|
||||||
|
kernelsu-objs += app_profile.o
|
||||||
kernelsu-objs += apk_sign.o
|
kernelsu-objs += apk_sign.o
|
||||||
kernelsu-objs += sucompat.o
|
kernelsu-objs += sucompat.o
|
||||||
|
kernelsu-objs += syscall_hook_manager.o
|
||||||
kernelsu-objs += throne_tracker.o
|
kernelsu-objs += throne_tracker.o
|
||||||
kernelsu-objs += pkg_observer.o
|
kernelsu-objs += pkg_observer.o
|
||||||
kernelsu-objs += core_hook.o
|
kernelsu-objs += setuid_hook.o
|
||||||
|
kernelsu-objs += lsm_hooks.o
|
||||||
|
kernelsu-objs += kernel_compat.o
|
||||||
|
kernelsu-objs += kernel_umount.o
|
||||||
kernelsu-objs += supercalls.o
|
kernelsu-objs += supercalls.o
|
||||||
kernelsu-objs += feature.o
|
kernelsu-objs += feature.o
|
||||||
kernelsu-objs += throne_tracker.o
|
kernelsu-objs += throne_tracker.o
|
||||||
kernelsu-objs += ksud.o
|
kernelsu-objs += ksud.o
|
||||||
kernelsu-objs += embed_ksud.o
|
kernelsu-objs += embed_ksud.o
|
||||||
kernelsu-objs += kernel_compat.o
|
kernelsu-objs += seccomp_cache.o
|
||||||
kernelsu-objs += file_wrapper.o
|
kernelsu-objs += file_wrapper.o
|
||||||
kernelsu-objs += throne_comm.o
|
kernelsu-objs += throne_comm.o
|
||||||
|
|
||||||
@@ -84,14 +89,33 @@ endif
|
|||||||
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
||||||
ccflags-y += -DKSU_VERSION_FULL=\"$(KSU_VERSION_FULL)\"
|
ccflags-y += -DKSU_VERSION_FULL=\"$(KSU_VERSION_FULL)\"
|
||||||
|
|
||||||
|
# RKSU: checks for available hook
|
||||||
|
## Logic flipped for HAVE_KSU_HOOK: 0 is success, 1 is failure, but not with KSU_DRY_RUN
|
||||||
|
HAVE_KSU_HOOK ?= 1
|
||||||
|
KSU_DRY_RUN ?= 0
|
||||||
|
|
||||||
# Checks hooks state
|
# Checks hooks state
|
||||||
|
ifeq ($(CONFIG_KSU_SHOULD_USE_NEW_TP), y)
|
||||||
|
$(info -- KernelSU: SHOULD_USE_NEW_TP)
|
||||||
|
ccflags-y += -DKSU_SHOULD_USE_NEW_TP
|
||||||
|
# Let's make it 0, so it would pass.
|
||||||
|
HAVE_KSU_HOOK := 0
|
||||||
|
endif
|
||||||
|
|
||||||
ifeq ($(CONFIG_KSU_MANUAL_HOOK), y)
|
ifeq ($(CONFIG_KSU_MANUAL_HOOK), y)
|
||||||
ccflags-y += -DKSU_MANUAL_HOOK
|
HAVE_KSU_HOOK := $(shell grep -q "ksu_handle_faccessat" $(srctree)/fs/open.c; echo $$?)
|
||||||
$(info -- SukiSU: KSU_MANUAL_HOOK)
|
ifeq ($(HAVE_KSU_HOOK),0)
|
||||||
|
$(info -- KernelSU: CONFIG_KSU_MANUAL_HOOK)
|
||||||
|
endif
|
||||||
|
endif
|
||||||
|
|
||||||
|
ifeq ($(KSU_DRY_RUN),0)
|
||||||
|
ifneq ($(HAVE_KSU_HOOK),0)
|
||||||
|
$(error -- KernelSU: No hooks were defined, bail!)
|
||||||
|
endif
|
||||||
else
|
else
|
||||||
ccflags-y += -DKSU_HAVE_SYSCALL_TRACEPOINTS_HOOK
|
$(info -- KernelSU in dry run mode, skip hook checks)
|
||||||
ccflags-y += -DKSU_KPROBES_HOOK
|
|
||||||
$(info -- SukiSU: KSU_TRACEPOINT_HOOK)
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# SELinux drivers check
|
# SELinux drivers check
|
||||||
@@ -122,16 +146,12 @@ ifeq ($(shell grep -q "task_security_struct\s\+\*selinux_cred" $(srctree)/securi
|
|||||||
ccflags-y += -DKSU_OPTIONAL_SELINUX_CRED
|
ccflags-y += -DKSU_OPTIONAL_SELINUX_CRED
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Check if __poll_t exists in linux or uapi types headers (handles backports)
|
|
||||||
ifneq ($(shell grep -q "__poll_t" \
|
|
||||||
$(srctree)/include/linux/types.h \
|
|
||||||
$(srctree)/include/uapi/linux/types.h; echo $$?),0)
|
|
||||||
ccflags-y += -DKSU_NO___POLL_T
|
|
||||||
endif
|
|
||||||
|
|
||||||
ifeq ($(shell grep -q "anon_inode_getfd_secure" $(srctree)/fs/anon_inodes.c; echo $$?),0)
|
ifeq ($(shell grep -q "anon_inode_getfd_secure" $(srctree)/fs/anon_inodes.c; echo $$?),0)
|
||||||
ccflags-y += -DKSU_HAS_GETFD_SECURE
|
ccflags-y += -DKSU_HAS_GETFD_SECURE
|
||||||
endif
|
endif
|
||||||
|
ifeq ($(shell grep -A1 "^int vfs_getattr" $(srctree)/fs/stat.c | grep -q "query_flags"; echo $$?),0)
|
||||||
|
ccflags-y += -DKSU_HAS_NEW_VFS_GETATTR
|
||||||
|
endif
|
||||||
|
|
||||||
# Checks Samsung
|
# Checks Samsung
|
||||||
ifeq ($(shell grep -q "CONFIG_KDP_CRED" $(srctree)/kernel/cred.c; echo $$?),0)
|
ifeq ($(shell grep -q "CONFIG_KDP_CRED" $(srctree)/kernel/cred.c; echo $$?),0)
|
||||||
|
|||||||
@@ -19,12 +19,13 @@
|
|||||||
#include <linux/compiler_types.h>
|
#include <linux/compiler_types.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "ksu.h"
|
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "ksud.h"
|
||||||
#include "selinux/selinux.h"
|
#include "selinux/selinux.h"
|
||||||
#include "kernel_compat.h"
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
|
||||||
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
|
#define FILE_MAGIC 0x7f4b5355 // ' KSU', u32
|
||||||
#define FILE_FORMAT_VERSION 3 // u32
|
#define FILE_FORMAT_VERSION 3 // u32
|
||||||
@@ -50,7 +51,7 @@ static void remove_uid_from_arr(uid_t uid)
|
|||||||
if (allow_list_pointer == 0)
|
if (allow_list_pointer == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
temp_arr = kzalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
||||||
if (temp_arr == NULL) {
|
if (temp_arr == NULL) {
|
||||||
pr_err("%s: unable to allocate memory\n", __func__);
|
pr_err("%s: unable to allocate memory\n", __func__);
|
||||||
return;
|
return;
|
||||||
@@ -205,7 +206,7 @@ bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// not found, alloc a new node!
|
// not found, alloc a new node!
|
||||||
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
|
p = (struct perm_data *)kzalloc(sizeof(struct perm_data), GFP_KERNEL);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
pr_err("ksu_set_app_profile alloc failed\n");
|
pr_err("ksu_set_app_profile alloc failed\n");
|
||||||
return false;
|
return false;
|
||||||
@@ -264,8 +265,11 @@ out:
|
|||||||
sizeof(default_root_profile));
|
sizeof(default_root_profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (persist)
|
if (persist) {
|
||||||
persistent_allow_list();
|
persistent_allow_list();
|
||||||
|
// FIXME: use a new flag
|
||||||
|
ksu_mark_running_process();
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -432,12 +436,7 @@ void persistent_allow_list(void)
|
|||||||
goto put_task;
|
goto put_task;
|
||||||
}
|
}
|
||||||
cb->func = do_persistent_allow_list;
|
cb->func = do_persistent_allow_list;
|
||||||
|
ksu_task_work_add(tsk, cb, TWA_RESUME);
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 8)
|
|
||||||
task_work_add(tsk, cb, TWA_RESUME);
|
|
||||||
#else
|
|
||||||
task_work_add(tsk, cb, true);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
put_task:
|
put_task:
|
||||||
put_task_struct(tsk);
|
put_task_struct(tsk);
|
||||||
@@ -506,6 +505,11 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *),
|
|||||||
struct perm_data *np = NULL;
|
struct perm_data *np = NULL;
|
||||||
struct perm_data *n = NULL;
|
struct perm_data *n = NULL;
|
||||||
|
|
||||||
|
if (!ksu_boot_completed) {
|
||||||
|
pr_info("boot not completed, skip prune\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
bool modified = false;
|
bool modified = false;
|
||||||
// TODO: use RCU!
|
// TODO: use RCU!
|
||||||
mutex_lock(&allowlist_mutex);
|
mutex_lock(&allowlist_mutex);
|
||||||
@@ -554,8 +558,6 @@ void ksu_allowlist_exit(void)
|
|||||||
struct perm_data *np = NULL;
|
struct perm_data *np = NULL;
|
||||||
struct perm_data *n = NULL;
|
struct perm_data *n = NULL;
|
||||||
|
|
||||||
persistent_allow_list();
|
|
||||||
|
|
||||||
// free allowlist
|
// free allowlist
|
||||||
mutex_lock(&allowlist_mutex);
|
mutex_lock(&allowlist_mutex);
|
||||||
list_for_each_entry_safe (np, n, &allow_list, list) {
|
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||||
|
|||||||
@@ -2,7 +2,12 @@
|
|||||||
#define __KSU_H_ALLOWLIST
|
#define __KSU_H_ALLOWLIST
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include "ksu.h"
|
#include <linux/uidgid.h>
|
||||||
|
#include "app_profile.h"
|
||||||
|
|
||||||
|
#define PER_USER_RANGE 100000
|
||||||
|
#define FIRST_APPLICATION_UID 10000
|
||||||
|
#define LAST_APPLICATION_UID 19999
|
||||||
|
|
||||||
void ksu_allowlist_init(void);
|
void ksu_allowlist_init(void);
|
||||||
|
|
||||||
@@ -31,4 +36,10 @@ bool ksu_set_app_profile(struct app_profile *, bool persist);
|
|||||||
|
|
||||||
bool ksu_uid_should_umount(uid_t uid);
|
bool ksu_uid_should_umount(uid_t uid);
|
||||||
struct root_profile *ksu_get_root_profile(uid_t uid);
|
struct root_profile *ksu_get_root_profile(uid_t uid);
|
||||||
|
|
||||||
|
static inline bool is_appuid(uid_t uid)
|
||||||
|
{
|
||||||
|
uid_t appid = uid % PER_USER_RANGE;
|
||||||
|
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ static struct sdesc *init_sdesc(struct crypto_shash *alg)
|
|||||||
int size;
|
int size;
|
||||||
|
|
||||||
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
|
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
|
||||||
sdesc = kmalloc(size, GFP_KERNEL);
|
sdesc = kzalloc(size, GFP_KERNEL);
|
||||||
if (!sdesc)
|
if (!sdesc)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
sdesc->shash.tfm = alg;
|
sdesc->shash.tfm = alg;
|
||||||
|
|||||||
309
kernel/app_profile.c
Normal file
309
kernel/app_profile.c
Normal file
@@ -0,0 +1,309 @@
|
|||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/fdtable.h>
|
||||||
|
#include <linux/file.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/proc_ns.h>
|
||||||
|
#include <linux/pid.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||||
|
#include <linux/sched/signal.h> // signal_struct
|
||||||
|
#include <linux/sched/task.h>
|
||||||
|
#endif
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
#include <linux/uidgid.h>
|
||||||
|
#include <linux/syscalls.h>
|
||||||
|
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "app_profile.h"
|
||||||
|
#include "arch.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
|
||||||
|
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
|
||||||
|
|
||||||
|
void setup_groups(struct root_profile *profile, struct cred *cred)
|
||||||
|
{
|
||||||
|
if (profile->groups_count > KSU_MAX_GROUPS) {
|
||||||
|
pr_warn("Failed to setgroups, too large group: %d!\n",
|
||||||
|
profile->uid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (profile->groups_count == 1 && profile->groups[0] == 0) {
|
||||||
|
// setgroup to root and return early.
|
||||||
|
if (cred->group_info)
|
||||||
|
put_group_info(cred->group_info);
|
||||||
|
cred->group_info = get_group_info(&root_groups);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 ngroups = profile->groups_count;
|
||||||
|
struct group_info *group_info = groups_alloc(ngroups);
|
||||||
|
if (!group_info) {
|
||||||
|
pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < ngroups; i++) {
|
||||||
|
gid_t gid = profile->groups[i];
|
||||||
|
kgid_t kgid = make_kgid(current_user_ns(), gid);
|
||||||
|
if (!gid_valid(kgid)) {
|
||||||
|
pr_warn("Failed to setgroups, invalid gid: %d\n", gid);
|
||||||
|
put_group_info(group_info);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
|
||||||
|
group_info->gid[i] = kgid;
|
||||||
|
#else
|
||||||
|
GROUP_AT(group_info, i) = kgid;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
groups_sort(group_info);
|
||||||
|
set_groups(cred, group_info);
|
||||||
|
put_group_info(group_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
||||||
|
extern long SYS_SETNS_SYMBOL(const struct pt_regs *regs);
|
||||||
|
static long ksu_sys_setns(int fd, int flags)
|
||||||
|
{
|
||||||
|
struct pt_regs regs;
|
||||||
|
memset(®s, 0, sizeof(regs));
|
||||||
|
|
||||||
|
PT_REGS_PARM1(®s) = fd;
|
||||||
|
PT_REGS_PARM2(®s) = flags;
|
||||||
|
|
||||||
|
#if (defined(__aarch64__) || defined(__x86_64__))
|
||||||
|
return SYS_SETNS_SYMBOL(®s);
|
||||||
|
#else
|
||||||
|
return -ENOSYS;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static long ksu_sys_setns(int fd, int flags)
|
||||||
|
{
|
||||||
|
return sys_setns(fd, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
__weak int ksys_unshare(unsigned long unshare_flags)
|
||||||
|
{
|
||||||
|
return sys_unshare(unshare_flags);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void setup_mount_namespace(int32_t ns_mode)
|
||||||
|
{
|
||||||
|
pr_info("setup mount namespace for pid: %d\n", current->pid);
|
||||||
|
|
||||||
|
if (ns_mode == 0) {
|
||||||
|
pr_info("mount namespace mode: inherit\n");
|
||||||
|
// do nothing
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ns_mode > 2) {
|
||||||
|
pr_warn("unknown mount namespace mode: %d\n", ns_mode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct cred *old_cred = NULL;
|
||||||
|
struct cred *new_cred = NULL;
|
||||||
|
if (!(capable(CAP_SYS_ADMIN) && capable(CAP_SYS_CHROOT))) {
|
||||||
|
pr_info("process dont have CAP_SYS_ADMIN or CAP_SYS_CHROOT, adding it temporarily.\n");
|
||||||
|
new_cred = prepare_creds();
|
||||||
|
if (!new_cred) {
|
||||||
|
pr_warn("failed to prepare new credentials\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cap_raise(new_cred->cap_effective, CAP_SYS_ADMIN);
|
||||||
|
cap_raise(new_cred->cap_effective, CAP_SYS_CHROOT);
|
||||||
|
old_cred = override_creds(new_cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ns_mode == 1) {
|
||||||
|
pr_info("mount namespace mode: global\n");
|
||||||
|
struct file *ns_file;
|
||||||
|
struct path ns_path;
|
||||||
|
struct task_struct *pid1_task = NULL;
|
||||||
|
struct pid *pid_struct = NULL;
|
||||||
|
rcu_read_lock();
|
||||||
|
// find init
|
||||||
|
pid_struct = find_pid_ns(1, &init_pid_ns);
|
||||||
|
if (unlikely(!pid_struct)) {
|
||||||
|
rcu_read_unlock();
|
||||||
|
pr_warn("failed to find pid_struct for PID 1\n");
|
||||||
|
goto try_drop_caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
pid1_task = get_pid_task(pid_struct, PIDTYPE_PID);
|
||||||
|
rcu_read_unlock();
|
||||||
|
if (unlikely(!pid1_task)) {
|
||||||
|
pr_warn("failed to get task_struct for PID 1\n");
|
||||||
|
goto try_drop_caps;
|
||||||
|
}
|
||||||
|
// mabe you can use &init_task for first stage init?
|
||||||
|
long ret = ns_get_path(&ns_path, pid1_task, &mntns_operations);
|
||||||
|
put_task_struct(pid1_task);
|
||||||
|
if (ret) {
|
||||||
|
pr_warn("failed to get path for init's mount namespace: %ld\n",
|
||||||
|
ret);
|
||||||
|
goto try_drop_caps;
|
||||||
|
}
|
||||||
|
ns_file = dentry_open(&ns_path, O_RDONLY, current_cred());
|
||||||
|
|
||||||
|
path_put(&ns_path);
|
||||||
|
if (IS_ERR(ns_file)) {
|
||||||
|
pr_warn("failed to open file for init's mount namespace: %ld\n",
|
||||||
|
PTR_ERR(ns_file));
|
||||||
|
goto try_drop_caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
int fd = get_unused_fd_flags(O_CLOEXEC);
|
||||||
|
if (fd < 0) {
|
||||||
|
pr_warn("failed to get an unused fd: %d\n", fd);
|
||||||
|
fput(ns_file);
|
||||||
|
goto try_drop_caps;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd_install(fd, ns_file);
|
||||||
|
pr_info("calling sys_setns with fd : %d\n", fd);
|
||||||
|
|
||||||
|
ret = ksu_sys_setns(fd, CLONE_NEWNS);
|
||||||
|
if (ret) {
|
||||||
|
pr_warn("sys_setns failed: %ld\n", ret);
|
||||||
|
}
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
close_fd(fd);
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
||||||
|
ksys_close(fd);
|
||||||
|
#else
|
||||||
|
sys_close(fd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ns_mode == 2) {
|
||||||
|
long ret;
|
||||||
|
pr_info("mount namespace mode: independent\n");
|
||||||
|
|
||||||
|
ret = ksys_unshare(CLONE_NEWNS);
|
||||||
|
if (ret) {
|
||||||
|
pr_warn("call ksys_unshare failed: %ld\n", ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try_drop_caps:
|
||||||
|
if (old_cred) {
|
||||||
|
pr_info("dropping temporarily capability.\n");
|
||||||
|
revert_creds(old_cred);
|
||||||
|
put_cred(new_cred);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// RKSU: Use it wisely, not static.
|
||||||
|
void disable_seccomp(struct task_struct *tsk)
|
||||||
|
{
|
||||||
|
if (unlikely(!tsk))
|
||||||
|
return;
|
||||||
|
|
||||||
|
assert_spin_locked(&tsk->sighand->siglock);
|
||||||
|
|
||||||
|
// disable seccomp
|
||||||
|
#if defined(CONFIG_GENERIC_ENTRY) && \
|
||||||
|
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
clear_syscall_work(SECCOMP);
|
||||||
|
#else
|
||||||
|
clear_thread_flag(TIF_SECCOMP);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CONFIG_SECCOMP
|
||||||
|
tsk->seccomp.mode = 0;
|
||||||
|
if (tsk->seccomp.filter) {
|
||||||
|
// 5.9+ have filter_count and use seccomp_filter_release
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
||||||
|
seccomp_filter_release(tsk);
|
||||||
|
atomic_set(&tsk->seccomp.filter_count, 0);
|
||||||
|
#else
|
||||||
|
// for 6.11+ kernel support?
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
|
||||||
|
put_seccomp_filter(tsk);
|
||||||
|
#endif
|
||||||
|
tsk->seccomp.filter = NULL;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void escape_with_root_profile(void)
|
||||||
|
{
|
||||||
|
struct cred *cred;
|
||||||
|
// a bit useless, but we just want less ifdefs
|
||||||
|
struct task_struct *p = current;
|
||||||
|
|
||||||
|
if (current_euid().val == 0) {
|
||||||
|
pr_warn("Already root, don't escape!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cred = prepare_creds();
|
||||||
|
if (!cred) {
|
||||||
|
pr_warn("prepare_creds failed!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
|
||||||
|
|
||||||
|
cred->uid.val = profile->uid;
|
||||||
|
cred->suid.val = profile->uid;
|
||||||
|
cred->euid.val = profile->uid;
|
||||||
|
cred->fsuid.val = profile->uid;
|
||||||
|
|
||||||
|
cred->gid.val = profile->gid;
|
||||||
|
cred->fsgid.val = profile->gid;
|
||||||
|
cred->sgid.val = profile->gid;
|
||||||
|
cred->egid.val = profile->gid;
|
||||||
|
cred->securebits = 0;
|
||||||
|
|
||||||
|
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
|
||||||
|
sizeof(kernel_cap_t));
|
||||||
|
|
||||||
|
// 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_permitted, &profile->capabilities.effective,
|
||||||
|
sizeof(cred->cap_permitted));
|
||||||
|
memcpy(&cred->cap_bset, &profile->capabilities.effective,
|
||||||
|
sizeof(cred->cap_bset));
|
||||||
|
|
||||||
|
setup_groups(profile, cred);
|
||||||
|
|
||||||
|
commit_creds(cred);
|
||||||
|
|
||||||
|
// Refer to kernel/seccomp.c: seccomp_set_mode_strict
|
||||||
|
// When disabling Seccomp, ensure that current->sighand->siglock is held during the operation.
|
||||||
|
spin_lock_irq(&p->sighand->siglock);
|
||||||
|
disable_seccomp(p);
|
||||||
|
spin_unlock_irq(&p->sighand->siglock);
|
||||||
|
|
||||||
|
setup_selinux(profile->selinux_domain);
|
||||||
|
setup_mount_namespace(profile->namespaces);
|
||||||
|
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
struct task_struct *t;
|
||||||
|
for_each_thread (p, t) {
|
||||||
|
ksu_set_task_tracepoint_flag(t);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
66
kernel/app_profile.h
Normal file
66
kernel/app_profile.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
#ifndef __KSU_H_APP_PROFILE
|
||||||
|
#define __KSU_H_APP_PROFILE
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
// Forward declarations
|
||||||
|
struct cred;
|
||||||
|
|
||||||
|
#define KSU_APP_PROFILE_VER 2
|
||||||
|
#define KSU_MAX_PACKAGE_NAME 256
|
||||||
|
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
||||||
|
#define KSU_MAX_GROUPS 32
|
||||||
|
#define KSU_SELINUX_DOMAIN 64
|
||||||
|
|
||||||
|
struct root_profile {
|
||||||
|
int32_t uid;
|
||||||
|
int32_t gid;
|
||||||
|
|
||||||
|
int32_t groups_count;
|
||||||
|
int32_t groups[KSU_MAX_GROUPS];
|
||||||
|
|
||||||
|
// kernel_cap_t is u32[2] for capabilities v3
|
||||||
|
struct {
|
||||||
|
u64 effective;
|
||||||
|
u64 permitted;
|
||||||
|
u64 inheritable;
|
||||||
|
} capabilities;
|
||||||
|
|
||||||
|
char selinux_domain[KSU_SELINUX_DOMAIN];
|
||||||
|
|
||||||
|
int32_t namespaces;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct non_root_profile {
|
||||||
|
bool umount_modules;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct app_profile {
|
||||||
|
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
|
||||||
|
u32 version;
|
||||||
|
|
||||||
|
// this is usually the package of the app, but can be other value for special apps
|
||||||
|
char key[KSU_MAX_PACKAGE_NAME];
|
||||||
|
int32_t current_uid;
|
||||||
|
bool allow_su;
|
||||||
|
|
||||||
|
union {
|
||||||
|
struct {
|
||||||
|
bool use_default;
|
||||||
|
char template_name[KSU_MAX_PACKAGE_NAME];
|
||||||
|
|
||||||
|
struct root_profile profile;
|
||||||
|
} rp_config;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool use_default;
|
||||||
|
|
||||||
|
struct non_root_profile profile;
|
||||||
|
} nrp_config;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Escalate current process to root with the appropriate profile
|
||||||
|
void escape_with_root_profile(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -21,19 +21,13 @@
|
|||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||||
#define REBOOT_SYMBOL "__arm64_sys_reboot"
|
#define REBOOT_SYMBOL "__arm64_sys_reboot"
|
||||||
#define SYS_READ_SYMBOL "__arm64_sys_read"
|
#define SYS_READ_SYMBOL "__arm64_sys_read"
|
||||||
#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat"
|
|
||||||
#define SYS_FSTATAT64_SYMBOL "__arm64_sys_fstatat64"
|
|
||||||
#define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat"
|
|
||||||
#define SYS_EXECVE_SYMBOL "__arm64_sys_execve"
|
#define SYS_EXECVE_SYMBOL "__arm64_sys_execve"
|
||||||
#define SYS_EXECVE_COMPAT_SYMBOL "__arm64_compat_sys_execve"
|
#define SYS_SETNS_SYMBOL __arm64_sys_setns
|
||||||
#else
|
#else
|
||||||
#define REBOOT_SYMBOL "sys_reboot"
|
#define REBOOT_SYMBOL "sys_reboot"
|
||||||
#define SYS_READ_SYMBOL "sys_read"
|
#define SYS_READ_SYMBOL "sys_read"
|
||||||
#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat"
|
|
||||||
#define SYS_FSTATAT64_SYMBOL "sys_fstatat64"
|
|
||||||
#define SYS_FACCESSAT_SYMBOL "sys_faccessat"
|
|
||||||
#define SYS_EXECVE_SYMBOL "sys_execve"
|
#define SYS_EXECVE_SYMBOL "sys_execve"
|
||||||
#define SYS_EXECVE_COMPAT_SYMBOL "compat_sys_execve"
|
#define SYS_SETNS_SYMBOL sys_setns
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#elif defined(__x86_64__)
|
#elif defined(__x86_64__)
|
||||||
@@ -51,26 +45,21 @@
|
|||||||
#define __PT_RC_REG ax
|
#define __PT_RC_REG ax
|
||||||
#define __PT_SP_REG sp
|
#define __PT_SP_REG sp
|
||||||
#define __PT_IP_REG ip
|
#define __PT_IP_REG ip
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||||
#define REBOOT_SYMBOL "__x64_sys_reboot"
|
#define REBOOT_SYMBOL "__x64_sys_reboot"
|
||||||
#define SYS_READ_SYMBOL "__x64_sys_read"
|
#define SYS_READ_SYMBOL "__x64_sys_read"
|
||||||
#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat"
|
|
||||||
#define SYS_FSTATAT64_SYMBOL "__x64_sys_fstatat64"
|
|
||||||
#define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat"
|
|
||||||
#define SYS_EXECVE_SYMBOL "__x64_sys_execve"
|
#define SYS_EXECVE_SYMBOL "__x64_sys_execve"
|
||||||
#define SYS_EXECVE_COMPAT_SYMBOL "__x64_compat_sys_execve"
|
#define SYS_SETNS_SYMBOL __x64_sys_setns
|
||||||
#else
|
#else
|
||||||
#define PRCTL_SYMBOL "sys_reboot"
|
#define REBOOT_SYMBOL "sys_reboot"
|
||||||
#define SYS_READ_SYMBOL "sys_read"
|
#define SYS_READ_SYMBOL "sys_read"
|
||||||
#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat"
|
|
||||||
#define SYS_FSTATAT64_SYMBOL "sys_fstatat64"
|
|
||||||
#define SYS_FACCESSAT_SYMBOL "sys_faccessat"
|
|
||||||
#define SYS_EXECVE_SYMBOL "sys_execve"
|
#define SYS_EXECVE_SYMBOL "sys_execve"
|
||||||
#define SYS_EXECVE_COMPAT_SYMBOL "compat_sys_execve"
|
#define SYS_SETNS_SYMBOL sys_setns
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#ifdef KSU_KPROBES_HOOK
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
#error "Unsupported arch"
|
#error "Unsupported arch"
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,835 +0,0 @@
|
|||||||
#include <linux/compiler.h>
|
|
||||||
#include <linux/version.h>
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
|
||||||
#include <linux/sched/task_stack.h>
|
|
||||||
#else
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#endif
|
|
||||||
#include <linux/slab.h>
|
|
||||||
#include <linux/task_work.h>
|
|
||||||
#include <linux/thread_info.h>
|
|
||||||
#include <linux/seccomp.h>
|
|
||||||
#include <linux/capability.h>
|
|
||||||
#include <linux/cred.h>
|
|
||||||
#include <linux/dcache.h>
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include <linux/init_task.h>
|
|
||||||
#include <linux/kernel.h>
|
|
||||||
#include <linux/kprobes.h>
|
|
||||||
#include <linux/mm.h>
|
|
||||||
#include <linux/mount.h>
|
|
||||||
#include <linux/namei.h>
|
|
||||||
#include <linux/nsproxy.h>
|
|
||||||
#include <linux/path.h>
|
|
||||||
#include <linux/printk.h>
|
|
||||||
#include <linux/sched.h>
|
|
||||||
#include <linux/stddef.h>
|
|
||||||
#include <linux/string.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <linux/uidgid.h>
|
|
||||||
#ifndef KSU_HAS_PATH_UMOUNT
|
|
||||||
#include <linux/syscalls.h> // sys_umount (<4.17) & ksys_umount (4.17+)
|
|
||||||
#endif
|
|
||||||
#include <linux/binfmts.h>
|
|
||||||
#include <linux/err.h>
|
|
||||||
|
|
||||||
#include "allowlist.h"
|
|
||||||
#include "arch.h"
|
|
||||||
#include "core_hook.h"
|
|
||||||
#include "feature.h"
|
|
||||||
#include "klog.h" // IWYU pragma: keep
|
|
||||||
#include "ksu.h"
|
|
||||||
#include "ksud.h"
|
|
||||||
#include "manager.h"
|
|
||||||
#include "selinux/selinux.h"
|
|
||||||
#include "throne_tracker.h"
|
|
||||||
#include "throne_comm.h"
|
|
||||||
#include "kernel_compat.h"
|
|
||||||
#include "supercalls.h"
|
|
||||||
#include "sucompat.h"
|
|
||||||
|
|
||||||
bool ksu_module_mounted __read_mostly = false;
|
|
||||||
|
|
||||||
#ifndef DEVPTS_SUPER_MAGIC
|
|
||||||
#define DEVPTS_SUPER_MAGIC 0x1cd1
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c
|
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
|
||||||
bool ksu_is_compat __read_mostly = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static bool ksu_kernel_umount_enabled = true;
|
|
||||||
static bool ksu_enhanced_security_enabled = false;
|
|
||||||
|
|
||||||
static int kernel_umount_feature_get(u64 *value)
|
|
||||||
{
|
|
||||||
*value = ksu_kernel_umount_enabled ? 1 : 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int kernel_umount_feature_set(u64 value)
|
|
||||||
{
|
|
||||||
bool enable = value != 0;
|
|
||||||
ksu_kernel_umount_enabled = enable;
|
|
||||||
pr_info("kernel_umount: set to %d\n", enable);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct ksu_feature_handler kernel_umount_handler = {
|
|
||||||
.feature_id = KSU_FEATURE_KERNEL_UMOUNT,
|
|
||||||
.name = "kernel_umount",
|
|
||||||
.get_handler = kernel_umount_feature_get,
|
|
||||||
.set_handler = kernel_umount_feature_set,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int enhanced_security_feature_get(u64 *value)
|
|
||||||
{
|
|
||||||
*value = ksu_enhanced_security_enabled ? 1 : 0;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int enhanced_security_feature_set(u64 value)
|
|
||||||
{
|
|
||||||
bool enable = value != 0;
|
|
||||||
ksu_enhanced_security_enabled = enable;
|
|
||||||
pr_info("enhanced_security: set to %d\n", enable);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const struct ksu_feature_handler enhanced_security_handler = {
|
|
||||||
.feature_id = KSU_FEATURE_ENHANCED_SECURITY,
|
|
||||||
.name = "enhanced_security",
|
|
||||||
.get_handler = enhanced_security_feature_get,
|
|
||||||
.set_handler = enhanced_security_feature_set,
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline bool is_allow_su(void)
|
|
||||||
{
|
|
||||||
if (is_manager()) {
|
|
||||||
// we are manager, allow!
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return ksu_is_allow_uid_for_current(current_uid().val);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline bool is_unsupported_uid(uid_t uid)
|
|
||||||
{
|
|
||||||
#define LAST_APPLICATION_UID 19999
|
|
||||||
uid_t appid = uid % 100000;
|
|
||||||
return appid > LAST_APPLICATION_UID;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 7, 0)
|
|
||||||
static struct group_info root_groups = {
|
|
||||||
.usage = REFCOUNT_INIT(2),
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void setup_groups(struct root_profile *profile, struct cred *cred)
|
|
||||||
{
|
|
||||||
if (profile->groups_count > KSU_MAX_GROUPS) {
|
|
||||||
pr_warn("Failed to setgroups, too large group: %d!\n",
|
|
||||||
profile->uid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (profile->groups_count == 1 && profile->groups[0] == 0) {
|
|
||||||
// setgroup to root and return early.
|
|
||||||
if (cred->group_info)
|
|
||||||
put_group_info(cred->group_info);
|
|
||||||
cred->group_info = get_group_info(&root_groups);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 ngroups = profile->groups_count;
|
|
||||||
struct group_info *group_info = groups_alloc(ngroups);
|
|
||||||
if (!group_info) {
|
|
||||||
pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < ngroups; i++) {
|
|
||||||
gid_t gid = profile->groups[i];
|
|
||||||
kgid_t kgid = make_kgid(current_user_ns(), gid);
|
|
||||||
if (!gid_valid(kgid)) {
|
|
||||||
pr_warn("Failed to setgroups, invalid gid: %d\n", gid);
|
|
||||||
put_group_info(group_info);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)
|
|
||||||
group_info->gid[i] = kgid;
|
|
||||||
#else
|
|
||||||
GROUP_AT(group_info, i) = kgid;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
groups_sort(group_info);
|
|
||||||
set_groups(cred, group_info);
|
|
||||||
put_group_info(group_info);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void disable_seccomp(struct task_struct *tsk)
|
|
||||||
{
|
|
||||||
assert_spin_locked(&tsk->sighand->siglock);
|
|
||||||
|
|
||||||
// disable seccomp
|
|
||||||
#if defined(CONFIG_GENERIC_ENTRY) && \
|
|
||||||
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
|
||||||
clear_syscall_work(SECCOMP);
|
|
||||||
#else
|
|
||||||
clear_thread_flag(TIF_SECCOMP);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_SECCOMP
|
|
||||||
tsk->seccomp.mode = 0;
|
|
||||||
if (tsk->seccomp.filter) {
|
|
||||||
// 5.9+ have filter_count and use seccomp_filter_release
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
|
||||||
seccomp_filter_release(tsk);
|
|
||||||
atomic_set(&tsk->seccomp.filter_count, 0);
|
|
||||||
#else
|
|
||||||
// for 6.11+ kernel support?
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
|
|
||||||
put_seccomp_filter(tsk);
|
|
||||||
#endif
|
|
||||||
tsk->seccomp.filter = NULL;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void escape_to_root(void)
|
|
||||||
{
|
|
||||||
struct cred *cred;
|
|
||||||
#ifdef KSU_SHOULD_USE_NEW_TP
|
|
||||||
struct task_struct *p = current;
|
|
||||||
struct task_struct *t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
cred = prepare_creds();
|
|
||||||
if (!cred) {
|
|
||||||
pr_warn("prepare_creds failed!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cred->euid.val == 0) {
|
|
||||||
pr_warn("Already root, don't escape!\n");
|
|
||||||
abort_creds(cred);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct root_profile *profile = ksu_get_root_profile(cred->uid.val);
|
|
||||||
|
|
||||||
cred->uid.val = profile->uid;
|
|
||||||
cred->suid.val = profile->uid;
|
|
||||||
cred->euid.val = profile->uid;
|
|
||||||
cred->fsuid.val = profile->uid;
|
|
||||||
|
|
||||||
cred->gid.val = profile->gid;
|
|
||||||
cred->fsgid.val = profile->gid;
|
|
||||||
cred->sgid.val = profile->gid;
|
|
||||||
cred->egid.val = profile->gid;
|
|
||||||
cred->securebits = 0;
|
|
||||||
|
|
||||||
BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
|
|
||||||
sizeof(kernel_cap_t));
|
|
||||||
|
|
||||||
// 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_permitted, &profile->capabilities.effective,
|
|
||||||
sizeof(cred->cap_permitted));
|
|
||||||
memcpy(&cred->cap_bset, &profile->capabilities.effective,
|
|
||||||
sizeof(cred->cap_bset));
|
|
||||||
|
|
||||||
setup_groups(profile, cred);
|
|
||||||
|
|
||||||
commit_creds(cred);
|
|
||||||
|
|
||||||
spin_lock_irq(¤t->sighand->siglock);
|
|
||||||
disable_seccomp(current);
|
|
||||||
spin_unlock_irq(¤t->sighand->siglock);
|
|
||||||
|
|
||||||
setup_selinux(profile->selinux_domain);
|
|
||||||
|
|
||||||
#ifdef KSU_SHOULD_USE_NEW_TP
|
|
||||||
for_each_thread (p, t) {
|
|
||||||
ksu_set_task_tracepoint_flag(t);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void ext4_unregister_sysfs(struct super_block *sb);
|
|
||||||
void nuke_ext4_sysfs(void)
|
|
||||||
{
|
|
||||||
#ifdef CONFIG_EXT4_FS
|
|
||||||
struct path path;
|
|
||||||
int err = kern_path("/data/adb/modules", 0, &path);
|
|
||||||
if (err) {
|
|
||||||
pr_err("%s: failed to get path, err %d\n", __func__, err);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct super_block *sb = path.dentry->d_inode->i_sb;
|
|
||||||
const char *name = sb->s_type->name;
|
|
||||||
if (strcmp(name, "ext4") != 0) {
|
|
||||||
pr_info("nuke_module: skipping s_type: %s\n", name);
|
|
||||||
path_put(&path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ext4_unregister_sysfs(sb);
|
|
||||||
pr_info("nuke_module: ext4 sysfs unregistered.\n");
|
|
||||||
path_put(&path);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_appuid(kuid_t uid)
|
|
||||||
{
|
|
||||||
#define PER_USER_RANGE 100000
|
|
||||||
#define FIRST_APPLICATION_UID 10000
|
|
||||||
#define LAST_APPLICATION_UID 19999
|
|
||||||
|
|
||||||
uid_t appid = uid.val % PER_USER_RANGE;
|
|
||||||
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool should_umount(struct path *path)
|
|
||||||
{
|
|
||||||
if (!path) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
|
|
||||||
pr_info("ignore global mnt namespace process: %d\n",
|
|
||||||
current_uid().val);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) {
|
|
||||||
const char *fstype = path->mnt->mnt_sb->s_type->name;
|
|
||||||
return strcmp(fstype, "overlay") == 0;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || \
|
|
||||||
defined(KSU_HAS_PATH_UMOUNT)
|
|
||||||
extern int path_umount(struct path *path, int flags);
|
|
||||||
#define ksu_umount_mnt(__unused, path, flags) (path_umount(path, flags))
|
|
||||||
#else
|
|
||||||
static int ksu_sys_umount(const char *mnt, int flags)
|
|
||||||
{
|
|
||||||
char __user *usermnt = (char __user *)mnt;
|
|
||||||
mm_segment_t old_fs;
|
|
||||||
int ret; // although asmlinkage long
|
|
||||||
|
|
||||||
old_fs = get_fs();
|
|
||||||
set_fs(KERNEL_DS);
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
|
||||||
ret = ksys_umount(usermnt, flags);
|
|
||||||
#else
|
|
||||||
ret = sys_umount(usermnt, flags); // cuz asmlinkage long sys##name
|
|
||||||
#endif
|
|
||||||
set_fs(old_fs);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define ksu_umount_mnt(mnt, __unused, flags) \
|
|
||||||
({ \
|
|
||||||
int ret; \
|
|
||||||
path_put(__unused); \
|
|
||||||
ret = ksu_sys_umount(mnt, flags); \
|
|
||||||
ret; \
|
|
||||||
})
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void try_umount(const char *mnt, bool check_mnt, int flags)
|
|
||||||
{
|
|
||||||
struct path path;
|
|
||||||
int ret;
|
|
||||||
int err = kern_path(mnt, 0, &path);
|
|
||||||
if (err) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (path.dentry != path.mnt->mnt_root) {
|
|
||||||
// it is not root mountpoint, maybe umounted by others already.
|
|
||||||
path_put(&path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// we are only interest in some specific mounts
|
|
||||||
if (check_mnt && !should_umount(&path)) {
|
|
||||||
path_put(&path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = ksu_umount_mnt(mnt, &path, flags);
|
|
||||||
if (ret) {
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_info("%s: path: %s, ret: %d\n", __func__, mnt, ret);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ksu_do_umount_lists(void)
|
|
||||||
{
|
|
||||||
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
|
||||||
// filter the mountpoint whose target is `/data/adb`
|
|
||||||
try_umount("/odm", true, 0);
|
|
||||||
try_umount("/system", true, 0);
|
|
||||||
try_umount("/vendor", true, 0);
|
|
||||||
try_umount("/product", true, 0);
|
|
||||||
try_umount("/system_ext", true, 0);
|
|
||||||
try_umount("/data/adb/modules", false, MNT_DETACH);
|
|
||||||
|
|
||||||
try_umount("/debug_ramdisk", false, MNT_DETACH);
|
|
||||||
try_umount("/sbin", false, MNT_DETACH);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(MODULE) || defined(KSU_KPROBES_HOOK)
|
|
||||||
struct umount_tw {
|
|
||||||
struct callback_head cb;
|
|
||||||
const struct cred *old_cred;
|
|
||||||
};
|
|
||||||
|
|
||||||
static void umount_tw_func(struct callback_head *cb)
|
|
||||||
{
|
|
||||||
struct umount_tw *tw = container_of(cb, struct umount_tw, cb);
|
|
||||||
const struct cred *saved = NULL;
|
|
||||||
if (tw->old_cred) {
|
|
||||||
saved = override_creds(tw->old_cred);
|
|
||||||
}
|
|
||||||
|
|
||||||
ksu_do_umount_lists();
|
|
||||||
|
|
||||||
if (saved)
|
|
||||||
revert_creds(saved);
|
|
||||||
|
|
||||||
if (tw->old_cred)
|
|
||||||
put_cred(tw->old_cred);
|
|
||||||
|
|
||||||
kfree(tw);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// force_sig kcompat, TODO: move it out of core_hook.c
|
|
||||||
// https://elixir.bootlin.com/linux/v5.3-rc1/source/kernel/signal.c#L1613
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
|
||||||
#define __force_sig(sig) force_sig(sig)
|
|
||||||
#else
|
|
||||||
#define __force_sig(sig) force_sig(sig, current)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
|
||||||
{
|
|
||||||
if (!new || !old) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
kuid_t new_uid = new->uid;
|
|
||||||
kuid_t old_uid = old->uid;
|
|
||||||
// pr_info("handle_setuid from %d to %d\n", old_uid.val, new_uid.val);
|
|
||||||
|
|
||||||
if (0 != old_uid.val) {
|
|
||||||
// old process is not root, ignore it.
|
|
||||||
if (ksu_enhanced_security_enabled) {
|
|
||||||
// disallow any non-ksu domain escalation from non-root to root!
|
|
||||||
if (unlikely(new_uid.val) == 0) {
|
|
||||||
if (!is_ksu_domain()) {
|
|
||||||
pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
|
|
||||||
current->pid, current->comm,
|
|
||||||
old_uid.val, new_uid.val);
|
|
||||||
__force_sig(SIGKILL);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// disallow appuid decrease to any other uid if it is allowed to su
|
|
||||||
if (is_appuid(old_uid)) {
|
|
||||||
if (new_uid.val < old_uid.val &&
|
|
||||||
!ksu_is_allow_uid_for_current(
|
|
||||||
old_uid.val)) {
|
|
||||||
pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
|
|
||||||
current->pid, current->comm,
|
|
||||||
old_uid.val, new_uid.val);
|
|
||||||
__force_sig(SIGKILL);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef KSU_SHOULD_USE_NEW_TP
|
|
||||||
if (new_uid.val == 2000 && ksu_su_compat_enabled) {
|
|
||||||
ksu_set_task_tracepoint_flag(current);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!is_appuid(new_uid) || is_unsupported_uid(new_uid.val)) {
|
|
||||||
// pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid.val);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// if on private space, see if its possibly the manager
|
|
||||||
if (new_uid.val > 100000 &&
|
|
||||||
new_uid.val % 100000 == ksu_get_manager_uid()) {
|
|
||||||
ksu_set_manager_uid(new_uid.val);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
|
||||||
if (ksu_get_manager_uid() == new_uid.val) {
|
|
||||||
pr_info("install fd for ksu manager(uid=%d)\n", new_uid.val);
|
|
||||||
ksu_install_fd();
|
|
||||||
spin_lock_irq(¤t->sighand->siglock);
|
|
||||||
ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot);
|
|
||||||
spin_unlock_irq(¤t->sighand->siglock);
|
|
||||||
ksu_set_task_tracepoint_flag(current);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ksu_is_allow_uid_for_current(new_uid.val)) {
|
|
||||||
if (current->seccomp.mode == SECCOMP_MODE_FILTER &&
|
|
||||||
current->seccomp.filter) {
|
|
||||||
spin_lock_irq(¤t->sighand->siglock);
|
|
||||||
ksu_seccomp_allow_cache(current->seccomp.filter,
|
|
||||||
__NR_reboot);
|
|
||||||
spin_unlock_irq(¤t->sighand->siglock);
|
|
||||||
}
|
|
||||||
if (ksu_su_compat_enabled) {
|
|
||||||
ksu_set_task_tracepoint_flag(current);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (ksu_su_compat_enabled) {
|
|
||||||
// Disable syscall tracepoint sucompat for non-allowed processes
|
|
||||||
ksu_clear_task_tracepoint_flag(current);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
if (ksu_is_allow_uid_for_current(new_uid.val)) {
|
|
||||||
spin_lock_irq(¤t->sighand->siglock);
|
|
||||||
disable_seccomp(current);
|
|
||||||
spin_unlock_irq(¤t->sighand->siglock);
|
|
||||||
|
|
||||||
if (ksu_get_manager_uid() == new_uid.val) {
|
|
||||||
pr_info("install fd for ksu manager(uid=%d)\n",
|
|
||||||
new_uid.val);
|
|
||||||
ksu_install_fd();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it!
|
|
||||||
if (!ksu_module_mounted) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ksu_kernel_umount_enabled) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ksu_uid_should_umount(new_uid.val)) {
|
|
||||||
return 0;
|
|
||||||
} else {
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_info("uid: %d should not umount!\n", current_uid().val);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// check old process's selinux context, if it is not zygote, ignore it!
|
|
||||||
// because some su apps may setuid to untrusted_app but they are in global mount namespace
|
|
||||||
// when we umount for such process, that is a disaster!
|
|
||||||
if (!is_zygote(old)) {
|
|
||||||
pr_info("handle umount ignore non zygote child: %d\n",
|
|
||||||
current->pid);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
// umount the target mnt
|
|
||||||
pr_info("handle umount for uid: %d, pid: %d\n", new_uid.val,
|
|
||||||
current->pid);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if defined(MODULE) || defined(KSU_KPROBES_HOOK)
|
|
||||||
struct umount_tw *tw;
|
|
||||||
tw = kmalloc(sizeof(*tw), GFP_ATOMIC);
|
|
||||||
if (!tw)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
tw->old_cred = get_current_cred();
|
|
||||||
tw->cb.func = umount_tw_func;
|
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 8)
|
|
||||||
int err = task_work_add(current, &tw->cb, TWA_RESUME);
|
|
||||||
#else
|
|
||||||
int err = task_work_add(current, &tw->cb, true);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
if (tw->old_cred) {
|
|
||||||
put_cred(tw->old_cred);
|
|
||||||
}
|
|
||||||
kfree(tw);
|
|
||||||
pr_warn("unmount add task_work failed\n");
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
ksu_do_umount_lists();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_inode_permission(struct inode *inode, int mask)
|
|
||||||
{
|
|
||||||
if (inode && inode->i_sb
|
|
||||||
&& unlikely(inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) {
|
|
||||||
//pr_info("%s: handling devpts for: %s \n", __func__, current->comm);
|
|
||||||
__ksu_handle_devpts(inode);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_bprm_check(struct linux_binprm *bprm)
|
|
||||||
{
|
|
||||||
char *filename = (char *)bprm->filename;
|
|
||||||
|
|
||||||
if (likely(!ksu_execveat_hook))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
|
||||||
static bool compat_check_done __read_mostly = false;
|
|
||||||
if ( unlikely(!compat_check_done) && unlikely(!strcmp(filename, "/data/adb/ksud"))
|
|
||||||
&& !memcmp(bprm->buf, "\x7f\x45\x4c\x46", 4) ) {
|
|
||||||
if (bprm->buf[4] == 0x01 )
|
|
||||||
ksu_is_compat = true;
|
|
||||||
|
|
||||||
pr_info("%s: %s ELF magic found! ksu_is_compat: %d \n", __func__, filename, ksu_is_compat);
|
|
||||||
compat_check_done = true;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ksu_handle_pre_ksud(filename);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd,
|
|
||||||
void __user **arg)
|
|
||||||
{
|
|
||||||
if (magic1 != KSU_INSTALL_MAGIC1)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_info("sys_reboot: intercepted call! magic: 0x%x id: %d\n", magic1,
|
|
||||||
magic2);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Check if this is a request to install KSU fd
|
|
||||||
if (magic2 == KSU_INSTALL_MAGIC2) {
|
|
||||||
int fd = ksu_install_fd();
|
|
||||||
// downstream: dereference all arg usage!
|
|
||||||
if (copy_to_user((void __user *)*arg, &fd, sizeof(fd))) {
|
|
||||||
pr_err("install ksu fd reply err\n");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// extensions
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -- For old kernel compat?
|
|
||||||
#if !defined(MODULE) && !defined(KSU_KPROBE_HOOK)
|
|
||||||
static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
|
|
||||||
int flags)
|
|
||||||
{
|
|
||||||
return ksu_handle_setuid(new, old);
|
|
||||||
}
|
|
||||||
|
|
||||||
// kernel 4.4 and 4.9
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
|
||||||
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
|
||||||
static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
|
|
||||||
unsigned perm)
|
|
||||||
{
|
|
||||||
if (init_session_keyring != NULL) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (strcmp(current->comm, "init")) {
|
|
||||||
// we are only interested in `init` process
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
init_session_keyring = cred->session_keyring;
|
|
||||||
pr_info("kernel_compat: got init_session_keyring\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <linux/lsm_hooks.h>
|
|
||||||
static struct security_hook_list ksu_hooks[] = {
|
|
||||||
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),
|
|
||||||
LSM_HOOK_INIT(inode_permission, ksu_inode_permission),
|
|
||||||
#ifndef KSU_KPROBES_HOOK
|
|
||||||
LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check),
|
|
||||||
#endif
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
|
||||||
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
|
||||||
LSM_HOOK_INIT(key_permission, ksu_key_permission)
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
|
|
||||||
static const struct lsm_id ksu_lsmid = {
|
|
||||||
.name = "ksu",
|
|
||||||
.id = 912,
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static void ksu_lsm_hook_init(void)
|
|
||||||
{
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
|
|
||||||
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), &ksu_lsmid);
|
|
||||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
|
||||||
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu");
|
|
||||||
#else
|
|
||||||
// https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892
|
|
||||||
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks));
|
|
||||||
#endif
|
|
||||||
pr_info("LSM hooks initialized.\n");
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static void ksu_lsm_hook_init(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// -- For KPROBE and LKM handler
|
|
||||||
#if defined(MODULE) || defined(KSU_KPROBES_HOOK)
|
|
||||||
static int reboot_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
|
||||||
int magic1 = (int)PT_REGS_PARM1(real_regs);
|
|
||||||
int magic2 = (int)PT_REGS_PARM2(real_regs);
|
|
||||||
int cmd = (int)PT_REGS_PARM3(real_regs);
|
|
||||||
void __user **arg = (void __user **)&PT_REGS_SYSCALL_PARM4(real_regs);
|
|
||||||
|
|
||||||
return ksu_handle_sys_reboot(magic1, magic2, cmd, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kprobe reboot_kp = {
|
|
||||||
.symbol_name = REBOOT_SYMBOL,
|
|
||||||
.pre_handler = reboot_handler_pre,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 2. cap_task_fix_setuid hook for handling setuid
|
|
||||||
static int cap_task_fix_setuid_handler_pre(struct kprobe *p,
|
|
||||||
struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
struct cred *new = (struct cred *)PT_REGS_PARM1(regs);
|
|
||||||
const struct cred *old = (const struct cred *)PT_REGS_PARM2(regs);
|
|
||||||
|
|
||||||
ksu_handle_setuid(new, old);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kprobe cap_task_fix_setuid_kp = {
|
|
||||||
.symbol_name = "cap_task_fix_setuid",
|
|
||||||
.pre_handler = cap_task_fix_setuid_handler_pre,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int ksu_kprobe_init(void)
|
|
||||||
{
|
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
// Register reboot kprobe
|
|
||||||
rc = register_kprobe(&reboot_kp);
|
|
||||||
if (rc) {
|
|
||||||
pr_err("reboot kprobe failed: %d\n", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
pr_info("reboot kprobe registered successfully\n");
|
|
||||||
|
|
||||||
// Register cap_task_fix_setuid kprobe
|
|
||||||
rc = register_kprobe(&cap_task_fix_setuid_kp);
|
|
||||||
if (rc) {
|
|
||||||
pr_err("cap_task_fix_setuid kprobe failed: %d\n", rc);
|
|
||||||
unregister_kprobe(&reboot_kp);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
pr_info("cap_task_fix_setuid kprobe registered successfully\n");
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void ksu_kprobe_exit(void)
|
|
||||||
{
|
|
||||||
unregister_kprobe(&cap_task_fix_setuid_kp);
|
|
||||||
unregister_kprobe(&reboot_kp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void __init ksu_core_init(void)
|
|
||||||
{
|
|
||||||
int rc = ksu_kprobe_init();
|
|
||||||
if (rc) {
|
|
||||||
pr_err("ksu_kprobe_init failed: %d\n", rc);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ksu_register_feature_handler(&kernel_umount_handler)) {
|
|
||||||
pr_err("Failed to register umount feature handler\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ksu_register_feature_handler(&enhanced_security_handler)) {
|
|
||||||
pr_err("Failed to register enhanced security feature handler\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ksu_core_exit(void)
|
|
||||||
{
|
|
||||||
pr_info("ksu_core_exit\n");
|
|
||||||
ksu_kprobe_exit();
|
|
||||||
ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
|
|
||||||
void __init ksu_core_init(void)
|
|
||||||
{
|
|
||||||
ksu_lsm_hook_init();
|
|
||||||
|
|
||||||
if (ksu_register_feature_handler(&kernel_umount_handler)) {
|
|
||||||
pr_err("Failed to register umount feature handler\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ksu_register_feature_handler(&enhanced_security_handler)) {
|
|
||||||
pr_err("Failed to register enhanced security feature handler\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ksu_core_exit(void)
|
|
||||||
{
|
|
||||||
ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT);
|
|
||||||
|
|
||||||
ksu_uid_exit();
|
|
||||||
ksu_throne_comm_exit();
|
|
||||||
|
|
||||||
ksu_unregister_feature_handler(KSU_FEATURE_ENHANCED_SECURITY);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#ifndef __KSU_H_KSU_CORE
|
|
||||||
#define __KSU_H_KSU_CORE
|
|
||||||
|
|
||||||
#include <linux/init.h>
|
|
||||||
#include "apk_sign.h"
|
|
||||||
|
|
||||||
void __init ksu_core_init(void);
|
|
||||||
void ksu_core_exit(void);
|
|
||||||
|
|
||||||
void escape_to_root(void);
|
|
||||||
|
|
||||||
void nuke_ext4_sysfs(void);
|
|
||||||
|
|
||||||
extern bool ksu_module_mounted;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -157,7 +157,7 @@ void ksu_feature_init(void)
|
|||||||
feature_handlers[i] = NULL;
|
feature_handlers[i] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("%s: feature management initialized\n", __func__);
|
pr_info("feature: feature management initialized\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_feature_exit(void)
|
void ksu_feature_exit(void)
|
||||||
@@ -172,5 +172,5 @@ void ksu_feature_exit(void)
|
|||||||
|
|
||||||
mutex_unlock(&feature_mutex);
|
mutex_unlock(&feature_mutex);
|
||||||
|
|
||||||
pr_info("%s: feature management cleaned up\n", __func__);
|
pr_info("feature: feature management cleaned up\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,188 +1,160 @@
|
|||||||
#include "linux/export.h"
|
#include <linux/export.h>
|
||||||
#include <linux/anon_inodes.h>
|
#include <linux/anon_inodes.h>
|
||||||
|
#include <linux/aio.h> // kernel 3.18
|
||||||
#include <linux/capability.h>
|
#include <linux/capability.h>
|
||||||
#include <linux/cred.h>
|
#include <linux/cred.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/file.h>
|
#include <linux/file.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/poll.h>
|
#include <linux/seq_file.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/types.h>
|
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
|
||||||
#include "allowlist.h"
|
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksu.h"
|
|
||||||
#include "ksud.h"
|
|
||||||
#include "manager.h"
|
|
||||||
#include "selinux/selinux.h"
|
#include "selinux/selinux.h"
|
||||||
#include "core_hook.h"
|
|
||||||
#include "objsec.h"
|
|
||||||
|
|
||||||
#include "file_wrapper.h"
|
#include "file_wrapper.h"
|
||||||
|
|
||||||
#ifdef KSU_NO___POLL_T
|
static loff_t ksu_wrapper_llseek(struct file *fp, loff_t off, int flags) {
|
||||||
typedef unsigned int ksu_poll_t;
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
#else
|
struct file* orig = data->orig;
|
||||||
typedef __poll_t ksu_poll_t;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static loff_t ksu_wrapper_llseek(struct file *fp, loff_t off, int flags)
|
|
||||||
{
|
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
return orig->f_op->llseek(data->orig, off, flags);
|
return orig->f_op->llseek(data->orig, off, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t ksu_wrapper_read(struct file *fp, char __user *ptr, size_t sz,
|
static ssize_t ksu_wrapper_read(struct file *fp, char __user *ptr, size_t sz, loff_t *off) {
|
||||||
loff_t *off)
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
{
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
return orig->f_op->read(orig, ptr, sz, off);
|
return orig->f_op->read(orig, ptr, sz, off);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t ksu_wrapper_write(struct file *fp, const char __user *ptr,
|
static ssize_t ksu_wrapper_write(struct file *fp, const char __user *ptr, size_t sz, loff_t *off) {
|
||||||
size_t sz, loff_t *off)
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
{
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
return orig->f_op->write(orig, ptr, sz, off);
|
return orig->f_op->write(orig, ptr, sz, off);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
|
||||||
static ssize_t ksu_wrapper_read_iter(struct kiocb *iocb, struct iov_iter *iovi)
|
static ssize_t ksu_wrapper_read_iter(struct kiocb *iocb, struct iov_iter *iovi) {
|
||||||
{
|
struct ksu_file_wrapper* data = iocb->ki_filp->private_data;
|
||||||
struct ksu_file_wrapper *data = iocb->ki_filp->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
iocb->ki_filp = orig;
|
iocb->ki_filp = orig;
|
||||||
return orig->f_op->read_iter(iocb, iovi);
|
return orig->f_op->read_iter(iocb, iovi);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t ksu_wrapper_write_iter(struct kiocb *iocb,
|
static ssize_t ksu_wrapper_write_iter(struct kiocb *iocb, struct iov_iter *iovi) {
|
||||||
struct iov_iter *iovi)
|
struct ksu_file_wrapper* data = iocb->ki_filp->private_data;
|
||||||
{
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = iocb->ki_filp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
iocb->ki_filp = orig;
|
iocb->ki_filp = orig;
|
||||||
return orig->f_op->write_iter(iocb, iovi);
|
return orig->f_op->write_iter(iocb, iovi);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||||
static int ksu_wrapper_iopoll(struct kiocb *kiocb, struct io_comp_batch *icb,
|
static int ksu_wrapper_iopoll(struct kiocb *kiocb, struct io_comp_batch* icb, unsigned int v) {
|
||||||
unsigned int v)
|
struct ksu_file_wrapper* data = kiocb->ki_filp->private_data;
|
||||||
{
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = kiocb->ki_filp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
kiocb->ki_filp = orig;
|
kiocb->ki_filp = orig;
|
||||||
return orig->f_op->iopoll(kiocb, icb, v);
|
return orig->f_op->iopoll(kiocb, icb, v);
|
||||||
}
|
}
|
||||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
|
||||||
static int ksu_wrapper_iopoll(struct kiocb *kiocb, bool spin)
|
static int ksu_wrapper_iopoll(struct kiocb *kiocb, bool spin) {
|
||||||
{
|
struct ksu_file_wrapper* data = kiocb->ki_filp->private_data;
|
||||||
struct ksu_file_wrapper *data = kiocb->ki_filp->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
kiocb->ki_filp = orig;
|
kiocb->ki_filp = orig;
|
||||||
return orig->f_op->iopoll(kiocb, spin);
|
return orig->f_op->iopoll(kiocb, spin);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) && (LINUX_VERSION_CODE > KERNEL_VERSION(3, 11, 0) || defined(KSU_HAS_ITERATE_DIR))
|
||||||
static int ksu_wrapper_iterate(struct file *fp, struct dir_context *dc)
|
static int ksu_wrapper_iterate (struct file *fp, struct dir_context *dc) {
|
||||||
{
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
return orig->f_op->iterate(orig, dc);
|
return orig->f_op->iterate(orig, dc);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// int (*readdir) (struct file *, void *, filldir_t);
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) && !defined(KSU_HAS_ITERATE_DIR)
|
||||||
|
static int ksu_wrapper_readdir(struct file *fp, void *ptr, filldir_t filler) {
|
||||||
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
|
struct file* orig = data->orig;
|
||||||
|
return orig->f_op->readdir(orig, ptr, filler);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
||||||
static int ksu_wrapper_iterate_shared(struct file *fp, struct dir_context *dc)
|
static int ksu_wrapper_iterate_shared(struct file *fp, struct dir_context *dc) {
|
||||||
{
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
return orig->f_op->iterate_shared(orig, dc);
|
return orig->f_op->iterate_shared(orig, dc);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static ksu_poll_t ksu_wrapper_poll(struct file *fp,
|
// typedef unsigned __bitwise __poll_t;
|
||||||
struct poll_table_struct *pts)
|
static unsigned __bitwise ksu_wrapper_poll(struct file *fp, struct poll_table_struct *pts) {
|
||||||
{
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
return orig->f_op->poll(orig, pts);
|
return orig->f_op->poll(orig, pts);
|
||||||
}
|
}
|
||||||
|
|
||||||
static long ksu_wrapper_unlocked_ioctl(struct file *fp, unsigned int cmd,
|
static long ksu_wrapper_unlocked_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) {
|
||||||
unsigned long arg)
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
{
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
return orig->f_op->unlocked_ioctl(orig, cmd, arg);
|
return orig->f_op->unlocked_ioctl(orig, cmd, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static long ksu_wrapper_compat_ioctl(struct file *fp, unsigned int cmd,
|
static long ksu_wrapper_compat_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) {
|
||||||
unsigned long arg)
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
{
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
return orig->f_op->compat_ioctl(orig, cmd, arg);
|
return orig->f_op->compat_ioctl(orig, cmd, arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ksu_wrapper_mmap(struct file *fp, struct vm_area_struct *vma)
|
static int ksu_wrapper_mmap(struct file *fp, struct vm_area_struct * vma) {
|
||||||
{
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
return orig->f_op->mmap(orig, vma);
|
return orig->f_op->mmap(orig, vma);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static unsigned long mmap_supported_flags {}
|
// static unsigned long mmap_supported_flags {}
|
||||||
|
|
||||||
static int ksu_wrapper_open(struct inode *ino, struct file *fp)
|
static int ksu_wrapper_open(struct inode *ino, struct file *fp) {
|
||||||
{
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
struct inode *orig_ino = file_inode(orig);
|
struct inode *orig_ino = file_inode(orig);
|
||||||
return orig->f_op->open(orig_ino, orig);
|
return orig->f_op->open(orig_ino, orig);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ksu_wrapper_flush(struct file *fp, fl_owner_t id)
|
static int ksu_wrapper_flush(struct file *fp, fl_owner_t id) {
|
||||||
{
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
return orig->f_op->flush(orig, id);
|
return orig->f_op->flush(orig, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ksu_wrapper_fsync(struct file *fp, loff_t off1, loff_t off2,
|
|
||||||
int datasync)
|
static int ksu_wrapper_fsync(struct file *fp, loff_t off1, loff_t off2, int datasync) {
|
||||||
{
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
return orig->f_op->fsync(orig, off1, off2, datasync);
|
return orig->f_op->fsync(orig, off1, off2, datasync);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ksu_wrapper_fasync(int arg, struct file *fp, int arg2)
|
static int ksu_wrapper_fasync(int arg, struct file *fp, int arg2) {
|
||||||
{
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
return orig->f_op->fasync(arg, orig, arg2);
|
return orig->f_op->fasync(arg, orig, arg2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ksu_wrapper_lock(struct file *fp, int arg1, struct file_lock *fl)
|
static int ksu_wrapper_lock(struct file *fp, int arg1, struct file_lock *fl) {
|
||||||
{
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
return orig->f_op->lock(orig, arg1, fl);
|
return orig->f_op->lock(orig, arg1, fl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||||
static ssize_t ksu_wrapper_sendpage(struct file *fp, struct page *pg, int arg1,
|
static ssize_t ksu_wrapper_sendpage(struct file *fp, struct page *pg, int arg1, size_t sz, loff_t *off, int arg2) {
|
||||||
size_t sz, loff_t *off, int arg2)
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
{
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->sendpage) {
|
if (orig->f_op->sendpage) {
|
||||||
return orig->f_op->sendpage(orig, pg, arg1, sz, off, arg2);
|
return orig->f_op->sendpage(orig, pg, arg1, sz, off, arg2);
|
||||||
}
|
}
|
||||||
@@ -190,51 +162,38 @@ static ssize_t ksu_wrapper_sendpage(struct file *fp, struct page *pg, int arg1,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static unsigned long ksu_wrapper_get_unmapped_area(struct file *fp,
|
static unsigned long ksu_wrapper_get_unmapped_area(struct file *fp, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4) {
|
||||||
unsigned long arg1,
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
unsigned long arg2,
|
struct file* orig = data->orig;
|
||||||
unsigned long arg3,
|
|
||||||
unsigned long arg4)
|
|
||||||
{
|
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->get_unmapped_area) {
|
if (orig->f_op->get_unmapped_area) {
|
||||||
return orig->f_op->get_unmapped_area(orig, arg1, arg2, arg3,
|
return orig->f_op->get_unmapped_area(orig, arg1, arg2, arg3, arg4);
|
||||||
arg4);
|
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static int ksu_wrapper_check_flags(int arg) {}
|
// static int ksu_wrapper_check_flags(int arg) {}
|
||||||
|
|
||||||
static int ksu_wrapper_flock(struct file *fp, int arg1, struct file_lock *fl)
|
static int ksu_wrapper_flock(struct file *fp, int arg1, struct file_lock *fl) {
|
||||||
{
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->flock) {
|
if (orig->f_op->flock) {
|
||||||
return orig->f_op->flock(orig, arg1, fl);
|
return orig->f_op->flock(orig, arg1, fl);
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t ksu_wrapper_splice_write(struct pipe_inode_info *pii,
|
static ssize_t ksu_wrapper_splice_write(struct pipe_inode_info * pii, struct file *fp, loff_t *off, size_t sz, unsigned int arg1) {
|
||||||
struct file *fp, loff_t *off,
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
size_t sz, unsigned int arg1)
|
struct file* orig = data->orig;
|
||||||
{
|
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->splice_write) {
|
if (orig->f_op->splice_write) {
|
||||||
return orig->f_op->splice_write(pii, orig, off, sz, arg1);
|
return orig->f_op->splice_write(pii, orig, off, sz, arg1);
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t ksu_wrapper_splice_read(struct file *fp, loff_t *off,
|
static ssize_t ksu_wrapper_splice_read(struct file *fp, loff_t *off, struct pipe_inode_info *pii, size_t sz, unsigned int arg1) {
|
||||||
struct pipe_inode_info *pii, size_t sz,
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
unsigned int arg1)
|
struct file* orig = data->orig;
|
||||||
{
|
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->splice_read) {
|
if (orig->f_op->splice_read) {
|
||||||
return orig->f_op->splice_read(orig, off, pii, sz, arg1);
|
return orig->f_op->splice_read(orig, off, pii, sz, arg1);
|
||||||
}
|
}
|
||||||
@@ -242,10 +201,9 @@ static ssize_t ksu_wrapper_splice_read(struct file *fp, loff_t *off,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||||
void ksu_wrapper_splice_eof(struct file *fp)
|
void ksu_wrapper_splice_eof(struct file *fp) {
|
||||||
{
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->splice_eof) {
|
if (orig->f_op->splice_eof) {
|
||||||
return orig->f_op->splice_eof(orig);
|
return orig->f_op->splice_eof(orig);
|
||||||
}
|
}
|
||||||
@@ -253,45 +211,36 @@ void ksu_wrapper_splice_eof(struct file *fp)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||||
static int ksu_wrapper_setlease(struct file *fp, int arg1,
|
static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lease **fl, void **p) {
|
||||||
struct file_lease **fl, void **p)
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
{
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->setlease) {
|
if (orig->f_op->setlease) {
|
||||||
return orig->f_op->setlease(orig, arg1, fl, p);
|
return orig->f_op->setlease(orig, arg1, fl, p);
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||||
static int ksu_wrapper_setlease(struct file *fp, int arg1,
|
static int ksu_wrapper_setlease(struct file *fp, int arg1, struct file_lock **fl, void **p) {
|
||||||
struct file_lock **fl, void **p)
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
{
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->setlease) {
|
if (orig->f_op->setlease) {
|
||||||
return orig->f_op->setlease(orig, arg1, fl, p);
|
return orig->f_op->setlease(orig, arg1, fl, p);
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
// int (*setlease)(struct file *, long, struct file_lock **, void **);
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0) // int (*setlease)(struct file *, long, struct file_lock **, void **);
|
||||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0)
|
static int ksu_wrapper_setlease(struct file *fp, long arg1, struct file_lock **fl, void **p) {
|
||||||
static int ksu_wrapper_setlease(struct file *fp, long arg1,
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
struct file_lock **fl, void **p)
|
struct file* orig = data->orig;
|
||||||
{
|
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->setlease) {
|
if (orig->f_op->setlease) {
|
||||||
return orig->f_op->setlease(orig, arg1, fl, p);
|
return orig->f_op->setlease(orig, arg1, fl, p);
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
#else // int (*setlease)(struct file *, long, struct file_lock **);
|
#else // int (*setlease)(struct file *, long, struct file_lock **);
|
||||||
static int ksu_wrapper_setlease(struct file *fp, long arg1,
|
static int ksu_wrapper_setlease(struct file *fp, long arg1, struct file_lock **fl) {
|
||||||
struct file_lock **fl)
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
{
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->setlease) {
|
if (orig->f_op->setlease) {
|
||||||
return orig->f_op->setlease(orig, arg1, fl);
|
return orig->f_op->setlease(orig, arg1, fl);
|
||||||
}
|
}
|
||||||
@@ -299,11 +248,9 @@ static int ksu_wrapper_setlease(struct file *fp, long arg1,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static long ksu_wrapper_fallocate(struct file *fp, int mode, loff_t offset,
|
static long ksu_wrapper_fallocate(struct file *fp, int mode, loff_t offset, loff_t len) {
|
||||||
loff_t len)
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
{
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->fallocate) {
|
if (orig->f_op->fallocate) {
|
||||||
return orig->f_op->fallocate(orig, mode, offset, len);
|
return orig->f_op->fallocate(orig, mode, offset, len);
|
||||||
}
|
}
|
||||||
@@ -311,20 +258,17 @@ static long ksu_wrapper_fallocate(struct file *fp, int mode, loff_t offset,
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0)
|
||||||
static void ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f)
|
static void ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) {
|
||||||
{
|
struct ksu_file_wrapper* data = f->private_data;
|
||||||
struct ksu_file_wrapper *data = m->file->private_data;
|
struct file* orig = data->orig;
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->show_fdinfo) {
|
if (orig->f_op->show_fdinfo) {
|
||||||
orig->f_op->show_fdinfo(m, orig);
|
orig->f_op->show_fdinfo(m, orig);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
||||||
#else
|
static int ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) {
|
||||||
static int ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f)
|
struct ksu_file_wrapper* data = f->private_data;
|
||||||
{
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = m->file->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->show_fdinfo) {
|
if (orig->f_op->show_fdinfo) {
|
||||||
orig->f_op->show_fdinfo(m, orig);
|
orig->f_op->show_fdinfo(m, orig);
|
||||||
}
|
}
|
||||||
@@ -333,44 +277,40 @@ static int ksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
||||||
static ssize_t ksu_wrapper_copy_file_range(struct file *f1, loff_t off1,
|
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/read_write.c;l=1593-1606;drc=398da7defe218d3e51b0f3bdff75147e28125b60
|
||||||
struct file *f2, loff_t off2,
|
static ssize_t ksu_wrapper_copy_file_range(struct file *file_in, loff_t pos_in, struct file *file_out,
|
||||||
size_t sz, unsigned int flags)
|
loff_t pos_out, size_t len, unsigned int flags) {
|
||||||
{
|
struct ksu_file_wrapper* data = file_out->private_data;
|
||||||
// TODO: determine which file to use
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = f1->private_data;
|
return orig->f_op->copy_file_range(file_in, pos_in, orig, pos_out, len, flags);
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->copy_file_range) {
|
|
||||||
return orig->f_op->copy_file_range(orig, off1, f2, off2, sz,
|
|
||||||
flags);
|
|
||||||
}
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
||||||
|
// no REMAP_FILE_DEDUP: use file_in
|
||||||
|
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/read_write.c;l=1598-1599;drc=398da7defe218d3e51b0f3bdff75147e28125b60
|
||||||
|
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/remap_range.c;l=403-404;drc=398da7defe218d3e51b0f3bdff75147e28125b60
|
||||||
|
// REMAP_FILE_DEDUP: use file_out
|
||||||
|
// https://cs.android.com/android/kernel/superproject/+/common-android-mainline:common/fs/remap_range.c;l=483-484;drc=398da7defe218d3e51b0f3bdff75147e28125b60
|
||||||
static loff_t ksu_wrapper_remap_file_range(struct file *file_in, loff_t pos_in,
|
static loff_t ksu_wrapper_remap_file_range(struct file *file_in, loff_t pos_in,
|
||||||
struct file *file_out,
|
struct file *file_out, loff_t pos_out,
|
||||||
loff_t pos_out, loff_t len,
|
loff_t len, unsigned int remap_flags) {
|
||||||
unsigned int remap_flags)
|
if (remap_flags & REMAP_FILE_DEDUP) {
|
||||||
{
|
struct ksu_file_wrapper* data = file_out->private_data;
|
||||||
// TODO: determine which file to use
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = file_in->private_data;
|
return orig->f_op->remap_file_range(file_in, pos_in, orig, pos_out, len, remap_flags);
|
||||||
struct file *orig = data->orig;
|
} else {
|
||||||
if (orig->f_op->remap_file_range) {
|
struct ksu_file_wrapper* data = file_in->private_data;
|
||||||
return orig->f_op->remap_file_range(orig, pos_in, file_out,
|
struct file* orig = data->orig;
|
||||||
pos_out, len, remap_flags);
|
return orig->f_op->remap_file_range(orig, pos_in, file_out, pos_out, len, remap_flags);
|
||||||
}
|
}
|
||||||
return -EINVAL;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
||||||
static int ksu_wrapper_fadvise(struct file *fp, loff_t off1, loff_t off2,
|
static int ksu_wrapper_fadvise(struct file *fp, loff_t off1, loff_t off2, int flags) {
|
||||||
int flags)
|
struct ksu_file_wrapper* data = fp->private_data;
|
||||||
{
|
struct file* orig = data->orig;
|
||||||
struct ksu_file_wrapper *data = fp->private_data;
|
|
||||||
struct file *orig = data->orig;
|
|
||||||
if (orig->f_op->fadvise) {
|
if (orig->f_op->fadvise) {
|
||||||
return orig->f_op->fadvise(orig, off1, off2, flags);
|
return orig->f_op->fadvise(orig, off1, off2, flags);
|
||||||
}
|
}
|
||||||
@@ -378,16 +318,13 @@ static int ksu_wrapper_fadvise(struct file *fp, loff_t off1, loff_t off2,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int ksu_wrapper_release(struct inode *inode, struct file *filp)
|
static int ksu_wrapper_release(struct inode *inode, struct file *filp) {
|
||||||
{
|
|
||||||
ksu_delete_file_wrapper(filp->private_data);
|
ksu_delete_file_wrapper(filp->private_data);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ksu_file_wrapper *ksu_create_file_wrapper(struct file *fp)
|
struct ksu_file_wrapper* ksu_create_file_wrapper(struct file* fp) {
|
||||||
{
|
struct ksu_file_wrapper* p = kcalloc(sizeof(struct ksu_file_wrapper), 1, GFP_KERNEL);
|
||||||
struct ksu_file_wrapper *p =
|
|
||||||
kcalloc(sizeof(struct ksu_file_wrapper), 1, GFP_KERNEL);
|
|
||||||
if (!p) {
|
if (!p) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -401,24 +338,23 @@ struct ksu_file_wrapper *ksu_create_file_wrapper(struct file *fp)
|
|||||||
p->ops.write = fp->f_op->write ? ksu_wrapper_write : NULL;
|
p->ops.write = fp->f_op->write ? ksu_wrapper_write : NULL;
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)
|
||||||
p->ops.read_iter = fp->f_op->read_iter ? ksu_wrapper_read_iter : NULL;
|
p->ops.read_iter = fp->f_op->read_iter ? ksu_wrapper_read_iter : NULL;
|
||||||
p->ops.write_iter =
|
p->ops.write_iter = fp->f_op->write_iter ? ksu_wrapper_write_iter : NULL;
|
||||||
fp->f_op->write_iter ? ksu_wrapper_write_iter : NULL;
|
|
||||||
#endif
|
#endif
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||||
p->ops.iopoll = fp->f_op->iopoll ? ksu_wrapper_iopoll : NULL;
|
p->ops.iopoll = fp->f_op->iopoll ? ksu_wrapper_iopoll : NULL;
|
||||||
#endif
|
#endif
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0) && (LINUX_VERSION_CODE > KERNEL_VERSION(3, 11, 0) || defined(KSU_HAS_ITERATE_DIR))
|
||||||
p->ops.iterate = fp->f_op->iterate ? ksu_wrapper_iterate : NULL;
|
p->ops.iterate = fp->f_op->iterate ? ksu_wrapper_iterate : NULL;
|
||||||
#endif
|
#endif
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 11, 0) && !defined(KSU_HAS_ITERATE_DIR)
|
||||||
|
p->ops.readdir = fp->f_op->readdir ? ksu_wrapper_readdir : NULL;
|
||||||
|
#endif
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
||||||
p->ops.iterate_shared =
|
p->ops.iterate_shared = fp->f_op->iterate_shared ? ksu_wrapper_iterate_shared : NULL;
|
||||||
fp->f_op->iterate_shared ? ksu_wrapper_iterate_shared : NULL;
|
|
||||||
#endif
|
#endif
|
||||||
p->ops.poll = fp->f_op->poll ? ksu_wrapper_poll : NULL;
|
p->ops.poll = fp->f_op->poll ? ksu_wrapper_poll : NULL;
|
||||||
p->ops.unlocked_ioctl =
|
p->ops.unlocked_ioctl = fp->f_op->unlocked_ioctl ? ksu_wrapper_unlocked_ioctl : NULL;
|
||||||
fp->f_op->unlocked_ioctl ? ksu_wrapper_unlocked_ioctl : NULL;
|
p->ops.compat_ioctl = fp->f_op->compat_ioctl ? ksu_wrapper_compat_ioctl : NULL;
|
||||||
p->ops.compat_ioctl =
|
|
||||||
fp->f_op->compat_ioctl ? ksu_wrapper_compat_ioctl : NULL;
|
|
||||||
p->ops.mmap = fp->f_op->mmap ? ksu_wrapper_mmap : NULL;
|
p->ops.mmap = fp->f_op->mmap ? ksu_wrapper_mmap : NULL;
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||||
p->ops.fop_flags = fp->f_op->fop_flags;
|
p->ops.fop_flags = fp->f_op->fop_flags;
|
||||||
@@ -434,42 +370,33 @@ struct ksu_file_wrapper *ksu_create_file_wrapper(struct file *fp)
|
|||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
|
||||||
p->ops.sendpage = fp->f_op->sendpage ? ksu_wrapper_sendpage : NULL;
|
p->ops.sendpage = fp->f_op->sendpage ? ksu_wrapper_sendpage : NULL;
|
||||||
#endif
|
#endif
|
||||||
p->ops.get_unmapped_area = fp->f_op->get_unmapped_area ?
|
p->ops.get_unmapped_area = fp->f_op->get_unmapped_area ? ksu_wrapper_get_unmapped_area : NULL;
|
||||||
ksu_wrapper_get_unmapped_area :
|
|
||||||
NULL;
|
|
||||||
p->ops.check_flags = fp->f_op->check_flags;
|
p->ops.check_flags = fp->f_op->check_flags;
|
||||||
p->ops.flock = fp->f_op->flock ? ksu_wrapper_flock : NULL;
|
p->ops.flock = fp->f_op->flock ? ksu_wrapper_flock : NULL;
|
||||||
p->ops.splice_write =
|
p->ops.splice_write = fp->f_op->splice_write ? ksu_wrapper_splice_write : NULL;
|
||||||
fp->f_op->splice_write ? ksu_wrapper_splice_write : NULL;
|
p->ops.splice_read = fp->f_op->splice_read ? ksu_wrapper_splice_read : NULL;
|
||||||
p->ops.splice_read =
|
|
||||||
fp->f_op->splice_read ? ksu_wrapper_splice_read : NULL;
|
|
||||||
p->ops.setlease = fp->f_op->setlease ? ksu_wrapper_setlease : NULL;
|
p->ops.setlease = fp->f_op->setlease ? ksu_wrapper_setlease : NULL;
|
||||||
p->ops.fallocate = fp->f_op->fallocate ? ksu_wrapper_fallocate : NULL;
|
p->ops.fallocate = fp->f_op->fallocate ? ksu_wrapper_fallocate : NULL;
|
||||||
p->ops.show_fdinfo =
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
|
||||||
fp->f_op->show_fdinfo ? ksu_wrapper_show_fdinfo : NULL;
|
p->ops.show_fdinfo = fp->f_op->show_fdinfo ? ksu_wrapper_show_fdinfo : NULL;
|
||||||
|
#endif
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 7, 0)
|
||||||
p->ops.copy_file_range =
|
p->ops.copy_file_range = fp->f_op->copy_file_range ? ksu_wrapper_copy_file_range : NULL;
|
||||||
fp->f_op->copy_file_range ? ksu_wrapper_copy_file_range : NULL;
|
|
||||||
#endif
|
#endif
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0)
|
||||||
p->ops.remap_file_range = fp->f_op->remap_file_range ?
|
p->ops.remap_file_range = fp->f_op->remap_file_range ? ksu_wrapper_remap_file_range : NULL;
|
||||||
ksu_wrapper_remap_file_range :
|
|
||||||
NULL;
|
|
||||||
#endif
|
#endif
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0)
|
||||||
p->ops.fadvise = fp->f_op->fadvise ? ksu_wrapper_fadvise : NULL;
|
p->ops.fadvise = fp->f_op->fadvise ? ksu_wrapper_fadvise : NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||||
p->ops.splice_eof =
|
p->ops.splice_eof = fp->f_op->splice_eof ? ksu_wrapper_splice_eof : NULL;
|
||||||
fp->f_op->splice_eof ? ksu_wrapper_splice_eof : NULL;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_delete_file_wrapper(struct ksu_file_wrapper *data)
|
void ksu_delete_file_wrapper(struct ksu_file_wrapper* data) {
|
||||||
{
|
fput((struct file*) data->orig);
|
||||||
fput((struct file *)data->orig);
|
|
||||||
kfree(data);
|
kfree(data);
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
#ifndef __KSU_H_FILE_WRAPPER
|
#ifndef KSU_FILE_WRAPPER_H
|
||||||
#define __KSU_H_FILE_WRAPPER
|
#define KSU_FILE_WRAPPER_H
|
||||||
|
|
||||||
#include <linux/file.h>
|
#include <linux/file.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
@@ -12,4 +12,4 @@ struct ksu_file_wrapper {
|
|||||||
struct ksu_file_wrapper *ksu_create_file_wrapper(struct file *fp);
|
struct ksu_file_wrapper *ksu_create_file_wrapper(struct file *fp);
|
||||||
void ksu_delete_file_wrapper(struct ksu_file_wrapper *data);
|
void ksu_delete_file_wrapper(struct ksu_file_wrapper *data);
|
||||||
|
|
||||||
#endif
|
#endif // KSU_FILE_WRAPPER_H
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
#ifndef __KSU_H_KSHOOK
|
|
||||||
#define __KSU_H_KSHOOK
|
|
||||||
|
|
||||||
#include <linux/fs.h>
|
|
||||||
#include <linux/types.h>
|
|
||||||
|
|
||||||
// For sucompat
|
|
||||||
|
|
||||||
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
|
||||||
int *flags);
|
|
||||||
|
|
||||||
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
|
||||||
|
|
||||||
// For ksud
|
|
||||||
|
|
||||||
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
|
||||||
size_t *count_ptr, loff_t **pos);
|
|
||||||
|
|
||||||
// For ksud and sucompat
|
|
||||||
|
|
||||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
|
||||||
void *envp, int *flags);
|
|
||||||
|
|
||||||
// For volume button
|
|
||||||
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
|
||||||
int *value);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/nsproxy.h>
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||||
#include <linux/sched/task.h>
|
#include <linux/sched/task.h>
|
||||||
#else
|
#else
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#endif
|
#endif
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/filter.h>
|
|
||||||
#include <linux/seccomp.h>
|
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "kernel_compat.h"
|
#include "kernel_compat.h"
|
||||||
|
|
||||||
@@ -17,10 +14,12 @@
|
|||||||
#include <linux/key.h>
|
#include <linux/key.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/cred.h>
|
#include <linux/cred.h>
|
||||||
|
#include <linux/lsm_hooks.h>
|
||||||
|
|
||||||
extern int install_session_keyring_to_cred(struct cred *, struct key *);
|
extern int install_session_keyring_to_cred(struct cred *, struct key *);
|
||||||
struct key *init_session_keyring = NULL;
|
struct key *init_session_keyring = NULL;
|
||||||
static inline int install_session_keyring(struct key *keyring)
|
|
||||||
|
static int install_session_keyring(struct key *keyring)
|
||||||
{
|
{
|
||||||
struct cred *new;
|
struct cred *new;
|
||||||
int ret;
|
int ret;
|
||||||
@@ -124,117 +123,3 @@ long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
long ksu_strncpy_from_user_retry(char *dst, const void __user *unsafe_addr,
|
|
||||||
long count)
|
|
||||||
{
|
|
||||||
long ret;
|
|
||||||
|
|
||||||
ret = ksu_strncpy_from_user_nofault(dst, unsafe_addr, count);
|
|
||||||
if (likely(ret >= 0))
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
// we faulted! fallback to slow path
|
|
||||||
if (unlikely(!ksu_access_ok(unsafe_addr, count))) {
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_err("%s: faulted!\n", __func__);
|
|
||||||
#endif
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// why we don't do like how strncpy_from_user_nofault?
|
|
||||||
ret = strncpy_from_user(dst, unsafe_addr, count);
|
|
||||||
|
|
||||||
if (ret >= count) {
|
|
||||||
ret = count;
|
|
||||||
dst[ret - 1] = '\0';
|
|
||||||
} else if (likely(ret >= 0)) {
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
|
||||||
|
|
||||||
struct action_cache {
|
|
||||||
DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR);
|
|
||||||
#ifdef SECCOMP_ARCH_COMPAT
|
|
||||||
DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR);
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
struct seccomp_filter {
|
|
||||||
refcount_t refs;
|
|
||||||
refcount_t users;
|
|
||||||
bool log;
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
|
||||||
bool wait_killable_recv;
|
|
||||||
#endif
|
|
||||||
struct action_cache cache;
|
|
||||||
struct seccomp_filter *prev;
|
|
||||||
struct bpf_prog *prog;
|
|
||||||
struct notification *notif;
|
|
||||||
struct mutex notify_lock;
|
|
||||||
wait_queue_head_t wqh;
|
|
||||||
};
|
|
||||||
|
|
||||||
void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr)
|
|
||||||
{
|
|
||||||
if (!filter) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
|
|
||||||
clear_bit(nr, filter->cache.allow_native);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SECCOMP_ARCH_COMPAT
|
|
||||||
if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
|
|
||||||
clear_bit(nr, filter->cache.allow_compat);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr)
|
|
||||||
{
|
|
||||||
if (!filter) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
|
|
||||||
set_bit(nr, filter->cache.allow_native);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef SECCOMP_ARCH_COMPAT
|
|
||||||
if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
|
|
||||||
set_bit(nr, filter->cache.allow_compat);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
#include "ss/policydb.h"
|
#include "ss/policydb.h"
|
||||||
#include "linux/key.h"
|
#include "linux/key.h"
|
||||||
|
|
||||||
@@ -27,21 +28,9 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) && defined(KSU_KPROBE_HOOK)
|
|
||||||
#define KSU_SHOULD_USE_NEW_TP
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern long ksu_strncpy_from_user_nofault(char *dst,
|
extern long ksu_strncpy_from_user_nofault(char *dst,
|
||||||
const void __user *unsafe_addr,
|
const void __user *unsafe_addr,
|
||||||
long count);
|
long count);
|
||||||
extern long ksu_strncpy_from_user_retry(char *dst,
|
|
||||||
const void __user *unsafe_addr,
|
|
||||||
long count);
|
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
|
||||||
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
|
||||||
extern struct key *init_session_keyring;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
|
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
|
||||||
umode_t mode);
|
umode_t mode);
|
||||||
@@ -67,9 +56,9 @@ static long ksu_copy_from_user_retry(void *to,
|
|||||||
return copy_from_user(to, from, count);
|
return copy_from_user(to, from, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||||
extern void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr);
|
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr);
|
extern struct key *init_session_keyring;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
||||||
@@ -78,4 +67,18 @@ extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr);
|
|||||||
#define ksu_access_ok(addr, size) access_ok(VERIFY_READ, addr, size)
|
#define ksu_access_ok(addr, size) access_ok(VERIFY_READ, addr, size)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Linux >= 5.7
|
||||||
|
// task_work_add (struct, struct, enum)
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0)
|
||||||
|
#define ksu_task_work_add(tsk, cb, notify) task_work_add(tsk, cb, notify)
|
||||||
|
#else
|
||||||
|
// Linux pre-5.7
|
||||||
|
// task_work_add (struct, struct, bool)
|
||||||
|
#define ksu_task_work_add(tsk, cb, notify) task_work_add(tsk, cb, notify)
|
||||||
|
// Decoy, so it wouldn't complain.
|
||||||
|
#ifndef TWA_RESUME
|
||||||
|
#define TWA_RESUME true
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
227
kernel/kernel_umount.c
Normal file
227
kernel/kernel_umount.c
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
|
#include <linux/nsproxy.h>
|
||||||
|
#include <linux/path.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#ifndef KSU_HAS_PATH_UMOUNT
|
||||||
|
#include <linux/syscalls.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "kernel_umount.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "feature.h"
|
||||||
|
#include "ksud.h"
|
||||||
|
|
||||||
|
static bool ksu_kernel_umount_enabled = true;
|
||||||
|
|
||||||
|
static int kernel_umount_feature_get(u64 *value)
|
||||||
|
{
|
||||||
|
*value = ksu_kernel_umount_enabled ? 1 : 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kernel_umount_feature_set(u64 value)
|
||||||
|
{
|
||||||
|
bool enable = value != 0;
|
||||||
|
ksu_kernel_umount_enabled = enable;
|
||||||
|
pr_info("kernel_umount: set to %d\n", enable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ksu_feature_handler kernel_umount_handler = {
|
||||||
|
.feature_id = KSU_FEATURE_KERNEL_UMOUNT,
|
||||||
|
.name = "kernel_umount",
|
||||||
|
.get_handler = kernel_umount_feature_get,
|
||||||
|
.set_handler = kernel_umount_feature_set,
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool should_umount(struct path *path)
|
||||||
|
{
|
||||||
|
if (!path) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
|
||||||
|
pr_info("ignore global mnt namespace process: %d\n",
|
||||||
|
current_uid().val);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) {
|
||||||
|
const char *fstype = path->mnt->mnt_sb->s_type->name;
|
||||||
|
return strcmp(fstype, "overlay") == 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || defined(KSU_HAS_PATH_UMOUNT)
|
||||||
|
extern int path_umount(struct path *path, int flags);
|
||||||
|
static void ksu_umount_mnt(const char *__never_use_mnt, struct path *path, int flags)
|
||||||
|
{
|
||||||
|
int err = path_umount(path, flags);
|
||||||
|
if (err) {
|
||||||
|
pr_info("umount %s failed: %d\n", path->dentry->d_iname, err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static void ksu_sys_umount(const char *mnt, int flags)
|
||||||
|
{
|
||||||
|
char __user *usermnt = (char __user *)mnt;
|
||||||
|
mm_segment_t old_fs;
|
||||||
|
|
||||||
|
old_fs = get_fs();
|
||||||
|
set_fs(KERNEL_DS);
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
||||||
|
ksys_umount(usermnt, flags);
|
||||||
|
#else
|
||||||
|
sys_umount(usermnt, flags); // cuz asmlinkage long sys##name
|
||||||
|
#endif
|
||||||
|
set_fs(old_fs);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ksu_umount_mnt(mnt, __unused, flags) \
|
||||||
|
({ \
|
||||||
|
path_put(__unused); \
|
||||||
|
ksu_sys_umount(mnt, flags); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void try_umount(const char *mnt, bool check_mnt, int flags)
|
||||||
|
{
|
||||||
|
struct path path;
|
||||||
|
int err = kern_path(mnt, 0, &path);
|
||||||
|
if (err) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (path.dentry != path.mnt->mnt_root) {
|
||||||
|
// it is not root mountpoint, maybe umounted by others already.
|
||||||
|
path_put(&path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// we are only interest in some specific mounts
|
||||||
|
if (check_mnt && !should_umount(&path)) {
|
||||||
|
path_put(&path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ksu_umount_mnt(mnt, &path, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void do_ksu_umount_lists(void)
|
||||||
|
{
|
||||||
|
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
||||||
|
// filter the mountpoint whose target is `/data/adb`
|
||||||
|
try_umount("/odm", true, 0);
|
||||||
|
try_umount("/system", true, 0);
|
||||||
|
try_umount("/vendor", true, 0);
|
||||||
|
try_umount("/product", true, 0);
|
||||||
|
try_umount("/system_ext", true, 0);
|
||||||
|
try_umount("/data/adb/modules", false, MNT_DETACH);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
struct umount_tw {
|
||||||
|
struct callback_head cb;
|
||||||
|
const struct cred *old_cred;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void umount_tw_func(struct callback_head *cb)
|
||||||
|
{
|
||||||
|
struct umount_tw *tw = container_of(cb, struct umount_tw, cb);
|
||||||
|
const struct cred *saved = NULL;
|
||||||
|
if (tw->old_cred) {
|
||||||
|
saved = override_creds(tw->old_cred);
|
||||||
|
}
|
||||||
|
|
||||||
|
do_ksu_umount_lists();
|
||||||
|
|
||||||
|
if (saved)
|
||||||
|
revert_creds(saved);
|
||||||
|
|
||||||
|
if (tw->old_cred)
|
||||||
|
put_cred(tw->old_cred);
|
||||||
|
|
||||||
|
kfree(tw);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
|
||||||
|
{
|
||||||
|
// this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it!
|
||||||
|
if (!ksu_module_mounted) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_kernel_umount_enabled) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: isolated process which directly forks from zygote is not handled
|
||||||
|
if (!is_appuid(new_uid)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ksu_uid_should_umount(new_uid)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check old process's selinux context, if it is not zygote, ignore it!
|
||||||
|
// because some su apps may setuid to untrusted_app but they are in global mount namespace
|
||||||
|
// when we umount for such process, that is a disaster!
|
||||||
|
bool is_zygote_child = is_zygote(get_current_cred());
|
||||||
|
if (!is_zygote_child) {
|
||||||
|
pr_info("handle umount ignore non zygote child: %d\n",
|
||||||
|
current->pid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// umount the target mnt
|
||||||
|
pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid);
|
||||||
|
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
struct umount_tw *tw;
|
||||||
|
tw = kzalloc(sizeof(*tw), GFP_ATOMIC);
|
||||||
|
if (!tw)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tw->old_cred = get_current_cred();
|
||||||
|
tw->cb.func = umount_tw_func;
|
||||||
|
|
||||||
|
int err = ksu_task_work_add(current, &tw->cb, TWA_RESUME);
|
||||||
|
if (err) {
|
||||||
|
if (tw->old_cred) {
|
||||||
|
put_cred(tw->old_cred);
|
||||||
|
}
|
||||||
|
kfree(tw);
|
||||||
|
pr_warn("unmount add task_work failed\n");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// Using task work for non-kp context is expansive?
|
||||||
|
do_ksu_umount_lists();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_kernel_umount_init(void)
|
||||||
|
{
|
||||||
|
if (ksu_register_feature_handler(&kernel_umount_handler)) {
|
||||||
|
pr_err("Failed to register kernel_umount feature handler\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_kernel_umount_exit(void)
|
||||||
|
{
|
||||||
|
ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT);
|
||||||
|
}
|
||||||
12
kernel/kernel_umount.h
Normal file
12
kernel/kernel_umount.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef __KSU_H_KERNEL_UMOUNT
|
||||||
|
#define __KSU_H_KERNEL_UMOUNT
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
void ksu_kernel_umount_init(void);
|
||||||
|
void ksu_kernel_umount_exit(void);
|
||||||
|
|
||||||
|
// Handler function to be called from setresuid hook
|
||||||
|
int ksu_handle_umount(uid_t old_uid, uid_t new_uid);
|
||||||
|
|
||||||
|
#endif
|
||||||
32
kernel/ksu.c
32
kernel/ksu.c
@@ -1,5 +1,6 @@
|
|||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
#include <linux/kobject.h>
|
#include <linux/kobject.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
@@ -9,15 +10,17 @@
|
|||||||
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "core_hook.h"
|
|
||||||
#include "feature.h"
|
#include "feature.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksu.h"
|
#include "ksu.h"
|
||||||
#include "throne_tracker.h"
|
#include "throne_tracker.h"
|
||||||
#include "sucompat.h"
|
#include "syscall_hook_manager.h"
|
||||||
#include "ksud.h"
|
#include "ksud.h"
|
||||||
#include "supercalls.h"
|
#include "supercalls.h"
|
||||||
|
|
||||||
|
#include "throne_comm.h"
|
||||||
|
#include "dynamic_manager.h"
|
||||||
|
|
||||||
static struct workqueue_struct *ksu_workqueue;
|
static struct workqueue_struct *ksu_workqueue;
|
||||||
|
|
||||||
bool ksu_queue_work(struct work_struct *work)
|
bool ksu_queue_work(struct work_struct *work)
|
||||||
@@ -25,6 +28,17 @@ bool ksu_queue_work(struct work_struct *work)
|
|||||||
return queue_work(ksu_workqueue, work);
|
return queue_work(ksu_workqueue, work);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void sukisu_custom_config_init(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void sukisu_custom_config_exit(void)
|
||||||
|
{
|
||||||
|
ksu_uid_exit();
|
||||||
|
ksu_throne_comm_exit();
|
||||||
|
ksu_dynamic_manager_exit();
|
||||||
|
}
|
||||||
|
|
||||||
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||||
void *argv, void *envp, int *flags);
|
void *argv, void *envp, int *flags);
|
||||||
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||||
@@ -39,8 +53,10 @@ int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
|||||||
|
|
||||||
int __init kernelsu_init(void)
|
int __init kernelsu_init(void)
|
||||||
{
|
{
|
||||||
|
#ifndef DDK_ENV
|
||||||
pr_info("Initialized on: %s (%s) with driver version: %u\n",
|
pr_info("Initialized on: %s (%s) with driver version: %u\n",
|
||||||
UTS_RELEASE, UTS_MACHINE, KSU_VERSION);
|
UTS_RELEASE, UTS_MACHINE, KSU_VERSION);
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_alert("*************************************************************");
|
pr_alert("*************************************************************");
|
||||||
@@ -56,7 +72,9 @@ int __init kernelsu_init(void)
|
|||||||
|
|
||||||
ksu_supercalls_init();
|
ksu_supercalls_init();
|
||||||
|
|
||||||
ksu_core_init();
|
sukisu_custom_config_init();
|
||||||
|
|
||||||
|
ksu_syscall_hook_manager_init();
|
||||||
|
|
||||||
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
||||||
|
|
||||||
@@ -64,8 +82,6 @@ int __init kernelsu_init(void)
|
|||||||
|
|
||||||
ksu_throne_tracker_init();
|
ksu_throne_tracker_init();
|
||||||
|
|
||||||
ksu_sucompat_init();
|
|
||||||
|
|
||||||
ksu_ksud_init();
|
ksu_ksud_init();
|
||||||
|
|
||||||
#ifdef MODULE
|
#ifdef MODULE
|
||||||
@@ -89,9 +105,11 @@ void kernelsu_exit(void)
|
|||||||
|
|
||||||
ksu_ksud_exit();
|
ksu_ksud_exit();
|
||||||
|
|
||||||
ksu_sucompat_exit();
|
ksu_syscall_hook_manager_exit();
|
||||||
|
|
||||||
ksu_core_exit();
|
sukisu_custom_config_exit();
|
||||||
|
|
||||||
|
ksu_supercalls_exit();
|
||||||
|
|
||||||
ksu_feature_exit();
|
ksu_feature_exit();
|
||||||
}
|
}
|
||||||
|
|||||||
56
kernel/ksu.h
56
kernel/ksu.h
@@ -13,12 +13,6 @@ extern bool ksu_uid_scanner_enabled;
|
|||||||
#define EVENT_BOOT_COMPLETED 2
|
#define EVENT_BOOT_COMPLETED 2
|
||||||
#define EVENT_MODULE_MOUNTED 3
|
#define EVENT_MODULE_MOUNTED 3
|
||||||
|
|
||||||
#define KSU_APP_PROFILE_VER 2
|
|
||||||
#define KSU_MAX_PACKAGE_NAME 256
|
|
||||||
// NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups.
|
|
||||||
#define KSU_MAX_GROUPS 32
|
|
||||||
#define KSU_SELINUX_DOMAIN 64
|
|
||||||
|
|
||||||
// SukiSU Ultra kernel su version full strings
|
// SukiSU Ultra kernel su version full strings
|
||||||
#ifndef KSU_VERSION_FULL
|
#ifndef KSU_VERSION_FULL
|
||||||
#define KSU_VERSION_FULL "v3.x-00000000@unknown"
|
#define KSU_VERSION_FULL "v3.x-00000000@unknown"
|
||||||
@@ -47,53 +41,7 @@ struct manager_list_info {
|
|||||||
} managers[2];
|
} managers[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct root_profile {
|
bool ksu_queue_work(struct work_struct *work);
|
||||||
int32_t uid;
|
|
||||||
int32_t gid;
|
|
||||||
|
|
||||||
int32_t groups_count;
|
|
||||||
int32_t groups[KSU_MAX_GROUPS];
|
|
||||||
|
|
||||||
// kernel_cap_t is u32[2] for capabilities v3
|
|
||||||
struct {
|
|
||||||
u64 effective;
|
|
||||||
u64 permitted;
|
|
||||||
u64 inheritable;
|
|
||||||
} capabilities;
|
|
||||||
|
|
||||||
char selinux_domain[KSU_SELINUX_DOMAIN];
|
|
||||||
|
|
||||||
int32_t namespaces;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct non_root_profile {
|
|
||||||
bool umount_modules;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct app_profile {
|
|
||||||
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
|
|
||||||
u32 version;
|
|
||||||
|
|
||||||
// this is usually the package of the app, but can be other value for special apps
|
|
||||||
char key[KSU_MAX_PACKAGE_NAME];
|
|
||||||
int32_t current_uid;
|
|
||||||
bool allow_su;
|
|
||||||
|
|
||||||
union {
|
|
||||||
struct {
|
|
||||||
bool use_default;
|
|
||||||
char template_name[KSU_MAX_PACKAGE_NAME];
|
|
||||||
|
|
||||||
struct root_profile profile;
|
|
||||||
} rp_config;
|
|
||||||
|
|
||||||
struct {
|
|
||||||
bool use_default;
|
|
||||||
|
|
||||||
struct non_root_profile profile;
|
|
||||||
} nrp_config;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
static inline int startswith(char *s, char *prefix)
|
static inline int startswith(char *s, char *prefix)
|
||||||
@@ -111,6 +59,4 @@ static inline int endswith(const char *s, const char *t)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool ksu_queue_work(struct work_struct *work);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -21,16 +21,25 @@
|
|||||||
#include <linux/printk.h>
|
#include <linux/printk.h>
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||||
|
#include <linux/sched/signal.h>
|
||||||
|
#else
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "manager.h"
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
|
#include "kernel_compat.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 "manager.h"
|
#include "throne_tracker.h"
|
||||||
#include "sucompat.h"
|
|
||||||
|
bool ksu_module_mounted __read_mostly = false;
|
||||||
|
bool ksu_boot_completed __read_mostly = false;
|
||||||
|
|
||||||
static const char KERNEL_SU_RC[] =
|
static const char KERNEL_SU_RC[] =
|
||||||
"\n"
|
"\n"
|
||||||
@@ -59,7 +68,7 @@ static void stop_vfs_read_hook(void);
|
|||||||
static void stop_execve_hook(void);
|
static void stop_execve_hook(void);
|
||||||
static void stop_input_hook(void);
|
static void stop_input_hook(void);
|
||||||
|
|
||||||
#ifdef KSU_KPROBES_HOOK
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
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_execve_hook_work;
|
||||||
static struct work_struct stop_input_hook_work;
|
static struct work_struct stop_input_hook_work;
|
||||||
@@ -78,13 +87,12 @@ 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();
|
||||||
ksu_mark_running_process();
|
|
||||||
ksu_observer_init();
|
ksu_observer_init();
|
||||||
// sanity check, this may influence the performance
|
// sanity check, this may influence the performance
|
||||||
stop_input_hook();
|
stop_input_hook();
|
||||||
@@ -96,6 +104,43 @@ void on_post_fs_data(void)
|
|||||||
pr_info("devpts sid: %d\n", ksu_file_sid);
|
pr_info("devpts sid: %d\n", ksu_file_sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern void ext4_unregister_sysfs(struct super_block *sb);
|
||||||
|
static void nuke_ext4_sysfs(void)
|
||||||
|
{
|
||||||
|
struct path path;
|
||||||
|
int err = kern_path("/data/adb/modules", 0, &path);
|
||||||
|
if (err) {
|
||||||
|
pr_err("nuke path err: %d\n", err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct super_block *sb = path.dentry->d_inode->i_sb;
|
||||||
|
const char *name = sb->s_type->name;
|
||||||
|
if (strcmp(name, "ext4") != 0) {
|
||||||
|
pr_info("nuke but module aren't mounted\n");
|
||||||
|
path_put(&path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ext4_unregister_sysfs(sb);
|
||||||
|
path_put(&path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_module_mounted(void)
|
||||||
|
{
|
||||||
|
pr_info("on_module_mounted!\n");
|
||||||
|
ksu_module_mounted = true;
|
||||||
|
nuke_ext4_sysfs();
|
||||||
|
}
|
||||||
|
|
||||||
|
void on_boot_completed(void)
|
||||||
|
{
|
||||||
|
ksu_boot_completed = true;
|
||||||
|
pr_info("on_boot_completed!\n");
|
||||||
|
track_throne(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
@@ -113,7 +158,9 @@ 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
|
// since _ksud handler only uses argv and envp for comparisons
|
||||||
// this can probably work
|
// this can probably work
|
||||||
@@ -207,13 +254,11 @@ first_app_process:
|
|||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
init_task = rcu_dereference(current->real_parent);
|
init_task = rcu_dereference(current->real_parent);
|
||||||
if (init_task) {
|
if (init_task) {
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 8)
|
ksu_task_work_add(init_task, &on_post_fs_data_cb,
|
||||||
task_work_add(init_task, &on_post_fs_data_cb, TWA_RESUME);
|
TWA_RESUME);
|
||||||
#else
|
|
||||||
task_work_add(init_task, &on_post_fs_data_cb, true);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
stop_execve_hook();
|
stop_execve_hook();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -315,7 +360,7 @@ static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to)
|
|||||||
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
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_SHOULD_USE_NEW_TP
|
||||||
if (!ksu_vfs_read_hook) {
|
if (!ksu_vfs_read_hook) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -428,7 +473,7 @@ static bool is_volumedown_enough(unsigned int count)
|
|||||||
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||||
int *value)
|
int *value)
|
||||||
{
|
{
|
||||||
#ifndef KSU_KPROBES_HOOK
|
#ifndef KSU_SHOULD_USE_NEW_TP
|
||||||
if (!ksu_input_hook) {
|
if (!ksu_input_hook) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -470,7 +515,8 @@ bool ksu_is_safe_mode(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef KSU_KPROBES_HOOK
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -689,7 +735,7 @@ int __maybe_unused ksu_handle_compat_execve_ksud(
|
|||||||
|
|
||||||
static void stop_vfs_read_hook(void)
|
static void stop_vfs_read_hook(void)
|
||||||
{
|
{
|
||||||
#ifdef KSU_KPROBES_HOOK
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
bool ret = schedule_work(&stop_vfs_read_work);
|
bool ret = schedule_work(&stop_vfs_read_work);
|
||||||
pr_info("unregister vfs_read kprobe: %d!\n", ret);
|
pr_info("unregister vfs_read kprobe: %d!\n", ret);
|
||||||
#else
|
#else
|
||||||
@@ -700,7 +746,7 @@ static void stop_vfs_read_hook(void)
|
|||||||
|
|
||||||
static void stop_execve_hook(void)
|
static void stop_execve_hook(void)
|
||||||
{
|
{
|
||||||
#ifdef KSU_KPROBES_HOOK
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
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
|
||||||
@@ -711,7 +757,7 @@ static void stop_execve_hook(void)
|
|||||||
|
|
||||||
static void stop_input_hook(void)
|
static void stop_input_hook(void)
|
||||||
{
|
{
|
||||||
#ifdef KSU_KPROBES_HOOK
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
static bool input_hook_stopped = false;
|
static bool input_hook_stopped = false;
|
||||||
if (input_hook_stopped) {
|
if (input_hook_stopped) {
|
||||||
return;
|
return;
|
||||||
@@ -731,7 +777,7 @@ static void stop_input_hook(void)
|
|||||||
// ksud: module support
|
// ksud: module support
|
||||||
void ksu_ksud_init(void)
|
void ksu_ksud_init(void)
|
||||||
{
|
{
|
||||||
#ifdef KSU_KPROBES_HOOK
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = register_kprobe(&execve_kp);
|
ret = register_kprobe(&execve_kp);
|
||||||
@@ -751,7 +797,7 @@ void ksu_ksud_init(void)
|
|||||||
|
|
||||||
void ksu_ksud_exit(void)
|
void ksu_ksud_exit(void)
|
||||||
{
|
{
|
||||||
#ifdef KSU_KPROBES_HOOK
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
unregister_kprobe(&execve_kp);
|
unregister_kprobe(&execve_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);
|
||||||
|
|||||||
@@ -5,14 +5,18 @@
|
|||||||
|
|
||||||
#define KSUD_PATH "/data/adb/ksud"
|
#define KSUD_PATH "/data/adb/ksud"
|
||||||
|
|
||||||
void ksu_ksud_init();
|
void ksu_ksud_init(void);
|
||||||
void ksu_ksud_exit();
|
void ksu_ksud_exit(void);
|
||||||
|
|
||||||
void on_post_fs_data(void);
|
void on_post_fs_data(void);
|
||||||
|
void on_module_mounted(void);
|
||||||
|
void on_boot_completed(void);
|
||||||
|
|
||||||
bool ksu_is_safe_mode(void);
|
bool ksu_is_safe_mode(void);
|
||||||
|
|
||||||
extern u32 ksu_file_sid;
|
extern u32 ksu_file_sid;
|
||||||
|
extern bool ksu_module_mounted;
|
||||||
|
extern bool ksu_boot_completed;
|
||||||
|
|
||||||
extern bool ksu_execveat_hook __read_mostly;
|
extern bool ksu_execveat_hook __read_mostly;
|
||||||
extern int ksu_handle_pre_ksud(const char *filename);
|
extern int ksu_handle_pre_ksud(const char *filename);
|
||||||
|
|||||||
122
kernel/lsm_hooks.c
Normal file
122
kernel/lsm_hooks.c
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
#include <linux/lsm_hooks.h>
|
||||||
|
#include <linux/uidgid.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/binfmts.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
#include "setuid_hook.h"
|
||||||
|
|
||||||
|
#ifndef KSU_SHOULD_USE_NEW_TP
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||||
|
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
|
static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
|
||||||
|
unsigned perm)
|
||||||
|
{
|
||||||
|
if (init_session_keyring != NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (strcmp(current->comm, "init")) {
|
||||||
|
// we are only interested in `init` process
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
init_session_keyring = cred->session_keyring;
|
||||||
|
pr_info("kernel_compat: got init_session_keyring\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
|
||||||
|
int flags)
|
||||||
|
{
|
||||||
|
kuid_t new_uid = new->uid;
|
||||||
|
kuid_t new_euid = new->euid;
|
||||||
|
|
||||||
|
return ksu_handle_setresuid((uid_t)new_uid.val, (uid_t)new_euid.val,
|
||||||
|
(uid_t)new_uid.val);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef DEVPTS_SUPER_MAGIC
|
||||||
|
#define DEVPTS_SUPER_MAGIC 0x1cd1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
bool ksu_is_compat __read_mostly = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int ksu_inode_permission(struct inode *inode, int mask)
|
||||||
|
{
|
||||||
|
if (inode && inode->i_sb
|
||||||
|
&& unlikely(inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) {
|
||||||
|
//pr_info("%s: handling devpts for: %s \n", __func__, current->comm);
|
||||||
|
__ksu_handle_devpts(inode);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_bprm_check(struct linux_binprm *bprm)
|
||||||
|
{
|
||||||
|
char *filename = (char *)bprm->filename;
|
||||||
|
|
||||||
|
if (likely(!ksu_execveat_hook))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
static bool compat_check_done __read_mostly = false;
|
||||||
|
if ( unlikely(!compat_check_done) && unlikely(!strcmp(filename, "/data/adb/ksud"))
|
||||||
|
&& !memcmp(bprm->buf, "\x7f\x45\x4c\x46", 4) ) {
|
||||||
|
if (bprm->buf[4] == 0x01 )
|
||||||
|
ksu_is_compat = true;
|
||||||
|
|
||||||
|
pr_info("%s: %s ELF magic found! ksu_is_compat: %d \n", __func__, filename, ksu_is_compat);
|
||||||
|
compat_check_done = true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ksu_handle_pre_ksud(filename);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct security_hook_list ksu_hooks[] = {
|
||||||
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||||
|
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
|
LSM_HOOK_INIT(key_permission, ksu_key_permission),
|
||||||
|
#endif
|
||||||
|
LSM_HOOK_INIT(inode_permission, ksu_inode_permission),
|
||||||
|
#ifndef KSU_SHOULD_USE_NEW_TP
|
||||||
|
LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check),
|
||||||
|
#endif
|
||||||
|
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid)
|
||||||
|
};
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
|
||||||
|
static const struct lsm_id ksu_lsmid = {
|
||||||
|
.name = "ksu",
|
||||||
|
.id = 912,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void ksu_lsm_hook_init(void)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
|
||||||
|
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), &ksu_lsmid);
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||||
|
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu");
|
||||||
|
#else
|
||||||
|
// https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892
|
||||||
|
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks));
|
||||||
|
#endif
|
||||||
|
pr_info("LSM hooks initialized.\n");
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
void ksu_lsm_hook_init(void)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@@ -31,7 +31,7 @@ static KSU_DECL_FSNOTIFY_OPS(ksu_handle_generic_event)
|
|||||||
if (ksu_fname_len(file_name) == 13 &&
|
if (ksu_fname_len(file_name) == 13 &&
|
||||||
!memcmp(ksu_fname_arg(file_name), "packages.list", 13)) {
|
!memcmp(ksu_fname_arg(file_name), "packages.list", 13)) {
|
||||||
pr_info("packages.list detected (mask=%d)\n", mask);
|
pr_info("packages.list detected (mask=%d)\n", mask);
|
||||||
track_throne();
|
track_throne(false);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
67
kernel/seccomp_cache.c
Normal file
67
kernel/seccomp_cache.c
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/sched/task.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/filter.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "seccomp_cache.h"
|
||||||
|
|
||||||
|
struct action_cache {
|
||||||
|
DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR);
|
||||||
|
#ifdef SECCOMP_ARCH_COMPAT
|
||||||
|
DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct seccomp_filter {
|
||||||
|
refcount_t refs;
|
||||||
|
refcount_t users;
|
||||||
|
bool log;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||||
|
bool wait_killable_recv;
|
||||||
|
#endif
|
||||||
|
struct action_cache cache;
|
||||||
|
struct seccomp_filter *prev;
|
||||||
|
struct bpf_prog *prog;
|
||||||
|
struct notification *notif;
|
||||||
|
struct mutex notify_lock;
|
||||||
|
wait_queue_head_t wqh;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr)
|
||||||
|
{
|
||||||
|
if (!filter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
|
||||||
|
clear_bit(nr, filter->cache.allow_native);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SECCOMP_ARCH_COMPAT
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
|
||||||
|
clear_bit(nr, filter->cache.allow_compat);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr)
|
||||||
|
{
|
||||||
|
if (!filter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
|
||||||
|
set_bit(nr, filter->cache.allow_native);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SECCOMP_ARCH_COMPAT
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
|
||||||
|
set_bit(nr, filter->cache.allow_compat);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#endif
|
||||||
12
kernel/seccomp_cache.h
Normal file
12
kernel/seccomp_cache.h
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#ifndef __KSU_H_SECCOMP_CACHE
|
||||||
|
#define __KSU_H_SECCOMP_CACHE
|
||||||
|
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
|
extern void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr);
|
||||||
|
extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -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"
|
#include "linux/lsm_audit.h" // IWYU pragma: keep
|
||||||
#include "xfrm.h"
|
#include "xfrm.h"
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
@@ -37,6 +37,7 @@ static struct policydb *get_policydb(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static DEFINE_MUTEX(ksu_rules);
|
static DEFINE_MUTEX(ksu_rules);
|
||||||
|
|
||||||
void apply_kernelsu_rules(void)
|
void apply_kernelsu_rules(void)
|
||||||
{
|
{
|
||||||
struct policydb *db;
|
struct policydb *db;
|
||||||
@@ -95,7 +96,6 @@ void apply_kernelsu_rules(void)
|
|||||||
ksu_allow(db, "init", "adb_data_file", "file", ALL);
|
ksu_allow(db, "init", "adb_data_file", "file", ALL);
|
||||||
ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289
|
ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289
|
||||||
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
|
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
|
||||||
|
|
||||||
// we need to umount modules in zygote
|
// we need to umount modules in zygote
|
||||||
ksu_allow(db, "zygote", "adb_data_file", "dir", "search");
|
ksu_allow(db, "zygote", "adb_data_file", "dir", "search");
|
||||||
|
|
||||||
@@ -139,9 +139,6 @@ void apply_kernelsu_rules(void)
|
|||||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
||||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
||||||
|
|
||||||
// https://android-review.googlesource.com/c/platform/system/logging/+/3725346
|
|
||||||
ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");
|
|
||||||
|
|
||||||
mutex_unlock(&ksu_rules);
|
mutex_unlock(&ksu_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,15 +155,15 @@ void apply_kernelsu_rules(void)
|
|||||||
#define CMD_GENFSCON 9
|
#define CMD_GENFSCON 9
|
||||||
|
|
||||||
struct sepol_data {
|
struct sepol_data {
|
||||||
uint32_t cmd;
|
u32 cmd;
|
||||||
uint32_t subcmd;
|
u32 subcmd;
|
||||||
uint64_t sepol1;
|
u64 sepol1;
|
||||||
uint64_t sepol2;
|
u64 sepol2;
|
||||||
uint64_t sepol3;
|
u64 sepol3;
|
||||||
uint64_t sepol4;
|
u64 sepol4;
|
||||||
uint64_t sepol5;
|
u64 sepol5;
|
||||||
uint64_t sepol6;
|
u64 sepol6;
|
||||||
uint64_t sepol7;
|
u64 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,
|
||||||
@@ -185,14 +182,12 @@ 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)
|
!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)
|
||||||
{
|
{
|
||||||
@@ -222,7 +217,7 @@ 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sepol_data data = { 0 };
|
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 -EINVAL;
|
return -EINVAL;
|
||||||
@@ -236,7 +231,6 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
db = get_policydb();
|
db = get_policydb();
|
||||||
|
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
|
|
||||||
switch (cmd) {
|
switch (cmd) {
|
||||||
case CMD_NORMAL_PERM: {
|
case CMD_NORMAL_PERM: {
|
||||||
char src_buf[MAX_SEPOL_LEN];
|
char src_buf[MAX_SEPOL_LEN];
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ is_ksu_transition(const struct task_security_struct *old_tsec,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
void setup_selinux(const char *domain)
|
void setup_selinux(const char *domain)
|
||||||
{
|
{
|
||||||
if (transive_to_domain(domain)) {
|
if (transive_to_domain(domain)) {
|
||||||
@@ -106,7 +107,7 @@ static int __security_secid_to_secctx(u32 secid, struct lsm_context *cp)
|
|||||||
}
|
}
|
||||||
static void __security_release_secctx(struct lsm_context *cp)
|
static void __security_release_secctx(struct lsm_context *cp)
|
||||||
{
|
{
|
||||||
return security_release_secctx(cp->context, cp->len);
|
security_release_secctx(cp->context, cp->len);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
#define __security_secid_to_secctx security_secid_to_secctx
|
#define __security_secid_to_secctx security_secid_to_secctx
|
||||||
@@ -139,7 +140,7 @@ bool is_ksu_domain(void)
|
|||||||
return is_task_ksu_domain(current_cred());
|
return is_task_ksu_domain(current_cred());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_zygote(const struct cred *cred)
|
bool is_context(const struct cred *cred, const char *context)
|
||||||
{
|
{
|
||||||
if (!cred) {
|
if (!cred) {
|
||||||
return false;
|
return false;
|
||||||
@@ -154,11 +155,21 @@ bool is_zygote(const struct cred *cred)
|
|||||||
if (err) {
|
if (err) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
result = strncmp("u:r:zygote:s0", ctx.context, ctx.len) == 0;
|
result = strncmp(context, ctx.context, ctx.len) == 0;
|
||||||
__security_release_secctx(&ctx);
|
__security_release_secctx(&ctx);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_zygote(const struct cred *cred)
|
||||||
|
{
|
||||||
|
return is_context(cred, "u:r:zygote:s0");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_init(const struct cred *cred)
|
||||||
|
{
|
||||||
|
return is_context(cred, "u:r:init:s0");
|
||||||
|
}
|
||||||
|
|
||||||
#define KSU_FILE_DOMAIN "u:object_r:ksu_file:s0"
|
#define KSU_FILE_DOMAIN "u:object_r:ksu_file:s0"
|
||||||
|
|
||||||
u32 ksu_get_ksu_file_sid(void)
|
u32 ksu_get_ksu_file_sid(void)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include "linux/types.h"
|
#include "linux/types.h"
|
||||||
#include "linux/version.h"
|
#include "linux/version.h"
|
||||||
#include "linux/sched.h"
|
#include "linux/cred.h"
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || \
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || \
|
||||||
defined(KSU_COMPAT_HAS_SELINUX_STATE)
|
defined(KSU_COMPAT_HAS_SELINUX_STATE)
|
||||||
@@ -14,14 +14,16 @@ void setup_selinux(const char *);
|
|||||||
|
|
||||||
void setenforce(bool);
|
void setenforce(bool);
|
||||||
|
|
||||||
bool is_task_ksu_domain(const struct cred *cred);
|
|
||||||
|
|
||||||
bool getenforce(void);
|
bool getenforce(void);
|
||||||
|
|
||||||
|
bool is_task_ksu_domain(const struct cred *cred);
|
||||||
|
|
||||||
bool is_ksu_domain(void);
|
bool is_ksu_domain(void);
|
||||||
|
|
||||||
bool is_zygote(const struct cred *cred);
|
bool is_zygote(const struct cred *cred);
|
||||||
|
|
||||||
|
bool is_init(const struct cred *cred);
|
||||||
|
|
||||||
void apply_kernelsu_rules(void);
|
void apply_kernelsu_rules(void);
|
||||||
|
|
||||||
u32 ksu_get_ksu_file_sid(void);
|
u32 ksu_get_ksu_file_sid(void);
|
||||||
|
|||||||
@@ -355,7 +355,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
|
|||||||
|
|
||||||
if (datum->u.xperms == NULL) {
|
if (datum->u.xperms == NULL) {
|
||||||
datum->u.xperms =
|
datum->u.xperms =
|
||||||
(struct avtab_extended_perms *)(kmalloc(
|
(struct avtab_extended_perms *)(kzalloc(
|
||||||
sizeof(xperms), GFP_KERNEL));
|
sizeof(xperms), GFP_KERNEL));
|
||||||
if (!datum->u.xperms) {
|
if (!datum->u.xperms) {
|
||||||
pr_err("alloc xperms failed\n");
|
pr_err("alloc xperms failed\n");
|
||||||
@@ -555,7 +555,7 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
|||||||
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
|
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
|
||||||
1, GFP_ATOMIC);
|
1, GFP_ATOMIC);
|
||||||
struct filename_trans_key *new_key =
|
struct filename_trans_key *new_key =
|
||||||
(struct filename_trans_key *)kmalloc(sizeof(*new_key),
|
(struct filename_trans_key *)kzalloc(sizeof(*new_key),
|
||||||
GFP_ATOMIC);
|
GFP_ATOMIC);
|
||||||
*new_key = key;
|
*new_key = key;
|
||||||
new_key->name = kstrdup(key.name, GFP_ATOMIC);
|
new_key->name = kstrdup(key.name, GFP_ATOMIC);
|
||||||
@@ -585,7 +585,7 @@ static bool add_filename_trans(struct policydb *db, const char *s,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
struct filename_trans *new_key =
|
struct filename_trans *new_key =
|
||||||
(struct filename_trans *)kmalloc(sizeof(*new_key),
|
(struct filename_trans *)kzalloc(sizeof(*new_key),
|
||||||
GFP_ATOMIC);
|
GFP_ATOMIC);
|
||||||
if (!new_key) {
|
if (!new_key) {
|
||||||
pr_err("add_filename_trans: Failed to alloc new_key\n");
|
pr_err("add_filename_trans: Failed to alloc new_key\n");
|
||||||
|
|||||||
186
kernel/setuid_hook.c
Normal file
186
kernel/setuid_hook.c
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
#include <linux/compiler.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||||
|
#include <linux/sched/signal.h>
|
||||||
|
#endif
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
|
#include <linux/capability.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/dcache.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/init_task.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
#include <linux/mm.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
#include <linux/namei.h>
|
||||||
|
#include <linux/nsproxy.h>
|
||||||
|
#include <linux/path.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/stddef.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/uidgid.h>
|
||||||
|
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "setuid_hook.h"
|
||||||
|
#include "feature.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "manager.h"
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include "seccomp_cache.h"
|
||||||
|
#include "supercalls.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
#include "kernel_umount.h"
|
||||||
|
|
||||||
|
static bool ksu_enhanced_security_enabled = false;
|
||||||
|
|
||||||
|
static int enhanced_security_feature_get(u64 *value)
|
||||||
|
{
|
||||||
|
*value = ksu_enhanced_security_enabled ? 1 : 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int enhanced_security_feature_set(u64 value)
|
||||||
|
{
|
||||||
|
bool enable = value != 0;
|
||||||
|
ksu_enhanced_security_enabled = enable;
|
||||||
|
pr_info("enhanced_security: set to %d\n", enable);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct ksu_feature_handler enhanced_security_handler = {
|
||||||
|
.feature_id = KSU_FEATURE_ENHANCED_SECURITY,
|
||||||
|
.name = "enhanced_security",
|
||||||
|
.get_handler = enhanced_security_feature_get,
|
||||||
|
.set_handler = enhanced_security_feature_set,
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline bool is_allow_su(void)
|
||||||
|
{
|
||||||
|
if (is_manager()) {
|
||||||
|
// we are manager, allow!
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return ksu_is_allow_uid_for_current(current_uid().val);
|
||||||
|
}
|
||||||
|
|
||||||
|
// force_sig kcompat, TODO: move it out of core_hook.c
|
||||||
|
// https://elixir.bootlin.com/linux/v5.3-rc1/source/kernel/signal.c#L1613
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
||||||
|
#define __force_sig(sig) force_sig(sig)
|
||||||
|
#else
|
||||||
|
#define __force_sig(sig) force_sig(sig, current)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern void disable_seccomp(struct task_struct *tsk);
|
||||||
|
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid)
|
||||||
|
{
|
||||||
|
// we rely on the fact that zygote always call setresuid(3) with same uids
|
||||||
|
uid_t new_uid = ruid;
|
||||||
|
uid_t old_uid = current_uid().val;
|
||||||
|
|
||||||
|
if (old_uid != new_uid)
|
||||||
|
pr_info("handle_setresuid from %d to %d\n", old_uid, new_uid);
|
||||||
|
|
||||||
|
// if old process is root, ignore it.
|
||||||
|
if (old_uid != 0 && ksu_enhanced_security_enabled) {
|
||||||
|
// disallow any non-ksu domain escalation from non-root to root!
|
||||||
|
// euid is what we care about here as it controls permission
|
||||||
|
if (unlikely(euid == 0)) {
|
||||||
|
if (!is_ksu_domain()) {
|
||||||
|
pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
|
||||||
|
current->pid, current->comm, old_uid,
|
||||||
|
new_uid);
|
||||||
|
__force_sig(SIGKILL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// disallow appuid decrease to any other uid if it is not allowed to su
|
||||||
|
if (is_appuid(old_uid)) {
|
||||||
|
if (euid < current_euid().val &&
|
||||||
|
!ksu_is_allow_uid_for_current(old_uid)) {
|
||||||
|
pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
|
||||||
|
current->pid, current->comm, old_uid,
|
||||||
|
new_uid);
|
||||||
|
__force_sig(SIGKILL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if on private space, see if its possibly the manager
|
||||||
|
if (new_uid > PER_USER_RANGE &&
|
||||||
|
new_uid % PER_USER_RANGE == ksu_get_manager_uid()) {
|
||||||
|
ksu_set_manager_uid(new_uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
|
if (ksu_get_manager_uid() == new_uid) {
|
||||||
|
pr_info("install fd for ksu manager(uid=%d)\n", new_uid);
|
||||||
|
ksu_install_fd();
|
||||||
|
spin_lock_irq(¤t->sighand->siglock);
|
||||||
|
ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot);
|
||||||
|
ksu_set_task_tracepoint_flag(current);
|
||||||
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ksu_is_allow_uid_for_current(new_uid)) {
|
||||||
|
if (current->seccomp.mode == SECCOMP_MODE_FILTER &&
|
||||||
|
current->seccomp.filter) {
|
||||||
|
spin_lock_irq(¤t->sighand->siglock);
|
||||||
|
ksu_seccomp_allow_cache(current->seccomp.filter,
|
||||||
|
__NR_reboot);
|
||||||
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
}
|
||||||
|
ksu_set_task_tracepoint_flag(current);
|
||||||
|
} else {
|
||||||
|
ksu_clear_task_tracepoint_flag_if_needed(current);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (ksu_is_allow_uid_for_current(new_uid)) {
|
||||||
|
spin_lock_irq(¤t->sighand->siglock);
|
||||||
|
disable_seccomp(current);
|
||||||
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
|
||||||
|
if (ksu_get_manager_uid() == new_uid) {
|
||||||
|
pr_info("install fd for ksu manager(uid=%d)\n",
|
||||||
|
new_uid);
|
||||||
|
ksu_install_fd();
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Handle kernel umount
|
||||||
|
ksu_handle_umount(old_uid, new_uid);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern void ksu_lsm_hook_init(void);
|
||||||
|
void ksu_setuid_hook_init(void)
|
||||||
|
{
|
||||||
|
ksu_kernel_umount_init();
|
||||||
|
ksu_lsm_hook_init(); // <4.11
|
||||||
|
if (ksu_register_feature_handler(&enhanced_security_handler)) {
|
||||||
|
pr_err("Failed to register enhanced security feature handler\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_setuid_hook_exit(void)
|
||||||
|
{
|
||||||
|
pr_info("ksu_core_exit\n");
|
||||||
|
ksu_kernel_umount_exit();
|
||||||
|
ksu_unregister_feature_handler(KSU_FEATURE_ENHANCED_SECURITY);
|
||||||
|
}
|
||||||
13
kernel/setuid_hook.h
Normal file
13
kernel/setuid_hook.h
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#ifndef __KSU_H_KSU_SETUID_HOOK
|
||||||
|
#define __KSU_H_KSU_SETUID_HOOK
|
||||||
|
|
||||||
|
#include <linux/init.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
void ksu_setuid_hook_init(void);
|
||||||
|
void ksu_setuid_hook_exit(void);
|
||||||
|
|
||||||
|
// Handler functions for hook_manager
|
||||||
|
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -1,109 +1,35 @@
|
|||||||
#include <linux/dcache.h>
|
|
||||||
#include <linux/security.h>
|
|
||||||
#include <asm/current.h>
|
#include <asm/current.h>
|
||||||
#include <linux/cred.h>
|
#include <linux/cred.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/kprobes.h>
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include <linux/ptrace.h>
|
#include <linux/ptrace.h>
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||||
|
#include <linux/compiler.h>
|
||||||
|
#endif
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||||
#include <linux/sched/task_stack.h>
|
#include <linux/sched/task_stack.h>
|
||||||
#else
|
#else
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "objsec.h"
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "arch.h"
|
|
||||||
#include "feature.h"
|
#include "feature.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksud.h"
|
#include "ksud.h"
|
||||||
#include "kernel_compat.h"
|
|
||||||
#include "sucompat.h"
|
#include "sucompat.h"
|
||||||
#include "core_hook.h"
|
#include "app_profile.h"
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
|
||||||
#define SU_PATH "/system/bin/su"
|
#define SU_PATH "/system/bin/su"
|
||||||
#define SH_PATH "/system/bin/sh"
|
#define SH_PATH "/system/bin/sh"
|
||||||
|
|
||||||
static const char su[] = SU_PATH;
|
|
||||||
static const char ksud_path[] = KSUD_PATH;
|
|
||||||
bool ksu_su_compat_enabled __read_mostly = true;
|
bool ksu_su_compat_enabled __read_mostly = true;
|
||||||
|
|
||||||
#ifdef KSU_SHOULD_USE_NEW_TP
|
static const char su[] = SU_PATH;
|
||||||
|
static const char ksud_path[] = KSUD_PATH;
|
||||||
#include "linux/compiler.h"
|
|
||||||
#include "linux/printk.h"
|
|
||||||
#include "selinux/selinux.h"
|
|
||||||
|
|
||||||
#include <linux/tracepoint.h>
|
|
||||||
#include <linux/spinlock.h>
|
|
||||||
#include <asm/syscall.h>
|
|
||||||
#include <trace/events/syscalls.h>
|
|
||||||
|
|
||||||
void ksu_mark_running_process(void)
|
|
||||||
{
|
|
||||||
struct task_struct *p, *t;
|
|
||||||
read_lock(&tasklist_lock);
|
|
||||||
for_each_process_thread (p, t) {
|
|
||||||
if (!t->mm) { // only user processes
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
int uid = task_uid(t).val;
|
|
||||||
bool ksu_root_process =
|
|
||||||
uid == 0 && is_task_ksu_domain(get_task_cred(t));
|
|
||||||
if (ksu_root_process || ksu_is_allow_uid(uid)) {
|
|
||||||
ksu_set_task_tracepoint_flag(t);
|
|
||||||
pr_info("sucompat: mark process: pid:%d, uid: %d, comm:%s\n",
|
|
||||||
t->pid, uid, t->comm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
read_unlock(&tasklist_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_process_mark(bool mark)
|
|
||||||
{
|
|
||||||
struct task_struct *p, *t;
|
|
||||||
read_lock(&tasklist_lock);
|
|
||||||
for_each_process_thread (p, t) {
|
|
||||||
if (mark)
|
|
||||||
ksu_set_task_tracepoint_flag(t);
|
|
||||||
else
|
|
||||||
ksu_clear_task_tracepoint_flag(t);
|
|
||||||
}
|
|
||||||
read_unlock(&tasklist_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mark_all_process(void)
|
|
||||||
{
|
|
||||||
handle_process_mark(true);
|
|
||||||
pr_info("sucompat: mark all user process done!\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void unmark_all_process(void)
|
|
||||||
{
|
|
||||||
handle_process_mark(false);
|
|
||||||
pr_info("sucompat: unmark all user process done!\n");
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
void ksu_mark_running_process(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void handle_process_mark(bool mark)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void mark_all_process(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static void unmark_all_process(void)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int su_compat_feature_get(u64 *value)
|
static int su_compat_feature_get(u64 *value)
|
||||||
{
|
{
|
||||||
@@ -114,21 +40,8 @@ static int su_compat_feature_get(u64 *value)
|
|||||||
static int su_compat_feature_set(u64 value)
|
static int su_compat_feature_set(u64 value)
|
||||||
{
|
{
|
||||||
bool enable = value != 0;
|
bool enable = value != 0;
|
||||||
|
|
||||||
if (enable == ksu_su_compat_enabled) {
|
|
||||||
pr_info("su_compat: no need to change\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enable) {
|
|
||||||
ksu_sucompat_enable();
|
|
||||||
} else {
|
|
||||||
ksu_sucompat_disable();
|
|
||||||
}
|
|
||||||
|
|
||||||
ksu_su_compat_enabled = enable;
|
ksu_su_compat_enabled = enable;
|
||||||
pr_info("su_compat: set to %d\n", enable);
|
pr_info("su_compat: set to %d\n", enable);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,29 +52,30 @@ static const struct ksu_feature_handler su_compat_handler = {
|
|||||||
.set_handler = su_compat_feature_set,
|
.set_handler = su_compat_feature_set,
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline void __user *userspace_stack_buffer(const void *d, size_t len)
|
static void __user *userspace_stack_buffer(const void *d, size_t len)
|
||||||
{
|
{
|
||||||
/* To avoid having to mmap a page in userspace, just write below the stack
|
// To avoid having to mmap a page in userspace, just write below the stack
|
||||||
* pointer. */
|
// pointer.
|
||||||
char __user *p = (void __user *)current_user_stack_pointer() - len;
|
char __user *p = (void __user *)current_user_stack_pointer() - len;
|
||||||
|
|
||||||
return copy_to_user(p, d, len) ? NULL : p;
|
return copy_to_user(p, d, len) ? NULL : p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline char __user *sh_user_path(void)
|
static char __user *sh_user_path(void)
|
||||||
{
|
{
|
||||||
const char sh_path[] = SH_PATH;
|
static const char sh_path[] = "/system/bin/sh";
|
||||||
|
|
||||||
return userspace_stack_buffer(sh_path, sizeof(sh_path));
|
return userspace_stack_buffer(sh_path, sizeof(sh_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline char __user *ksud_user_path(void)
|
static char __user *ksud_user_path(void)
|
||||||
{
|
{
|
||||||
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
|
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool __is_su_allowed(const void *ptr_to_check)
|
static inline bool __is_su_allowed(const void *ptr_to_check)
|
||||||
{
|
{
|
||||||
#ifdef KSU_KPROBES_HOOK
|
#ifndef KSU_SHOULD_USE_NEW_TP
|
||||||
if (!ksu_su_compat_enabled)
|
if (!ksu_su_compat_enabled)
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
@@ -182,7 +96,7 @@ static int ksu_sucompat_user_common(const char __user **filename_user,
|
|||||||
char path[sizeof(su)]; // sizeof includes nullterm already!
|
char path[sizeof(su)]; // sizeof includes nullterm already!
|
||||||
memset(path, 0, sizeof(path));
|
memset(path, 0, sizeof(path));
|
||||||
|
|
||||||
ksu_strncpy_from_user_retry(path, *filename_user, sizeof(path));
|
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
if (memcmp(path, su, sizeof(su)))
|
if (memcmp(path, su, sizeof(su)))
|
||||||
return 0;
|
return 0;
|
||||||
@@ -190,7 +104,7 @@ static int ksu_sucompat_user_common(const char __user **filename_user,
|
|||||||
if (escalate) {
|
if (escalate) {
|
||||||
pr_info("%s su found\n", syscall_name);
|
pr_info("%s su found\n", syscall_name);
|
||||||
*filename_user = ksud_user_path();
|
*filename_user = ksud_user_path();
|
||||||
escape_to_root(); // escalate !!
|
escape_with_root_profile(); // escalate !!
|
||||||
} else {
|
} else {
|
||||||
pr_info("%s su->sh!\n", syscall_name);
|
pr_info("%s su->sh!\n", syscall_name);
|
||||||
*filename_user = sh_user_path();
|
*filename_user = sh_user_path();
|
||||||
@@ -245,14 +159,14 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
|||||||
pr_info("do_execveat_common su found\n");
|
pr_info("do_execveat_common su found\n");
|
||||||
memcpy((void *)filename->name, ksud_path, sizeof(ksud_path));
|
memcpy((void *)filename->name, ksud_path, sizeof(ksud_path));
|
||||||
|
|
||||||
escape_to_root();
|
escape_with_root_profile();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __ksu_handle_devpts(struct inode *inode)
|
int __ksu_handle_devpts(struct inode *inode)
|
||||||
{
|
{
|
||||||
#ifndef KSU_KPROBES_HOOK
|
#ifndef KSU_SHOULD_USE_NEW_TP
|
||||||
if (!ksu_su_compat_enabled)
|
if (!ksu_su_compat_enabled)
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
@@ -281,193 +195,15 @@ int __ksu_handle_devpts(struct inode *inode)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef KSU_SHOULD_USE_NEW_TP
|
// sucompat: permitted process can execute 'su' to gain root access.
|
||||||
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
|
||||||
|
|
||||||
// Tracepoint probe for sys_enter
|
|
||||||
static void sucompat_sys_enter_handler(void *data, struct pt_regs *regs,
|
|
||||||
long id)
|
|
||||||
{
|
|
||||||
// Handle newfstatat
|
|
||||||
if (unlikely(id == __NR_newfstatat)) {
|
|
||||||
int *dfd = (int *)&PT_REGS_PARM1(regs);
|
|
||||||
const char __user **filename_user =
|
|
||||||
(const char __user **)&PT_REGS_PARM2(regs);
|
|
||||||
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(regs);
|
|
||||||
ksu_handle_stat(dfd, filename_user, flags);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle faccessat
|
|
||||||
if (unlikely(id == __NR_faccessat)) {
|
|
||||||
int *dfd = (int *)&PT_REGS_PARM1(regs);
|
|
||||||
const char __user **filename_user =
|
|
||||||
(const char __user **)&PT_REGS_PARM2(regs);
|
|
||||||
int *mode = (int *)&PT_REGS_PARM3(regs);
|
|
||||||
ksu_handle_faccessat(dfd, filename_user, mode, NULL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle execve
|
|
||||||
if (unlikely(id == __NR_execve)) {
|
|
||||||
const char __user **filename_user =
|
|
||||||
(const char __user **)&PT_REGS_PARM1(regs);
|
|
||||||
ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL,
|
|
||||||
NULL);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
|
||||||
|
|
||||||
#ifdef CONFIG_KRETPROBES
|
|
||||||
|
|
||||||
static struct kretprobe *init_kretprobe(const char *name,
|
|
||||||
kretprobe_handler_t handler)
|
|
||||||
{
|
|
||||||
struct kretprobe *rp = kzalloc(sizeof(struct kretprobe), GFP_KERNEL);
|
|
||||||
if (!rp)
|
|
||||||
return NULL;
|
|
||||||
rp->kp.symbol_name = name;
|
|
||||||
rp->handler = handler;
|
|
||||||
rp->data_size = 0;
|
|
||||||
rp->maxactive = 0;
|
|
||||||
|
|
||||||
int ret = register_kretprobe(rp);
|
|
||||||
pr_info("sucompat: register_%s kretprobe: %d\n", name, ret);
|
|
||||||
if (ret) {
|
|
||||||
kfree(rp);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return rp;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void destroy_kretprobe(struct kretprobe **rp_ptr)
|
|
||||||
{
|
|
||||||
struct kretprobe *rp = *rp_ptr;
|
|
||||||
if (!rp)
|
|
||||||
return;
|
|
||||||
unregister_kretprobe(rp);
|
|
||||||
synchronize_rcu();
|
|
||||||
kfree(rp);
|
|
||||||
*rp_ptr = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int tracepoint_reg_count = 0;
|
|
||||||
static DEFINE_SPINLOCK(tracepoint_reg_lock);
|
|
||||||
|
|
||||||
static int syscall_regfunc_handler(struct kretprobe_instance *ri,
|
|
||||||
struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
spin_lock_irqsave(&tracepoint_reg_lock, flags);
|
|
||||||
if (tracepoint_reg_count < 1) {
|
|
||||||
// while install our tracepoint, mark our processes
|
|
||||||
unmark_all_process();
|
|
||||||
ksu_mark_running_process();
|
|
||||||
} else {
|
|
||||||
// while installing other tracepoint, mark all processes
|
|
||||||
mark_all_process();
|
|
||||||
}
|
|
||||||
tracepoint_reg_count++;
|
|
||||||
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int syscall_unregfunc_handler(struct kretprobe_instance *ri,
|
|
||||||
struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
spin_lock_irqsave(&tracepoint_reg_lock, flags);
|
|
||||||
if (tracepoint_reg_count <= 1) {
|
|
||||||
// while uninstall our tracepoint, unmark all processes
|
|
||||||
unmark_all_process();
|
|
||||||
} else {
|
|
||||||
// while uninstalling other tracepoint, mark our processes
|
|
||||||
unmark_all_process();
|
|
||||||
ksu_mark_running_process();
|
|
||||||
}
|
|
||||||
tracepoint_reg_count--;
|
|
||||||
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kretprobe *syscall_regfunc_rp = NULL;
|
|
||||||
static struct kretprobe *syscall_unregfunc_rp = NULL;
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void ksu_sucompat_enable(void)
|
|
||||||
{
|
|
||||||
#ifdef KSU_SHOULD_USE_NEW_TP
|
|
||||||
int ret;
|
|
||||||
pr_info("sucompat: ksu_sucompat_enable called\n");
|
|
||||||
|
|
||||||
#ifdef CONFIG_KRETPROBES
|
|
||||||
// Register kretprobe for syscall_regfunc
|
|
||||||
syscall_regfunc_rp =
|
|
||||||
init_kretprobe("syscall_regfunc", syscall_regfunc_handler);
|
|
||||||
// Register kretprobe for syscall_unregfunc
|
|
||||||
syscall_unregfunc_rp =
|
|
||||||
init_kretprobe("syscall_unregfunc", syscall_unregfunc_handler);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
|
||||||
ret = register_trace_sys_enter(sucompat_sys_enter_handler, NULL);
|
|
||||||
#ifndef CONFIG_KRETPROBES
|
|
||||||
unmark_all_process();
|
|
||||||
ksu_mark_running_process();
|
|
||||||
#endif
|
|
||||||
if (ret) {
|
|
||||||
pr_err("sucompat: failed to register sys_enter tracepoint: %d\n",
|
|
||||||
ret);
|
|
||||||
} else {
|
|
||||||
pr_info("sucompat: sys_enter tracepoint registered\n");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
ksu_su_compat_enabled = true;
|
|
||||||
pr_info("init sucompat\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void ksu_sucompat_disable(void)
|
|
||||||
{
|
|
||||||
#ifdef KSU_SHOULD_USE_NEW_TP
|
|
||||||
pr_info("sucompat: ksu_sucompat_disable called\n");
|
|
||||||
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
|
||||||
unregister_trace_sys_enter(sucompat_sys_enter_handler, NULL);
|
|
||||||
tracepoint_synchronize_unregister();
|
|
||||||
pr_info("sucompat: sys_enter tracepoint unregistered\n");
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_KRETPROBES
|
|
||||||
destroy_kretprobe(&syscall_regfunc_rp);
|
|
||||||
destroy_kretprobe(&syscall_unregfunc_rp);
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
ksu_su_compat_enabled = false;
|
|
||||||
pr_info("deinit sucompat\n");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
// sucompat: permited process can execute 'su' to gain root access.
|
|
||||||
void ksu_sucompat_init(void)
|
void ksu_sucompat_init(void)
|
||||||
{
|
{
|
||||||
if (ksu_register_feature_handler(&su_compat_handler)) {
|
if (ksu_register_feature_handler(&su_compat_handler)) {
|
||||||
pr_err("Failed to register su_compat feature handler\n");
|
pr_err("Failed to register su_compat feature handler\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ksu_su_compat_enabled) {
|
|
||||||
ksu_sucompat_enable();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_sucompat_exit(void)
|
void ksu_sucompat_exit(void)
|
||||||
{
|
{
|
||||||
if (ksu_su_compat_enabled) {
|
|
||||||
ksu_sucompat_disable();
|
|
||||||
}
|
|
||||||
ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT);
|
ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,18 @@
|
|||||||
#ifndef __KSU_H_SUCOMPAT
|
#ifndef __KSU_H_SUCOMPAT
|
||||||
#define __KSU_H_SUCOMPAT
|
#define __KSU_H_SUCOMPAT
|
||||||
#include <linux/sched.h>
|
#include <linux/types.h>
|
||||||
#include <linux/version.h>
|
|
||||||
|
|
||||||
extern bool ksu_su_compat_enabled;
|
extern bool ksu_su_compat_enabled;
|
||||||
|
|
||||||
void ksu_sucompat_init(void);
|
void ksu_sucompat_init(void);
|
||||||
void ksu_sucompat_exit(void);
|
void ksu_sucompat_exit(void);
|
||||||
|
|
||||||
void ksu_sucompat_enable(void);
|
// Handler functions exported for hook_manager
|
||||||
void ksu_sucompat_disable(void);
|
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||||
|
int *__unused_flags);
|
||||||
void ksu_mark_running_process(void);
|
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
||||||
|
int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) && defined(KSU_KPROBE_HOOK)
|
void *__never_use_argv, void *__never_use_envp,
|
||||||
|
int *__never_use_flags);
|
||||||
#include <linux/thread_info.h>
|
|
||||||
|
|
||||||
static inline void ksu_set_task_tracepoint_flag(struct task_struct *t)
|
|
||||||
{
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
|
||||||
set_task_syscall_work(t, SYSCALL_TRACEPOINT);
|
|
||||||
#else
|
|
||||||
set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t)
|
|
||||||
{
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
|
||||||
clear_task_syscall_work(t, SYSCALL_TRACEPOINT);
|
|
||||||
#else
|
|
||||||
clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static inline void ksu_set_task_tracepoint_flag(struct task_struct *t)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -4,23 +4,26 @@
|
|||||||
#include <linux/capability.h>
|
#include <linux/capability.h>
|
||||||
#include <linux/cred.h>
|
#include <linux/cred.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
|
#include <linux/fdtable.h>
|
||||||
#include <linux/file.h>
|
#include <linux/file.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
#include <linux/syscalls.h>
|
||||||
|
#include <linux/task_work.h>
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
#include "arch.h"
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "feature.h"
|
#include "feature.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksu.h"
|
|
||||||
#include "ksud.h"
|
#include "ksud.h"
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
#include "selinux/selinux.h"
|
#include "selinux/selinux.h"
|
||||||
#include "core_hook.h"
|
|
||||||
#include "objsec.h"
|
#include "objsec.h"
|
||||||
#include "file_wrapper.h"
|
#include "file_wrapper.h"
|
||||||
#include "kernel_compat.h"
|
#include "syscall_hook_manager.h"
|
||||||
#include "throne_comm.h"
|
#include "throne_comm.h"
|
||||||
#include "dynamic_manager.h"
|
#include "dynamic_manager.h"
|
||||||
|
|
||||||
@@ -50,8 +53,7 @@ bool always_allow(void)
|
|||||||
|
|
||||||
bool allowed_for_su(void)
|
bool allowed_for_su(void)
|
||||||
{
|
{
|
||||||
bool is_allowed =
|
bool is_allowed = is_manager() || ksu_is_allow_uid_for_current(current_uid().val);
|
||||||
is_manager() || ksu_is_allow_uid_for_current(current_uid().val);
|
|
||||||
return is_allowed;
|
return is_allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +75,7 @@ static int do_grant_root(void __user *arg)
|
|||||||
// we already check uid above on allowed_for_su()
|
// we already check uid above on allowed_for_su()
|
||||||
|
|
||||||
pr_info("allow root for: %d\n", current_uid().val);
|
pr_info("allow root for: %d\n", current_uid().val);
|
||||||
escape_to_root();
|
escape_with_root_profile();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -123,13 +125,13 @@ static int do_report_event(void __user *arg)
|
|||||||
if (!boot_complete_lock) {
|
if (!boot_complete_lock) {
|
||||||
boot_complete_lock = true;
|
boot_complete_lock = true;
|
||||||
pr_info("boot_complete triggered\n");
|
pr_info("boot_complete triggered\n");
|
||||||
|
on_boot_completed();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EVENT_MODULE_MOUNTED: {
|
case EVENT_MODULE_MOUNTED: {
|
||||||
ksu_module_mounted = true;
|
|
||||||
pr_info("module mounted!\n");
|
pr_info("module mounted!\n");
|
||||||
nuke_ext4_sysfs();
|
on_module_mounted();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
@@ -345,13 +347,11 @@ static int do_set_feature(void __user *arg)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int do_get_wrapper_fd(void __user *arg)
|
static int do_get_wrapper_fd(void __user *arg) {
|
||||||
{
|
|
||||||
if (!ksu_file_sid) {
|
if (!ksu_file_sid) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *anon_name = "[ksu_fdwrapper]";
|
|
||||||
struct ksu_get_wrapper_fd_cmd cmd;
|
struct ksu_get_wrapper_fd_cmd cmd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -360,7 +360,7 @@ static int do_get_wrapper_fd(void __user *arg)
|
|||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct file *f = fget(cmd.fd);
|
struct file* f = fget(cmd.fd);
|
||||||
if (!f) {
|
if (!f) {
|
||||||
return -EBADF;
|
return -EBADF;
|
||||||
}
|
}
|
||||||
@@ -373,37 +373,26 @@ static int do_get_wrapper_fd(void __user *arg)
|
|||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
|
||||||
#define getfd_secure anon_inode_create_getfd
|
#define getfd_secure anon_inode_create_getfd
|
||||||
#elif defined(KSU_HAS_GETFD_SECURE)
|
#else
|
||||||
#define getfd_secure anon_inode_getfd_secure
|
#define getfd_secure anon_inode_getfd_secure
|
||||||
#endif
|
#endif
|
||||||
|
ret = getfd_secure("[ksu_fdwrapper]", &data->ops, data, f->f_flags, NULL);
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0) || defined(KSU_HAS_GETFD_SECURE)
|
|
||||||
ret = getfd_secure(anon_name, &data->ops, data, f->f_flags, NULL);
|
|
||||||
#else
|
|
||||||
ret = anon_inode_getfd(anon_name, &data->ops, data, f->f_flags);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
pr_err("ksu_fdwrapper: getfd failed: %d\n", ret);
|
pr_err("ksu_fdwrapper: getfd failed: %d\n", ret);
|
||||||
goto put_wrapper_data;
|
goto put_wrapper_data;
|
||||||
}
|
}
|
||||||
|
struct file* pf = fget(ret);
|
||||||
|
|
||||||
struct file *pf = fget(ret);
|
struct inode* wrapper_inode = file_inode(pf);
|
||||||
struct inode *wrapper_inode = file_inode(pf);
|
// copy original inode mode
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || \
|
wrapper_inode->i_mode = file_inode(f)->i_mode;
|
||||||
defined(KSU_OPTIONAL_SELINUX_INODE)
|
|
||||||
struct inode_security_struct *sec = selinux_inode(wrapper_inode);
|
struct inode_security_struct *sec = selinux_inode(wrapper_inode);
|
||||||
#else
|
|
||||||
struct inode_security_struct *sec =
|
|
||||||
(struct inode_security_struct *)wrapper_inode->i_security;
|
|
||||||
#endif
|
|
||||||
if (sec) {
|
if (sec) {
|
||||||
sec->sid = ksu_file_sid;
|
sec->sid = ksu_file_sid;
|
||||||
}
|
}
|
||||||
|
|
||||||
fput(pf);
|
fput(pf);
|
||||||
goto put_orig_file;
|
goto put_orig_file;
|
||||||
|
|
||||||
put_wrapper_data:
|
put_wrapper_data:
|
||||||
ksu_delete_file_wrapper(data);
|
ksu_delete_file_wrapper(data);
|
||||||
put_orig_file:
|
put_orig_file:
|
||||||
@@ -412,6 +401,71 @@ put_orig_file:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int do_manage_mark(void __user *arg)
|
||||||
|
{
|
||||||
|
struct ksu_manage_mark_cmd cmd;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||||
|
pr_err("manage_mark: copy_from_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cmd.operation) {
|
||||||
|
case KSU_MARK_GET: {
|
||||||
|
// Get task mark status
|
||||||
|
ret = ksu_get_task_mark(cmd.pid);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("manage_mark: get failed for pid %d: %d\n", cmd.pid, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
cmd.result = (u32)ret;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KSU_MARK_MARK: {
|
||||||
|
if (cmd.pid == 0) {
|
||||||
|
ksu_mark_all_process();
|
||||||
|
} else {
|
||||||
|
ret = ksu_set_task_mark(cmd.pid, true);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("manage_mark: set_mark failed for pid %d: %d\n", cmd.pid,
|
||||||
|
ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KSU_MARK_UNMARK: {
|
||||||
|
if (cmd.pid == 0) {
|
||||||
|
ksu_unmark_all_process();
|
||||||
|
} else {
|
||||||
|
ret = ksu_set_task_mark(cmd.pid, false);
|
||||||
|
if (ret < 0) {
|
||||||
|
pr_err("manage_mark: set_unmark failed for pid %d: %d\n",
|
||||||
|
cmd.pid, ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KSU_MARK_REFRESH: {
|
||||||
|
ksu_mark_running_process();
|
||||||
|
pr_info("manage_mark: refreshed running processes\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
pr_err("manage_mark: invalid operation %u\n", cmd.operation);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||||
|
pr_err("manage_mark: copy_to_user failed\n");
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// 100. GET_FULL_VERSION - Get full version string
|
// 100. GET_FULL_VERSION - Get full version string
|
||||||
static int do_get_full_version(void __user *arg)
|
static int do_get_full_version(void __user *arg)
|
||||||
{
|
{
|
||||||
@@ -435,13 +489,12 @@ static int do_get_full_version(void __user *arg)
|
|||||||
static int do_get_hook_type(void __user *arg)
|
static int do_get_hook_type(void __user *arg)
|
||||||
{
|
{
|
||||||
struct ksu_hook_type_cmd cmd = {0};
|
struct ksu_hook_type_cmd cmd = {0};
|
||||||
const char *type = "Unknown";
|
const char *type = "Tracepoint";
|
||||||
|
|
||||||
#if defined(KSU_HAVE_SYSCALL_TRACEPOINTS_HOOK)
|
#if defined(CONFIG_KSU_MANUAL_HOOK)
|
||||||
type = "Tracepoint";
|
|
||||||
#elif defined(KSU_MANUAL_HOOK)
|
|
||||||
type = "Manual";
|
type = "Manual";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
strscpy(cmd.hook_type, type, sizeof(cmd.hook_type));
|
strscpy(cmd.hook_type, type, sizeof(cmd.hook_type));
|
||||||
#else
|
#else
|
||||||
@@ -587,7 +640,8 @@ static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
|
|||||||
{ .cmd = KSU_IOCTL_SET_APP_PROFILE, .name = "SET_APP_PROFILE", .handler = do_set_app_profile, .perm_check = only_manager },
|
{ .cmd = KSU_IOCTL_SET_APP_PROFILE, .name = "SET_APP_PROFILE", .handler = do_set_app_profile, .perm_check = only_manager },
|
||||||
{ .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root },
|
{ .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root },
|
||||||
{ .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root },
|
{ .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root },
|
||||||
{ .cmd = KSU_IOCTL_PROXY_FILE, .name = "PROXY_FILE", .handler = do_get_wrapper_fd, .perm_check = manager_or_root },
|
{ .cmd = KSU_IOCTL_GET_WRAPPER_FD, .name = "GET_WRAPPER_FD", .handler = do_get_wrapper_fd, .perm_check = manager_or_root },
|
||||||
|
{ .cmd = KSU_IOCTL_MANAGE_MARK, .name = "MANAGE_MARK", .handler = do_manage_mark, .perm_check = manager_or_root },
|
||||||
{ .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow},
|
{ .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow},
|
||||||
{ .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root},
|
{ .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root},
|
||||||
{ .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root},
|
{ .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root},
|
||||||
@@ -600,18 +654,126 @@ static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
|
|||||||
{ .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL} // Sentine
|
{ .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL} // Sentine
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
struct ksu_install_fd_tw {
|
||||||
|
struct callback_head cb;
|
||||||
|
int __user *outp;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void ksu_install_fd_tw_func(struct callback_head *cb)
|
||||||
|
{
|
||||||
|
struct ksu_install_fd_tw *tw =
|
||||||
|
container_of(cb, struct ksu_install_fd_tw, cb);
|
||||||
|
int fd = ksu_install_fd();
|
||||||
|
pr_info("[%d] install ksu fd: %d\n", current->pid, fd);
|
||||||
|
|
||||||
|
if (copy_to_user(tw->outp, &fd, sizeof(fd))) {
|
||||||
|
pr_err("install ksu fd reply err\n");
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
close_fd(fd);
|
||||||
|
#else
|
||||||
|
ksys_close(fd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
kfree(tw);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int reboot_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
|
int magic1 = (int)PT_REGS_PARM1(real_regs);
|
||||||
|
int magic2 = (int)PT_REGS_PARM2(real_regs);
|
||||||
|
unsigned long arg4;
|
||||||
|
|
||||||
|
// Check if this is a request to install KSU fd
|
||||||
|
if (magic1 == KSU_INSTALL_MAGIC1 && magic2 == KSU_INSTALL_MAGIC2) {
|
||||||
|
struct ksu_install_fd_tw *tw;
|
||||||
|
|
||||||
|
arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs);
|
||||||
|
|
||||||
|
tw = kzalloc(sizeof(*tw), GFP_ATOMIC);
|
||||||
|
if (!tw)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
tw->outp = (int __user *)arg4;
|
||||||
|
tw->cb.func = ksu_install_fd_tw_func;
|
||||||
|
|
||||||
|
if (ksu_task_work_add(current, &tw->cb, TWA_RESUME)) {
|
||||||
|
kfree(tw);
|
||||||
|
pr_warn("install fd add task_work failed\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kprobe reboot_kp = {
|
||||||
|
.symbol_name = REBOOT_SYMBOL,
|
||||||
|
.pre_handler = reboot_handler_pre,
|
||||||
|
};
|
||||||
|
#else
|
||||||
|
int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd,
|
||||||
|
void __user **arg)
|
||||||
|
{
|
||||||
|
if (magic1 != KSU_INSTALL_MAGIC1)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
|
pr_info("sys_reboot: intercepted call! magic: 0x%x id: %d\n", magic1,
|
||||||
|
magic2);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Check if this is a request to install KSU fd
|
||||||
|
if (magic2 == KSU_INSTALL_MAGIC2) {
|
||||||
|
int fd = ksu_install_fd();
|
||||||
|
// downstream: dereference all arg usage!
|
||||||
|
if (copy_to_user((void __user *)*arg, &fd, sizeof(fd))) {
|
||||||
|
pr_err("install ksu fd reply err\n");
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
close_fd(fd);
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
|
||||||
|
ksys_close(fd);
|
||||||
|
#else
|
||||||
|
sys_close(fd);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void ksu_supercalls_init(void)
|
void ksu_supercalls_init(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
pr_info("KernelSU IOCTL Commands:\n");
|
pr_info("KernelSU IOCTL Commands:\n");
|
||||||
for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
|
for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
|
||||||
pr_info(" %-18s = 0x%08x\n", ksu_ioctl_handlers[i].name, ksu_ioctl_handlers[i].cmd);
|
pr_info(" %-18s = 0x%08x\n", ksu_ioctl_handlers[i].name,
|
||||||
|
ksu_ioctl_handlers[i].cmd);
|
||||||
}
|
}
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
int rc = register_kprobe(&reboot_kp);
|
||||||
|
if (rc) {
|
||||||
|
pr_err("reboot kprobe failed: %d\n", rc);
|
||||||
|
} else {
|
||||||
|
pr_info("reboot kprobe registered successfully\n");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_supercalls_exit(void)
|
||||||
|
{
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
unregister_kprobe(&reboot_kp);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// IOCTL dispatcher
|
// IOCTL dispatcher
|
||||||
static long anon_ksu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
static long anon_ksu_ioctl(struct file *filp, unsigned int cmd,
|
||||||
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
void __user *argp = (void __user *)arg;
|
void __user *argp = (void __user *)arg;
|
||||||
int i;
|
int i;
|
||||||
@@ -630,8 +792,7 @@ static long anon_ksu_ioctl(struct file *filp, unsigned int cmd, unsigned long ar
|
|||||||
return -EPERM;
|
return -EPERM;
|
||||||
}
|
}
|
||||||
// Execute handler
|
// Execute handler
|
||||||
int ret = ksu_ioctl_handlers[i].handler(argp);
|
return ksu_ioctl_handlers[i].handler(argp);
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -642,7 +803,9 @@ static long anon_ksu_ioctl(struct file *filp, unsigned int cmd, unsigned long ar
|
|||||||
// File release handler
|
// File release handler
|
||||||
static int anon_ksu_release(struct inode *inode, struct file *filp)
|
static int anon_ksu_release(struct inode *inode, struct file *filp)
|
||||||
{
|
{
|
||||||
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_info("ksu fd released\n");
|
pr_info("ksu fd released\n");
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,7 +831,8 @@ int ksu_install_fd(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create anonymous inode file
|
// Create anonymous inode file
|
||||||
filp = anon_inode_getfile("[ksu_driver]", &anon_ksu_fops, NULL, O_RDWR | O_CLOEXEC);
|
filp = anon_inode_getfile("[ksu_driver]", &anon_ksu_fops, NULL,
|
||||||
|
O_RDWR | O_CLOEXEC);
|
||||||
if (IS_ERR(filp)) {
|
if (IS_ERR(filp)) {
|
||||||
pr_err("ksu_install_fd: failed to create anon inode file\n");
|
pr_err("ksu_install_fd: failed to create anon inode file\n");
|
||||||
put_unused_fd(fd);
|
put_unused_fd(fd);
|
||||||
@@ -678,7 +842,8 @@ int ksu_install_fd(void)
|
|||||||
// Install fd
|
// Install fd
|
||||||
fd_install(fd, filp);
|
fd_install(fd, filp);
|
||||||
|
|
||||||
pr_info("ksu fd[%d] installed for %s/%d\n", fd, current->comm, current->pid);
|
pr_info("ksu fd[%d] installed for %s/%d\n", fd, current->comm,
|
||||||
|
current->pid);
|
||||||
|
|
||||||
return fd;
|
return fd;
|
||||||
}
|
}
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/ioctl.h>
|
#include <linux/ioctl.h>
|
||||||
#include "ksu.h"
|
#include "app_profile.h"
|
||||||
|
|
||||||
#ifdef CONFIG_KPM
|
#ifdef CONFIG_KPM
|
||||||
#include "kpm/kpm.h"
|
#include "kpm/kpm.h"
|
||||||
@@ -105,10 +105,21 @@ struct ksu_enable_uid_scanner_cmd {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct ksu_get_wrapper_fd_cmd {
|
struct ksu_get_wrapper_fd_cmd {
|
||||||
__u32 fd;
|
__u32 fd; // Input: userspace fd
|
||||||
__u32 flags; // CLOEXEC
|
__u32 flags; // Input: flags of userspace fd
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ksu_manage_mark_cmd {
|
||||||
|
__u32 operation; // Input: KSU_MARK_*
|
||||||
|
__s32 pid; // Input: target pid (0 for all processes)
|
||||||
|
__u32 result; // Output: for get operation - mark status or reg_count
|
||||||
|
};
|
||||||
|
|
||||||
|
#define KSU_MARK_GET 1
|
||||||
|
#define KSU_MARK_MARK 2
|
||||||
|
#define KSU_MARK_UNMARK 3
|
||||||
|
#define KSU_MARK_REFRESH 4
|
||||||
|
|
||||||
// IOCTL command definitions
|
// IOCTL command definitions
|
||||||
#define KSU_IOCTL_GRANT_ROOT _IOC(_IOC_NONE, 'K', 1, 0)
|
#define KSU_IOCTL_GRANT_ROOT _IOC(_IOC_NONE, 'K', 1, 0)
|
||||||
#define KSU_IOCTL_GET_INFO _IOC(_IOC_READ, 'K', 2, 0)
|
#define KSU_IOCTL_GET_INFO _IOC(_IOC_READ, 'K', 2, 0)
|
||||||
@@ -124,7 +135,8 @@ struct ksu_get_wrapper_fd_cmd {
|
|||||||
#define KSU_IOCTL_SET_APP_PROFILE _IOC(_IOC_WRITE, 'K', 12, 0)
|
#define KSU_IOCTL_SET_APP_PROFILE _IOC(_IOC_WRITE, 'K', 12, 0)
|
||||||
#define KSU_IOCTL_GET_FEATURE _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0)
|
#define KSU_IOCTL_GET_FEATURE _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0)
|
||||||
#define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0)
|
#define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0)
|
||||||
#define KSU_IOCTL_PROXY_FILE _IOC(_IOC_NONE, 'K', 15, 0)
|
#define KSU_IOCTL_GET_WRAPPER_FD _IOC(_IOC_WRITE, 'K', 15, 0)
|
||||||
|
#define KSU_IOCTL_MANAGE_MARK _IOC(_IOC_READ | _IOC_WRITE, 'K', 16, 0)
|
||||||
// Other IOCTL command definitions
|
// Other IOCTL command definitions
|
||||||
#define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0)
|
#define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0)
|
||||||
#define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0)
|
#define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0)
|
||||||
@@ -148,6 +160,6 @@ struct ksu_ioctl_cmd_map {
|
|||||||
// Install KSU fd to current process
|
// Install KSU fd to current process
|
||||||
int ksu_install_fd(void);
|
int ksu_install_fd(void);
|
||||||
|
|
||||||
void ksu_supercalls_init();
|
void ksu_supercalls_init(void);
|
||||||
|
void ksu_supercalls_exit(void);
|
||||||
#endif // __KSU_H_SUPERCALLS
|
#endif // __KSU_H_SUPERCALLS
|
||||||
|
|||||||
381
kernel/syscall_hook_manager.c
Normal file
381
kernel/syscall_hook_manager.c
Normal file
@@ -0,0 +1,381 @@
|
|||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
#include "linux/compiler.h"
|
||||||
|
#include "linux/cred.h"
|
||||||
|
#include "linux/printk.h"
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/kprobes.h>
|
||||||
|
#include <linux/tracepoint.h>
|
||||||
|
#include <asm/syscall.h>
|
||||||
|
#include <linux/ptrace.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <trace/events/syscalls.h>
|
||||||
|
|
||||||
|
#include "allowlist.h"
|
||||||
|
#include "arch.h"
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
#include "sucompat.h"
|
||||||
|
#include "setuid_hook.h"
|
||||||
|
#include "selinux/selinux.h"
|
||||||
|
|
||||||
|
// Tracepoint registration count management
|
||||||
|
// == 1: just us
|
||||||
|
// > 1: someone else is also using syscall tracepoint e.g. ftrace
|
||||||
|
static int tracepoint_reg_count = 0;
|
||||||
|
static DEFINE_SPINLOCK(tracepoint_reg_lock);
|
||||||
|
|
||||||
|
void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
spin_lock_irqsave(&tracepoint_reg_lock, flags);
|
||||||
|
if (tracepoint_reg_count <= 1) {
|
||||||
|
ksu_clear_task_tracepoint_flag(t);
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process marking management
|
||||||
|
static void handle_process_mark(bool mark)
|
||||||
|
{
|
||||||
|
struct task_struct *p, *t;
|
||||||
|
read_lock(&tasklist_lock);
|
||||||
|
for_each_process_thread (p, t) {
|
||||||
|
if (mark)
|
||||||
|
ksu_set_task_tracepoint_flag(t);
|
||||||
|
else
|
||||||
|
ksu_clear_task_tracepoint_flag(t);
|
||||||
|
}
|
||||||
|
read_unlock(&tasklist_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_mark_all_process(void)
|
||||||
|
{
|
||||||
|
handle_process_mark(true);
|
||||||
|
pr_info("hook_manager: mark all user process done!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_unmark_all_process(void)
|
||||||
|
{
|
||||||
|
handle_process_mark(false);
|
||||||
|
pr_info("hook_manager: unmark all user process done!\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ksu_mark_running_process_locked()
|
||||||
|
{
|
||||||
|
struct task_struct *p, *t;
|
||||||
|
read_lock(&tasklist_lock);
|
||||||
|
for_each_process_thread (p, t) {
|
||||||
|
if (!t->mm) { // only user processes
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int uid = task_uid(t).val;
|
||||||
|
const struct cred *cred = get_task_cred(t);
|
||||||
|
bool ksu_root_process = uid == 0 && is_task_ksu_domain(cred);
|
||||||
|
bool is_zygote_process = is_zygote(cred);
|
||||||
|
bool is_shell = uid == 2000;
|
||||||
|
// before boot completed, we shall mark init for marking zygote
|
||||||
|
bool is_init = t->pid == 1;
|
||||||
|
if (ksu_root_process || is_zygote_process || is_shell ||
|
||||||
|
is_init || ksu_is_allow_uid(uid)) {
|
||||||
|
ksu_set_task_tracepoint_flag(t);
|
||||||
|
pr_info("hook_manager: mark process: pid:%d, uid: %d, comm:%s\n",
|
||||||
|
t->pid, uid, t->comm);
|
||||||
|
} else {
|
||||||
|
ksu_clear_task_tracepoint_flag(t);
|
||||||
|
pr_info("hook_manager: unmark process: pid:%d, uid: %d, comm:%s\n",
|
||||||
|
t->pid, uid, t->comm);
|
||||||
|
}
|
||||||
|
put_cred(cred);
|
||||||
|
}
|
||||||
|
read_unlock(&tasklist_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_mark_running_process()
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
spin_lock_irqsave(&tracepoint_reg_lock, flags);
|
||||||
|
if (tracepoint_reg_count <= 1) {
|
||||||
|
ksu_mark_running_process_locked();
|
||||||
|
} else {
|
||||||
|
pr_info("hook_manager: not mark running process since syscall tracepoint is in use\n");
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get task mark status
|
||||||
|
// Returns: 1 if marked, 0 if not marked, -ESRCH if task not found
|
||||||
|
int ksu_get_task_mark(pid_t pid)
|
||||||
|
{
|
||||||
|
struct task_struct *task;
|
||||||
|
int marked = -ESRCH;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
task = find_task_by_vpid(pid);
|
||||||
|
if (task) {
|
||||||
|
get_task_struct(task);
|
||||||
|
rcu_read_unlock();
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
marked = test_task_syscall_work(task, SYSCALL_TRACEPOINT) ? 1 :
|
||||||
|
0;
|
||||||
|
#else
|
||||||
|
marked = test_tsk_thread_flag(task, TIF_SYSCALL_TRACEPOINT) ?
|
||||||
|
1 :
|
||||||
|
0;
|
||||||
|
#endif
|
||||||
|
put_task_struct(task);
|
||||||
|
} else {
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return marked;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set task mark status
|
||||||
|
// Returns: 0 on success, -ESRCH if task not found
|
||||||
|
int ksu_set_task_mark(pid_t pid, bool mark)
|
||||||
|
{
|
||||||
|
struct task_struct *task;
|
||||||
|
int ret = -ESRCH;
|
||||||
|
|
||||||
|
rcu_read_lock();
|
||||||
|
task = find_task_by_vpid(pid);
|
||||||
|
if (task) {
|
||||||
|
get_task_struct(task);
|
||||||
|
rcu_read_unlock();
|
||||||
|
if (mark) {
|
||||||
|
ksu_set_task_tracepoint_flag(task);
|
||||||
|
pr_info("hook_manager: marked task pid=%d comm=%s\n",
|
||||||
|
pid, task->comm);
|
||||||
|
} else {
|
||||||
|
ksu_clear_task_tracepoint_flag(task);
|
||||||
|
pr_info("hook_manager: unmarked task pid=%d comm=%s\n",
|
||||||
|
pid, task->comm);
|
||||||
|
}
|
||||||
|
put_task_struct(task);
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
rcu_read_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kretprobe *init_kretprobe(const char *name,
|
||||||
|
kretprobe_handler_t handler)
|
||||||
|
{
|
||||||
|
struct kretprobe *rp = kzalloc(sizeof(struct kretprobe), GFP_KERNEL);
|
||||||
|
if (!rp)
|
||||||
|
return NULL;
|
||||||
|
rp->kp.symbol_name = name;
|
||||||
|
rp->handler = handler;
|
||||||
|
rp->data_size = 0;
|
||||||
|
rp->maxactive = 0;
|
||||||
|
|
||||||
|
int ret = register_kretprobe(rp);
|
||||||
|
pr_info("hook_manager: register_%s kretprobe: %d\n", name, ret);
|
||||||
|
if (ret) {
|
||||||
|
kfree(rp);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return rp;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destroy_kretprobe(struct kretprobe **rp_ptr)
|
||||||
|
{
|
||||||
|
struct kretprobe *rp = *rp_ptr;
|
||||||
|
if (!rp)
|
||||||
|
return;
|
||||||
|
unregister_kretprobe(rp);
|
||||||
|
synchronize_rcu();
|
||||||
|
kfree(rp);
|
||||||
|
*rp_ptr = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int syscall_regfunc_handler(struct kretprobe_instance *ri,
|
||||||
|
struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
spin_lock_irqsave(&tracepoint_reg_lock, flags);
|
||||||
|
if (tracepoint_reg_count < 1) {
|
||||||
|
// while install our tracepoint, mark our processes
|
||||||
|
ksu_mark_running_process_locked();
|
||||||
|
} else if (tracepoint_reg_count == 1) {
|
||||||
|
// while other tracepoint first added, mark all processes
|
||||||
|
ksu_mark_all_process();
|
||||||
|
}
|
||||||
|
tracepoint_reg_count++;
|
||||||
|
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int syscall_unregfunc_handler(struct kretprobe_instance *ri,
|
||||||
|
struct pt_regs *regs)
|
||||||
|
{
|
||||||
|
unsigned long flags;
|
||||||
|
spin_lock_irqsave(&tracepoint_reg_lock, flags);
|
||||||
|
tracepoint_reg_count--;
|
||||||
|
if (tracepoint_reg_count <= 0) {
|
||||||
|
// while no tracepoint left, unmark all processes
|
||||||
|
ksu_unmark_all_process();
|
||||||
|
} else if (tracepoint_reg_count == 1) {
|
||||||
|
// while just our tracepoint left, unmark disallowed processes
|
||||||
|
ksu_mark_running_process_locked();
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&tracepoint_reg_lock, flags);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct kretprobe *syscall_regfunc_rp = NULL;
|
||||||
|
static struct kretprobe *syscall_unregfunc_rp = NULL;
|
||||||
|
|
||||||
|
static inline bool check_syscall_fastpath(int nr)
|
||||||
|
{
|
||||||
|
switch (nr) {
|
||||||
|
case __NR_newfstatat:
|
||||||
|
case __NR_faccessat:
|
||||||
|
case __NR_execve:
|
||||||
|
case __NR_setresuid:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmark init's child that are not zygote, adbd or ksud
|
||||||
|
int ksu_handle_init_mark_tracker(const char __user **filename_user)
|
||||||
|
{
|
||||||
|
char path[64];
|
||||||
|
|
||||||
|
if (unlikely(!filename_user))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
memset(path, 0, sizeof(path));
|
||||||
|
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
|
if (likely(strstr(path, "/app_process") == NULL &&
|
||||||
|
strstr(path, "/adbd") == NULL &&
|
||||||
|
strstr(path, "/ksud") == NULL)) {
|
||||||
|
pr_info("hook_manager: unmark %d exec %s", current->pid, path);
|
||||||
|
ksu_clear_task_tracepoint_flag_if_needed(current);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic sys_enter handler that dispatches to specific handlers
|
||||||
|
static void ksu_sys_enter_handler(void *data, struct pt_regs *regs, long id)
|
||||||
|
{
|
||||||
|
if (unlikely(check_syscall_fastpath(id))) {
|
||||||
|
if (ksu_su_compat_enabled) {
|
||||||
|
// Handle newfstatat
|
||||||
|
if (id == __NR_newfstatat) {
|
||||||
|
int *dfd = (int *)&PT_REGS_PARM1(regs);
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char __user **)&PT_REGS_PARM2(
|
||||||
|
regs);
|
||||||
|
int *flags =
|
||||||
|
(int *)&PT_REGS_SYSCALL_PARM4(regs);
|
||||||
|
ksu_handle_stat(dfd, filename_user, flags);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle faccessat
|
||||||
|
if (id == __NR_faccessat) {
|
||||||
|
int *dfd = (int *)&PT_REGS_PARM1(regs);
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char __user **)&PT_REGS_PARM2(
|
||||||
|
regs);
|
||||||
|
int *mode = (int *)&PT_REGS_PARM3(regs);
|
||||||
|
ksu_handle_faccessat(dfd, filename_user, mode,
|
||||||
|
NULL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle execve
|
||||||
|
if (id == __NR_execve) {
|
||||||
|
const char __user **filename_user =
|
||||||
|
(const char __user **)&PT_REGS_PARM1(
|
||||||
|
regs);
|
||||||
|
if (current->pid != 1 &&
|
||||||
|
is_init(get_current_cred())) {
|
||||||
|
ksu_handle_init_mark_tracker(
|
||||||
|
filename_user);
|
||||||
|
} else {
|
||||||
|
ksu_handle_execve_sucompat(
|
||||||
|
NULL, filename_user, NULL,
|
||||||
|
NULL, NULL);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle setresuid
|
||||||
|
if (id == __NR_setresuid) {
|
||||||
|
uid_t ruid = (uid_t)PT_REGS_PARM1(regs);
|
||||||
|
uid_t euid = (uid_t)PT_REGS_PARM2(regs);
|
||||||
|
uid_t suid = (uid_t)PT_REGS_PARM3(regs);
|
||||||
|
ksu_handle_setresuid(ruid, euid, suid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_syscall_hook_manager_init(void)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
pr_info("hook_manager: ksu_hook_manager_init called\n");
|
||||||
|
|
||||||
|
// Register kretprobe for syscall_regfunc
|
||||||
|
syscall_regfunc_rp =
|
||||||
|
init_kretprobe("syscall_regfunc", syscall_regfunc_handler);
|
||||||
|
// Register kretprobe for syscall_unregfunc
|
||||||
|
syscall_unregfunc_rp =
|
||||||
|
init_kretprobe("syscall_unregfunc", syscall_unregfunc_handler);
|
||||||
|
|
||||||
|
ret = register_trace_sys_enter(ksu_sys_enter_handler, NULL);
|
||||||
|
if (ret) {
|
||||||
|
pr_err("hook_manager: failed to register sys_enter tracepoint: %d\n",
|
||||||
|
ret);
|
||||||
|
} else {
|
||||||
|
pr_info("hook_manager: sys_enter tracepoint registered\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
ksu_setuid_hook_init();
|
||||||
|
ksu_sucompat_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_syscall_hook_manager_exit(void)
|
||||||
|
{
|
||||||
|
pr_info("hook_manager: ksu_hook_manager_exit called\n");
|
||||||
|
unregister_trace_sys_enter(ksu_sys_enter_handler, NULL);
|
||||||
|
tracepoint_synchronize_unregister();
|
||||||
|
pr_info("hook_manager: sys_enter tracepoint unregistered\n");
|
||||||
|
|
||||||
|
destroy_kretprobe(&syscall_regfunc_rp);
|
||||||
|
destroy_kretprobe(&syscall_unregfunc_rp);
|
||||||
|
|
||||||
|
ksu_sucompat_exit();
|
||||||
|
ksu_setuid_hook_exit();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#include "klog.h" // IWYU pragma: keep
|
||||||
|
#include "syscall_hook_manager.h"
|
||||||
|
#include "sucompat.h"
|
||||||
|
#include "setuid_hook.h"
|
||||||
|
|
||||||
|
void ksu_syscall_hook_manager_init(void)
|
||||||
|
{
|
||||||
|
pr_info("hook_manager: initializing..\n");
|
||||||
|
ksu_setuid_hook_init();
|
||||||
|
ksu_sucompat_init();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_syscall_hook_manager_exit(void)
|
||||||
|
{
|
||||||
|
pr_info("hook_manager: exiting..\n");
|
||||||
|
ksu_sucompat_exit();
|
||||||
|
ksu_setuid_hook_exit();
|
||||||
|
}
|
||||||
|
#endif
|
||||||
85
kernel/syscall_hook_manager.h
Normal file
85
kernel/syscall_hook_manager.h
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
#ifndef __KSU_H_HOOK_MANAGER
|
||||||
|
#define __KSU_H_HOOK_MANAGER
|
||||||
|
|
||||||
|
#include <linux/version.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
|
||||||
|
// Hook manager initialization and cleanup
|
||||||
|
void ksu_syscall_hook_manager_init(void);
|
||||||
|
void ksu_syscall_hook_manager_exit(void);
|
||||||
|
|
||||||
|
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||||
|
// Process marking for tracepoint
|
||||||
|
void ksu_mark_all_process(void);
|
||||||
|
void ksu_unmark_all_process(void);
|
||||||
|
void ksu_mark_running_process(void);
|
||||||
|
|
||||||
|
// Per-task mark operations
|
||||||
|
int ksu_get_task_mark(pid_t pid);
|
||||||
|
int ksu_set_task_mark(pid_t pid, bool mark);
|
||||||
|
|
||||||
|
static inline void ksu_set_task_tracepoint_flag(struct task_struct *t)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
set_task_syscall_work(t, SYSCALL_TRACEPOINT);
|
||||||
|
#else
|
||||||
|
set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||||
|
clear_task_syscall_work(t, SYSCALL_TRACEPOINT);
|
||||||
|
#else
|
||||||
|
clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t);
|
||||||
|
#else
|
||||||
|
// Process marking for tracepoint
|
||||||
|
static inline void ksu_mark_all_process(void)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ksu_unmark_all_process(void)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ksu_mark_running_process(void)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per-task mark operations
|
||||||
|
static inline int ksu_get_task_mark(pid_t pid)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int ksu_set_task_mark(pid_t pid, bool mark)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ksu_set_task_tracepoint_flag(struct task_struct *t)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
static inline void ksu_clear_task_tracepoint_flag_if_needed(struct task_struct *t)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -10,11 +10,9 @@
|
|||||||
|
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksu.h"
|
|
||||||
#include "apk_sign.h"
|
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
#include "throne_tracker.h"
|
#include "throne_tracker.h"
|
||||||
#include "kernel_compat.h"
|
#include "apk_sign.h"
|
||||||
#include "dynamic_manager.h"
|
#include "dynamic_manager.h"
|
||||||
#include "throne_comm.h"
|
#include "throne_comm.h"
|
||||||
|
|
||||||
@@ -40,7 +38,7 @@ static int uid_from_um_list(struct list_head *uid_list)
|
|||||||
ssize_t nr;
|
ssize_t nr;
|
||||||
int cnt = 0;
|
int cnt = 0;
|
||||||
|
|
||||||
fp = ksu_filp_open_compat(KSU_UID_LIST_PATH, O_RDONLY, 0);
|
fp = filp_open(KSU_UID_LIST_PATH, O_RDONLY, 0);
|
||||||
if (IS_ERR(fp))
|
if (IS_ERR(fp))
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
@@ -57,7 +55,7 @@ static int uid_from_um_list(struct list_head *uid_list)
|
|||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
nr = ksu_kernel_read_compat(fp, buf, size, &pos);
|
nr = kernel_read(fp, buf, size, &pos);
|
||||||
filp_close(fp, NULL);
|
filp_close(fp, NULL);
|
||||||
if (nr != size) {
|
if (nr != size) {
|
||||||
pr_err("uid_list: short read %zd/%lld\n", nr, size);
|
pr_err("uid_list: short read %zd/%lld\n", nr, size);
|
||||||
@@ -235,7 +233,6 @@ struct my_dir_context {
|
|||||||
#define FILLDIR_ACTOR_CONTINUE 0
|
#define FILLDIR_ACTOR_CONTINUE 0
|
||||||
#define FILLDIR_ACTOR_STOP -EINVAL
|
#define FILLDIR_ACTOR_STOP -EINVAL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||||
int namelen, loff_t off, u64 ino,
|
int namelen, loff_t off, u64 ino,
|
||||||
unsigned int d_type)
|
unsigned int d_type)
|
||||||
@@ -271,7 +268,7 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
|||||||
|
|
||||||
if (d_type == DT_DIR && my_ctx->depth > 0 &&
|
if (d_type == DT_DIR && my_ctx->depth > 0 &&
|
||||||
(my_ctx->stop && !*my_ctx->stop)) {
|
(my_ctx->stop && !*my_ctx->stop)) {
|
||||||
struct data_path *data = kmalloc(sizeof(struct data_path), GFP_ATOMIC);
|
struct data_path *data = kzalloc(sizeof(struct data_path), GFP_ATOMIC);
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
pr_err("Failed to allocate memory for %s\n", dirpath);
|
pr_err("Failed to allocate memory for %s\n", dirpath);
|
||||||
@@ -306,29 +303,24 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
|||||||
// Check for dynamic sign or multi-manager signatures
|
// Check for dynamic sign or multi-manager signatures
|
||||||
if (is_multi_manager && (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2)) {
|
if (is_multi_manager && (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2)) {
|
||||||
crown_manager(dirpath, my_ctx->private_data, signature_index);
|
crown_manager(dirpath, my_ctx->private_data, signature_index);
|
||||||
|
} else if (is_manager_apk(dirpath)) {
|
||||||
|
crown_manager(dirpath, my_ctx->private_data, 0);
|
||||||
|
*my_ctx->stop = 1;
|
||||||
|
}
|
||||||
|
|
||||||
struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC);
|
struct apk_path_hash *apk_data = kzalloc(sizeof(*apk_data), GFP_ATOMIC);
|
||||||
if (apk_data) {
|
if (apk_data) {
|
||||||
apk_data->hash = hash;
|
apk_data->hash = hash;
|
||||||
apk_data->exists = true;
|
apk_data->exists = true;
|
||||||
list_add_tail(&apk_data->list, &apk_path_hash_list);
|
list_add_tail(&apk_data->list, &apk_path_hash_list);
|
||||||
}
|
}
|
||||||
} else if (is_manager_apk(dirpath)) {
|
|
||||||
crown_manager(dirpath, my_ctx->private_data, 0);
|
|
||||||
*my_ctx->stop = 1;
|
|
||||||
|
|
||||||
|
if (is_manager_apk(dirpath)) {
|
||||||
// Manager found, clear APK cache list
|
// Manager found, clear APK cache list
|
||||||
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
|
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
|
||||||
list_del(&pos->list);
|
list_del(&pos->list);
|
||||||
kfree(pos);
|
kfree(pos);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC);
|
|
||||||
if (apk_data) {
|
|
||||||
apk_data->hash = hash;
|
|
||||||
apk_data->exists = true;
|
|
||||||
list_add_tail(&apk_data->list, &apk_path_hash_list);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -345,7 +337,7 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
|||||||
|
|
||||||
// Initialize APK cache list
|
// Initialize APK cache list
|
||||||
struct apk_path_hash *pos, *n;
|
struct apk_path_hash *pos, *n;
|
||||||
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
list_for_each_entry (pos, &apk_path_hash_list, list) {
|
||||||
pos->exists = false;
|
pos->exists = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -358,7 +350,7 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
|||||||
for (i = depth; i >= 0; i--) {
|
for (i = depth; i >= 0; i--) {
|
||||||
struct data_path *pos, *n;
|
struct data_path *pos, *n;
|
||||||
|
|
||||||
list_for_each_entry_safe(pos, n, &data_path_list, list) {
|
list_for_each_entry_safe (pos, n, &data_path_list, list) {
|
||||||
struct my_dir_context ctx = { .ctx.actor = my_actor,
|
struct my_dir_context ctx = { .ctx.actor = my_actor,
|
||||||
.data_path_list = &data_path_list,
|
.data_path_list = &data_path_list,
|
||||||
.parent_dir = pos->dirpath,
|
.parent_dir = pos->dirpath,
|
||||||
@@ -368,9 +360,10 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
|||||||
struct file *file;
|
struct file *file;
|
||||||
|
|
||||||
if (!stop) {
|
if (!stop) {
|
||||||
file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0);
|
file = filp_open(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0);
|
||||||
if (IS_ERR(file)) {
|
if (IS_ERR(file)) {
|
||||||
pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file));
|
pr_err("Failed to open directory: %s, err: %ld\n",
|
||||||
|
pos->dirpath, PTR_ERR(file));
|
||||||
goto skip_iterate;
|
goto skip_iterate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -378,7 +371,8 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
|||||||
if (!data_app_magic) {
|
if (!data_app_magic) {
|
||||||
if (file->f_inode->i_sb->s_magic) {
|
if (file->f_inode->i_sb->s_magic) {
|
||||||
data_app_magic = file->f_inode->i_sb->s_magic;
|
data_app_magic = file->f_inode->i_sb->s_magic;
|
||||||
pr_info("%s: dir: %s got magic! 0x%lx\n", __func__, pos->dirpath, data_app_magic);
|
pr_info("%s: dir: %s got magic! 0x%lx\n", __func__,
|
||||||
|
pos->dirpath, data_app_magic);
|
||||||
} else {
|
} else {
|
||||||
filp_close(file, NULL);
|
filp_close(file, NULL);
|
||||||
goto skip_iterate;
|
goto skip_iterate;
|
||||||
@@ -386,7 +380,8 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (file->f_inode->i_sb->s_magic != data_app_magic) {
|
if (file->f_inode->i_sb->s_magic != data_app_magic) {
|
||||||
pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n", __func__, pos->dirpath,
|
pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n",
|
||||||
|
__func__, pos->dirpath,
|
||||||
file->f_inode->i_sb->s_magic, data_app_magic);
|
file->f_inode->i_sb->s_magic, data_app_magic);
|
||||||
filp_close(file, NULL);
|
filp_close(file, NULL);
|
||||||
goto skip_iterate;
|
goto skip_iterate;
|
||||||
@@ -395,7 +390,7 @@ void search_manager(const char *path, int depth, struct list_head *uid_data)
|
|||||||
iterate_dir(file, &ctx.ctx);
|
iterate_dir(file, &ctx.ctx);
|
||||||
filp_close(file, NULL);
|
filp_close(file, NULL);
|
||||||
}
|
}
|
||||||
skip_iterate:
|
skip_iterate:
|
||||||
list_del(&pos->list);
|
list_del(&pos->list);
|
||||||
if (pos != &data)
|
if (pos != &data)
|
||||||
kfree(pos);
|
kfree(pos);
|
||||||
@@ -403,7 +398,7 @@ skip_iterate:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Remove stale cached APK entries
|
// Remove stale cached APK entries
|
||||||
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
|
list_for_each_entry_safe (pos, n, &apk_path_hash_list, list) {
|
||||||
if (!pos->exists) {
|
if (!pos->exists) {
|
||||||
list_del(&pos->list);
|
list_del(&pos->list);
|
||||||
kfree(pos);
|
kfree(pos);
|
||||||
@@ -427,7 +422,7 @@ static bool is_uid_exist(uid_t uid, char *package, void *data)
|
|||||||
return exist;
|
return exist;
|
||||||
}
|
}
|
||||||
|
|
||||||
void track_throne(void)
|
void track_throne(bool prune_only)
|
||||||
{
|
{
|
||||||
struct list_head uid_list;
|
struct list_head uid_list;
|
||||||
struct uid_data *np, *n;
|
struct uid_data *np, *n;
|
||||||
@@ -456,7 +451,7 @@ void track_throne(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
fp = ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
fp = filp_open(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n", __func__, PTR_ERR(fp));
|
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n", __func__, PTR_ERR(fp));
|
||||||
return;
|
return;
|
||||||
@@ -464,13 +459,13 @@ void track_throne(void)
|
|||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
ssize_t count =
|
ssize_t count =
|
||||||
ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
|
kernel_read(fp, &chr, sizeof(chr), &pos);
|
||||||
if (count != sizeof(chr))
|
if (count != sizeof(chr))
|
||||||
break;
|
break;
|
||||||
if (chr != '\n')
|
if (chr != '\n')
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
count = ksu_kernel_read_compat(fp, buf, sizeof(buf),
|
count = kernel_read(fp, buf, sizeof(buf),
|
||||||
&line_start);
|
&line_start);
|
||||||
struct uid_data *data =
|
struct uid_data *data =
|
||||||
kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
|
||||||
@@ -504,6 +499,9 @@ void track_throne(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
uid_ready:
|
uid_ready:
|
||||||
|
if (prune_only)
|
||||||
|
goto prune;
|
||||||
|
|
||||||
// first, check if manager_uid exist!
|
// first, check if manager_uid exist!
|
||||||
list_for_each_entry(np, &uid_list, list) {
|
list_for_each_entry(np, &uid_list, list) {
|
||||||
if (np->uid == current_manager_uid) {
|
if (np->uid == current_manager_uid) {
|
||||||
@@ -547,6 +545,7 @@ uid_ready:
|
|||||||
pr_info("Manager search finished\n");
|
pr_info("Manager search finished\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
prune:
|
||||||
// then prune the allowlist
|
// then prune the allowlist
|
||||||
ksu_prune_allowlist(is_uid_exist, &uid_list);
|
ksu_prune_allowlist(is_uid_exist, &uid_list);
|
||||||
out:
|
out:
|
||||||
|
|||||||
@@ -5,6 +5,6 @@ void ksu_throne_tracker_init(void);
|
|||||||
|
|
||||||
void ksu_throne_tracker_exit(void);
|
void ksu_throne_tracker_exit(void);
|
||||||
|
|
||||||
void track_throne(void);
|
void track_throne(bool prune_only);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Reference in New Issue
Block a user