kernel, manager: Track upstream changes (#195)
* These commits are carefully picked from upstream (tiann/KernelSU)
- Picked range:
8c5f485f27..e5f43a3427
Signed-off-by: Faris <rissu.ntk@gmail.com>
Co-authored-by: Wang Han <416810799@qq.com>
Co-authored-by: TwinbornPlate75 <3342733415@qq.com>
Co-authored-by: KOWX712 <leecc0503@gmail.com>
Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com>
Co-authored-by: backslashxx <118538522+backslashxx@users.noreply.github.com>
Co-authored-by: 5ec1cff <56485584+5ec1cff@users.noreply.github.com>
Co-authored-by: weishu <twsxtd@gmail.com>
This commit is contained in:
22
kernel/.gitignore
vendored
Normal file
22
kernel/.gitignore
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
.cache/
|
||||
.thinlto-cache/
|
||||
compile_commands.json
|
||||
*.ko
|
||||
*.o
|
||||
*.mod
|
||||
*.lds
|
||||
*.mod.o
|
||||
.*.o*
|
||||
.*.mod*
|
||||
*.ko*
|
||||
*.mod.c
|
||||
*.symvers*
|
||||
*.order
|
||||
.*.ko.cmd
|
||||
.tmp_versions/
|
||||
libs/
|
||||
obj/
|
||||
|
||||
CLAUDE.md
|
||||
.ddk-version
|
||||
.vscode/settings.json
|
||||
@@ -118,10 +118,15 @@ endif
|
||||
ifeq ($(shell grep -q "inode_security_struct\s\+\*selinux_inode" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
||||
ccflags-y += -DKSU_OPTIONAL_SELINUX_INODE
|
||||
endif
|
||||
ifeq ($(shell grep -q "task_security_struct\s\+\*selinux_cred" $(srctree)/security/selinux/include/objsec.h; echo $$?),0)
|
||||
ccflags-y += -DKSU_OPTIONAL_SELINUX_CRED
|
||||
endif
|
||||
|
||||
# Check if __poll_t is available
|
||||
ifeq ($(shell grep -q "__poll_t" $(srctree)/include/linux/poll.h; echo $$?),1)
|
||||
ccflags-y += -DKSU_NEED_POLL_T_DEF
|
||||
# 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)
|
||||
@@ -178,7 +183,7 @@ else
|
||||
$(info -- KPM is disabled)
|
||||
endif
|
||||
|
||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||
ccflags-y += -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function -Wno-missing-prototypes
|
||||
|
||||
# Keep a new line here!! Because someone may append config
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/task_work.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/fs.h>
|
||||
@@ -8,6 +10,11 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
|
||||
#include <linux/sched/task.h>
|
||||
#else
|
||||
#include <linux/sched.h>
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||
#include <linux/compiler_types.h>
|
||||
#endif
|
||||
@@ -93,10 +100,7 @@ static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
|
||||
|
||||
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
|
||||
|
||||
static struct work_struct ksu_save_work;
|
||||
static struct work_struct ksu_load_work;
|
||||
|
||||
bool persistent_allow_list(void);
|
||||
void persistent_allow_list(void);
|
||||
|
||||
void ksu_show_allow_list(void)
|
||||
{
|
||||
@@ -231,9 +235,9 @@ out:
|
||||
} else {
|
||||
if (profile->allow_su) {
|
||||
/*
|
||||
* 1024 apps with uid higher than BITMAP_UID_MAX
|
||||
* registered to request superuser?
|
||||
*/
|
||||
* 1024 apps with uid higher than BITMAP_UID_MAX
|
||||
* registered to request superuser?
|
||||
*/
|
||||
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
|
||||
pr_err("too many apps registered\n");
|
||||
WARN_ON(1);
|
||||
@@ -270,11 +274,6 @@ bool __ksu_is_allow_uid(uid_t uid)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (unlikely(uid == 0)) {
|
||||
// already root, but only allow our domain.
|
||||
return is_ksu_domain();
|
||||
}
|
||||
|
||||
if (forbid_system_uid(uid)) {
|
||||
// do not bother going through the list if it's system
|
||||
return false;
|
||||
@@ -299,6 +298,15 @@ bool __ksu_is_allow_uid(uid_t uid)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool __ksu_is_allow_uid_for_current(uid_t uid)
|
||||
{
|
||||
if (unlikely(uid == 0)) {
|
||||
// already root, but only allow our domain.
|
||||
return is_ksu_domain();
|
||||
}
|
||||
return __ksu_is_allow_uid(uid);
|
||||
}
|
||||
|
||||
bool ksu_uid_should_umount(uid_t uid)
|
||||
{
|
||||
struct app_profile profile = { .current_uid = uid };
|
||||
@@ -360,7 +368,7 @@ bool ksu_get_allow_list(int *array, int *length, bool allow)
|
||||
return true;
|
||||
}
|
||||
|
||||
void do_save_allow_list(struct work_struct *work)
|
||||
static void do_persistent_allow_list(struct callback_head *_cb)
|
||||
{
|
||||
u32 magic = FILE_MAGIC;
|
||||
u32 version = FILE_FORMAT_VERSION;
|
||||
@@ -368,11 +376,13 @@ void do_save_allow_list(struct work_struct *work)
|
||||
struct list_head *pos = NULL;
|
||||
loff_t off = 0;
|
||||
|
||||
mutex_lock(&allowlist_mutex);
|
||||
struct file *fp = ksu_filp_open_compat(
|
||||
KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("save_allow_list create file failed: %ld\n",
|
||||
PTR_ERR(fp));
|
||||
mutex_unlock(&allowlist_mutex);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -391,7 +401,7 @@ void do_save_allow_list(struct work_struct *work)
|
||||
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
pr_info("save allow list, name: %s uid: %d, allow: %d\n",
|
||||
pr_info("save allow list, name: %s uid :%d, allow: %d\n",
|
||||
p->profile.key, p->profile.current_uid,
|
||||
p->profile.allow_su);
|
||||
|
||||
@@ -401,9 +411,38 @@ void do_save_allow_list(struct work_struct *work)
|
||||
|
||||
exit:
|
||||
filp_close(fp, 0);
|
||||
mutex_unlock(&allowlist_mutex);
|
||||
}
|
||||
|
||||
void do_load_allow_list(struct work_struct *work)
|
||||
void persistent_allow_list(void)
|
||||
{
|
||||
struct task_struct *tsk;
|
||||
|
||||
tsk = get_pid_task(find_vpid(1), PIDTYPE_PID);
|
||||
if (!tsk) {
|
||||
pr_err("save_allow_list find init task err\n");
|
||||
return;
|
||||
}
|
||||
|
||||
struct callback_head *cb =
|
||||
kzalloc(sizeof(struct callback_head), GFP_KERNEL);
|
||||
if (!cb) {
|
||||
pr_err("save_allow_list alloc cb err\b");
|
||||
goto put_task;
|
||||
}
|
||||
cb->func = do_persistent_allow_list;
|
||||
|
||||
#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_struct(tsk);
|
||||
}
|
||||
|
||||
void ksu_load_allow_list(void)
|
||||
{
|
||||
loff_t off = 0;
|
||||
ssize_t ret = 0;
|
||||
@@ -494,17 +533,6 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *),
|
||||
}
|
||||
}
|
||||
|
||||
// make sure allow list works cross boot
|
||||
bool persistent_allow_list(void)
|
||||
{
|
||||
return ksu_queue_work(&ksu_save_work);
|
||||
}
|
||||
|
||||
bool ksu_load_allow_list(void)
|
||||
{
|
||||
return ksu_queue_work(&ksu_load_work);
|
||||
}
|
||||
|
||||
void ksu_allowlist_init(void)
|
||||
{
|
||||
int i;
|
||||
@@ -517,9 +545,6 @@ void ksu_allowlist_init(void)
|
||||
|
||||
INIT_LIST_HEAD(&allow_list);
|
||||
|
||||
INIT_WORK(&ksu_save_work, do_save_allow_list);
|
||||
INIT_WORK(&ksu_load_work, do_load_allow_list);
|
||||
|
||||
init_default_profiles();
|
||||
}
|
||||
|
||||
@@ -528,7 +553,7 @@ void ksu_allowlist_exit(void)
|
||||
struct perm_data *np = NULL;
|
||||
struct perm_data *n = NULL;
|
||||
|
||||
do_save_allow_list(NULL);
|
||||
persistent_allow_list();
|
||||
|
||||
// free allowlist
|
||||
mutex_lock(&allowlist_mutex);
|
||||
|
||||
@@ -8,13 +8,19 @@ void ksu_allowlist_init(void);
|
||||
|
||||
void ksu_allowlist_exit(void);
|
||||
|
||||
bool ksu_load_allow_list(void);
|
||||
void ksu_load_allow_list(void);
|
||||
|
||||
void ksu_show_allow_list(void);
|
||||
|
||||
// Check if the uid is in allow list
|
||||
bool __ksu_is_allow_uid(uid_t uid);
|
||||
#define ksu_is_allow_uid(uid) unlikely(__ksu_is_allow_uid(uid))
|
||||
|
||||
// Check if the uid is in allow list, or current is ksu domain root
|
||||
bool __ksu_is_allow_uid_for_current(uid_t uid);
|
||||
#define ksu_is_allow_uid_for_current(uid) \
|
||||
unlikely(__ksu_is_allow_uid_for_current(uid))
|
||||
|
||||
bool ksu_get_allow_list(int *array, int *length, bool allow);
|
||||
|
||||
void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, char *, void *),
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
#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>
|
||||
@@ -22,7 +29,6 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/uidgid.h>
|
||||
#include <linux/version.h>
|
||||
#ifndef KSU_HAS_PATH_UMOUNT
|
||||
#include <linux/syscalls.h> // sys_umount (<4.17) & ksys_umount (4.17+)
|
||||
#endif
|
||||
@@ -42,6 +48,7 @@
|
||||
#include "throne_comm.h"
|
||||
#include "kernel_compat.h"
|
||||
#include "supercalls.h"
|
||||
#include "sucompat.h"
|
||||
|
||||
bool ksu_module_mounted __read_mostly = false;
|
||||
|
||||
@@ -56,6 +63,7 @@ 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)
|
||||
{
|
||||
@@ -78,13 +86,34 @@ static const struct ksu_feature_handler kernel_umount_handler = {
|
||||
.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(current_uid().val);
|
||||
return ksu_is_allow_uid_for_current(current_uid().val);
|
||||
}
|
||||
|
||||
static inline bool is_unsupported_uid(uid_t uid)
|
||||
@@ -179,6 +208,10 @@ static void disable_seccomp(struct task_struct *tsk)
|
||||
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) {
|
||||
@@ -229,8 +262,15 @@ void escape_to_root(void)
|
||||
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
|
||||
@@ -244,12 +284,13 @@ void nuke_ext4_sysfs(void)
|
||||
struct super_block *sb = path.dentry->d_inode->i_sb;
|
||||
const char *name = sb->s_type->name;
|
||||
if (strcmp(name, "ext4") != 0) {
|
||||
pr_info("skipping s_type: %s\n", name);
|
||||
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\n");
|
||||
path_put(&path);
|
||||
#endif
|
||||
}
|
||||
@@ -285,6 +326,7 @@ static bool should_umount(struct path *path)
|
||||
|
||||
#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)
|
||||
@@ -384,6 +426,14 @@ static void umount_tw_func(struct callback_head *cb)
|
||||
}
|
||||
#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) {
|
||||
@@ -396,9 +446,39 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
|
||||
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;
|
||||
@@ -417,10 +497,11 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
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(new_uid.val)) {
|
||||
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);
|
||||
@@ -428,9 +509,17 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
__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(new_uid.val)) {
|
||||
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);
|
||||
@@ -465,7 +554,7 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
||||
// 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->security)) {
|
||||
if (!is_zygote(old)) {
|
||||
pr_info("handle umount ignore non zygote child: %d\n",
|
||||
current->pid);
|
||||
return 0;
|
||||
@@ -706,6 +795,10 @@ void __init ksu_core_init(void)
|
||||
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)
|
||||
@@ -723,13 +816,20 @@ void __init ksu_core_init(void)
|
||||
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
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
enum ksu_feature_id {
|
||||
KSU_FEATURE_SU_COMPAT = 0,
|
||||
KSU_FEATURE_KERNEL_UMOUNT = 1,
|
||||
KSU_FEATURE_ENHANCED_SECURITY = 2,
|
||||
|
||||
KSU_FEATURE_MAX
|
||||
};
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <linux/errno.h>
|
||||
#include <linux/cred.h>
|
||||
|
||||
extern int install_session_keyring_to_cred(struct cred *, struct key *);
|
||||
struct key *init_session_keyring = NULL;
|
||||
static inline int install_session_keyring(struct key *keyring)
|
||||
{
|
||||
@@ -38,50 +39,6 @@ static inline int install_session_keyring(struct key *keyring)
|
||||
}
|
||||
#endif
|
||||
|
||||
extern struct task_struct init_task;
|
||||
|
||||
// mnt_ns context switch for environment that android_init->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns, such as WSA
|
||||
struct ksu_ns_fs_saved {
|
||||
struct nsproxy *ns;
|
||||
struct fs_struct *fs;
|
||||
};
|
||||
|
||||
static void ksu_save_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
||||
{
|
||||
ns_fs_saved->ns = current->nsproxy;
|
||||
ns_fs_saved->fs = current->fs;
|
||||
}
|
||||
|
||||
static void ksu_load_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
||||
{
|
||||
current->nsproxy = ns_fs_saved->ns;
|
||||
current->fs = ns_fs_saved->fs;
|
||||
}
|
||||
|
||||
static bool android_context_saved_checked = false;
|
||||
static bool android_context_saved_enabled = false;
|
||||
static struct ksu_ns_fs_saved android_context_saved;
|
||||
|
||||
void ksu_android_ns_fs_check(void)
|
||||
{
|
||||
if (android_context_saved_checked)
|
||||
return;
|
||||
android_context_saved_checked = true;
|
||||
task_lock(current);
|
||||
if (current->nsproxy && current->fs &&
|
||||
current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) {
|
||||
android_context_saved_enabled = true;
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n",
|
||||
current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns);
|
||||
#endif
|
||||
ksu_save_ns_fs(&android_context_saved);
|
||||
} else {
|
||||
pr_info("android context saved disabled\n");
|
||||
}
|
||||
task_unlock(current);
|
||||
}
|
||||
|
||||
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
||||
{
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||
@@ -92,27 +49,7 @@ struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
||||
install_session_keyring(init_session_keyring);
|
||||
}
|
||||
#endif
|
||||
// switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns
|
||||
struct ksu_ns_fs_saved saved;
|
||||
if (android_context_saved_enabled) {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("start switch current nsproxy and fs to android context\n");
|
||||
#endif
|
||||
task_lock(current);
|
||||
ksu_save_ns_fs(&saved);
|
||||
ksu_load_ns_fs(&android_context_saved);
|
||||
task_unlock(current);
|
||||
}
|
||||
struct file *fp = filp_open(filename, flags, mode);
|
||||
if (android_context_saved_enabled) {
|
||||
task_lock(current);
|
||||
ksu_load_ns_fs(&saved);
|
||||
task_unlock(current);
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("switch current nsproxy and fs back to saved successfully\n");
|
||||
#endif
|
||||
}
|
||||
return fp;
|
||||
return filp_open(filename, flags, mode);
|
||||
}
|
||||
|
||||
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||
@@ -300,4 +237,4 @@ void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr)
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -27,6 +27,10 @@
|
||||
#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,
|
||||
const void __user *unsafe_addr,
|
||||
long count);
|
||||
@@ -38,7 +42,7 @@ extern long ksu_strncpy_from_user_retry(char *dst,
|
||||
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||
extern struct key *init_session_keyring;
|
||||
#endif
|
||||
extern void ksu_android_ns_fs_check(void);
|
||||
|
||||
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
|
||||
umode_t mode);
|
||||
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||
|
||||
21
kernel/ksu.c
21
kernel/ksu.c
@@ -5,7 +5,6 @@
|
||||
#include <generated/utsrelease.h>
|
||||
#include <generated/compile.h>
|
||||
#include <linux/version.h> /* LINUX_VERSION_CODE, KERNEL_VERSION macros */
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "arch.h"
|
||||
@@ -18,14 +17,10 @@
|
||||
#include "ksud.h"
|
||||
#include "supercalls.h"
|
||||
|
||||
static struct workqueue_struct *ksu_workqueue;
|
||||
|
||||
bool ksu_queue_work(struct work_struct *work)
|
||||
{
|
||||
return queue_work(ksu_workqueue, work);
|
||||
}
|
||||
|
||||
|
||||
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags);
|
||||
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags);
|
||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||
void *envp, int *flags)
|
||||
{
|
||||
@@ -55,17 +50,13 @@ int __init kernelsu_init(void)
|
||||
|
||||
ksu_core_init();
|
||||
|
||||
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
||||
|
||||
ksu_allowlist_init();
|
||||
|
||||
ksu_throne_tracker_init();
|
||||
|
||||
ksu_sucompat_init();
|
||||
|
||||
#ifdef KSU_KPROBES_HOOK
|
||||
ksu_ksud_init();
|
||||
#endif
|
||||
|
||||
#ifdef MODULE
|
||||
#ifndef CONFIG_KSU_DEBUG
|
||||
@@ -75,6 +66,7 @@ int __init kernelsu_init(void)
|
||||
return 0;
|
||||
}
|
||||
|
||||
extern void ksu_observer_exit(void);
|
||||
void kernelsu_exit(void)
|
||||
{
|
||||
ksu_allowlist_exit();
|
||||
@@ -85,13 +77,12 @@ void kernelsu_exit(void)
|
||||
|
||||
destroy_workqueue(ksu_workqueue);
|
||||
|
||||
#ifdef KSU_KPROBES_HOOK
|
||||
ksu_ksud_exit();
|
||||
#endif
|
||||
|
||||
ksu_sucompat_exit();
|
||||
|
||||
ksu_core_exit();
|
||||
|
||||
ksu_feature_exit();
|
||||
}
|
||||
|
||||
|
||||
@@ -95,8 +95,7 @@ struct app_profile {
|
||||
};
|
||||
};
|
||||
|
||||
bool ksu_queue_work(struct work_struct *work);
|
||||
|
||||
#if 0
|
||||
static inline int startswith(char *s, char *prefix)
|
||||
{
|
||||
return strncmp(s, prefix, strlen(prefix));
|
||||
@@ -110,5 +109,6 @@ static inline int endswith(const char *s, const char *t)
|
||||
return 1;
|
||||
return strcmp(s + slen - tlen, t);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,3 +1,6 @@
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/task_work.h>
|
||||
#include <asm/current.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/cred.h>
|
||||
@@ -26,6 +29,8 @@
|
||||
#include "ksud.h"
|
||||
#include "kernel_compat.h"
|
||||
#include "selinux/selinux.h"
|
||||
#include "manager.h"
|
||||
#include "sucompat.h"
|
||||
|
||||
static const char KERNEL_SU_RC[] =
|
||||
"\n"
|
||||
@@ -79,6 +84,7 @@ void on_post_fs_data(void)
|
||||
done = true;
|
||||
pr_info("%s!\n", __func__);
|
||||
ksu_load_allow_list();
|
||||
ksu_mark_running_process();
|
||||
ksu_observer_init();
|
||||
// sanity check, this may influence the performance
|
||||
stop_input_hook();
|
||||
@@ -102,6 +108,13 @@ struct user_arg_ptr {
|
||||
} ptr;
|
||||
};
|
||||
|
||||
static void on_post_fs_data_cbfun(struct callback_head *cb)
|
||||
{
|
||||
on_post_fs_data();
|
||||
}
|
||||
|
||||
static struct callback_head on_post_fs_data_cb = { .func = on_post_fs_data_cbfun };
|
||||
|
||||
// since _ksud handler only uses argv and envp for comparisons
|
||||
// this can probably work
|
||||
// adapted from ksu_handle_execveat_ksud
|
||||
@@ -146,7 +159,6 @@ static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const c
|
||||
pr_info("%s: /system/bin/init second_stage executed\n", __func__);
|
||||
apply_kernelsu_rules();
|
||||
init_second_stage_executed = true;
|
||||
ksu_android_ns_fs_check();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +169,6 @@ static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const c
|
||||
pr_info("%s: /init --second-stage executed\n", __func__);
|
||||
apply_kernelsu_rules();
|
||||
init_second_stage_executed = true;
|
||||
ksu_android_ns_fs_check();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,15 +195,25 @@ static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const c
|
||||
pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__);
|
||||
apply_kernelsu_rules();
|
||||
init_second_stage_executed = true;
|
||||
ksu_android_ns_fs_check();
|
||||
}
|
||||
}
|
||||
|
||||
first_app_process:
|
||||
if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) {
|
||||
first_app_process = false;
|
||||
pr_info("%s: exec app_process, /data prepared, second_stage: %d\n", __func__, init_second_stage_executed);
|
||||
on_post_fs_data();
|
||||
pr_info("exec app_process, /data prepared, second_stage: %d\n",
|
||||
init_second_stage_executed);
|
||||
struct task_struct *init_task;
|
||||
rcu_read_lock();
|
||||
init_task = rcu_dereference(current->real_parent);
|
||||
if (init_task) {
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 8)
|
||||
task_work_add(init_task, &on_post_fs_data_cb, TWA_RESUME);
|
||||
#else
|
||||
task_work_add(init_task, &on_post_fs_data_cb, true);
|
||||
#endif
|
||||
}
|
||||
rcu_read_unlock();
|
||||
stop_execve_hook();
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,4 @@ static inline void ksu_invalidate_manager_uid(void)
|
||||
}
|
||||
|
||||
int ksu_observer_init(void);
|
||||
void ksu_observer_exit(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -186,6 +186,13 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \
|
||||
!defined(KSU_COMPAT_USE_SELINUX_STATE)
|
||||
extern int avc_ss_reset(u32 seqno);
|
||||
#else
|
||||
extern int avc_ss_reset(struct selinux_avc *avc, u32 seqno);
|
||||
#endif
|
||||
|
||||
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
||||
static void reset_avc_cache(void)
|
||||
{
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#include <linux/version.h>
|
||||
#include "linux/cred.h"
|
||||
#include "linux/sched.h"
|
||||
#include "linux/security.h"
|
||||
#include "linux/version.h"
|
||||
#include "selinux_defs.h"
|
||||
#include "../klog.h" // IWYU pragma: keep
|
||||
|
||||
@@ -24,14 +27,12 @@ static int transive_to_domain(const char *domain)
|
||||
pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n",
|
||||
domain, sid, error);
|
||||
}
|
||||
|
||||
if (!error) {
|
||||
tsec->sid = sid;
|
||||
tsec->create_sid = 0;
|
||||
tsec->keycreate_sid = 0;
|
||||
tsec->sockcreate_sid = 0;
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -93,65 +94,68 @@ static inline u32 current_sid(void)
|
||||
}
|
||||
#endif
|
||||
|
||||
bool is_ksu_domain(void)
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 14, 0)
|
||||
struct lsm_context {
|
||||
char *context;
|
||||
u32 len;
|
||||
};
|
||||
|
||||
static int __security_secid_to_secctx(u32 secid, struct lsm_context *cp)
|
||||
{
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
||||
struct lsm_context ctx;
|
||||
return security_secid_to_secctx(secid, &cp->context, &cp->len);
|
||||
}
|
||||
static void __security_release_secctx(struct lsm_context *cp)
|
||||
{
|
||||
return security_release_secctx(cp->context, cp->len);
|
||||
}
|
||||
#else
|
||||
char *domain;
|
||||
u32 seclen;
|
||||
#endif
|
||||
bool result;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
||||
int err = security_secid_to_secctx(current_sid(), &ctx);
|
||||
#else
|
||||
int err = security_secid_to_secctx(current_sid(), &domain, &seclen);
|
||||
#define __security_secid_to_secctx security_secid_to_secctx
|
||||
#define __security_release_secctx security_release_secctx
|
||||
#endif
|
||||
|
||||
if (err) {
|
||||
bool is_task_ksu_domain(const struct cred *cred)
|
||||
{
|
||||
struct lsm_context ctx;
|
||||
bool result;
|
||||
if (!cred) {
|
||||
return false;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
||||
result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0;
|
||||
security_release_secctx(&ctx);
|
||||
#else
|
||||
result = strncmp(KERNEL_SU_DOMAIN, domain, seclen) == 0;
|
||||
security_release_secctx(domain, seclen);
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_zygote(void *sec)
|
||||
{
|
||||
struct task_security_struct *tsec = (struct task_security_struct *)sec;
|
||||
const struct task_security_struct *tsec = __selinux_cred(cred);
|
||||
if (!tsec) {
|
||||
return false;
|
||||
}
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
||||
struct lsm_context ctx;
|
||||
#else
|
||||
char *domain;
|
||||
u32 seclen;
|
||||
#endif
|
||||
bool result;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
||||
int err = security_secid_to_secctx(tsec->sid, &ctx);
|
||||
#else
|
||||
int err = security_secid_to_secctx(tsec->sid, &domain, &seclen);
|
||||
#endif
|
||||
int err = __security_secid_to_secctx(tsec->sid, &ctx);
|
||||
if (err) {
|
||||
return false;
|
||||
}
|
||||
result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0;
|
||||
__security_release_secctx(&ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
||||
bool is_ksu_domain(void)
|
||||
{
|
||||
current_sid();
|
||||
return is_task_ksu_domain(current_cred());
|
||||
}
|
||||
|
||||
bool is_zygote(const struct cred *cred)
|
||||
{
|
||||
if (!cred) {
|
||||
return false;
|
||||
}
|
||||
const struct task_security_struct *tsec = __selinux_cred(cred);
|
||||
if (!tsec) {
|
||||
return false;
|
||||
}
|
||||
struct lsm_context ctx;
|
||||
bool result;
|
||||
int err = __security_secid_to_secctx(tsec->sid, &ctx);
|
||||
if (err) {
|
||||
return false;
|
||||
}
|
||||
result = strncmp("u:r:zygote:s0", ctx.context, ctx.len) == 0;
|
||||
security_release_secctx(&ctx);
|
||||
#else
|
||||
result = strncmp("u:r:zygote:s0", domain, seclen) == 0;
|
||||
security_release_secctx(domain, seclen);
|
||||
#endif
|
||||
__security_release_secctx(&ctx);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
#ifndef __KSU_H_SELINUX
|
||||
#define __KSU_H_SELINUX
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/version.h>
|
||||
#include "linux/types.h"
|
||||
#include "linux/version.h"
|
||||
#include "linux/sched.h"
|
||||
|
||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || \
|
||||
defined(KSU_COMPAT_HAS_SELINUX_STATE)
|
||||
@@ -13,11 +14,13 @@ void setup_selinux(const char *);
|
||||
|
||||
void setenforce(bool);
|
||||
|
||||
bool is_task_ksu_domain(const struct cred *cred);
|
||||
|
||||
bool getenforce(void);
|
||||
|
||||
bool is_ksu_domain(void);
|
||||
|
||||
bool is_zygote(void *cred);
|
||||
bool is_zygote(const struct cred *cred);
|
||||
|
||||
void apply_kernelsu_rules(void);
|
||||
|
||||
|
||||
@@ -33,4 +33,10 @@
|
||||
#define __setenforce(val)
|
||||
#endif
|
||||
|
||||
#ifdef KSU_OPTIONAL_SELINUX_CRED
|
||||
#define __selinux_cred(cred) (selinux_cred(cred))
|
||||
#else
|
||||
#define __selinux_cred(cred) (cred->security)
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -30,8 +30,80 @@
|
||||
|
||||
static const char su[] = SU_PATH;
|
||||
static const char ksud_path[] = KSUD_PATH;
|
||||
bool ksu_su_compat_enabled __read_mostly = true;
|
||||
|
||||
static bool ksu_su_compat_enabled __read_mostly = true;
|
||||
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||
|
||||
#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)
|
||||
{
|
||||
@@ -93,7 +165,7 @@ static inline bool __is_su_allowed(const void *ptr_to_check)
|
||||
if (!ksu_su_compat_enabled)
|
||||
return false;
|
||||
#endif
|
||||
if (likely(!ksu_is_allow_uid(current_uid().val)))
|
||||
if (!ksu_is_allow_uid_for_current(current_uid().val))
|
||||
return false;
|
||||
|
||||
if (unlikely(!ptr_to_check))
|
||||
@@ -209,86 +281,150 @@ int __ksu_handle_devpts(struct inode *inode)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef KSU_KPROBES_HOOK
|
||||
#ifdef KSU_SHOULD_USE_NEW_TP
|
||||
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
||||
|
||||
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
// Tracepoint probe for sys_enter
|
||||
static void sucompat_sys_enter_handler(void *data, struct pt_regs *regs,
|
||||
long id)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||
const char __user **filename_user =
|
||||
(const char **)&PT_REGS_PARM2(real_regs);
|
||||
int *mode = (int *)&PT_REGS_PARM3(real_regs);
|
||||
// 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;
|
||||
}
|
||||
|
||||
return ksu_handle_faccessat(dfd, filename_user, mode, NULL);
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
#endif // CONFIG_HAVE_SYSCALL_TRACEPOINTS
|
||||
|
||||
#ifdef CONFIG_KRETPROBES
|
||||
|
||||
static struct kretprobe *init_kretprobe(const char *name,
|
||||
kretprobe_handler_t handler)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||
const char __user **filename_user =
|
||||
(const char **)&PT_REGS_PARM2(real_regs);
|
||||
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs);
|
||||
|
||||
return ksu_handle_stat(dfd, filename_user, flags);
|
||||
}
|
||||
|
||||
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
const char __user **filename_user =
|
||||
(const char **)&PT_REGS_PARM1(real_regs);
|
||||
|
||||
return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL,
|
||||
NULL);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
static struct kprobe *su_kps[5];
|
||||
#else
|
||||
static struct kprobe *su_kps[3];
|
||||
#endif
|
||||
|
||||
static struct kprobe *init_kprobe(const char *name,
|
||||
kprobe_pre_handler_t handler)
|
||||
{
|
||||
struct kprobe *kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
|
||||
if (!kp)
|
||||
struct kretprobe *rp = kzalloc(sizeof(struct kretprobe), GFP_KERNEL);
|
||||
if (!rp)
|
||||
return NULL;
|
||||
kp->symbol_name = name;
|
||||
kp->pre_handler = handler;
|
||||
rp->kp.symbol_name = name;
|
||||
rp->handler = handler;
|
||||
rp->data_size = 0;
|
||||
rp->maxactive = 0;
|
||||
|
||||
int ret = register_kprobe(kp);
|
||||
pr_info("sucompat: register_%s kprobe: %d\n", name, ret);
|
||||
int ret = register_kretprobe(rp);
|
||||
pr_info("sucompat: register_%s kretprobe: %d\n", name, ret);
|
||||
if (ret) {
|
||||
kfree(kp);
|
||||
kfree(rp);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return kp;
|
||||
return rp;
|
||||
}
|
||||
|
||||
static void destroy_kprobe(struct kprobe **kp_ptr)
|
||||
static void destroy_kretprobe(struct kretprobe **rp_ptr)
|
||||
{
|
||||
struct kprobe *kp = *kp_ptr;
|
||||
if (!kp)
|
||||
struct kretprobe *rp = *rp_ptr;
|
||||
if (!rp)
|
||||
return;
|
||||
unregister_kprobe(kp);
|
||||
unregister_kretprobe(rp);
|
||||
synchronize_rcu();
|
||||
kfree(kp);
|
||||
*kp_ptr = NULL;
|
||||
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_KPROBES_HOOK
|
||||
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
||||
su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
|
||||
su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
|
||||
#ifdef CONFIG_COMPAT
|
||||
su_kps[3] = init_kprobe(SYS_EXECVE_COMPAT_SYMBOL, execve_handler_pre);
|
||||
su_kps[4] = init_kprobe(SYS_FSTATAT64_SYMBOL, newfstatat_handler_pre);
|
||||
#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;
|
||||
@@ -298,11 +434,18 @@ void ksu_sucompat_enable(void)
|
||||
|
||||
void ksu_sucompat_disable(void)
|
||||
{
|
||||
#ifdef KSU_KPROBES_HOOK
|
||||
int i;
|
||||
for (i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
||||
destroy_kprobe(&su_kps[i]);
|
||||
}
|
||||
#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");
|
||||
@@ -315,6 +458,7 @@ void ksu_sucompat_init(void)
|
||||
if (ksu_register_feature_handler(&su_compat_handler)) {
|
||||
pr_err("Failed to register su_compat feature handler\n");
|
||||
}
|
||||
|
||||
if (ksu_su_compat_enabled) {
|
||||
ksu_sucompat_enable();
|
||||
}
|
||||
|
||||
@@ -3,15 +3,47 @@
|
||||
#include <linux/sched.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
extern bool ksu_su_compat_enabled;
|
||||
|
||||
void ksu_sucompat_init(void);
|
||||
void ksu_sucompat_exit(void);
|
||||
|
||||
void ksu_sucompat_enable(void);
|
||||
void ksu_sucompat_disable(void);
|
||||
|
||||
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags);
|
||||
void ksu_mark_running_process(void);
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) && defined(KSU_KPROBE_HOOK)
|
||||
|
||||
#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
|
||||
|
||||
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags);
|
||||
#endif
|
||||
|
||||
@@ -50,8 +50,9 @@ bool always_allow(void)
|
||||
|
||||
bool allowed_for_su(void)
|
||||
{
|
||||
bool is_allowed = is_manager() || ksu_is_allow_uid_for_current(current_uid().val);
|
||||
return is_allowed;
|
||||
bool is_allowed =
|
||||
is_manager() || ksu_is_allow_uid_for_current(current_uid().val);
|
||||
return is_allowed;
|
||||
}
|
||||
|
||||
static void init_uid_scanner(void)
|
||||
@@ -219,7 +220,7 @@ static int do_uid_granted_root(void __user *arg)
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
cmd.granted = ksu_is_allow_uid_for_current(cmd.uid);
|
||||
cmd.granted = ksu_is_allow_uid_for_current(cmd.uid);
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("uid_granted_root: copy_to_user failed\n");
|
||||
@@ -677,7 +678,7 @@ int ksu_install_fd(void)
|
||||
// Install fd
|
||||
fd_install(fd, filp);
|
||||
|
||||
pr_info("ksu fd installed: %d for pid %d\n", fd, current->pid);
|
||||
pr_info("ksu fd[%d] installed for %s/%d\n", fd, current->comm, current->pid);
|
||||
|
||||
return fd;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "allowlist.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksu.h"
|
||||
#include "apk_sign.h"
|
||||
#include "manager.h"
|
||||
#include "throne_tracker.h"
|
||||
#include "kernel_compat.h"
|
||||
|
||||
Reference in New Issue
Block a user