new supercall impl (#511)
* refactor: replace throne tracker with ksud token
* use snprintf
* refactor: new supercall impl
- Import the sukisu command
* disable seccomp for supercall users
* kernel: fmt clear
* kernel: Enable macro protection for sulog
- Only enabled on kernel versions greater than 5.10.245
* kernel: Refactor kprobe hooks and implement LSM hooks for improved security handling
* debug mode
* kernel: Add functionality to generate and validate authentication tokens for cmd_su
* kernel: Simplified manual SU command processing for code
* kernel: replace renameat hook with fsnotify
* Revert "refactor: replace throne tracker with ksud token"
This reverts commit aa2cbbf9cd.
* kernel: fix compile
* kernel: fix compile below 6.0
* Fix compile err; Add become_manager
* kernel: install fd for manager automaticlly
- extend to import the corresponding command
* manager: new supercall impl
* temp changes for ksud
* ksud: fix compile
* fix wrong opcode
* kernel: fix compile
* kernel: Fixed hook type and KPM status retrieval errors
* kernel: Fixed potential null pointer issue with current->mm in kernel version 5.10
When calling get_full_comm() within system call hooks, current->mm may be null (prctl). A fallback mechanism for current->comm must be added beforehand to prevent null pointer dereferences when accessing mm->arg_start/arg_end.
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
* ksud: fix cargo check
* manager: Fixed an issue where the KSUD release and user-mode scanning switch failed to function correctly.
- kernel: fix spin lock mutual
kernel: Fixed potential null pointer issue with current->mm in kernel version 5.10
When calling get_full_comm() within system call hooks, current->mm may be null (prctl). A fallback mechanism for current->comm must be added beforehand to prevent null pointer dereferences when accessing mm->arg_start/arg_end.
kernel: try introduce like susfs's method to fix prctl delay
* seccomp: allow reboot
* use u32
* update clang-format
* 4 spaces save the world
* ksud: Fix build on macOS
* manager: bump minimal supported kernel.
- When get_hook_type is empty, display “Unknown”.
* Fix ksud build (#2841)
* try fix ksud
* fix for macos
* remove any
* Fix ksud build, take 3
* try fix allowlist
* bring lsm hook back
* fix: a lot again
* Fix ksud build, take 4 (#2846)
Remove init_driver_fd function for non-linux/android targets
* manager: Return to the native method via KSUd installation
* Merge with susfs-mian format
---------
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
Co-authored-by: Ylarod <me@ylarod.cn>
Co-authored-by: weishu <twsxtd@gmail.com>
Co-authored-by: AlexLiuDev233 <wzylin11@outlook.com>
Co-authored-by: Wang Han <416810799@qq.com>
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
.idea
|
||||
.vscode
|
||||
CLAUDE.md
|
||||
.DS_Store
|
||||
|
||||
@@ -56,8 +56,8 @@ ColumnLimit: 80
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
#CompactNamespaces: false # Unknown to clang-format-4.0
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
ConstructorInitializerIndentWidth: 8
|
||||
ContinuationIndentWidth: 8
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: false
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
@@ -501,7 +501,7 @@ IncludeCategories:
|
||||
IncludeIsMainRegex: '(Test)?$'
|
||||
IndentCaseLabels: false
|
||||
#IndentPPDirectives: None # Unknown to clang-format-5.0
|
||||
IndentWidth: 8
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
@@ -511,7 +511,7 @@ MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
|
||||
ObjCBlockIndentWidth: 8
|
||||
ObjCBlockIndentWidth: 4
|
||||
ObjCSpaceAfterProperty: true
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
|
||||
@@ -543,6 +543,6 @@ SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Cpp03
|
||||
TabWidth: 8
|
||||
UseTab: Always
|
||||
TabWidth: 4
|
||||
UseTab: Never
|
||||
...
|
||||
|
||||
@@ -3,8 +3,10 @@ kernelsu-objs += allowlist.o
|
||||
kernelsu-objs += dynamic_manager.o
|
||||
kernelsu-objs += apk_sign.o
|
||||
kernelsu-objs += sucompat.o
|
||||
kernelsu-objs += pkg_observer.o
|
||||
kernelsu-objs += throne_tracker.o
|
||||
kernelsu-objs += core_hook.o
|
||||
kernelsu-objs += supercalls.o
|
||||
kernelsu-objs += ksud.o
|
||||
kernelsu-objs += embed_ksud.o
|
||||
kernelsu-objs += kernel_compat.o
|
||||
@@ -134,6 +136,11 @@ else
|
||||
$(info -- KPM is disabled)
|
||||
endif
|
||||
|
||||
# Check new vfs_getattr()
|
||||
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
|
||||
|
||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
||||
|
||||
|
||||
@@ -8,7 +8,9 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/version.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)
|
||||
#include <linux/compiler_types.h>
|
||||
#endif
|
||||
|
||||
#include "ksu.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
@@ -61,7 +63,7 @@ static void remove_uid_from_arr(uid_t uid)
|
||||
kfree(temp_arr);
|
||||
}
|
||||
|
||||
static void init_default_profiles()
|
||||
static void init_default_profiles(void)
|
||||
{
|
||||
kernel_cap_t full_cap = CAP_FULL_SET;
|
||||
|
||||
@@ -93,7 +95,7 @@ static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
|
||||
static struct work_struct ksu_save_work;
|
||||
static struct work_struct ksu_load_work;
|
||||
|
||||
bool persistent_allow_list(void);
|
||||
static bool persistent_allow_list(void);
|
||||
|
||||
void ksu_show_allow_list(void)
|
||||
{
|
||||
@@ -108,7 +110,7 @@ void ksu_show_allow_list(void)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
static void ksu_grant_root_to_shell()
|
||||
static void ksu_grant_root_to_shell(void)
|
||||
{
|
||||
struct app_profile profile = {
|
||||
.version = KSU_APP_PROFILE_VER,
|
||||
@@ -349,7 +351,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_save_allow_list(struct work_struct *work)
|
||||
{
|
||||
u32 magic = FILE_MAGIC;
|
||||
u32 version = FILE_FORMAT_VERSION;
|
||||
@@ -379,7 +381,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);
|
||||
|
||||
@@ -391,7 +393,7 @@ exit:
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
|
||||
void do_load_allow_list(struct work_struct *work)
|
||||
static void do_load_allow_list(struct work_struct *work)
|
||||
{
|
||||
loff_t off = 0;
|
||||
ssize_t ret = 0;
|
||||
@@ -481,7 +483,7 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), void *data
|
||||
}
|
||||
|
||||
// make sure allow list works cross boot
|
||||
bool persistent_allow_list(void)
|
||||
static bool persistent_allow_list(void)
|
||||
{
|
||||
return ksu_queue_work(&ksu_save_work);
|
||||
}
|
||||
|
||||
@@ -171,7 +171,7 @@ static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, i
|
||||
}
|
||||
ksu_kernel_read_compat(fp, cert, *size4, pos);
|
||||
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||
if (IS_ERR(ksu_sha256(cert, *size4, digest))) {
|
||||
if (ksu_sha256(cert, *size4, digest) < 0 ) {
|
||||
pr_info("sha256 error\n");
|
||||
return false;
|
||||
}
|
||||
@@ -230,7 +230,8 @@ static bool has_v1_signature_file(struct file *fp)
|
||||
fileName[header.file_name_length] = '\0';
|
||||
|
||||
// Check if the entry matches META-INF/MANIFEST.MF
|
||||
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) == 0) {
|
||||
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) ==
|
||||
0) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
@@ -250,7 +251,9 @@ static __always_inline bool check_v2_signature(char *path, bool check_multi_mana
|
||||
unsigned char buffer[0x11] = { 0 };
|
||||
u32 size4;
|
||||
u64 size8, size_of_block;
|
||||
|
||||
loff_t pos;
|
||||
|
||||
bool v2_signing_valid = false;
|
||||
int v2_signing_blocks = 0;
|
||||
bool v3_signing_exist = false;
|
||||
|
||||
@@ -19,10 +19,17 @@
|
||||
#define __PT_IP_REG pc
|
||||
|
||||
#define PRCTL_SYMBOL "__arm64_sys_prctl"
|
||||
#define REBOOT_SYMBOL "__arm64_sys_reboot"
|
||||
#define SYS_READ_SYMBOL "__arm64_sys_read"
|
||||
#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat"
|
||||
#define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat"
|
||||
#define SYS_EXECVE_SYMBOL "__arm64_sys_execve"
|
||||
/*LSM HOOK*/
|
||||
#define SECURITY_TASK_FIX_SETUID_SYMBOL "security_task_fix_setuid"
|
||||
#define PRCTL_SYMBOL "__arm64_sys_prctl"
|
||||
#define INODE_PERMISSION_SYMBOL "security_inode_permission"
|
||||
#define BPRM_CHECK_SECURITY_SYMBOL "security_bprm_check"
|
||||
#define TASK_ALLOC_SYMBOL "security_task_alloc"
|
||||
|
||||
#elif defined(__x86_64__)
|
||||
|
||||
@@ -40,10 +47,17 @@
|
||||
#define __PT_SP_REG sp
|
||||
#define __PT_IP_REG ip
|
||||
#define PRCTL_SYMBOL "__x64_sys_prctl"
|
||||
#define REBOOT_SYMBOL "__x64_sys_reboot"
|
||||
#define SYS_READ_SYMBOL "__x64_sys_read"
|
||||
#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat"
|
||||
#define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat"
|
||||
#define SYS_EXECVE_SYMBOL "__x64_sys_execve"
|
||||
/*LSM HOOK*/
|
||||
#define SECURITY_TASK_FIX_SETUID_SYMBOL "security_task_fix_setuid"
|
||||
#define PRCTL_SYMBOL "__x64_sys_prctl"
|
||||
#define INODE_PERMISSION_SYMBOL "security_inode_permission"
|
||||
#define BPRM_CHECK_SECURITY_SYMBOL "security_bprm_check"
|
||||
#define TASK_ALLOC_SYMBOL "security_task_alloc"
|
||||
|
||||
#else
|
||||
#error "Unsupported arch"
|
||||
|
||||
1077
kernel/core_hook.c
1077
kernel/core_hook.c
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,8 @@
|
||||
#include <linux/nsproxy.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 "kernel_compat.h"
|
||||
|
||||
@@ -93,3 +95,58 @@ long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||
return strncpy_from_user_nofault(dst, unsafe_addr, count);
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -94,4 +94,7 @@ static long ksu_copy_from_user_retry(void *to,
|
||||
return copy_from_user(to, from, count);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -73,6 +73,8 @@ void kernelsu_exit(void)
|
||||
{
|
||||
ksu_allowlist_exit();
|
||||
|
||||
ksu_observer_exit();
|
||||
|
||||
ksu_throne_tracker_exit();
|
||||
|
||||
destroy_workqueue(ksu_workqueue);
|
||||
|
||||
31
kernel/ksu.h
31
kernel/ksu.h
@@ -5,37 +5,14 @@
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#define KERNEL_SU_VERSION KSU_VERSION
|
||||
#define KERNEL_SU_OPTION 0xDEADBEEF
|
||||
#define KERNEL_SU_OPTION 0xBADC0DE
|
||||
|
||||
#define CMD_GRANT_ROOT 0
|
||||
#define CMD_BECOME_MANAGER 1
|
||||
#define CMD_GET_VERSION 2
|
||||
#define CMD_ALLOW_SU 3
|
||||
#define CMD_DENY_SU 4
|
||||
#define CMD_GET_ALLOW_LIST 5
|
||||
#define CMD_GET_DENY_LIST 6
|
||||
#define CMD_REPORT_EVENT 7
|
||||
#define CMD_SET_SEPOLICY 8
|
||||
#define CMD_CHECK_SAFEMODE 9
|
||||
#define CMD_GET_APP_PROFILE 10
|
||||
#define CMD_SET_APP_PROFILE 11
|
||||
#define CMD_UID_GRANTED_ROOT 12
|
||||
#define CMD_UID_SHOULD_UMOUNT 13
|
||||
#define CMD_IS_SU_ENABLED 14
|
||||
#define CMD_ENABLE_SU 15
|
||||
extern bool ksu_uid_scanner_enabled;
|
||||
|
||||
#ifdef CONFIG_KSU_MANUAL_SU
|
||||
#define CMD_MANUAL_SU_REQUEST 50
|
||||
#endif
|
||||
|
||||
#define CMD_GET_FULL_VERSION 0xC0FFEE1A
|
||||
|
||||
#define CMD_ENABLE_KPM 100
|
||||
#define CMD_HOOK_TYPE 101
|
||||
#define CMD_DYNAMIC_MANAGER 103
|
||||
#define CMD_GET_MANAGERS 104
|
||||
#define CMD_ENABLE_UID_SCANNER 105
|
||||
|
||||
#define EVENT_POST_FS_DATA 1
|
||||
#define EVENT_BOOT_COMPLETED 2
|
||||
#define EVENT_MODULE_MOUNTED 3
|
||||
@@ -56,6 +33,10 @@
|
||||
#define DYNAMIC_MANAGER_OP_GET 1
|
||||
#define DYNAMIC_MANAGER_OP_CLEAR 2
|
||||
|
||||
#define UID_SCANNER_OP_GET_STATUS 0
|
||||
#define UID_SCANNER_OP_TOGGLE 1
|
||||
#define UID_SCANNER_OP_CLEAR_ENV 2
|
||||
|
||||
struct dynamic_manager_user_config {
|
||||
unsigned int operation;
|
||||
unsigned int size;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "manager.h"
|
||||
#include <asm/current.h>
|
||||
#include <linux/compat.h>
|
||||
#include <linux/cred.h>
|
||||
@@ -73,6 +74,7 @@ void on_post_fs_data(void)
|
||||
done = true;
|
||||
pr_info("on_post_fs_data!\n");
|
||||
ksu_load_allow_list();
|
||||
ksu_observer_init();
|
||||
// sanity check, this may influence the performance
|
||||
stop_input_hook();
|
||||
|
||||
|
||||
@@ -13,18 +13,18 @@ extern void ksu_add_manager(uid_t uid, int signature_index);
|
||||
extern void ksu_remove_manager(uid_t uid);
|
||||
extern int ksu_get_manager_signature_index(uid_t uid);
|
||||
|
||||
static inline bool ksu_is_manager_uid_valid()
|
||||
static inline bool ksu_is_manager_uid_valid(void)
|
||||
{
|
||||
return ksu_manager_uid != KSU_INVALID_UID;
|
||||
}
|
||||
|
||||
static inline bool is_manager()
|
||||
static inline bool is_manager(void)
|
||||
{
|
||||
return unlikely(ksu_is_any_manager(current_uid().val) ||
|
||||
(ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val));
|
||||
}
|
||||
|
||||
static inline uid_t ksu_get_manager_uid()
|
||||
static inline uid_t ksu_get_manager_uid(void)
|
||||
{
|
||||
return ksu_manager_uid;
|
||||
}
|
||||
@@ -34,9 +34,12 @@ static inline void ksu_set_manager_uid(uid_t uid)
|
||||
ksu_manager_uid = uid;
|
||||
}
|
||||
|
||||
static inline void ksu_invalidate_manager_uid()
|
||||
static inline void ksu_invalidate_manager_uid(void)
|
||||
{
|
||||
ksu_manager_uid = KSU_INVALID_UID;
|
||||
}
|
||||
|
||||
int ksu_observer_init(void);
|
||||
void ksu_observer_exit(void);
|
||||
|
||||
#endif
|
||||
@@ -3,6 +3,11 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0)
|
||||
#define mmap_lock mmap_sem
|
||||
#endif
|
||||
|
||||
#define ksu_task_is_dead(t) ((t)->exit_state != 0)
|
||||
|
||||
|
||||
133
kernel/pkg_observer.c
Normal file
133
kernel/pkg_observer.c
Normal file
@@ -0,0 +1,133 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
#include <linux/module.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/fsnotify_backend.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/version.h>
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksu.h"
|
||||
#include "throne_tracker.h"
|
||||
#include "throne_comm.h"
|
||||
|
||||
#define MASK_SYSTEM (FS_CREATE | FS_MOVE | FS_EVENT_ON_CHILD)
|
||||
|
||||
struct watch_dir {
|
||||
const char *path;
|
||||
u32 mask;
|
||||
struct path kpath;
|
||||
struct inode *inode;
|
||||
struct fsnotify_mark *mark;
|
||||
};
|
||||
|
||||
static struct fsnotify_group *g;
|
||||
|
||||
static int ksu_handle_inode_event(struct fsnotify_mark *mark, u32 mask,
|
||||
struct inode *inode, struct inode *dir,
|
||||
const struct qstr *file_name, u32 cookie)
|
||||
{
|
||||
if (!file_name)
|
||||
return 0;
|
||||
if (mask & FS_ISDIR)
|
||||
return 0;
|
||||
if (file_name->len == 13 &&
|
||||
!memcmp(file_name->name, "packages.list", 13)) {
|
||||
pr_info("packages.list detected: %d\n", mask);
|
||||
if (ksu_uid_scanner_enabled) {
|
||||
ksu_request_userspace_scan();
|
||||
}
|
||||
track_throne();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct fsnotify_ops ksu_ops = {
|
||||
.handle_inode_event = ksu_handle_inode_event,
|
||||
};
|
||||
|
||||
static int add_mark_on_inode(struct inode *inode, u32 mask,
|
||||
struct fsnotify_mark **out)
|
||||
{
|
||||
struct fsnotify_mark *m;
|
||||
|
||||
m = kzalloc(sizeof(*m), GFP_KERNEL);
|
||||
if (!m)
|
||||
return -ENOMEM;
|
||||
|
||||
fsnotify_init_mark(m, g);
|
||||
m->mask = mask;
|
||||
|
||||
if (fsnotify_add_inode_mark(m, inode, 0)) {
|
||||
fsnotify_put_mark(m);
|
||||
return -EINVAL;
|
||||
}
|
||||
*out = m;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int watch_one_dir(struct watch_dir *wd)
|
||||
{
|
||||
int ret = kern_path(wd->path, LOOKUP_FOLLOW, &wd->kpath);
|
||||
if (ret) {
|
||||
pr_info("path not ready: %s (%d)\n", wd->path, ret);
|
||||
return ret;
|
||||
}
|
||||
wd->inode = d_inode(wd->kpath.dentry);
|
||||
ihold(wd->inode);
|
||||
|
||||
ret = add_mark_on_inode(wd->inode, wd->mask, &wd->mark);
|
||||
if (ret) {
|
||||
pr_err("Add mark failed for %s (%d)\n", wd->path, ret);
|
||||
path_put(&wd->kpath);
|
||||
iput(wd->inode);
|
||||
wd->inode = NULL;
|
||||
return ret;
|
||||
}
|
||||
pr_info("watching %s\n", wd->path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void unwatch_one_dir(struct watch_dir *wd)
|
||||
{
|
||||
if (wd->mark) {
|
||||
fsnotify_destroy_mark(wd->mark, g);
|
||||
fsnotify_put_mark(wd->mark);
|
||||
wd->mark = NULL;
|
||||
}
|
||||
if (wd->inode) {
|
||||
iput(wd->inode);
|
||||
wd->inode = NULL;
|
||||
}
|
||||
if (wd->kpath.dentry) {
|
||||
path_put(&wd->kpath);
|
||||
memset(&wd->kpath, 0, sizeof(wd->kpath));
|
||||
}
|
||||
}
|
||||
|
||||
static struct watch_dir g_watch = { .path = "/data/system",
|
||||
.mask = MASK_SYSTEM };
|
||||
|
||||
int ksu_observer_init(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
|
||||
g = fsnotify_alloc_group(&ksu_ops, 0);
|
||||
#else
|
||||
g = fsnotify_alloc_group(&ksu_ops);
|
||||
#endif
|
||||
if (IS_ERR(g))
|
||||
return PTR_ERR(g);
|
||||
|
||||
ret = watch_one_dir(&g_watch);
|
||||
pr_info("observer init done\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ksu_observer_exit(void)
|
||||
{
|
||||
unwatch_one_dir(&g_watch);
|
||||
fsnotify_put_group(g);
|
||||
pr_info("observer exit done\n");
|
||||
}
|
||||
@@ -15,6 +15,23 @@ extern struct timezone sys_tz;
|
||||
#define SULOG_COMM_LEN 256
|
||||
#define DEDUP_SECS 10
|
||||
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
|
||||
#include <linux/rtc.h>
|
||||
|
||||
static inline void time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
|
||||
{
|
||||
struct rtc_time rtc_tm;
|
||||
rtc_time64_to_tm(totalsecs, &rtc_tm);
|
||||
|
||||
result->tm_sec = rtc_tm.tm_sec;
|
||||
result->tm_min = rtc_tm.tm_min;
|
||||
result->tm_hour = rtc_tm.tm_hour;
|
||||
result->tm_mday = rtc_tm.tm_mday;
|
||||
result->tm_mon = rtc_tm.tm_mon;
|
||||
result->tm_year = rtc_tm.tm_year;
|
||||
}
|
||||
#endif
|
||||
|
||||
struct dedup_key {
|
||||
u32 crc;
|
||||
uid_t uid;
|
||||
|
||||
609
kernel/supercalls.c
Normal file
609
kernel/supercalls.c
Normal file
@@ -0,0 +1,609 @@
|
||||
#include "supercalls.h"
|
||||
|
||||
#include <linux/anon_inodes.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/cred.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <linux/version.h>
|
||||
|
||||
#include "allowlist.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "ksud.h"
|
||||
#include "manager.h"
|
||||
#include "sulog.h"
|
||||
#include "selinux/selinux.h"
|
||||
#include "kernel_compat.h"
|
||||
#include "throne_comm.h"
|
||||
#include "dynamic_manager.h"
|
||||
|
||||
#ifdef CONFIG_KSU_MANUAL_SU
|
||||
#include "manual_su.h"
|
||||
#endif
|
||||
|
||||
// Forward declarations from core_hook.c
|
||||
extern void escape_to_root(void);
|
||||
extern void nuke_ext4_sysfs(void);
|
||||
extern bool ksu_module_mounted;
|
||||
extern int handle_sepolicy(unsigned long arg3, void __user *arg4);
|
||||
extern void ksu_sucompat_init(void);
|
||||
extern void ksu_sucompat_exit(void);
|
||||
|
||||
// Forward declaration for anon_ksu_fops
|
||||
static const struct file_operations anon_ksu_fops;
|
||||
|
||||
static bool ksu_su_compat_enabled = true;
|
||||
bool ksu_uid_scanner_enabled = false;
|
||||
|
||||
// Permission check functions
|
||||
bool perm_check_manager(void)
|
||||
{
|
||||
return is_manager();
|
||||
}
|
||||
|
||||
bool perm_check_root(void)
|
||||
{
|
||||
return current_uid().val == 0;
|
||||
}
|
||||
|
||||
bool perm_check_basic(void)
|
||||
{
|
||||
return current_uid().val == 0 || is_manager();
|
||||
}
|
||||
|
||||
bool perm_check_all(void)
|
||||
{
|
||||
return true; // No permission check
|
||||
}
|
||||
|
||||
static void init_uid_scanner(void)
|
||||
{
|
||||
ksu_uid_init();
|
||||
do_load_throne_state(NULL);
|
||||
|
||||
if (ksu_uid_scanner_enabled) {
|
||||
int ret = ksu_throne_comm_init();
|
||||
if (ret != 0) {
|
||||
pr_err("Failed to initialize throne communication: %d\n", ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int do_grant_root(void __user *arg)
|
||||
{
|
||||
// Check if current UID is allowed
|
||||
bool is_allowed = is_manager() || ksu_is_allow_uid(current_uid().val);
|
||||
|
||||
if (!is_allowed) {
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
pr_info("allow root for: %d\n", current_uid().val);
|
||||
escape_to_root();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_get_info(void __user *arg)
|
||||
{
|
||||
struct ksu_get_info_cmd cmd = {.version = KERNEL_SU_VERSION, .flags = 0};
|
||||
|
||||
#ifdef MODULE
|
||||
cmd.flags |= 0x1;
|
||||
#endif
|
||||
if (is_manager()) {
|
||||
cmd.flags |= 0x2;
|
||||
}
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("get_version: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_report_event(void __user *arg)
|
||||
{
|
||||
struct ksu_report_event_cmd cmd;
|
||||
|
||||
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
switch (cmd.event) {
|
||||
case EVENT_POST_FS_DATA: {
|
||||
static bool post_fs_data_lock = false;
|
||||
if (!post_fs_data_lock) {
|
||||
post_fs_data_lock = true;
|
||||
pr_info("post-fs-data triggered\n");
|
||||
on_post_fs_data();
|
||||
init_uid_scanner();
|
||||
#if __SULOG_GATE
|
||||
ksu_sulog_init();
|
||||
#endif
|
||||
ksu_dynamic_manager_init();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EVENT_BOOT_COMPLETED: {
|
||||
static bool boot_complete_lock = false;
|
||||
if (!boot_complete_lock) {
|
||||
boot_complete_lock = true;
|
||||
pr_info("boot_complete triggered\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case EVENT_MODULE_MOUNTED: {
|
||||
ksu_module_mounted = true;
|
||||
pr_info("module mounted!\n");
|
||||
nuke_ext4_sysfs();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_set_sepolicy(void __user *arg)
|
||||
{
|
||||
struct ksu_set_sepolicy_cmd cmd;
|
||||
|
||||
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return handle_sepolicy(cmd.cmd, (void __user *)cmd.arg);
|
||||
}
|
||||
|
||||
static int do_check_safemode(void __user *arg)
|
||||
{
|
||||
struct ksu_check_safemode_cmd cmd;
|
||||
|
||||
cmd.in_safe_mode = ksu_is_safe_mode();
|
||||
|
||||
if (cmd.in_safe_mode) {
|
||||
pr_warn("safemode enabled!\n");
|
||||
}
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("check_safemode: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_get_allow_list(void __user *arg)
|
||||
{
|
||||
struct ksu_get_allow_list_cmd cmd;
|
||||
|
||||
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, true);
|
||||
|
||||
if (!success) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("get_allow_list: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_get_deny_list(void __user *arg)
|
||||
{
|
||||
struct ksu_get_allow_list_cmd cmd;
|
||||
|
||||
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
bool success = ksu_get_allow_list((int *)cmd.uids, (int *)&cmd.count, false);
|
||||
|
||||
if (!success) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("get_deny_list: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_uid_granted_root(void __user *arg)
|
||||
{
|
||||
struct ksu_uid_granted_root_cmd cmd;
|
||||
|
||||
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
cmd.granted = ksu_is_allow_uid(cmd.uid);
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("uid_granted_root: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_uid_should_umount(void __user *arg)
|
||||
{
|
||||
struct ksu_uid_should_umount_cmd cmd;
|
||||
|
||||
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
cmd.should_umount = ksu_uid_should_umount(cmd.uid);
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("uid_should_umount: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_get_manager_uid(void __user *arg)
|
||||
{
|
||||
struct ksu_get_manager_uid_cmd cmd;
|
||||
|
||||
cmd.uid = ksu_get_manager_uid();
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("get_manager_uid: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_get_app_profile(void __user *arg)
|
||||
{
|
||||
struct ksu_get_app_profile_cmd cmd;
|
||||
|
||||
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||
pr_err("get_app_profile: copy_from_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (!ksu_get_app_profile(&cmd.profile)) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("get_app_profile: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_set_app_profile(void __user *arg)
|
||||
{
|
||||
struct ksu_set_app_profile_cmd cmd;
|
||||
|
||||
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||
pr_err("set_app_profile: copy_from_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (!ksu_set_app_profile(&cmd.profile, true)) {
|
||||
#if __SULOG_GATE
|
||||
ksu_sulog_report_manager_operation("SET_APP_PROFILE",
|
||||
current_uid().val, cmd.profile.current_uid);
|
||||
#endif
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_is_su_enabled(void __user *arg)
|
||||
{
|
||||
struct ksu_is_su_enabled_cmd cmd;
|
||||
|
||||
cmd.enabled = ksu_su_compat_enabled;
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("is_su_enabled: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_enable_su(void __user *arg)
|
||||
{
|
||||
struct ksu_enable_su_cmd cmd;
|
||||
|
||||
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||
pr_err("enable_su: copy_from_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
if (cmd.enable == ksu_su_compat_enabled) {
|
||||
pr_info("enable_su: no need to change\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (cmd.enable) {
|
||||
ksu_sucompat_init();
|
||||
} else {
|
||||
ksu_sucompat_exit();
|
||||
}
|
||||
|
||||
ksu_su_compat_enabled = cmd.enable;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 100. GET_FULL_VERSION - Get full version string
|
||||
static int do_get_full_version(void __user *arg)
|
||||
{
|
||||
struct ksu_get_full_version_cmd cmd = {0};
|
||||
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||
strscpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full));
|
||||
#else
|
||||
strlcpy(cmd.version_full, KSU_VERSION_FULL, sizeof(cmd.version_full));
|
||||
#endif
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("get_full_version: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 101. HOOK_TYPE - Get hook type
|
||||
static int do_get_hook_type(void __user *arg)
|
||||
{
|
||||
struct ksu_hook_type_cmd cmd = {0};
|
||||
const char *type = "Kprobes";
|
||||
|
||||
#if defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
type = "Tracepoint";
|
||||
#elif defined(CONFIG_KSU_MANUAL_HOOK)
|
||||
type = "Manual";
|
||||
#endif
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||
strscpy(cmd.hook_type, type, sizeof(cmd.hook_type));
|
||||
#else
|
||||
strlcpy(cmd.hook_type, type, sizeof(cmd.hook_type));
|
||||
#endif
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("get_hook_type: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// 102. ENABLE_KPM - Check if KPM is enabled
|
||||
static int do_enable_kpm(void __user *arg)
|
||||
{
|
||||
struct ksu_enable_kpm_cmd cmd;
|
||||
|
||||
cmd.enabled = IS_ENABLED(CONFIG_KPM);
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("enable_kpm: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_dynamic_manager(void __user *arg)
|
||||
{
|
||||
struct ksu_dynamic_manager_cmd cmd;
|
||||
|
||||
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||
pr_err("dynamic_manager: copy_from_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
int ret = ksu_handle_dynamic_manager(&cmd.config);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (cmd.config.operation == DYNAMIC_MANAGER_OP_GET &&
|
||||
copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("dynamic_manager: copy_to_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_get_managers(void __user *arg)
|
||||
{
|
||||
struct ksu_get_managers_cmd cmd;
|
||||
|
||||
int ret = ksu_get_active_managers(&cmd.manager_info);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
|
||||
pr_err("get_managers: copy_from_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_enable_uid_scanner(void __user *arg)
|
||||
{
|
||||
struct ksu_enable_uid_scanner_cmd cmd;
|
||||
|
||||
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
|
||||
pr_err("enable_uid_scanner: copy_from_user failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
switch (cmd.operation) {
|
||||
case UID_SCANNER_OP_GET_STATUS: {
|
||||
bool status = ksu_uid_scanner_enabled;
|
||||
if (copy_to_user((void __user *)cmd.status_ptr, &status, sizeof(status))) {
|
||||
pr_err("enable_uid_scanner: copy status failed\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case UID_SCANNER_OP_TOGGLE: {
|
||||
bool enabled = cmd.enabled;
|
||||
|
||||
if (enabled == ksu_uid_scanner_enabled) {
|
||||
pr_info("enable_uid_scanner: no need to change, already %s\n",
|
||||
enabled ? "enabled" : "disabled");
|
||||
break;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
// Enable UID scanner
|
||||
int ret = ksu_throne_comm_init();
|
||||
if (ret != 0) {
|
||||
pr_err("enable_uid_scanner: failed to initialize: %d\n", ret);
|
||||
return -EFAULT;
|
||||
}
|
||||
pr_info("enable_uid_scanner: enabled\n");
|
||||
} else {
|
||||
// Disable UID scanner
|
||||
ksu_throne_comm_exit();
|
||||
pr_info("enable_uid_scanner: disabled\n");
|
||||
}
|
||||
|
||||
ksu_uid_scanner_enabled = enabled;
|
||||
ksu_throne_comm_save_state();
|
||||
break;
|
||||
}
|
||||
case UID_SCANNER_OP_CLEAR_ENV: {
|
||||
// Clear environment (force exit)
|
||||
ksu_throne_comm_exit();
|
||||
ksu_uid_scanner_enabled = false;
|
||||
ksu_throne_comm_save_state();
|
||||
pr_info("enable_uid_scanner: environment cleared\n");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
pr_err("enable_uid_scanner: invalid operation\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// IOCTL handlers mapping table
|
||||
static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
|
||||
{ .cmd = KSU_IOCTL_GRANT_ROOT, .handler = do_grant_root, .perm_check = perm_check_basic},
|
||||
{ .cmd = KSU_IOCTL_GET_INFO, .handler = do_get_info, .perm_check = perm_check_all},
|
||||
{ .cmd = KSU_IOCTL_REPORT_EVENT, .handler = do_report_event, .perm_check = perm_check_root},
|
||||
{ .cmd = KSU_IOCTL_SET_SEPOLICY, .handler = do_set_sepolicy, .perm_check = perm_check_root},
|
||||
{ .cmd = KSU_IOCTL_CHECK_SAFEMODE, .handler = do_check_safemode, .perm_check = perm_check_all},
|
||||
{ .cmd = KSU_IOCTL_GET_ALLOW_LIST, .handler = do_get_allow_list, .perm_check = perm_check_basic},
|
||||
{ .cmd = KSU_IOCTL_GET_DENY_LIST, .handler = do_get_deny_list, .perm_check = perm_check_basic},
|
||||
{ .cmd = KSU_IOCTL_UID_GRANTED_ROOT, .handler = do_uid_granted_root, .perm_check = perm_check_basic},
|
||||
{ .cmd = KSU_IOCTL_UID_SHOULD_UMOUNT, .handler = do_uid_should_umount, .perm_check = perm_check_basic},
|
||||
{ .cmd = KSU_IOCTL_GET_MANAGER_UID, .handler = do_get_manager_uid, .perm_check = perm_check_basic},
|
||||
{ .cmd = KSU_IOCTL_GET_APP_PROFILE, .handler = do_get_app_profile, .perm_check = perm_check_manager},
|
||||
{ .cmd = KSU_IOCTL_SET_APP_PROFILE, .handler = do_set_app_profile, .perm_check = perm_check_manager},
|
||||
{ .cmd = KSU_IOCTL_IS_SU_ENABLED, .handler = do_is_su_enabled, .perm_check = perm_check_manager},
|
||||
{ .cmd = KSU_IOCTL_ENABLE_SU, .handler = do_enable_su, .perm_check = perm_check_manager},
|
||||
{ .cmd = KSU_IOCTL_GET_FULL_VERSION, .handler = do_get_full_version, .perm_check = perm_check_manager},
|
||||
{ .cmd = KSU_IOCTL_HOOK_TYPE, .handler = do_get_hook_type, .perm_check = perm_check_basic},
|
||||
{ .cmd = KSU_IOCTL_ENABLE_KPM, .handler = do_enable_kpm, .perm_check = perm_check_basic},
|
||||
{ .cmd = KSU_IOCTL_DYNAMIC_MANAGER, .handler = do_dynamic_manager, .perm_check = perm_check_basic},
|
||||
{ .cmd = KSU_IOCTL_GET_MANAGERS, .handler = do_get_managers, .perm_check = perm_check_basic},
|
||||
{ .cmd = KSU_IOCTL_ENABLE_UID_SCANNER, .handler = do_enable_uid_scanner, .perm_check = perm_check_basic},
|
||||
{ .cmd = 0, .handler = NULL, .perm_check = NULL} // Sentinel
|
||||
};
|
||||
|
||||
// IOCTL dispatcher
|
||||
static long anon_ksu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
int i;
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("ksu ioctl: cmd=0x%x from uid=%d\n", cmd, current_uid().val);
|
||||
#endif
|
||||
|
||||
for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
|
||||
if (cmd == ksu_ioctl_handlers[i].cmd) {
|
||||
// Check permission first
|
||||
if (ksu_ioctl_handlers[i].perm_check &&
|
||||
!ksu_ioctl_handlers[i].perm_check()) {
|
||||
pr_warn("ksu ioctl: permission denied for cmd=0x%x uid=%d\n",
|
||||
cmd, current_uid().val);
|
||||
return -EPERM;
|
||||
}
|
||||
// Execute handler
|
||||
return ksu_ioctl_handlers[i].handler(argp);
|
||||
}
|
||||
}
|
||||
|
||||
pr_warn("ksu ioctl: unsupported command 0x%x\n", cmd);
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
// File release handler
|
||||
static int anon_ksu_release(struct inode *inode, struct file *filp)
|
||||
{
|
||||
pr_info("ksu fd released\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// File operations structure
|
||||
static const struct file_operations anon_ksu_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = anon_ksu_ioctl,
|
||||
.compat_ioctl = anon_ksu_ioctl,
|
||||
.release = anon_ksu_release,
|
||||
};
|
||||
|
||||
// Install KSU fd to current process
|
||||
int ksu_install_fd(void)
|
||||
{
|
||||
struct file *filp;
|
||||
int fd;
|
||||
|
||||
// Get unused fd
|
||||
fd = get_unused_fd_flags(O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
pr_err("ksu_install_fd: failed to get unused fd\n");
|
||||
return fd;
|
||||
}
|
||||
|
||||
// Create anonymous inode file
|
||||
filp = anon_inode_getfile("[ksu_driver]", &anon_ksu_fops, NULL, O_RDWR | O_CLOEXEC);
|
||||
if (IS_ERR(filp)) {
|
||||
pr_err("ksu_install_fd: failed to create anon inode file\n");
|
||||
put_unused_fd(fd);
|
||||
return PTR_ERR(filp);
|
||||
}
|
||||
|
||||
// Install fd
|
||||
fd_install(fd, filp);
|
||||
|
||||
pr_info("ksu fd installed: %d for pid %d\n", fd, current->pid);
|
||||
|
||||
return fd;
|
||||
}
|
||||
142
kernel/supercalls.h
Normal file
142
kernel/supercalls.h
Normal file
@@ -0,0 +1,142 @@
|
||||
#ifndef __KSU_H_SUPERCALLS
|
||||
#define __KSU_H_SUPERCALLS
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/ioctl.h>
|
||||
#include "ksu.h"
|
||||
|
||||
// Magic numbers for reboot hook to install fd
|
||||
#define KSU_INSTALL_MAGIC1 0xDEADBEEF
|
||||
#define KSU_INSTALL_MAGIC2 0xCAFEBABE
|
||||
|
||||
// Command structures for ioctl
|
||||
|
||||
struct ksu_become_daemon_cmd {
|
||||
__u8 token[65]; // Input: daemon token (null-terminated)
|
||||
};
|
||||
|
||||
struct ksu_get_info_cmd {
|
||||
__u32 version; // Output: KERNEL_SU_VERSION
|
||||
__u32 flags; // Output: flags (bit 0: MODULE mode)
|
||||
};
|
||||
|
||||
struct ksu_report_event_cmd {
|
||||
__u32 event; // Input: EVENT_POST_FS_DATA, EVENT_BOOT_COMPLETED, etc.
|
||||
};
|
||||
|
||||
struct ksu_set_sepolicy_cmd {
|
||||
__u64 cmd; // Input: sepolicy command
|
||||
__aligned_u64 arg; // Input: sepolicy argument pointer
|
||||
};
|
||||
|
||||
struct ksu_check_safemode_cmd {
|
||||
__u8 in_safe_mode; // Output: true if in safe mode, false otherwise
|
||||
};
|
||||
|
||||
struct ksu_get_allow_list_cmd {
|
||||
__u32 uids[128]; // Output: array of allowed/denied UIDs
|
||||
__u32 count; // Output: number of UIDs in array
|
||||
__u8 allow; // Input: true for allow list, false for deny list
|
||||
};
|
||||
|
||||
struct ksu_uid_granted_root_cmd {
|
||||
__u32 uid; // Input: target UID to check
|
||||
__u8 granted; // Output: true if granted, false otherwise
|
||||
};
|
||||
|
||||
struct ksu_uid_should_umount_cmd {
|
||||
__u32 uid; // Input: target UID to check
|
||||
__u8 should_umount; // Output: true if should umount, false otherwise
|
||||
};
|
||||
|
||||
struct ksu_get_manager_uid_cmd {
|
||||
__u32 uid; // Output: manager UID
|
||||
};
|
||||
|
||||
struct ksu_get_app_profile_cmd {
|
||||
struct app_profile profile; // Input/Output: app profile structure
|
||||
};
|
||||
|
||||
struct ksu_set_app_profile_cmd {
|
||||
struct app_profile profile; // Input: app profile structure
|
||||
};
|
||||
|
||||
struct ksu_is_su_enabled_cmd {
|
||||
__u8 enabled; // Output: true if su compat enabled
|
||||
};
|
||||
|
||||
struct ksu_enable_su_cmd {
|
||||
__u8 enable; // Input: true to enable, false to disable
|
||||
};
|
||||
|
||||
// Other command structures
|
||||
struct ksu_get_full_version_cmd {
|
||||
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string
|
||||
};
|
||||
|
||||
struct ksu_hook_type_cmd {
|
||||
char hook_type[32]; // Output: hook type string
|
||||
};
|
||||
|
||||
struct ksu_enable_kpm_cmd {
|
||||
__u8 enabled; // Output: true if KPM is enabled
|
||||
};
|
||||
|
||||
struct ksu_dynamic_manager_cmd {
|
||||
struct dynamic_manager_user_config config; // Input/Output: dynamic manager config
|
||||
};
|
||||
|
||||
struct ksu_get_managers_cmd {
|
||||
struct manager_list_info manager_info; // Output: manager list information
|
||||
};
|
||||
|
||||
struct ksu_enable_uid_scanner_cmd {
|
||||
__u32 operation; // Input: operation type (UID_SCANNER_OP_GET_STATUS, UID_SCANNER_OP_TOGGLE, UID_SCANNER_OP_CLEAR_ENV)
|
||||
__u32 enabled; // Input: enable or disable (for UID_SCANNER_OP_TOGGLE)
|
||||
void __user *status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS)
|
||||
};
|
||||
|
||||
// IOCTL command definitions
|
||||
#define KSU_IOCTL_GRANT_ROOT _IO('K', 1)
|
||||
#define KSU_IOCTL_GET_INFO _IOR('K', 2, struct ksu_get_info_cmd)
|
||||
#define KSU_IOCTL_REPORT_EVENT _IOW('K', 3, struct ksu_report_event_cmd)
|
||||
#define KSU_IOCTL_SET_SEPOLICY _IOWR('K', 4, struct ksu_set_sepolicy_cmd)
|
||||
#define KSU_IOCTL_CHECK_SAFEMODE _IOR('K', 5, struct ksu_check_safemode_cmd)
|
||||
#define KSU_IOCTL_GET_ALLOW_LIST _IOWR('K', 6, struct ksu_get_allow_list_cmd)
|
||||
#define KSU_IOCTL_GET_DENY_LIST _IOWR('K', 7, struct ksu_get_allow_list_cmd)
|
||||
#define KSU_IOCTL_UID_GRANTED_ROOT _IOWR('K', 8, struct ksu_uid_granted_root_cmd)
|
||||
#define KSU_IOCTL_UID_SHOULD_UMOUNT _IOWR('K', 9, struct ksu_uid_should_umount_cmd)
|
||||
#define KSU_IOCTL_GET_MANAGER_UID _IOR('K', 10, struct ksu_get_manager_uid_cmd)
|
||||
#define KSU_IOCTL_GET_APP_PROFILE _IOWR('K', 11, struct ksu_get_app_profile_cmd)
|
||||
#define KSU_IOCTL_SET_APP_PROFILE _IOW('K', 12, struct ksu_set_app_profile_cmd)
|
||||
#define KSU_IOCTL_IS_SU_ENABLED _IOR('K', 13, struct ksu_is_su_enabled_cmd)
|
||||
#define KSU_IOCTL_ENABLE_SU _IOW('K', 14, struct ksu_enable_su_cmd)
|
||||
// Other IOCTL command definitions
|
||||
#define KSU_IOCTL_GET_FULL_VERSION _IOR('K', 100, struct ksu_get_full_version_cmd)
|
||||
#define KSU_IOCTL_HOOK_TYPE _IOR('K', 101, struct ksu_hook_type_cmd)
|
||||
#define KSU_IOCTL_ENABLE_KPM _IOR('K', 102, struct ksu_enable_kpm_cmd)
|
||||
#define KSU_IOCTL_DYNAMIC_MANAGER _IOWR('K', 103, struct ksu_dynamic_manager_cmd)
|
||||
#define KSU_IOCTL_GET_MANAGERS _IOWR('K', 104, struct ksu_get_managers_cmd)
|
||||
#define KSU_IOCTL_ENABLE_UID_SCANNER _IOWR('K', 105, struct ksu_enable_uid_scanner_cmd)
|
||||
|
||||
// IOCTL handler types
|
||||
typedef int (*ksu_ioctl_handler_t)(void __user *arg);
|
||||
typedef bool (*ksu_perm_check_t)(void);
|
||||
|
||||
// Permission check functions
|
||||
bool perm_check_manager(void);
|
||||
bool perm_check_root(void);
|
||||
bool perm_check_basic(void);
|
||||
bool perm_check_all(void);
|
||||
|
||||
// IOCTL command mapping
|
||||
struct ksu_ioctl_cmd_map {
|
||||
unsigned int cmd;
|
||||
ksu_ioctl_handler_t handler;
|
||||
ksu_perm_check_t perm_check; // Permission check function
|
||||
};
|
||||
|
||||
// Install KSU fd to current process
|
||||
int ksu_install_fd(void);
|
||||
|
||||
#endif // __KSU_H_SUPERCALLS
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "klog.h"
|
||||
#include "throne_comm.h"
|
||||
#include "kernel_compat.h"
|
||||
#include "ksu.h"
|
||||
|
||||
#define PROC_UID_SCANNER "ksu_uid_scanner"
|
||||
#define UID_SCANNER_STATE_FILE "/data/adb/ksu/.uid_scanner"
|
||||
@@ -18,7 +19,6 @@ static struct work_struct scan_work;
|
||||
static struct work_struct ksu_state_save_work;
|
||||
static struct work_struct ksu_state_load_work;
|
||||
|
||||
extern bool ksu_uid_scanner_enabled;
|
||||
|
||||
// Signal userspace to rescan
|
||||
static bool need_rescan = false;
|
||||
@@ -145,6 +145,7 @@ static ssize_t uid_scanner_write(struct file *file, const char __user *buffer,
|
||||
return count;
|
||||
}
|
||||
|
||||
#ifdef KSU_COMPAT_HAS_PROC_OPS
|
||||
static const struct proc_ops uid_scanner_proc_ops = {
|
||||
.proc_open = uid_scanner_open,
|
||||
.proc_read = seq_read,
|
||||
@@ -152,6 +153,16 @@ static const struct proc_ops uid_scanner_proc_ops = {
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = single_release,
|
||||
};
|
||||
#else
|
||||
static const struct file_operations uid_scanner_proc_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = uid_scanner_open,
|
||||
.read = seq_read,
|
||||
.write = uid_scanner_write,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
#endif
|
||||
|
||||
int ksu_throne_comm_init(void)
|
||||
{
|
||||
|
||||
@@ -289,8 +289,23 @@ FILLDIR_RETURN_TYPE user_data_actor(struct dir_context *ctx, const char *name,
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
|
||||
/*
|
||||
4.11, also backported on lineage common kernel 4.9 !!
|
||||
int vfs_getattr(const struct path *path, struct kstat *stat,
|
||||
u32 request_mask, unsigned int query_flags)
|
||||
|
||||
4.10
|
||||
int vfs_getattr(struct path *path, struct kstat *stat)
|
||||
|
||||
basically no mask and flags for =< 4.10
|
||||
|
||||
*/
|
||||
struct kstat stat;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0) || defined(KSU_HAS_NEW_VFS_GETATTR)
|
||||
err = vfs_getattr(&path, &stat, STATX_UID, AT_STATX_SYNC_AS_STAT);
|
||||
#else
|
||||
err = vfs_getattr(&path, &stat);
|
||||
#endif
|
||||
path_put(&path);
|
||||
|
||||
if (err) {
|
||||
@@ -393,7 +408,6 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
return FILLDIR_ACTOR_CONTINUE; // Skip staging package
|
||||
}
|
||||
|
||||
|
||||
if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir,
|
||||
namelen, name) >= DATA_PATH_LEN) {
|
||||
pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen,
|
||||
@@ -416,7 +430,11 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
} else {
|
||||
if ((namelen == 8) && (strncmp(name, "base.apk", namelen) == 0)) {
|
||||
struct apk_path_hash *pos, *n;
|
||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
|
||||
unsigned int hash = full_name_hash(dirpath, strlen(dirpath));
|
||||
#else
|
||||
unsigned int hash = full_name_hash(NULL, dirpath, strlen(dirpath));
|
||||
#endif
|
||||
list_for_each_entry(pos, &apk_path_hash_list, list) {
|
||||
if (hash == pos->hash) {
|
||||
pos->exists = true;
|
||||
@@ -441,7 +459,6 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
apk_data->exists = true;
|
||||
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;
|
||||
@@ -556,9 +573,7 @@ static bool is_uid_exist(uid_t uid, char *package, void *data)
|
||||
return exist;
|
||||
}
|
||||
|
||||
extern bool ksu_uid_scanner_enabled;
|
||||
|
||||
void track_throne()
|
||||
void track_throne(void)
|
||||
{
|
||||
struct list_head uid_list;
|
||||
struct uid_data *np, *n;
|
||||
@@ -636,12 +651,12 @@ out:
|
||||
}
|
||||
}
|
||||
|
||||
void ksu_throne_tracker_init()
|
||||
void ksu_throne_tracker_init(void)
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
void ksu_throne_tracker_exit()
|
||||
void ksu_throne_tracker_exit(void)
|
||||
{
|
||||
// nothing to do
|
||||
}
|
||||
@@ -6,17 +6,12 @@
|
||||
#include <android/log.h>
|
||||
#include <string.h>
|
||||
|
||||
|
||||
NativeBridge(becomeManager, jboolean, jstring pkg) {
|
||||
const char* cpkg = GetEnvironment()->GetStringUTFChars(env, pkg, JNI_FALSE);
|
||||
bool result = become_manager(cpkg);
|
||||
|
||||
GetEnvironment()->ReleaseStringUTFChars(env, pkg, cpkg);
|
||||
return result;
|
||||
}
|
||||
|
||||
NativeBridgeNP(getVersion, jint) {
|
||||
return get_version();
|
||||
uint32_t version = get_version();
|
||||
if (version > INT32_MAX) {
|
||||
LogDebug("Version overflow: %u", version);
|
||||
}
|
||||
return (jint)version;
|
||||
}
|
||||
|
||||
// get VERSION FULL
|
||||
@@ -27,15 +22,18 @@ NativeBridgeNP(getFullVersion, jstring) {
|
||||
}
|
||||
|
||||
NativeBridgeNP(getAllowList, jintArray) {
|
||||
int uids[1024];
|
||||
int size = 0;
|
||||
bool result = get_allow_list(uids, &size);
|
||||
|
||||
LogDebug("getAllowList: %d, size: %d", result, size);
|
||||
struct ksu_get_allow_list_cmd cmd = {};
|
||||
bool result = get_allow_list(&cmd);
|
||||
|
||||
if (result) {
|
||||
jintArray array = GetEnvironment()->NewIntArray(env, size);
|
||||
GetEnvironment()->SetIntArrayRegion(env, array, 0, size, uids);
|
||||
jsize array_size = (jsize)cmd.count;
|
||||
if (array_size < 0 || (unsigned int)array_size != cmd.count) {
|
||||
LogDebug("Invalid array size: %u", cmd.count);
|
||||
return GetEnvironment()->NewIntArray(env, 0);
|
||||
}
|
||||
|
||||
jintArray array = GetEnvironment()->NewIntArray(env, array_size);
|
||||
GetEnvironment()->SetIntArrayRegion(env, array, 0, array_size, (const jint *)(cmd.uids));
|
||||
|
||||
return array;
|
||||
}
|
||||
@@ -51,6 +49,10 @@ NativeBridgeNP(isLkmMode, jboolean) {
|
||||
return is_lkm_mode();
|
||||
}
|
||||
|
||||
NativeBridgeNP(isManager, jboolean) {
|
||||
return is_manager();
|
||||
}
|
||||
|
||||
static void fillIntArray(JNIEnv *env, jobject list, int *data, int count) {
|
||||
jclass cls = GetEnvironment()->GetObjectClass(env, list);
|
||||
jmethodID add = GetEnvironment()->GetMethodID(env, cls, "add", "(Ljava/lang/Object;)Z");
|
||||
@@ -124,7 +126,7 @@ NativeBridge(getAppProfile, jobject, jstring pkg, jint uid) {
|
||||
strcpy(profile.key, key);
|
||||
profile.current_uid = uid;
|
||||
|
||||
bool useDefaultProfile = !get_app_profile(key, &profile);
|
||||
bool useDefaultProfile = get_app_profile(&profile) != 0;
|
||||
|
||||
jclass cls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$Profile");
|
||||
jmethodID constructor = GetEnvironment()->GetMethodID(env, cls, "<init>", "()V");
|
||||
@@ -305,8 +307,8 @@ NativeBridgeNP(isKPMEnabled, jboolean) {
|
||||
|
||||
// Get HOOK type
|
||||
NativeBridgeNP(getHookType, jstring) {
|
||||
char hook_type[16];
|
||||
get_hook_type(hook_type, sizeof(hook_type));
|
||||
char hook_type[32] = { 0 };
|
||||
get_hook_type((char *) &hook_type);
|
||||
return GetEnvironment()->NewStringUTF(env, hook_type);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,11 @@
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <android/log.h>
|
||||
#include <dirent.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <sys/syscall.h>
|
||||
|
||||
#include "prelude.h"
|
||||
#include "ksu.h"
|
||||
@@ -21,170 +26,193 @@ extern const char* zako_file_verrcidx2str(uint8_t index);
|
||||
|
||||
#endif // __aarch64__ || _M_ARM64 || __arm__ || _M_ARM
|
||||
|
||||
#define KERNEL_SU_OPTION 0xDEADBEEF
|
||||
static int fd = -1;
|
||||
|
||||
#define CMD_GRANT_ROOT 0
|
||||
|
||||
#define CMD_BECOME_MANAGER 1
|
||||
#define CMD_GET_VERSION 2
|
||||
#define CMD_ALLOW_SU 3
|
||||
#define CMD_DENY_SU 4
|
||||
#define CMD_GET_SU_LIST 5
|
||||
#define CMD_GET_DENY_LIST 6
|
||||
#define CMD_CHECK_SAFEMODE 9
|
||||
|
||||
#define CMD_GET_APP_PROFILE 10
|
||||
#define CMD_SET_APP_PROFILE 11
|
||||
|
||||
#define CMD_IS_UID_GRANTED_ROOT 12
|
||||
#define CMD_IS_UID_SHOULD_UMOUNT 13
|
||||
#define CMD_IS_SU_ENABLED 14
|
||||
#define CMD_ENABLE_SU 15
|
||||
|
||||
#define CMD_GET_VERSION_FULL 0xC0FFEE1A
|
||||
|
||||
#define CMD_ENABLE_KPM 100
|
||||
#define CMD_HOOK_TYPE 101
|
||||
#define CMD_DYNAMIC_MANAGER 103
|
||||
#define CMD_GET_MANAGERS 104
|
||||
#define CMD_ENABLE_UID_SCANNER 105
|
||||
|
||||
#define DYNAMIC_MANAGER_OP_SET 0
|
||||
#define DYNAMIC_MANAGER_OP_GET 1
|
||||
#define DYNAMIC_MANAGER_OP_CLEAR 2
|
||||
|
||||
static bool ksuctl(int cmd, void* arg1, void* arg2) {
|
||||
int32_t result = 0;
|
||||
int32_t rtn = prctl(KERNEL_SU_OPTION, cmd, arg1, arg2, &result);
|
||||
|
||||
return result == KERNEL_SU_OPTION && rtn == -1;
|
||||
}
|
||||
|
||||
bool become_manager(const char* pkg) {
|
||||
char param[128];
|
||||
uid_t uid = getuid();
|
||||
uint32_t userId = uid / 100000;
|
||||
if (userId == 0) {
|
||||
sprintf(param, "/data/data/%s", pkg);
|
||||
} else {
|
||||
snprintf(param, sizeof(param), "/data/user/%d/%s", userId, pkg);
|
||||
static inline int scan_driver_fd() {
|
||||
const char *kName = "[ksu_driver]";
|
||||
DIR *fd_dir = opendir("/proc/self/fd");
|
||||
if (!fd_dir) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return ksuctl(CMD_BECOME_MANAGER, param, NULL);
|
||||
}
|
||||
int found = -1;
|
||||
struct dirent *de;
|
||||
char path[64];
|
||||
char target[PATH_MAX];
|
||||
|
||||
// cache the result to avoid unnecessary syscall
|
||||
static bool is_lkm;
|
||||
int get_version() {
|
||||
int32_t version = -1;
|
||||
int32_t flags = 0;
|
||||
ksuctl(CMD_GET_VERSION, &version, &flags);
|
||||
if (!is_lkm && (flags & 0x1)) {
|
||||
is_lkm = true;
|
||||
while ((de = readdir(fd_dir)) != NULL) {
|
||||
if (de->d_name[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
return version;
|
||||
|
||||
char *endptr = nullptr;
|
||||
long fd_long = strtol(de->d_name, &endptr, 10);
|
||||
if (!de->d_name[0] || *endptr != '\0' || fd_long < 0 || fd_long > INT_MAX) {
|
||||
continue;
|
||||
}
|
||||
|
||||
snprintf(path, sizeof(path), "/proc/self/fd/%s", de->d_name);
|
||||
ssize_t n = readlink(path, target, sizeof(target) - 1);
|
||||
if (n < 0) {
|
||||
continue;
|
||||
}
|
||||
target[n] = '\0';
|
||||
|
||||
const char *base = strrchr(target, '/');
|
||||
base = base ? base + 1 : target;
|
||||
|
||||
if (strstr(base, kName)) {
|
||||
found = (int)fd_long;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
closedir(fd_dir);
|
||||
return found;
|
||||
}
|
||||
|
||||
void get_full_version(char* buff) {
|
||||
ksuctl(CMD_GET_VERSION_FULL, buff, NULL);
|
||||
static int ksuctl(unsigned long op, void* arg) {
|
||||
if (fd < 0) {
|
||||
fd = scan_driver_fd();
|
||||
}
|
||||
return ioctl(fd, op, arg);
|
||||
}
|
||||
|
||||
bool get_allow_list(int *uids, int *size) {
|
||||
return ksuctl(CMD_GET_SU_LIST, uids, size);
|
||||
static struct ksu_get_info_cmd g_version = {0};
|
||||
|
||||
struct ksu_get_info_cmd get_info() {
|
||||
if (!g_version.version) {
|
||||
ksuctl(KSU_IOCTL_GET_INFO, &g_version);
|
||||
}
|
||||
return g_version;
|
||||
}
|
||||
|
||||
uint32_t get_version() {
|
||||
auto info = get_info();
|
||||
return info.version;
|
||||
}
|
||||
|
||||
bool get_allow_list(struct ksu_get_allow_list_cmd *cmd) {
|
||||
return ksuctl(KSU_IOCTL_GET_ALLOW_LIST, cmd) == 0;
|
||||
}
|
||||
|
||||
bool is_safe_mode() {
|
||||
return ksuctl(CMD_CHECK_SAFEMODE, NULL, NULL);
|
||||
struct ksu_check_safemode_cmd cmd = {};
|
||||
ksuctl(KSU_IOCTL_CHECK_SAFEMODE, &cmd);
|
||||
return cmd.in_safe_mode;
|
||||
}
|
||||
|
||||
bool is_lkm_mode() {
|
||||
// you should call get_version first!
|
||||
return is_lkm;
|
||||
auto info = get_info();
|
||||
return (info.flags & 0x1) != 0;
|
||||
}
|
||||
|
||||
bool is_manager() {
|
||||
auto info = get_info();
|
||||
return (info.flags & 0x2) != 0;
|
||||
}
|
||||
|
||||
bool uid_should_umount(int uid) {
|
||||
int should;
|
||||
return ksuctl(CMD_IS_UID_SHOULD_UMOUNT, (void*) ((size_t) uid), &should) && should;
|
||||
struct ksu_uid_should_umount_cmd cmd = {};
|
||||
cmd.uid = uid;
|
||||
ksuctl(KSU_IOCTL_UID_SHOULD_UMOUNT, &cmd);
|
||||
return cmd.should_umount;
|
||||
}
|
||||
|
||||
bool set_app_profile(const struct app_profile* profile) {
|
||||
return ksuctl(CMD_SET_APP_PROFILE, (void*) profile, NULL);
|
||||
bool set_app_profile(const struct app_profile *profile) {
|
||||
struct ksu_set_app_profile_cmd cmd = {};
|
||||
cmd.profile = *profile;
|
||||
return ksuctl(KSU_IOCTL_SET_APP_PROFILE, &cmd) == 0;
|
||||
}
|
||||
|
||||
bool get_app_profile(char* key, struct app_profile* profile) {
|
||||
return ksuctl(CMD_GET_APP_PROFILE, profile, NULL);
|
||||
int get_app_profile(struct app_profile *profile) {
|
||||
struct ksu_get_app_profile_cmd cmd = {.profile = *profile};
|
||||
int ret = ksuctl(KSU_IOCTL_GET_APP_PROFILE, &cmd);
|
||||
*profile = cmd.profile;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool set_su_enabled(bool enabled) {
|
||||
return ksuctl(CMD_ENABLE_SU, (void*) enabled, NULL);
|
||||
struct ksu_enable_su_cmd cmd = {.enable = enabled};
|
||||
return ksuctl(KSU_IOCTL_ENABLE_SU, &cmd) == 0;
|
||||
}
|
||||
|
||||
bool is_su_enabled() {
|
||||
int enabled = true;
|
||||
// if ksuctl failed, we assume su is enabled, and it cannot be disabled.
|
||||
ksuctl(CMD_IS_SU_ENABLED, &enabled, NULL);
|
||||
return enabled;
|
||||
struct ksu_is_su_enabled_cmd cmd = {};
|
||||
return ksuctl(KSU_IOCTL_IS_SU_ENABLED, &cmd) == 0 && cmd.enabled;
|
||||
}
|
||||
|
||||
bool is_KPM_enable() {
|
||||
int enabled = false;
|
||||
ksuctl(CMD_ENABLE_KPM, &enabled, NULL);
|
||||
return enabled;
|
||||
void get_full_version(char* buff) {
|
||||
struct ksu_get_full_version_cmd cmd = {0};
|
||||
if (ksuctl(KSU_IOCTL_GET_FULL_VERSION, &cmd) == 0) {
|
||||
strncpy(buff, cmd.version_full, KSU_FULL_VERSION_STRING - 1);
|
||||
buff[KSU_FULL_VERSION_STRING - 1] = '\0';
|
||||
} else {
|
||||
buff[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
bool get_hook_type(char* hook_type, size_t size) {
|
||||
if (hook_type == NULL || size == 0) {
|
||||
bool is_KPM_enable(void)
|
||||
{
|
||||
struct ksu_enable_kpm_cmd cmd = {};
|
||||
return ksuctl(KSU_IOCTL_ENABLE_KPM, &cmd) == 0 && cmd.enabled;
|
||||
}
|
||||
|
||||
void get_hook_type(char *buff)
|
||||
{
|
||||
struct ksu_hook_type_cmd cmd = {0};
|
||||
if (ksuctl(KSU_IOCTL_HOOK_TYPE, &cmd) == 0) {
|
||||
strncpy(buff, cmd.hook_type, 32 - 1);
|
||||
buff[32 - 1] = '\0';
|
||||
} else {
|
||||
strcpy(buff, "Unknown");
|
||||
}
|
||||
}
|
||||
|
||||
bool set_dynamic_manager(unsigned int size, const char *hash)
|
||||
{
|
||||
struct ksu_dynamic_manager_cmd cmd = {0};
|
||||
cmd.config.operation = DYNAMIC_MANAGER_OP_SET;
|
||||
cmd.config.size = size;
|
||||
strlcpy(cmd.config.hash, hash, sizeof(cmd.config.hash));
|
||||
|
||||
return ksuctl(KSU_IOCTL_DYNAMIC_MANAGER, &cmd) == 0;
|
||||
}
|
||||
|
||||
bool get_dynamic_manager(struct dynamic_manager_user_config *cfg)
|
||||
{
|
||||
if (!cfg)
|
||||
return false;
|
||||
}
|
||||
|
||||
static char cached_hook_type[16] = {0};
|
||||
if (cached_hook_type[0] == '\0') {
|
||||
if (!ksuctl(CMD_HOOK_TYPE, cached_hook_type, NULL)) {
|
||||
strcpy(cached_hook_type, "Unknown");
|
||||
}
|
||||
}
|
||||
struct ksu_dynamic_manager_cmd cmd = {0};
|
||||
cmd.config.operation = DYNAMIC_MANAGER_OP_GET;
|
||||
|
||||
strncpy(hook_type, cached_hook_type, size);
|
||||
hook_type[size - 1] = '\0';
|
||||
if (ksuctl(KSU_IOCTL_DYNAMIC_MANAGER, &cmd) != 0)
|
||||
return false;
|
||||
|
||||
*cfg = cmd.config;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool set_dynamic_manager(unsigned int size, const char* hash) {
|
||||
if (hash == NULL) {
|
||||
bool clear_dynamic_manager(void)
|
||||
{
|
||||
struct ksu_dynamic_manager_cmd cmd = {0};
|
||||
cmd.config.operation = DYNAMIC_MANAGER_OP_CLEAR;
|
||||
return ksuctl(KSU_IOCTL_DYNAMIC_MANAGER, &cmd) == 0;
|
||||
}
|
||||
|
||||
bool get_managers_list(struct manager_list_info *info)
|
||||
{
|
||||
if (!info)
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dynamic_manager_user_config config;
|
||||
config.operation = DYNAMIC_MANAGER_OP_SET;
|
||||
config.size = size;
|
||||
strncpy(config.hash, hash, sizeof(config.hash) - 1);
|
||||
config.hash[sizeof(config.hash) - 1] = '\0';
|
||||
|
||||
return ksuctl(CMD_DYNAMIC_MANAGER, &config, NULL);
|
||||
}
|
||||
|
||||
bool get_dynamic_manager(struct dynamic_manager_user_config* config) {
|
||||
if (config == NULL) {
|
||||
struct ksu_get_managers_cmd cmd = {0};
|
||||
if (ksuctl(KSU_IOCTL_GET_MANAGERS, &cmd) != 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
config->operation = DYNAMIC_MANAGER_OP_GET;
|
||||
return ksuctl(CMD_DYNAMIC_MANAGER, config, NULL);
|
||||
*info = cmd.manager_info;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool clear_dynamic_manager() {
|
||||
struct dynamic_manager_user_config config;
|
||||
config.operation = DYNAMIC_MANAGER_OP_CLEAR;
|
||||
return ksuctl(CMD_DYNAMIC_MANAGER, &config, NULL);
|
||||
}
|
||||
|
||||
bool get_managers_list(struct manager_list_info* info) {
|
||||
if (info == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ksuctl(CMD_GET_MANAGERS, info, NULL);
|
||||
}
|
||||
|
||||
bool verify_module_signature(const char* input) {
|
||||
#if defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM)
|
||||
@@ -193,13 +221,13 @@ bool verify_module_signature(const char* input) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd = zako_sys_file_open(input);
|
||||
if (fd < 0) {
|
||||
int file_fd = zako_sys_file_open(input);
|
||||
if (file_fd < 0) {
|
||||
LogDebug("verify_module_signature: failed to open file: %s", input);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t results = zako_file_verify_esig(fd, 0);
|
||||
uint32_t results = zako_file_verify_esig(file_fd, 0);
|
||||
|
||||
if (results != 0) {
|
||||
/* If important error occured, verification process should
|
||||
@@ -233,7 +261,7 @@ bool verify_module_signature(const char* input) {
|
||||
}
|
||||
|
||||
exit:
|
||||
close(fd);
|
||||
close(file_fd);
|
||||
LogDebug("verify_module_signature: path=%s, results=0x%x, success=%s",
|
||||
input, results, (results == 0) ? "true" : "false");
|
||||
return results == 0;
|
||||
@@ -243,16 +271,31 @@ bool verify_module_signature(const char* input) {
|
||||
#endif
|
||||
}
|
||||
|
||||
bool is_uid_scanner_enabled() {
|
||||
bool is_uid_scanner_enabled(void)
|
||||
{
|
||||
bool status = false;
|
||||
ksuctl(CMD_ENABLE_UID_SCANNER, (void*)0, &status);
|
||||
return status;
|
||||
|
||||
struct ksu_enable_uid_scanner_cmd cmd = {
|
||||
.operation = UID_SCANNER_OP_GET_STATUS,
|
||||
.status_ptr = (__u64)(uintptr_t)&status
|
||||
};
|
||||
|
||||
return ksuctl(KSU_IOCTL_ENABLE_UID_SCANNER, &cmd) == 0 != 0 && status;
|
||||
}
|
||||
|
||||
bool set_uid_scanner_enabled(bool enabled) {
|
||||
return ksuctl(CMD_ENABLE_UID_SCANNER, (void*)1, (void*)enabled);
|
||||
bool set_uid_scanner_enabled(bool enabled)
|
||||
{
|
||||
struct ksu_enable_uid_scanner_cmd cmd = {
|
||||
.operation = UID_SCANNER_OP_TOGGLE,
|
||||
.enabled = enabled
|
||||
};
|
||||
return ksuctl(KSU_IOCTL_ENABLE_UID_SCANNER, &cmd);
|
||||
}
|
||||
|
||||
bool clear_uid_scanner_environment() {
|
||||
return ksuctl(CMD_ENABLE_UID_SCANNER, (void*)2, NULL);
|
||||
bool clear_uid_scanner_environment(void)
|
||||
{
|
||||
struct ksu_enable_uid_scanner_cmd cmd = {
|
||||
.operation = UID_SCANNER_OP_CLEAR_ENV
|
||||
};
|
||||
return ksuctl(KSU_IOCTL_ENABLE_UID_SCANNER, &cmd);
|
||||
}
|
||||
@@ -8,14 +8,11 @@
|
||||
#include "prelude.h"
|
||||
#include <linux/capability.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
bool become_manager(const char *);
|
||||
#define KSU_FULL_VERSION_STRING 255
|
||||
|
||||
void get_full_version(char* buff);
|
||||
|
||||
int get_version();
|
||||
|
||||
bool get_allow_list(int *uids, int *size);
|
||||
uint32_t get_version();
|
||||
|
||||
bool uid_should_umount(int uid);
|
||||
|
||||
@@ -23,6 +20,10 @@ bool is_safe_mode();
|
||||
|
||||
bool is_lkm_mode();
|
||||
|
||||
bool is_manager();
|
||||
|
||||
void get_full_version(char* buff);
|
||||
|
||||
#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.
|
||||
@@ -33,6 +34,10 @@ bool is_lkm_mode();
|
||||
#define DYNAMIC_MANAGER_OP_GET 1
|
||||
#define DYNAMIC_MANAGER_OP_CLEAR 2
|
||||
|
||||
#define UID_SCANNER_OP_GET_STATUS 0
|
||||
#define UID_SCANNER_OP_TOGGLE 1
|
||||
#define UID_SCANNER_OP_CLEAR_ENV 2
|
||||
|
||||
struct dynamic_manager_user_config {
|
||||
unsigned int operation;
|
||||
unsigned int size;
|
||||
@@ -98,7 +103,7 @@ struct manager_list_info {
|
||||
|
||||
bool set_app_profile(const struct app_profile* profile);
|
||||
|
||||
bool get_app_profile(char* key, struct app_profile* profile);
|
||||
int get_app_profile(struct app_profile* profile);
|
||||
|
||||
bool set_su_enabled(bool enabled);
|
||||
|
||||
@@ -106,8 +111,7 @@ bool is_su_enabled();
|
||||
|
||||
bool is_KPM_enable();
|
||||
|
||||
bool get_hook_type(char* hook_type, size_t size);
|
||||
|
||||
void get_hook_type(char* hook_type);
|
||||
|
||||
bool set_dynamic_manager(unsigned int size, const char* hash);
|
||||
|
||||
@@ -125,4 +129,119 @@ bool set_uid_scanner_enabled(bool enabled);
|
||||
|
||||
bool clear_uid_scanner_environment();
|
||||
|
||||
struct ksu_become_daemon_cmd {
|
||||
__u8 token[65]; // Input: daemon token (null-terminated)
|
||||
};
|
||||
|
||||
struct ksu_get_info_cmd {
|
||||
__u32 version; // Output: KERNEL_SU_VERSION
|
||||
__u32 flags; // Output: flags (bit 0: MODULE mode)
|
||||
};
|
||||
|
||||
struct ksu_report_event_cmd {
|
||||
__u32 event; // Input: EVENT_POST_FS_DATA, EVENT_BOOT_COMPLETED, etc.
|
||||
};
|
||||
|
||||
struct ksu_set_sepolicy_cmd {
|
||||
__u64 cmd; // Input: sepolicy command
|
||||
__aligned_u64 arg; // Input: sepolicy argument pointer
|
||||
};
|
||||
|
||||
struct ksu_check_safemode_cmd {
|
||||
__u8 in_safe_mode; // Output: true if in safe mode, false otherwise
|
||||
};
|
||||
|
||||
struct ksu_get_allow_list_cmd {
|
||||
__u32 uids[128]; // Output: array of allowed/denied UIDs
|
||||
__u32 count; // Output: number of UIDs in array
|
||||
__u8 allow; // Input: true for allow list, false for deny list
|
||||
};
|
||||
|
||||
struct ksu_uid_granted_root_cmd {
|
||||
__u32 uid; // Input: target UID to check
|
||||
__u8 granted; // Output: true if granted, false otherwise
|
||||
};
|
||||
|
||||
struct ksu_uid_should_umount_cmd {
|
||||
__u32 uid; // Input: target UID to check
|
||||
__u8 should_umount; // Output: true if should umount, false otherwise
|
||||
};
|
||||
|
||||
struct ksu_get_manager_uid_cmd {
|
||||
__u32 uid; // Output: manager UID
|
||||
};
|
||||
|
||||
struct ksu_set_manager_uid_cmd {
|
||||
__u32 uid; // Input: new manager UID
|
||||
};
|
||||
|
||||
struct ksu_get_app_profile_cmd {
|
||||
struct app_profile profile; // Input/Output: app profile structure
|
||||
};
|
||||
|
||||
struct ksu_set_app_profile_cmd {
|
||||
struct app_profile profile; // Input: app profile structure
|
||||
};
|
||||
|
||||
struct ksu_is_su_enabled_cmd {
|
||||
__u8 enabled; // Output: true if su compat enabled
|
||||
};
|
||||
|
||||
struct ksu_enable_su_cmd {
|
||||
__u8 enable; // Input: true to enable, false to disable
|
||||
};
|
||||
|
||||
// Other command structures
|
||||
struct ksu_get_full_version_cmd {
|
||||
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string
|
||||
};
|
||||
|
||||
struct ksu_hook_type_cmd {
|
||||
char hook_type[32]; // Output: hook type string
|
||||
};
|
||||
|
||||
struct ksu_enable_kpm_cmd {
|
||||
__u8 enabled; // Output: true if KPM is enabled
|
||||
};
|
||||
|
||||
struct ksu_dynamic_manager_cmd {
|
||||
struct dynamic_manager_user_config config; // Input/Output: dynamic manager config
|
||||
};
|
||||
|
||||
struct ksu_get_managers_cmd {
|
||||
struct manager_list_info manager_info; // Output: manager list information
|
||||
};
|
||||
|
||||
struct ksu_enable_uid_scanner_cmd {
|
||||
__u32 operation; // Input: operation type (UID_SCANNER_OP_GET_STATUS, UID_SCANNER_OP_TOGGLE, UID_SCANNER_OP_CLEAR_ENV)
|
||||
__u32 enabled; // Input: enable or disable (for UID_SCANNER_OP_TOGGLE)
|
||||
__u64 status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS)
|
||||
};
|
||||
|
||||
// IOCTL command definitions
|
||||
#define KSU_IOCTL_GRANT_ROOT _IO('K', 1)
|
||||
#define KSU_IOCTL_GET_INFO _IOR('K', 2, struct ksu_get_info_cmd)
|
||||
#define KSU_IOCTL_REPORT_EVENT _IOW('K', 3, struct ksu_report_event_cmd)
|
||||
#define KSU_IOCTL_SET_SEPOLICY _IOWR('K', 4, struct ksu_set_sepolicy_cmd)
|
||||
#define KSU_IOCTL_CHECK_SAFEMODE _IOR('K', 5, struct ksu_check_safemode_cmd)
|
||||
#define KSU_IOCTL_GET_ALLOW_LIST _IOWR('K', 6, struct ksu_get_allow_list_cmd)
|
||||
#define KSU_IOCTL_GET_DENY_LIST _IOWR('K', 7, struct ksu_get_allow_list_cmd)
|
||||
#define KSU_IOCTL_UID_GRANTED_ROOT _IOWR('K', 8, struct ksu_uid_granted_root_cmd)
|
||||
#define KSU_IOCTL_UID_SHOULD_UMOUNT _IOWR('K', 9, struct ksu_uid_should_umount_cmd)
|
||||
#define KSU_IOCTL_GET_MANAGER_UID _IOR('K', 10, struct ksu_get_manager_uid_cmd)
|
||||
#define KSU_IOCTL_GET_APP_PROFILE _IOWR('K', 11, struct ksu_get_app_profile_cmd)
|
||||
#define KSU_IOCTL_SET_APP_PROFILE _IOW('K', 12, struct ksu_set_app_profile_cmd)
|
||||
#define KSU_IOCTL_IS_SU_ENABLED _IOR('K', 13, struct ksu_is_su_enabled_cmd)
|
||||
#define KSU_IOCTL_ENABLE_SU _IOW('K', 14, struct ksu_enable_su_cmd)
|
||||
|
||||
// Other IOCTL command definitions
|
||||
#define KSU_IOCTL_GET_FULL_VERSION _IOR('K', 100, struct ksu_get_full_version_cmd)
|
||||
#define KSU_IOCTL_HOOK_TYPE _IOR('K', 101, struct ksu_hook_type_cmd)
|
||||
#define KSU_IOCTL_ENABLE_KPM _IOR('K', 102, struct ksu_enable_kpm_cmd)
|
||||
#define KSU_IOCTL_DYNAMIC_MANAGER _IOWR('K', 103, struct ksu_dynamic_manager_cmd)
|
||||
#define KSU_IOCTL_GET_MANAGERS _IOWR('K', 104, struct ksu_get_managers_cmd)
|
||||
#define KSU_IOCTL_ENABLE_UID_SCANNER _IOWR('K', 105, struct ksu_enable_uid_scanner_cmd)
|
||||
|
||||
bool get_allow_list(struct ksu_get_allow_list_cmd*);
|
||||
|
||||
#endif //KERNELSU_KSU_H
|
||||
@@ -16,17 +16,15 @@ object Natives {
|
||||
// 10946: add capabilities
|
||||
// 10977: change groups_count and groups to avoid overflow write
|
||||
// 11071: Fix the issue of failing to set a custom SELinux type.
|
||||
const val MINIMAL_SUPPORTED_KERNEL = 11071
|
||||
const val MINIMAL_SUPPORTED_KERNEL_FULL = "v3.1.8"
|
||||
|
||||
// 11640: Support query working mode, LKM or GKI
|
||||
// when MINIMAL_SUPPORTED_KERNEL > 11640, we can remove this constant.
|
||||
const val MINIMAL_SUPPORTED_KERNEL_LKM = 11648
|
||||
// 12143: breaking: new supercall impl
|
||||
const val MINIMAL_SUPPORTED_KERNEL = 12143
|
||||
|
||||
// 12040: Support disable sucompat mode
|
||||
const val MINIMAL_SUPPORTED_SU_COMPAT = 12040
|
||||
const val KERNEL_SU_DOMAIN = "u:r:su:s0"
|
||||
|
||||
const val MINIMAL_SUPPORTED_KERNEL_FULL = "v3.1.8"
|
||||
|
||||
const val MINIMAL_SUPPORTED_KPM = 12800
|
||||
|
||||
const val MINIMAL_SUPPORTED_DYNAMIC_MANAGER = 13215
|
||||
@@ -66,8 +64,6 @@ object Natives {
|
||||
System.loadLibrary("kernelsu")
|
||||
}
|
||||
|
||||
// become root manager, return true if success.
|
||||
external fun becomeManager(pkg: String?): Boolean
|
||||
val version: Int
|
||||
external get
|
||||
|
||||
@@ -81,6 +77,9 @@ object Natives {
|
||||
val isLkmMode: Boolean
|
||||
external get
|
||||
|
||||
val isManager: Boolean
|
||||
external get
|
||||
|
||||
external fun uidShouldUmount(uid: Int): Boolean
|
||||
|
||||
/**
|
||||
@@ -99,6 +98,7 @@ object Natives {
|
||||
*/
|
||||
external fun isSuEnabled(): Boolean
|
||||
external fun setSuEnabled(enabled: Boolean): Boolean
|
||||
external fun grantRoot(): Boolean
|
||||
external fun isKPMEnabled(): Boolean
|
||||
external fun getHookType(): String
|
||||
|
||||
|
||||
@@ -74,7 +74,7 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val isManager = Natives.becomeManager(packageName)
|
||||
val isManager = Natives.isManager
|
||||
if (isManager) {
|
||||
install()
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import com.ramcosta.composedestinations.spec.RouteOrDirection
|
||||
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
|
||||
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
|
||||
import com.sukisu.ultra.Natives
|
||||
import com.sukisu.ultra.ksuApp
|
||||
import com.sukisu.ultra.ui.MainActivity
|
||||
import com.sukisu.ultra.ui.activity.util.*
|
||||
import com.sukisu.ultra.ui.activity.util.AppData.getKpmVersionUse
|
||||
@@ -29,7 +28,7 @@ import com.sukisu.ultra.ui.util.*
|
||||
@Composable
|
||||
fun BottomBar(navController: NavHostController) {
|
||||
val navigator = navController.rememberDestinationsNavigator()
|
||||
val isFullFeatured = AppData.isFullFeatured(ksuApp.packageName)
|
||||
val isFullFeatured = AppData.isFullFeatured()
|
||||
val kpmVersion = getKpmVersionUse()
|
||||
val cardColor = MaterialTheme.colorScheme.surfaceContainer
|
||||
val activity = LocalContext.current as MainActivity
|
||||
|
||||
@@ -175,8 +175,8 @@ object AppData {
|
||||
/**
|
||||
* 检查是否是完整功能模式
|
||||
*/
|
||||
fun isFullFeatured(packageName: String): Boolean {
|
||||
val isManager = Natives.becomeManager(packageName)
|
||||
fun isFullFeatured(): Boolean {
|
||||
val isManager = Natives.isManager
|
||||
return isManager && !Natives.requireNewKernel() && rootAvailable()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import com.sukisu.ultra.ksuApp
|
||||
fun KsuIsValid(
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val isManager = Natives.isManager
|
||||
val ksuVersion = if (isManager) Natives.version else null
|
||||
|
||||
if (ksuVersion != null) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.sukisu.ultra.ui.screen
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
@@ -48,8 +49,9 @@ import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
|
||||
import com.sukisu.ultra.ui.theme.getCardColors
|
||||
import com.sukisu.ultra.ui.theme.getCardElevation
|
||||
import com.sukisu.ultra.ui.util.*
|
||||
import com.topjohnwu.superuser.ShellUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.time.LocalDateTime
|
||||
@@ -179,127 +181,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
)
|
||||
// UID 扫描开关
|
||||
if (Natives.version >= Natives.MINIMAL_SUPPORTED_UID_SCANNER) {
|
||||
var uidAutoScanEnabled by rememberSaveable {
|
||||
mutableStateOf(prefs.getBoolean("uid_auto_scan", false))
|
||||
}
|
||||
|
||||
var uidMultiUserScanEnabled by rememberSaveable {
|
||||
mutableStateOf(prefs.getBoolean("uid_multi_user_scan", false))
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
uidAutoScanEnabled = Natives.isUidScannerEnabled()
|
||||
uidMultiUserScanEnabled = getUidMultiUserScan()
|
||||
|
||||
prefs.edit {
|
||||
putBoolean("uid_auto_scan", uidAutoScanEnabled)
|
||||
putBoolean("uid_multi_user_scan", uidMultiUserScanEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
// 用户态扫描应用列表开关
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.Scanner,
|
||||
title = stringResource(R.string.uid_auto_scan_title),
|
||||
summary = stringResource(R.string.uid_auto_scan_summary),
|
||||
checked = uidAutoScanEnabled,
|
||||
onCheckedChange = { enabled ->
|
||||
scope.launch {
|
||||
try {
|
||||
if (setUidAutoScan(enabled)) {
|
||||
uidAutoScanEnabled = enabled
|
||||
prefs.edit { putBoolean("uid_auto_scan", enabled) }
|
||||
|
||||
if (!enabled) {
|
||||
uidMultiUserScanEnabled = false
|
||||
prefs.edit { putBoolean("uid_multi_user_scan", false) }
|
||||
}
|
||||
} else {
|
||||
snackBarHost.showSnackbar(context.getString(R.string.uid_scanner_setting_failed))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
snackBarHost.showSnackbar(
|
||||
context.getString(
|
||||
R.string.uid_scanner_setting_error,
|
||||
e.message ?: ""
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// 多用户应用扫描开关 - 仅在启用用户态扫描时显示
|
||||
AnimatedVisibility(
|
||||
visible = uidAutoScanEnabled,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically()
|
||||
) {
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.Groups,
|
||||
title = stringResource(R.string.uid_multi_user_scan_title),
|
||||
summary = stringResource(R.string.uid_multi_user_scan_summary),
|
||||
checked = uidMultiUserScanEnabled,
|
||||
onCheckedChange = { enabled ->
|
||||
scope.launch {
|
||||
try {
|
||||
if (setUidMultiUserScan(enabled)) {
|
||||
uidMultiUserScanEnabled = enabled
|
||||
prefs.edit { putBoolean("uid_multi_user_scan", enabled) }
|
||||
} else {
|
||||
snackBarHost.showSnackbar(context.getString(R.string.uid_scanner_setting_failed))
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
snackBarHost.showSnackbar(
|
||||
context.getString(
|
||||
R.string.uid_scanner_setting_error,
|
||||
e.message ?: ""
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
// 清理运行环境
|
||||
AnimatedVisibility(
|
||||
visible = uidAutoScanEnabled,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically()
|
||||
) {
|
||||
val confirmDialog = rememberConfirmDialog()
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
SettingItem(
|
||||
icon = Icons.Filled.CleaningServices,
|
||||
title = stringResource(R.string.clean_runtime_environment),
|
||||
summary = stringResource(R.string.clean_runtime_environment_summary),
|
||||
onClick = {
|
||||
scope.launch {
|
||||
val result = confirmDialog.awaitConfirm(
|
||||
title = context.getString(R.string.clean_runtime_environment),
|
||||
content = context.getString(R.string.clean_runtime_environment_confirm)
|
||||
)
|
||||
if (result == ConfirmResult.Confirmed) {
|
||||
val cleanResult = cleanRuntimeEnvironment()
|
||||
if (cleanResult) {
|
||||
uidAutoScanEnabled = false
|
||||
prefs.edit { putBoolean("uid_auto_scan", false) }
|
||||
|
||||
uidMultiUserScanEnabled = false
|
||||
prefs.edit { putBoolean("uid_multi_user_scan", false) }
|
||||
|
||||
Natives.setUidScannerEnabled(false)
|
||||
|
||||
snackBarHost.showSnackbar(context.getString(R.string.clean_runtime_environment_success))
|
||||
} else {
|
||||
snackBarHost.showSnackbar(context.getString(R.string.clean_runtime_environment_failed))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
UidScannerSection(prefs, snackBarHost, scope, context)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -453,7 +335,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
)
|
||||
}
|
||||
|
||||
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
|
||||
val lkmMode = Natives.isLkmMode
|
||||
if (lkmMode) {
|
||||
UninstallItem(navigator) {
|
||||
loadingDialog.withLoading(it)
|
||||
@@ -481,24 +363,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
|
||||
fun cleanRuntimeEnvironment(): Boolean {
|
||||
val shell = getRootShell()
|
||||
return try {
|
||||
try {
|
||||
ShellUtils.fastCmd(shell, "/data/adb/uid_scanner stop")
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
ShellUtils.fastCmdResult(shell, "rm -rf /data/misc/user_uid")
|
||||
ShellUtils.fastCmdResult(shell, "rm -rf /data/adb/uid_scanner")
|
||||
ShellUtils.fastCmdResult(shell, "rm -rf /data/adb/ksu/bin/user_uid")
|
||||
ShellUtils.fastCmdResult(shell, "rm -rf /data/adb/service.d/uid_scanner.sh")
|
||||
Natives.clearUidScannerEnvironment()
|
||||
true
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SettingsGroupCard(
|
||||
title: String,
|
||||
@@ -961,3 +825,124 @@ private fun TopBar(
|
||||
scrollBehavior = scrollBehavior
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UidScannerSection(
|
||||
prefs: SharedPreferences,
|
||||
snackBarHost: SnackbarHostState,
|
||||
scope: CoroutineScope,
|
||||
context: Context
|
||||
) {
|
||||
if (Natives.version < Natives.MINIMAL_SUPPORTED_UID_SCANNER) return
|
||||
|
||||
val realAuto = Natives.isUidScannerEnabled()
|
||||
val realMulti = getUidMultiUserScan()
|
||||
|
||||
var autoOn by remember { mutableStateOf(realAuto) }
|
||||
var multiOn by remember { mutableStateOf(realMulti) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
autoOn = realAuto
|
||||
multiOn = realMulti
|
||||
prefs.edit {
|
||||
putBoolean("uid_auto_scan", autoOn)
|
||||
putBoolean("uid_multi_user_scan", multiOn)
|
||||
}
|
||||
}
|
||||
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.Scanner,
|
||||
title = stringResource(R.string.uid_auto_scan_title),
|
||||
summary = stringResource(R.string.uid_auto_scan_summary),
|
||||
checked = autoOn,
|
||||
onCheckedChange = { target ->
|
||||
autoOn = target
|
||||
if (!target) multiOn = false
|
||||
|
||||
scope.launch(Dispatchers.IO) {
|
||||
setUidAutoScan(target)
|
||||
val actual = Natives.isUidScannerEnabled() || readUidScannerFile()
|
||||
withContext(Dispatchers.Main) {
|
||||
autoOn = actual
|
||||
if (!actual) multiOn = false
|
||||
prefs.edit {
|
||||
putBoolean("uid_auto_scan", actual)
|
||||
putBoolean("uid_multi_user_scan", multiOn)
|
||||
}
|
||||
if (actual != target) {
|
||||
snackBarHost.showSnackbar(
|
||||
context.getString(R.string.uid_scanner_setting_failed)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = autoOn,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically()
|
||||
) {
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.Groups,
|
||||
title = stringResource(R.string.uid_multi_user_scan_title),
|
||||
summary = stringResource(R.string.uid_multi_user_scan_summary),
|
||||
checked = multiOn,
|
||||
onCheckedChange = { target ->
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val ok = setUidMultiUserScan(target)
|
||||
withContext(Dispatchers.Main) {
|
||||
if (ok) {
|
||||
multiOn = target
|
||||
prefs.edit { putBoolean("uid_multi_user_scan", target) }
|
||||
} else {
|
||||
snackBarHost.showSnackbar(
|
||||
context.getString(R.string.uid_scanner_setting_failed)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = autoOn,
|
||||
enter = fadeIn() + expandVertically(),
|
||||
exit = fadeOut() + shrinkVertically()
|
||||
) {
|
||||
val confirmDialog = rememberConfirmDialog()
|
||||
SettingItem(
|
||||
icon = Icons.Filled.CleaningServices,
|
||||
title = stringResource(R.string.clean_runtime_environment),
|
||||
summary = stringResource(R.string.clean_runtime_environment_summary),
|
||||
onClick = {
|
||||
scope.launch {
|
||||
if (confirmDialog.awaitConfirm(
|
||||
title = context.getString(R.string.clean_runtime_environment),
|
||||
content = context.getString(R.string.clean_runtime_environment_confirm)
|
||||
) == ConfirmResult.Confirmed
|
||||
) {
|
||||
if (cleanRuntimeEnvironment()) {
|
||||
autoOn = false
|
||||
multiOn = false
|
||||
prefs.edit {
|
||||
putBoolean("uid_auto_scan", false)
|
||||
putBoolean("uid_multi_user_scan", false)
|
||||
}
|
||||
Natives.setUidScannerEnabled(false)
|
||||
snackBarHost.showSnackbar(
|
||||
context.getString(R.string.clean_runtime_environment_success)
|
||||
)
|
||||
} else {
|
||||
snackBarHost.showSnackbar(
|
||||
context.getString(R.string.clean_runtime_environment_failed)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import com.sukisu.ultra.Natives
|
||||
import com.sukisu.ultra.ksuApp
|
||||
import org.json.JSONArray
|
||||
import java.io.File
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
|
||||
/**
|
||||
@@ -34,7 +35,7 @@ private fun getKsuDaemonPath(): String {
|
||||
}
|
||||
|
||||
object KsuCli {
|
||||
val SHELL: Shell = createRootShell()
|
||||
var SHELL: Shell = createRootShell()
|
||||
val GLOBAL_MNT_SHELL: Shell = createRootShell(true)
|
||||
}
|
||||
|
||||
@@ -578,11 +579,10 @@ fun getUidScannerDaemonPath(): String {
|
||||
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libuid_scanner.so"
|
||||
}
|
||||
|
||||
private const val targetPath = "/data/adb/uid_scanner"
|
||||
fun ensureUidScannerExecutable(): Boolean {
|
||||
val shell = getRootShell()
|
||||
val uidScannerPath = getUidScannerDaemonPath()
|
||||
val targetPath = "/data/adb/uid_scanner"
|
||||
|
||||
if (!ShellUtils.fastCmdResult(shell, "test -f $targetPath")) {
|
||||
val copyResult = ShellUtils.fastCmdResult(shell, "cp $uidScannerPath $targetPath")
|
||||
if (!copyResult) {
|
||||
@@ -593,7 +593,6 @@ fun ensureUidScannerExecutable(): Boolean {
|
||||
val result = ShellUtils.fastCmdResult(shell, "chmod 755 $targetPath")
|
||||
return result
|
||||
}
|
||||
private const val targetPath = "/data/adb/uid_scanner"
|
||||
|
||||
fun setUidAutoScan(enabled: Boolean): Boolean {
|
||||
val shell = getRootShell()
|
||||
@@ -634,3 +633,30 @@ fun getUidMultiUserScan(): Boolean {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun cleanRuntimeEnvironment(): Boolean {
|
||||
val shell = getRootShell()
|
||||
return try {
|
||||
try {
|
||||
ShellUtils.fastCmd(shell, "/data/adb/uid_scanner stop")
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
ShellUtils.fastCmdResult(shell, "rm -rf /data/misc/user_uid")
|
||||
ShellUtils.fastCmdResult(shell, "rm -rf /data/adb/uid_scanner")
|
||||
ShellUtils.fastCmdResult(shell, "rm -rf /data/adb/ksu/bin/user_uid")
|
||||
ShellUtils.fastCmdResult(shell, "rm -rf /data/adb/service.d/uid_scanner.sh")
|
||||
Natives.clearUidScannerEnvironment()
|
||||
true
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun readUidScannerFile(): Boolean {
|
||||
val shell = getRootShell()
|
||||
return try {
|
||||
ShellUtils.fastCmd(shell, "cat /data/adb/ksu/.uid_scanner").trim() == "1"
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,18 +121,12 @@ class HomeViewModel : ViewModel() {
|
||||
try {
|
||||
val kernelVersion = getKernelVersion()
|
||||
val isManager = try {
|
||||
Natives.becomeManager(ksuApp.packageName ?: "com.sukisu.ultra")
|
||||
Natives.isManager
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
val ksuVersion = if (isManager) {
|
||||
try {
|
||||
Natives.version
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
} else null
|
||||
val ksuVersion = if (isManager) Natives.version else null
|
||||
|
||||
val fullVersion = try {
|
||||
Natives.getFullVersion()
|
||||
@@ -163,13 +157,7 @@ class HomeViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
val lkmMode = ksuVersion?.let {
|
||||
try {
|
||||
if (it >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && kernelVersion.isGKI()) {
|
||||
Natives.isLkmMode
|
||||
} else null
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
if (kernelVersion.isGKI()) Natives.isLkmMode else null
|
||||
}
|
||||
|
||||
val isRootAvailable = try {
|
||||
@@ -346,7 +334,7 @@ class HomeViewModel : ViewModel() {
|
||||
try {
|
||||
// 检查KSU状态是否发生变化
|
||||
val currentKsuVersion = try {
|
||||
if (Natives.becomeManager(ksuApp.packageName ?: "com.sukisu.ultra")) {
|
||||
if (Natives.isManager) {
|
||||
Natives.version
|
||||
} else null
|
||||
} catch (_: Exception) {
|
||||
|
||||
@@ -17,7 +17,7 @@ class KsuLibSuProvider : IProvider {
|
||||
|
||||
override fun isAvailable() = true
|
||||
|
||||
override suspend fun isAuthorized() = Natives.becomeManager(ksuApp.packageName)
|
||||
override suspend fun isAuthorized() = Natives.isManager
|
||||
|
||||
private val serviceIntent
|
||||
get() = PlatformIntent(
|
||||
|
||||
@@ -1,10 +1,133 @@
|
||||
const EVENT_POST_FS_DATA: u64 = 1;
|
||||
const EVENT_BOOT_COMPLETED: u64 = 2;
|
||||
const EVENT_MODULE_MOUNTED: u64 = 3;
|
||||
use std::fs;
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
use std::os::fd::RawFd;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
// Event constants
|
||||
const EVENT_POST_FS_DATA: u32 = 1;
|
||||
const EVENT_BOOT_COMPLETED: u32 = 2;
|
||||
const EVENT_MODULE_MOUNTED: u32 = 3;
|
||||
|
||||
const KSU_IOCTL_GRANT_ROOT: u32 = 0x4B01; // _IO('K', 1)
|
||||
const KSU_IOCTL_GET_INFO: u32 = 0x80084b02; // _IOR('K', 2, struct ksu_get_info_cmd)
|
||||
const KSU_IOCTL_REPORT_EVENT: u32 = 0x40044b03; // _IOW('K', 3, struct ksu_report_event_cmd)
|
||||
const KSU_IOCTL_SET_SEPOLICY: u32 = 0xc0104b04; // _IOWR('K', 4, struct ksu_set_sepolicy_cmd)
|
||||
const KSU_IOCTL_CHECK_SAFEMODE: u32 = 0x80014b05; // _IOR('K', 5, struct ksu_check_safemode_cmd)
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
struct GetInfoCmd {
|
||||
version: u32,
|
||||
flags: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
struct ReportEventCmd {
|
||||
event: u32,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
pub struct SetSepolicyCmd {
|
||||
pub cmd: u64,
|
||||
pub arg: u64,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone, Copy, Default)]
|
||||
struct CheckSafemodeCmd {
|
||||
in_safe_mode: u8,
|
||||
}
|
||||
|
||||
// Global driver fd cache
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
static DRIVER_FD: OnceLock<RawFd> = OnceLock::new();
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
static INFO_CACHE: OnceLock<GetInfoCmd> = OnceLock::new();
|
||||
|
||||
const KSU_INSTALL_MAGIC1: u32 = 0xDEADBEEF;
|
||||
const KSU_INSTALL_MAGIC2: u32 = 0xCAFEBABE;
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn scan_driver_fd() -> Option<RawFd> {
|
||||
let fd_dir = fs::read_dir("/proc/self/fd").ok()?;
|
||||
|
||||
for entry in fd_dir.flatten() {
|
||||
if let Ok(fd_num) = entry.file_name().to_string_lossy().parse::<i32>() {
|
||||
let link_path = format!("/proc/self/fd/{}", fd_num);
|
||||
if let Ok(target) = fs::read_link(&link_path) {
|
||||
let target_str = target.to_string_lossy();
|
||||
if target_str.contains("[ksu_driver]") {
|
||||
return Some(fd_num);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
// Get cached driver fd
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn init_driver_fd() -> Option<RawFd> {
|
||||
let fd = scan_driver_fd();
|
||||
if fd.is_none() {
|
||||
let mut fd = -1;
|
||||
unsafe {
|
||||
libc::syscall(
|
||||
libc::SYS_reboot,
|
||||
KSU_INSTALL_MAGIC1,
|
||||
KSU_INSTALL_MAGIC2,
|
||||
0,
|
||||
&mut fd,
|
||||
);
|
||||
};
|
||||
if fd >= 0 { Some(fd) } else { None }
|
||||
} else {
|
||||
fd
|
||||
}
|
||||
}
|
||||
|
||||
// ioctl wrapper using libc
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn ksuctl<T>(request: u32, arg: *mut T) -> std::io::Result<i32> {
|
||||
use std::io;
|
||||
|
||||
let fd = *DRIVER_FD.get_or_init(|| init_driver_fd().unwrap_or(-1));
|
||||
unsafe {
|
||||
#[cfg(not(target_env = "gnu"))]
|
||||
let ret = libc::ioctl(fd as libc::c_int, request as i32, arg);
|
||||
#[cfg(target_env = "gnu")]
|
||||
let ret = libc::ioctl(fd as libc::c_int, request as u64, arg);
|
||||
if ret < 0 {
|
||||
Err(io::Error::last_os_error())
|
||||
} else {
|
||||
Ok(ret)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
fn ksuctl<T>(_request: u32, _arg: *mut T) -> std::io::Result<i32> {
|
||||
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
||||
}
|
||||
|
||||
// API implementations
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn get_info() -> GetInfoCmd {
|
||||
*INFO_CACHE.get_or_init(|| {
|
||||
let mut cmd = GetInfoCmd {
|
||||
version: 0,
|
||||
flags: 0,
|
||||
};
|
||||
let _ = ksuctl(KSU_IOCTL_GET_INFO, &mut cmd as *mut _);
|
||||
cmd
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn get_version() -> i32 {
|
||||
rustix::process::ksu_get_version()
|
||||
get_info().version as i32
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
@@ -13,22 +136,24 @@ pub fn get_version() -> i32 {
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
fn report_event(event: u64) {
|
||||
rustix::process::ksu_report_event(event)
|
||||
pub fn grant_root() -> std::io::Result<()> {
|
||||
ksuctl(KSU_IOCTL_GRANT_ROOT, std::ptr::null_mut::<u8>())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
fn report_event(_event: u64) {}
|
||||
pub fn grant_root() -> std::io::Result<()> {
|
||||
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn check_kernel_safemode() -> bool {
|
||||
rustix::process::ksu_check_kernel_safemode()
|
||||
fn report_event(event: u32) {
|
||||
let mut cmd = ReportEventCmd { event };
|
||||
let _ = ksuctl(KSU_IOCTL_REPORT_EVENT, &mut cmd as *mut _);
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn check_kernel_safemode() -> bool {
|
||||
false
|
||||
}
|
||||
fn report_event(_event: u32) {}
|
||||
|
||||
pub fn report_post_fs_data() {
|
||||
report_event(EVENT_POST_FS_DATA);
|
||||
@@ -41,3 +166,21 @@ pub fn report_boot_complete() {
|
||||
pub fn report_module_mounted() {
|
||||
report_event(EVENT_MODULE_MOUNTED);
|
||||
}
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn check_kernel_safemode() -> bool {
|
||||
let mut cmd = CheckSafemodeCmd { in_safe_mode: 0 };
|
||||
let _ = ksuctl(KSU_IOCTL_CHECK_SAFEMODE, &mut cmd as *mut _);
|
||||
cmd.in_safe_mode != 0
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "linux", target_os = "android")))]
|
||||
pub fn check_kernel_safemode() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub fn set_sepolicy(cmd: &SetSepolicyCmd) -> std::io::Result<()> {
|
||||
let mut ioctl_cmd = *cmd;
|
||||
ksuctl(KSU_IOCTL_SET_SEPOLICY, &mut ioctl_cmd as *mut _)?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -20,7 +20,7 @@ fn parse_single_word(input: &str) -> IResult<&str, &str> {
|
||||
take_while1(is_sepolicy_char).parse(input)
|
||||
}
|
||||
|
||||
fn parse_bracket_objs<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> {
|
||||
fn parse_bracket_objs(input: &str) -> IResult<&str, SeObject<'_>> {
|
||||
let (input, (_, words, _)) = (
|
||||
tag("{"),
|
||||
take_while_m_n(1, 100, |c: char| is_sepolicy_char(c) || c.is_whitespace()),
|
||||
@@ -30,12 +30,12 @@ fn parse_bracket_objs<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> {
|
||||
Ok((input, words.split_whitespace().collect()))
|
||||
}
|
||||
|
||||
fn parse_single_obj<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> {
|
||||
fn parse_single_obj(input: &str) -> IResult<&str, SeObject<'_>> {
|
||||
let (input, word) = take_while1(is_sepolicy_char).parse(input)?;
|
||||
Ok((input, vec![word]))
|
||||
}
|
||||
|
||||
fn parse_star<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> {
|
||||
fn parse_star(input: &str) -> IResult<&str, SeObject<'_>> {
|
||||
let (input, _) = tag("*").parse(input)?;
|
||||
Ok((input, vec!["*"]))
|
||||
}
|
||||
@@ -43,12 +43,12 @@ fn parse_star<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> {
|
||||
// 1. a single sepolicy word
|
||||
// 2. { obj1 obj2 obj3 ...}
|
||||
// 3. *
|
||||
fn parse_seobj<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> {
|
||||
fn parse_seobj(input: &str) -> IResult<&str, SeObject<'_>> {
|
||||
let (input, strs) = alt((parse_single_obj, parse_bracket_objs, parse_star)).parse(input)?;
|
||||
Ok((input, strs))
|
||||
}
|
||||
|
||||
fn parse_seobj_no_star<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> {
|
||||
fn parse_seobj_no_star(input: &str) -> IResult<&str, SeObject<'_>> {
|
||||
let (input, strs) = alt((parse_single_obj, parse_bracket_objs)).parse(input)?;
|
||||
Ok((input, strs))
|
||||
}
|
||||
@@ -697,7 +697,12 @@ fn apply_one_rule<'a>(statement: &'a PolicyStatement<'a>, strict: bool) -> Resul
|
||||
let policies: Vec<AtomicStatement> = statement.try_into()?;
|
||||
|
||||
for policy in policies {
|
||||
if !rustix::process::ksu_set_policy(&FfiPolicy::from(policy)) {
|
||||
let ffi_policy = FfiPolicy::from(policy);
|
||||
let cmd = crate::ksucalls::SetSepolicyCmd {
|
||||
cmd: 0,
|
||||
arg: &ffi_policy as *const _ as u64,
|
||||
};
|
||||
if crate::ksucalls::set_sepolicy(&cmd).is_err() {
|
||||
log::warn!("apply rule: {statement:?} failed.");
|
||||
if strict {
|
||||
return Err(anyhow::anyhow!("apply rule {:?} failed.", statement));
|
||||
|
||||
@@ -17,7 +17,7 @@ use crate::{
|
||||
|
||||
#[cfg(any(target_os = "linux", target_os = "android"))]
|
||||
pub fn grant_root(global_mnt: bool) -> Result<()> {
|
||||
rustix::process::ksu_grant_root()?;
|
||||
crate::ksucalls::grant_root()?;
|
||||
|
||||
let mut command = Command::new("sh");
|
||||
let command = unsafe {
|
||||
|
||||
Reference in New Issue
Block a user