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 aa2cbbf.
* 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:
@@ -56,8 +56,8 @@ ColumnLimit: 80
|
|||||||
CommentPragmas: '^ IWYU pragma:'
|
CommentPragmas: '^ IWYU pragma:'
|
||||||
#CompactNamespaces: false # Unknown to clang-format-4.0
|
#CompactNamespaces: false # Unknown to clang-format-4.0
|
||||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||||
ConstructorInitializerIndentWidth: 8
|
ConstructorInitializerIndentWidth: 4
|
||||||
ContinuationIndentWidth: 8
|
ContinuationIndentWidth: 4
|
||||||
Cpp11BracedListStyle: false
|
Cpp11BracedListStyle: false
|
||||||
DerivePointerAlignment: false
|
DerivePointerAlignment: false
|
||||||
DisableFormat: false
|
DisableFormat: false
|
||||||
@@ -501,7 +501,7 @@ IncludeCategories:
|
|||||||
IncludeIsMainRegex: '(Test)?$'
|
IncludeIsMainRegex: '(Test)?$'
|
||||||
IndentCaseLabels: false
|
IndentCaseLabels: false
|
||||||
#IndentPPDirectives: None # Unknown to clang-format-5.0
|
#IndentPPDirectives: None # Unknown to clang-format-5.0
|
||||||
IndentWidth: 8
|
IndentWidth: 4
|
||||||
IndentWrappedFunctionNames: false
|
IndentWrappedFunctionNames: false
|
||||||
JavaScriptQuotes: Leave
|
JavaScriptQuotes: Leave
|
||||||
JavaScriptWrapImports: true
|
JavaScriptWrapImports: true
|
||||||
@@ -511,7 +511,7 @@ MacroBlockEnd: ''
|
|||||||
MaxEmptyLinesToKeep: 1
|
MaxEmptyLinesToKeep: 1
|
||||||
NamespaceIndentation: None
|
NamespaceIndentation: None
|
||||||
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
|
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
|
||||||
ObjCBlockIndentWidth: 8
|
ObjCBlockIndentWidth: 4
|
||||||
ObjCSpaceAfterProperty: true
|
ObjCSpaceAfterProperty: true
|
||||||
ObjCSpaceBeforeProtocolList: true
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
|
||||||
@@ -543,6 +543,6 @@ SpacesInCStyleCastParentheses: false
|
|||||||
SpacesInParentheses: false
|
SpacesInParentheses: false
|
||||||
SpacesInSquareBrackets: false
|
SpacesInSquareBrackets: false
|
||||||
Standard: Cpp03
|
Standard: Cpp03
|
||||||
TabWidth: 8
|
TabWidth: 4
|
||||||
UseTab: Always
|
UseTab: Never
|
||||||
...
|
...
|
||||||
|
|||||||
@@ -1,42 +1,42 @@
|
|||||||
menu "KernelSU"
|
menu "KernelSU"
|
||||||
|
|
||||||
config KSU
|
config KSU
|
||||||
tristate "KernelSU function support"
|
tristate "KernelSU function support"
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
Enable kernel-level root privileges on Android System.
|
Enable kernel-level root privileges on Android System.
|
||||||
To compile as a module, choose M here: the
|
To compile as a module, choose M here: the
|
||||||
module will be called kernelsu.
|
module will be called kernelsu.
|
||||||
|
|
||||||
config KSU_DEBUG
|
config KSU_DEBUG
|
||||||
bool "KernelSU debug mode"
|
bool "KernelSU debug mode"
|
||||||
depends on KSU
|
depends on KSU
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enable KernelSU debug mode.
|
Enable KernelSU debug mode.
|
||||||
|
|
||||||
config KSU_MULTI_MANAGER_SUPPORT
|
config KSU_MULTI_MANAGER_SUPPORT
|
||||||
bool "Multi KernelSU manager support"
|
bool "Multi KernelSU manager support"
|
||||||
depends on KSU
|
depends on KSU
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enable multi KernelSU manager support
|
Enable multi KernelSU manager support
|
||||||
|
|
||||||
config KSU_THRONE_TRACKER_LEGACY
|
config KSU_THRONE_TRACKER_LEGACY
|
||||||
bool "Use legacy throne tracker (packages.list scanning)"
|
bool "Use legacy throne tracker (packages.list scanning)"
|
||||||
depends on KSU
|
depends on KSU
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Use legacy throne tracker that scans packages.list for app UIDs.
|
Use legacy throne tracker that scans packages.list for app UIDs.
|
||||||
This is kept for Ultra-Legacy Linux 4.4-3.X kernels which are prone to deadlocks.
|
This is kept for Ultra-Legacy Linux 4.4-3.X kernels which are prone to deadlocks.
|
||||||
Enable this if default scanning deadlocks/crashes on you.
|
Enable this if default scanning deadlocks/crashes on you.
|
||||||
|
|
||||||
config KSU_MANUAL_SU
|
config KSU_MANUAL_SU
|
||||||
bool "Use manual su"
|
bool "Use manual su"
|
||||||
depends on KSU
|
depends on KSU
|
||||||
default y
|
default y
|
||||||
help
|
help
|
||||||
Use manual su and authorize the corresponding command line and application via prctl
|
Use manual su and authorize the corresponding command line and application via prctl
|
||||||
|
|
||||||
config KSU_ALLOWLIST_WORKAROUND
|
config KSU_ALLOWLIST_WORKAROUND
|
||||||
bool "KernelSU Session Keyring Init workaround"
|
bool "KernelSU Session Keyring Init workaround"
|
||||||
@@ -47,23 +47,23 @@ config KSU_ALLOWLIST_WORKAROUND
|
|||||||
Useful for situations where the SU allowlist is not kept after a reboot
|
Useful for situations where the SU allowlist is not kept after a reboot
|
||||||
|
|
||||||
config KSU_CMDLINE
|
config KSU_CMDLINE
|
||||||
bool "Enable KernelSU cmdline"
|
bool "Enable KernelSU cmdline"
|
||||||
depends on KSU && KSU != m
|
depends on KSU && KSU != m
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enable a cmdline called kernelsu.enabled
|
Enable a cmdline called kernelsu.enabled
|
||||||
Value 1 means enabled, value 0 means disabled.
|
Value 1 means enabled, value 0 means disabled.
|
||||||
|
|
||||||
config KPM
|
config KPM
|
||||||
bool "Enable SukiSU KPM"
|
bool "Enable SukiSU KPM"
|
||||||
depends on KSU && 64BIT
|
depends on KSU && 64BIT
|
||||||
select KALLSYMS
|
select KALLSYMS
|
||||||
select KALLSYMS_ALL
|
select KALLSYMS_ALL
|
||||||
default n
|
default n
|
||||||
help
|
help
|
||||||
Enabling this option will activate the KPM feature of SukiSU.
|
Enabling this option will activate the KPM feature of SukiSU.
|
||||||
This option is suitable for scenarios where you need to force KPM to be enabled.
|
This option is suitable for scenarios where you need to force KPM to be enabled.
|
||||||
but it may affect system stability.
|
but it may affect system stability.
|
||||||
|
|
||||||
choice
|
choice
|
||||||
prompt "KernelSU hook type"
|
prompt "KernelSU hook type"
|
||||||
@@ -74,21 +74,21 @@ choice
|
|||||||
|
|
||||||
config KSU_KPROBES_HOOK
|
config KSU_KPROBES_HOOK
|
||||||
bool "Hook KernelSU with Kprobes"
|
bool "Hook KernelSU with Kprobes"
|
||||||
depends on KPROBES
|
depends on KPROBES
|
||||||
help
|
help
|
||||||
If enabled, Hook required KernelSU syscalls with Kernel-probe.
|
If enabled, Hook required KernelSU syscalls with Kernel-probe.
|
||||||
|
|
||||||
config KSU_TRACEPOINT_HOOK
|
config KSU_TRACEPOINT_HOOK
|
||||||
bool "Hook KernelSU with Tracepoint"
|
bool "Hook KernelSU with Tracepoint"
|
||||||
depends on TRACEPOINTS
|
depends on TRACEPOINTS
|
||||||
help
|
help
|
||||||
If enabled, Hook required KernelSU syscalls with Tracepoint.
|
If enabled, Hook required KernelSU syscalls with Tracepoint.
|
||||||
|
|
||||||
config KSU_MANUAL_HOOK
|
config KSU_MANUAL_HOOK
|
||||||
bool "Hook KernelSU manually"
|
bool "Hook KernelSU manually"
|
||||||
depends on KSU != m
|
depends on KSU != m
|
||||||
help
|
help
|
||||||
If enabled, Hook required KernelSU syscalls with manually-patched function.
|
If enabled, Hook required KernelSU syscalls with manually-patched function.
|
||||||
|
|
||||||
endchoice
|
endchoice
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,9 @@ kernelsu-objs += allowlist.o
|
|||||||
kernelsu-objs += dynamic_manager.o
|
kernelsu-objs += dynamic_manager.o
|
||||||
kernelsu-objs += apk_sign.o
|
kernelsu-objs += apk_sign.o
|
||||||
kernelsu-objs += sucompat.o
|
kernelsu-objs += sucompat.o
|
||||||
|
kernelsu-objs += pkg_observer.o
|
||||||
kernelsu-objs += core_hook.o
|
kernelsu-objs += core_hook.o
|
||||||
|
kernelsu-objs += supercalls.o
|
||||||
kernelsu-objs += ksud.o
|
kernelsu-objs += ksud.o
|
||||||
kernelsu-objs += embed_ksud.o
|
kernelsu-objs += embed_ksud.o
|
||||||
kernelsu-objs += kernel_compat.o
|
kernelsu-objs += kernel_compat.o
|
||||||
|
|||||||
@@ -36,53 +36,53 @@ static int allow_list_pointer __read_mostly = 0;
|
|||||||
|
|
||||||
static void remove_uid_from_arr(uid_t uid)
|
static void remove_uid_from_arr(uid_t uid)
|
||||||
{
|
{
|
||||||
int *temp_arr;
|
int *temp_arr;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
if (allow_list_pointer == 0)
|
if (allow_list_pointer == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
||||||
if (temp_arr == NULL) {
|
if (temp_arr == NULL) {
|
||||||
pr_err("%s: unable to allocate memory\n", __func__);
|
pr_err("%s: unable to allocate memory\n", __func__);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = j = 0; i < allow_list_pointer; i++) {
|
for (i = j = 0; i < allow_list_pointer; i++) {
|
||||||
if (allow_list_arr[i] == uid)
|
if (allow_list_arr[i] == uid)
|
||||||
continue;
|
continue;
|
||||||
temp_arr[j++] = allow_list_arr[i];
|
temp_arr[j++] = allow_list_arr[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
allow_list_pointer = j;
|
allow_list_pointer = j;
|
||||||
|
|
||||||
for (; j < ARRAY_SIZE(allow_list_arr); j++)
|
for (; j < ARRAY_SIZE(allow_list_arr); j++)
|
||||||
temp_arr[j] = -1;
|
temp_arr[j] = -1;
|
||||||
|
|
||||||
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
|
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
|
||||||
kfree(temp_arr);
|
kfree(temp_arr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void init_default_profiles(void)
|
static void init_default_profiles(void)
|
||||||
{
|
{
|
||||||
kernel_cap_t full_cap = CAP_FULL_SET;
|
kernel_cap_t full_cap = CAP_FULL_SET;
|
||||||
|
|
||||||
default_root_profile.uid = 0;
|
default_root_profile.uid = 0;
|
||||||
default_root_profile.gid = 0;
|
default_root_profile.gid = 0;
|
||||||
default_root_profile.groups_count = 1;
|
default_root_profile.groups_count = 1;
|
||||||
default_root_profile.groups[0] = 0;
|
default_root_profile.groups[0] = 0;
|
||||||
memcpy(&default_root_profile.capabilities.effective, &full_cap,
|
memcpy(&default_root_profile.capabilities.effective, &full_cap,
|
||||||
sizeof(default_root_profile.capabilities.effective));
|
sizeof(default_root_profile.capabilities.effective));
|
||||||
default_root_profile.namespaces = 0;
|
default_root_profile.namespaces = 0;
|
||||||
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||||
|
|
||||||
// This means that we will umount modules by default!
|
// This means that we will umount modules by default!
|
||||||
default_non_root_profile.umount_modules = true;
|
default_non_root_profile.umount_modules = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct perm_data {
|
struct perm_data {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct app_profile profile;
|
struct app_profile profile;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct list_head allow_list;
|
static struct list_head allow_list;
|
||||||
@@ -99,432 +99,432 @@ static bool persistent_allow_list(void);
|
|||||||
|
|
||||||
void ksu_show_allow_list(void)
|
void ksu_show_allow_list(void)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
pr_info("ksu_show_allow_list\n");
|
pr_info("ksu_show_allow_list\n");
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
|
pr_info("uid :%d, allow: %d\n", p->profile.current_uid,
|
||||||
p->profile.allow_su);
|
p->profile.allow_su);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
static void ksu_grant_root_to_shell(void)
|
static void ksu_grant_root_to_shell(void)
|
||||||
{
|
{
|
||||||
struct app_profile profile = {
|
struct app_profile profile = {
|
||||||
.version = KSU_APP_PROFILE_VER,
|
.version = KSU_APP_PROFILE_VER,
|
||||||
.allow_su = true,
|
.allow_su = true,
|
||||||
.current_uid = 2000,
|
.current_uid = 2000,
|
||||||
};
|
};
|
||||||
strcpy(profile.key, "com.android.shell");
|
strcpy(profile.key, "com.android.shell");
|
||||||
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
|
||||||
ksu_set_app_profile(&profile, false);
|
ksu_set_app_profile(&profile, false);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool ksu_get_app_profile(struct app_profile *profile)
|
bool ksu_get_app_profile(struct app_profile *profile)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
bool uid_match = profile->current_uid == p->profile.current_uid;
|
bool uid_match = profile->current_uid == p->profile.current_uid;
|
||||||
if (uid_match) {
|
if (uid_match) {
|
||||||
// found it, override it with ours
|
// found it, override it with ours
|
||||||
memcpy(profile, &p->profile, sizeof(*profile));
|
memcpy(profile, &p->profile, sizeof(*profile));
|
||||||
found = true;
|
found = true;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
return found;
|
return found;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool forbid_system_uid(uid_t uid) {
|
static inline bool forbid_system_uid(uid_t uid) {
|
||||||
#define SHELL_UID 2000
|
#define SHELL_UID 2000
|
||||||
#define SYSTEM_UID 1000
|
#define SYSTEM_UID 1000
|
||||||
return uid < SHELL_UID && uid != SYSTEM_UID;
|
return uid < SHELL_UID && uid != SYSTEM_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool profile_valid(struct app_profile *profile)
|
static bool profile_valid(struct app_profile *profile)
|
||||||
{
|
{
|
||||||
if (!profile) {
|
if (!profile) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (profile->version < KSU_APP_PROFILE_VER) {
|
if (profile->version < KSU_APP_PROFILE_VER) {
|
||||||
pr_info("Unsupported profile version: %d\n", profile->version);
|
pr_info("Unsupported profile version: %d\n", profile->version);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (profile->allow_su) {
|
if (profile->allow_su) {
|
||||||
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
|
if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
|
if (strlen(profile->rp_config.profile.selinux_domain) == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
bool result = false;
|
bool result = false;
|
||||||
|
|
||||||
if (!profile_valid(profile)) {
|
if (!profile_valid(profile)) {
|
||||||
pr_err("Failed to set app profile: invalid profile!\n");
|
pr_err("Failed to set app profile: invalid profile!\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
// both uid and package must match, otherwise it will break multiple package with different user id
|
// both uid and package must match, otherwise it will break multiple package with different user id
|
||||||
if (profile->current_uid == p->profile.current_uid &&
|
if (profile->current_uid == p->profile.current_uid &&
|
||||||
!strcmp(profile->key, p->profile.key)) {
|
!strcmp(profile->key, p->profile.key)) {
|
||||||
// found it, just override it all!
|
// found it, just override it all!
|
||||||
memcpy(&p->profile, profile, sizeof(*profile));
|
memcpy(&p->profile, profile, sizeof(*profile));
|
||||||
result = true;
|
result = true;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// not found, alloc a new node!
|
// not found, alloc a new node!
|
||||||
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
|
p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL);
|
||||||
if (!p) {
|
if (!p) {
|
||||||
pr_err("ksu_set_app_profile alloc failed\n");
|
pr_err("ksu_set_app_profile alloc failed\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(&p->profile, profile, sizeof(*profile));
|
memcpy(&p->profile, profile, sizeof(*profile));
|
||||||
if (profile->allow_su) {
|
if (profile->allow_su) {
|
||||||
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
|
pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n",
|
||||||
profile->key, profile->current_uid,
|
profile->key, profile->current_uid,
|
||||||
profile->rp_config.profile.gid,
|
profile->rp_config.profile.gid,
|
||||||
profile->rp_config.profile.selinux_domain);
|
profile->rp_config.profile.selinux_domain);
|
||||||
} else {
|
} else {
|
||||||
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
|
pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n",
|
||||||
profile->key, profile->current_uid,
|
profile->key, profile->current_uid,
|
||||||
profile->nrp_config.profile.umount_modules);
|
profile->nrp_config.profile.umount_modules);
|
||||||
}
|
}
|
||||||
list_add_tail(&p->list, &allow_list);
|
list_add_tail(&p->list, &allow_list);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
if (profile->current_uid <= BITMAP_UID_MAX) {
|
if (profile->current_uid <= BITMAP_UID_MAX) {
|
||||||
if (profile->allow_su)
|
if (profile->allow_su)
|
||||||
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= 1 << (profile->current_uid % BITS_PER_BYTE);
|
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= 1 << (profile->current_uid % BITS_PER_BYTE);
|
||||||
else
|
else
|
||||||
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= ~(1 << (profile->current_uid % BITS_PER_BYTE));
|
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= ~(1 << (profile->current_uid % BITS_PER_BYTE));
|
||||||
} else {
|
} else {
|
||||||
if (profile->allow_su) {
|
if (profile->allow_su) {
|
||||||
/*
|
/*
|
||||||
* 1024 apps with uid higher than BITMAP_UID_MAX
|
* 1024 apps with uid higher than BITMAP_UID_MAX
|
||||||
* registered to request superuser?
|
* registered to request superuser?
|
||||||
*/
|
*/
|
||||||
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
|
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
|
||||||
pr_err("too many apps registered\n");
|
pr_err("too many apps registered\n");
|
||||||
WARN_ON(1);
|
WARN_ON(1);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
allow_list_arr[allow_list_pointer++] = profile->current_uid;
|
allow_list_arr[allow_list_pointer++] = profile->current_uid;
|
||||||
} else {
|
} else {
|
||||||
remove_uid_from_arr(profile->current_uid);
|
remove_uid_from_arr(profile->current_uid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
result = true;
|
result = true;
|
||||||
|
|
||||||
// check if the default profiles is changed, cache it to a single struct to accelerate access.
|
// check if the default profiles is changed, cache it to a single struct to accelerate access.
|
||||||
if (unlikely(!strcmp(profile->key, "$"))) {
|
if (unlikely(!strcmp(profile->key, "$"))) {
|
||||||
// set default non root profile
|
// set default non root profile
|
||||||
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
|
memcpy(&default_non_root_profile, &profile->nrp_config.profile,
|
||||||
sizeof(default_non_root_profile));
|
sizeof(default_non_root_profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (unlikely(!strcmp(profile->key, "#"))) {
|
if (unlikely(!strcmp(profile->key, "#"))) {
|
||||||
// set default root profile
|
// set default root profile
|
||||||
memcpy(&default_root_profile, &profile->rp_config.profile,
|
memcpy(&default_root_profile, &profile->rp_config.profile,
|
||||||
sizeof(default_root_profile));
|
sizeof(default_root_profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (persist)
|
if (persist)
|
||||||
persistent_allow_list();
|
persistent_allow_list();
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool __ksu_is_allow_uid(uid_t uid)
|
bool __ksu_is_allow_uid(uid_t uid)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (unlikely(uid == 0)) {
|
if (unlikely(uid == 0)) {
|
||||||
// already root, but only allow our domain.
|
// already root, but only allow our domain.
|
||||||
return is_ksu_domain();
|
return is_ksu_domain();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (forbid_system_uid(uid)) {
|
if (forbid_system_uid(uid)) {
|
||||||
// do not bother going through the list if it's system
|
// do not bother going through the list if it's system
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
||||||
// manager is always allowed!
|
// manager is always allowed!
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (likely(uid <= BITMAP_UID_MAX)) {
|
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||||
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
|
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < allow_list_pointer; i++) {
|
for (i = 0; i < allow_list_pointer; i++) {
|
||||||
if (allow_list_arr[i] == uid)
|
if (allow_list_arr[i] == uid)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ksu_uid_should_umount(uid_t uid)
|
bool ksu_uid_should_umount(uid_t uid)
|
||||||
{
|
{
|
||||||
struct app_profile profile = { .current_uid = uid };
|
struct app_profile profile = { .current_uid = uid };
|
||||||
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) {
|
||||||
// we should not umount on manager!
|
// we should not umount on manager!
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
bool found = ksu_get_app_profile(&profile);
|
bool found = ksu_get_app_profile(&profile);
|
||||||
if (!found) {
|
if (!found) {
|
||||||
// no app profile found, it must be non root app
|
// no app profile found, it must be non root app
|
||||||
return default_non_root_profile.umount_modules;
|
return default_non_root_profile.umount_modules;
|
||||||
}
|
}
|
||||||
if (profile.allow_su) {
|
if (profile.allow_su) {
|
||||||
// if found and it is granted to su, we shouldn't umount for it
|
// if found and it is granted to su, we shouldn't umount for it
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// found an app profile
|
// found an app profile
|
||||||
if (profile.nrp_config.use_default) {
|
if (profile.nrp_config.use_default) {
|
||||||
return default_non_root_profile.umount_modules;
|
return default_non_root_profile.umount_modules;
|
||||||
} else {
|
} else {
|
||||||
return profile.nrp_config.profile.umount_modules;
|
return profile.nrp_config.profile.umount_modules;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct root_profile *ksu_get_root_profile(uid_t uid)
|
struct root_profile *ksu_get_root_profile(uid_t uid)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
|
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
if (uid == p->profile.current_uid && p->profile.allow_su) {
|
if (uid == p->profile.current_uid && p->profile.allow_su) {
|
||||||
if (!p->profile.rp_config.use_default) {
|
if (!p->profile.rp_config.use_default) {
|
||||||
return &p->profile.rp_config.profile;
|
return &p->profile.rp_config.profile;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// use default profile
|
// use default profile
|
||||||
return &default_root_profile;
|
return &default_root_profile;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ksu_get_allow_list(int *array, int *length, bool allow)
|
bool ksu_get_allow_list(int *array, int *length, bool allow)
|
||||||
{
|
{
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
int i = 0;
|
int i = 0;
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, list);
|
p = list_entry(pos, struct perm_data, list);
|
||||||
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
|
// pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow);
|
||||||
if (p->profile.allow_su == allow) {
|
if (p->profile.allow_su == allow) {
|
||||||
array[i++] = p->profile.current_uid;
|
array[i++] = p->profile.current_uid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*length = i;
|
*length = i;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_save_allow_list(struct work_struct *work)
|
static void do_save_allow_list(struct work_struct *work)
|
||||||
{
|
{
|
||||||
u32 magic = FILE_MAGIC;
|
u32 magic = FILE_MAGIC;
|
||||||
u32 version = FILE_FORMAT_VERSION;
|
u32 version = FILE_FORMAT_VERSION;
|
||||||
struct perm_data *p = NULL;
|
struct perm_data *p = NULL;
|
||||||
struct list_head *pos = NULL;
|
struct list_head *pos = NULL;
|
||||||
loff_t off = 0;
|
loff_t off = 0;
|
||||||
|
|
||||||
struct file *fp =
|
struct file *fp =
|
||||||
ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
|
pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// store magic and version
|
// store magic and version
|
||||||
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
|
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
|
||||||
sizeof(magic)) {
|
sizeof(magic)) {
|
||||||
pr_err("save_allow_list write magic failed.\n");
|
pr_err("save_allow_list write magic failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
|
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
|
||||||
sizeof(version)) {
|
sizeof(version)) {
|
||||||
pr_err("save_allow_list write version failed.\n");
|
pr_err("save_allow_list write version failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each (pos, &allow_list) {
|
list_for_each (pos, &allow_list) {
|
||||||
p = list_entry(pos, struct perm_data, 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.key, p->profile.current_uid,
|
||||||
p->profile.allow_su);
|
p->profile.allow_su);
|
||||||
|
|
||||||
ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile),
|
ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile),
|
||||||
&off);
|
&off);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_load_allow_list(struct work_struct *work)
|
static void do_load_allow_list(struct work_struct *work)
|
||||||
{
|
{
|
||||||
loff_t off = 0;
|
loff_t off = 0;
|
||||||
ssize_t ret = 0;
|
ssize_t ret = 0;
|
||||||
struct file *fp = NULL;
|
struct file *fp = NULL;
|
||||||
u32 magic;
|
u32 magic;
|
||||||
u32 version;
|
u32 version;
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
// always allow adb shell by default
|
// always allow adb shell by default
|
||||||
ksu_grant_root_to_shell();
|
ksu_grant_root_to_shell();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// load allowlist now!
|
// load allowlist now!
|
||||||
fp = ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
|
fp = ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_RDONLY, 0);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
|
pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify magic
|
// verify magic
|
||||||
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) !=
|
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) !=
|
||||||
sizeof(magic) ||
|
sizeof(magic) ||
|
||||||
magic != FILE_MAGIC) {
|
magic != FILE_MAGIC) {
|
||||||
pr_err("allowlist file invalid: %d!\n", magic);
|
pr_err("allowlist file invalid: %d!\n", magic);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) !=
|
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) !=
|
||||||
sizeof(version)) {
|
sizeof(version)) {
|
||||||
pr_err("allowlist read version: %d failed\n", version);
|
pr_err("allowlist read version: %d failed\n", version);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("allowlist version: %d\n", version);
|
pr_info("allowlist version: %d\n", version);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
struct app_profile profile;
|
struct app_profile profile;
|
||||||
|
|
||||||
ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
|
ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
|
||||||
&off);
|
&off);
|
||||||
|
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
pr_info("load_allow_list read err: %zd\n", ret);
|
pr_info("load_allow_list read err: %zd\n", ret);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n",
|
pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n",
|
||||||
profile.key, profile.current_uid, profile.allow_su);
|
profile.key, profile.current_uid, profile.allow_su);
|
||||||
ksu_set_app_profile(&profile, false);
|
ksu_set_app_profile(&profile, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
ksu_show_allow_list();
|
ksu_show_allow_list();
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), void *data)
|
void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), void *data)
|
||||||
{
|
{
|
||||||
struct perm_data *np = NULL;
|
struct perm_data *np = NULL;
|
||||||
struct perm_data *n = NULL;
|
struct perm_data *n = NULL;
|
||||||
|
|
||||||
bool modified = false;
|
bool modified = false;
|
||||||
// TODO: use RCU!
|
// TODO: use RCU!
|
||||||
mutex_lock(&allowlist_mutex);
|
mutex_lock(&allowlist_mutex);
|
||||||
list_for_each_entry_safe (np, n, &allow_list, list) {
|
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||||
uid_t uid = np->profile.current_uid;
|
uid_t uid = np->profile.current_uid;
|
||||||
char *package = np->profile.key;
|
char *package = np->profile.key;
|
||||||
// we use this uid for special cases, don't prune it!
|
// we use this uid for special cases, don't prune it!
|
||||||
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
|
bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID;
|
||||||
if (!is_preserved_uid && !is_uid_valid(uid, package, data)) {
|
if (!is_preserved_uid && !is_uid_valid(uid, package, data)) {
|
||||||
modified = true;
|
modified = true;
|
||||||
pr_info("prune uid: %d, package: %s\n", uid, package);
|
pr_info("prune uid: %d, package: %s\n", uid, package);
|
||||||
list_del(&np->list);
|
list_del(&np->list);
|
||||||
if (likely(uid <= BITMAP_UID_MAX)) {
|
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||||
allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE));
|
allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE));
|
||||||
}
|
}
|
||||||
remove_uid_from_arr(uid);
|
remove_uid_from_arr(uid);
|
||||||
smp_mb();
|
smp_mb();
|
||||||
kfree(np);
|
kfree(np);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mutex_unlock(&allowlist_mutex);
|
mutex_unlock(&allowlist_mutex);
|
||||||
|
|
||||||
if (modified) {
|
if (modified) {
|
||||||
persistent_allow_list();
|
persistent_allow_list();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure allow list works cross boot
|
// make sure allow list works cross boot
|
||||||
static bool persistent_allow_list(void)
|
static bool persistent_allow_list(void)
|
||||||
{
|
{
|
||||||
return ksu_queue_work(&ksu_save_work);
|
return ksu_queue_work(&ksu_save_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ksu_load_allow_list(void)
|
bool ksu_load_allow_list(void)
|
||||||
{
|
{
|
||||||
return ksu_queue_work(&ksu_load_work);
|
return ksu_queue_work(&ksu_load_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_allowlist_init(void)
|
void ksu_allowlist_init(void)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
|
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
|
||||||
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
|
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
|
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
|
||||||
allow_list_arr[i] = -1;
|
allow_list_arr[i] = -1;
|
||||||
|
|
||||||
INIT_LIST_HEAD(&allow_list);
|
INIT_LIST_HEAD(&allow_list);
|
||||||
|
|
||||||
INIT_WORK(&ksu_save_work, do_save_allow_list);
|
INIT_WORK(&ksu_save_work, do_save_allow_list);
|
||||||
INIT_WORK(&ksu_load_work, do_load_allow_list);
|
INIT_WORK(&ksu_load_work, do_load_allow_list);
|
||||||
|
|
||||||
init_default_profiles();
|
init_default_profiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_allowlist_exit(void)
|
void ksu_allowlist_exit(void)
|
||||||
{
|
{
|
||||||
struct perm_data *np = NULL;
|
struct perm_data *np = NULL;
|
||||||
struct perm_data *n = NULL;
|
struct perm_data *n = NULL;
|
||||||
|
|
||||||
do_save_allow_list(NULL);
|
do_save_allow_list(NULL);
|
||||||
|
|
||||||
// free allowlist
|
// free allowlist
|
||||||
mutex_lock(&allowlist_mutex);
|
mutex_lock(&allowlist_mutex);
|
||||||
list_for_each_entry_safe (np, n, &allow_list, list) {
|
list_for_each_entry_safe (np, n, &allow_list, list) {
|
||||||
list_del(&np->list);
|
list_del(&np->list);
|
||||||
kfree(np);
|
kfree(np);
|
||||||
}
|
}
|
||||||
mutex_unlock(&allowlist_mutex);
|
mutex_unlock(&allowlist_mutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_MANUAL_SU
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
@@ -555,7 +555,7 @@ bool ksu_temp_grant_root_once(uid_t uid)
|
|||||||
strcpy(profile.key, default_key);
|
strcpy(profile.key, default_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
profile.rp_config.profile.uid = default_root_profile.uid;
|
profile.rp_config.profile.uid = default_root_profile.uid;
|
||||||
profile.rp_config.profile.gid = default_root_profile.gid;
|
profile.rp_config.profile.gid = default_root_profile.gid;
|
||||||
profile.rp_config.profile.groups_count = default_root_profile.groups_count;
|
profile.rp_config.profile.groups_count = default_root_profile.groups_count;
|
||||||
memcpy(profile.rp_config.profile.groups, default_root_profile.groups, sizeof(default_root_profile.groups));
|
memcpy(profile.rp_config.profile.groups, default_root_profile.groups, sizeof(default_root_profile.groups));
|
||||||
|
|||||||
@@ -21,68 +21,68 @@
|
|||||||
#include "manager_sign.h"
|
#include "manager_sign.h"
|
||||||
|
|
||||||
struct sdesc {
|
struct sdesc {
|
||||||
struct shash_desc shash;
|
struct shash_desc shash;
|
||||||
char ctx[];
|
char ctx[];
|
||||||
};
|
};
|
||||||
|
|
||||||
static apk_sign_key_t apk_sign_keys[] = {
|
static apk_sign_key_t apk_sign_keys[] = {
|
||||||
{EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // SukiSU
|
{EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // SukiSU
|
||||||
#ifdef CONFIG_KSU_MULTI_MANAGER_SUPPORT
|
#ifdef CONFIG_KSU_MULTI_MANAGER_SUPPORT
|
||||||
{EXPECTED_SIZE_WEISHU, EXPECTED_HASH_WEISHU}, // Official
|
{EXPECTED_SIZE_WEISHU, EXPECTED_HASH_WEISHU}, // Official
|
||||||
{EXPECTED_SIZE_5EC1CFF, EXPECTED_HASH_5EC1CFF}, // 5ec1cff/KernelSU
|
{EXPECTED_SIZE_5EC1CFF, EXPECTED_HASH_5EC1CFF}, // 5ec1cff/KernelSU
|
||||||
{EXPECTED_SIZE_RSUNTK, EXPECTED_HASH_RSUNTK}, // rsuntk/KernelSU
|
{EXPECTED_SIZE_RSUNTK, EXPECTED_HASH_RSUNTK}, // rsuntk/KernelSU
|
||||||
{EXPECTED_SIZE_NEKO, EXPECTED_HASH_NEKO}, // Neko/KernelSU
|
{EXPECTED_SIZE_NEKO, EXPECTED_HASH_NEKO}, // Neko/KernelSU
|
||||||
#ifdef EXPECTED_SIZE
|
#ifdef EXPECTED_SIZE
|
||||||
{EXPECTED_SIZE, EXPECTED_HASH}, // Custom
|
{EXPECTED_SIZE, EXPECTED_HASH}, // Custom
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sdesc *init_sdesc(struct crypto_shash *alg)
|
static struct sdesc *init_sdesc(struct crypto_shash *alg)
|
||||||
{
|
{
|
||||||
struct sdesc *sdesc;
|
struct sdesc *sdesc;
|
||||||
int size;
|
int size;
|
||||||
|
|
||||||
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
|
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
|
||||||
sdesc = kmalloc(size, GFP_KERNEL);
|
sdesc = kmalloc(size, GFP_KERNEL);
|
||||||
if (!sdesc)
|
if (!sdesc)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
sdesc->shash.tfm = alg;
|
sdesc->shash.tfm = alg;
|
||||||
return sdesc;
|
return sdesc;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int calc_hash(struct crypto_shash *alg, const unsigned char *data,
|
static int calc_hash(struct crypto_shash *alg, const unsigned char *data,
|
||||||
unsigned int datalen, unsigned char *digest)
|
unsigned int datalen, unsigned char *digest)
|
||||||
{
|
{
|
||||||
struct sdesc *sdesc;
|
struct sdesc *sdesc;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
sdesc = init_sdesc(alg);
|
sdesc = init_sdesc(alg);
|
||||||
if (IS_ERR(sdesc)) {
|
if (IS_ERR(sdesc)) {
|
||||||
pr_info("can't alloc sdesc\n");
|
pr_info("can't alloc sdesc\n");
|
||||||
return PTR_ERR(sdesc);
|
return PTR_ERR(sdesc);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
|
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
|
||||||
kfree(sdesc);
|
kfree(sdesc);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int ksu_sha256(const unsigned char *data, unsigned int datalen,
|
static int ksu_sha256(const unsigned char *data, unsigned int datalen,
|
||||||
unsigned char *digest)
|
unsigned char *digest)
|
||||||
{
|
{
|
||||||
struct crypto_shash *alg;
|
struct crypto_shash *alg;
|
||||||
char *hash_alg_name = "sha256";
|
char *hash_alg_name = "sha256";
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
alg = crypto_alloc_shash(hash_alg_name, 0, 0);
|
alg = crypto_alloc_shash(hash_alg_name, 0, 0);
|
||||||
if (IS_ERR(alg)) {
|
if (IS_ERR(alg)) {
|
||||||
pr_info("can't alloc alg %s\n", hash_alg_name);
|
pr_info("can't alloc alg %s\n", hash_alg_name);
|
||||||
return PTR_ERR(alg);
|
return PTR_ERR(alg);
|
||||||
}
|
}
|
||||||
ret = calc_hash(alg, data, datalen, digest);
|
ret = calc_hash(alg, data, datalen, digest);
|
||||||
crypto_free_shash(alg);
|
crypto_free_shash(alg);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -90,307 +90,307 @@ static struct dynamic_sign_key dynamic_sign = DYNAMIC_SIGN_DEFAULT_CONFIG;
|
|||||||
|
|
||||||
static bool check_dynamic_sign(struct file *fp, u32 size4, loff_t *pos, int *matched_index)
|
static bool check_dynamic_sign(struct file *fp, u32 size4, loff_t *pos, int *matched_index)
|
||||||
{
|
{
|
||||||
struct dynamic_sign_key current_dynamic_key = dynamic_sign;
|
struct dynamic_sign_key current_dynamic_key = dynamic_sign;
|
||||||
|
|
||||||
if (ksu_get_dynamic_manager_config(¤t_dynamic_key.size, ¤t_dynamic_key.hash)) {
|
if (ksu_get_dynamic_manager_config(¤t_dynamic_key.size, ¤t_dynamic_key.hash)) {
|
||||||
pr_debug("Using dynamic manager config: size=0x%x, hash=%.16s...\n",
|
pr_debug("Using dynamic manager config: size=0x%x, hash=%.16s...\n",
|
||||||
current_dynamic_key.size, current_dynamic_key.hash);
|
current_dynamic_key.size, current_dynamic_key.hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (size4 != current_dynamic_key.size) {
|
if (size4 != current_dynamic_key.size) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#define CERT_MAX_LENGTH 1024
|
#define CERT_MAX_LENGTH 1024
|
||||||
char cert[CERT_MAX_LENGTH];
|
char cert[CERT_MAX_LENGTH];
|
||||||
if (size4 > CERT_MAX_LENGTH) {
|
if (size4 > CERT_MAX_LENGTH) {
|
||||||
pr_info("cert length overlimit\n");
|
pr_info("cert length overlimit\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ksu_kernel_read_compat(fp, cert, size4, pos);
|
ksu_kernel_read_compat(fp, cert, size4, pos);
|
||||||
|
|
||||||
unsigned char digest[SHA256_DIGEST_SIZE];
|
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||||
if (ksu_sha256(cert, size4, digest) < 0) {
|
if (ksu_sha256(cert, size4, digest) < 0) {
|
||||||
pr_info("sha256 error\n");
|
pr_info("sha256 error\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
||||||
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
||||||
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
||||||
|
|
||||||
pr_info("sha256: %s, expected: %s, index: dynamic\n", hash_str, current_dynamic_key.hash);
|
pr_info("sha256: %s, expected: %s, index: dynamic\n", hash_str, current_dynamic_key.hash);
|
||||||
|
|
||||||
if (strcmp(current_dynamic_key.hash, hash_str) == 0) {
|
if (strcmp(current_dynamic_key.hash, hash_str) == 0) {
|
||||||
if (matched_index) {
|
if (matched_index) {
|
||||||
*matched_index = DYNAMIC_SIGN_INDEX;
|
*matched_index = DYNAMIC_SIGN_INDEX;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, int *matched_index)
|
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, int *matched_index)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
apk_sign_key_t sign_key;
|
apk_sign_key_t sign_key;
|
||||||
bool signature_valid = false;
|
bool signature_valid = false;
|
||||||
|
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
|
||||||
|
|
||||||
*offset += 0x4 * 3;
|
*offset += 0x4 * 3;
|
||||||
|
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length
|
||||||
|
|
||||||
*pos += *size4;
|
*pos += *size4;
|
||||||
*offset += 0x4 + *size4;
|
*offset += 0x4 + *size4;
|
||||||
|
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length
|
||||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
|
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
|
||||||
*offset += 0x4 * 2;
|
*offset += 0x4 * 2;
|
||||||
|
|
||||||
if (ksu_is_dynamic_manager_enabled()) {
|
if (ksu_is_dynamic_manager_enabled()) {
|
||||||
loff_t temp_pos = *pos;
|
loff_t temp_pos = *pos;
|
||||||
if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) {
|
if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) {
|
||||||
*pos = temp_pos;
|
*pos = temp_pos;
|
||||||
*offset += *size4;
|
*offset += *size4;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
|
for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
|
||||||
sign_key = apk_sign_keys[i];
|
sign_key = apk_sign_keys[i];
|
||||||
|
|
||||||
if (*size4 != sign_key.size)
|
if (*size4 != sign_key.size)
|
||||||
continue;
|
continue;
|
||||||
*offset += *size4;
|
*offset += *size4;
|
||||||
|
|
||||||
#define CERT_MAX_LENGTH 1024
|
#define CERT_MAX_LENGTH 1024
|
||||||
char cert[CERT_MAX_LENGTH];
|
char cert[CERT_MAX_LENGTH];
|
||||||
if (*size4 > CERT_MAX_LENGTH) {
|
if (*size4 > CERT_MAX_LENGTH) {
|
||||||
pr_info("cert length overlimit\n");
|
pr_info("cert length overlimit\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ksu_kernel_read_compat(fp, cert, *size4, pos);
|
ksu_kernel_read_compat(fp, cert, *size4, pos);
|
||||||
unsigned char digest[SHA256_DIGEST_SIZE];
|
unsigned char digest[SHA256_DIGEST_SIZE];
|
||||||
if (ksu_sha256(cert, *size4, digest) < 0 ) {
|
if (ksu_sha256(cert, *size4, digest) < 0 ) {
|
||||||
pr_info("sha256 error\n");
|
pr_info("sha256 error\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
|
||||||
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
||||||
|
|
||||||
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
||||||
pr_info("sha256: %s, expected: %s, index: %d\n", hash_str, sign_key.sha256, i);
|
pr_info("sha256: %s, expected: %s, index: %d\n", hash_str, sign_key.sha256, i);
|
||||||
|
|
||||||
if (strcmp(sign_key.sha256, hash_str) == 0) {
|
if (strcmp(sign_key.sha256, hash_str) == 0) {
|
||||||
signature_valid = true;
|
signature_valid = true;
|
||||||
if (matched_index) {
|
if (matched_index) {
|
||||||
*matched_index = i;
|
*matched_index = i;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return signature_valid;
|
return signature_valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct zip_entry_header {
|
struct zip_entry_header {
|
||||||
uint32_t signature;
|
uint32_t signature;
|
||||||
uint16_t version;
|
uint16_t version;
|
||||||
uint16_t flags;
|
uint16_t flags;
|
||||||
uint16_t compression;
|
uint16_t compression;
|
||||||
uint16_t mod_time;
|
uint16_t mod_time;
|
||||||
uint16_t mod_date;
|
uint16_t mod_date;
|
||||||
uint32_t crc32;
|
uint32_t crc32;
|
||||||
uint32_t compressed_size;
|
uint32_t compressed_size;
|
||||||
uint32_t uncompressed_size;
|
uint32_t uncompressed_size;
|
||||||
uint16_t file_name_length;
|
uint16_t file_name_length;
|
||||||
uint16_t extra_field_length;
|
uint16_t extra_field_length;
|
||||||
} __attribute__((packed));
|
} __attribute__((packed));
|
||||||
|
|
||||||
// This is a necessary but not sufficient condition, but it is enough for us
|
// This is a necessary but not sufficient condition, but it is enough for us
|
||||||
static bool has_v1_signature_file(struct file *fp)
|
static bool has_v1_signature_file(struct file *fp)
|
||||||
{
|
{
|
||||||
struct zip_entry_header header;
|
struct zip_entry_header header;
|
||||||
const char MANIFEST[] = "META-INF/MANIFEST.MF";
|
const char MANIFEST[] = "META-INF/MANIFEST.MF";
|
||||||
|
|
||||||
loff_t pos = 0;
|
loff_t pos = 0;
|
||||||
|
|
||||||
while (ksu_kernel_read_compat(fp, &header,
|
while (ksu_kernel_read_compat(fp, &header,
|
||||||
sizeof(struct zip_entry_header), &pos) ==
|
sizeof(struct zip_entry_header), &pos) ==
|
||||||
sizeof(struct zip_entry_header)) {
|
sizeof(struct zip_entry_header)) {
|
||||||
if (header.signature != 0x04034b50) {
|
if (header.signature != 0x04034b50) {
|
||||||
// ZIP magic: 'PK'
|
// ZIP magic: 'PK'
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Read the entry file name
|
// Read the entry file name
|
||||||
if (header.file_name_length == sizeof(MANIFEST) - 1) {
|
if (header.file_name_length == sizeof(MANIFEST) - 1) {
|
||||||
char fileName[sizeof(MANIFEST)];
|
char fileName[sizeof(MANIFEST)];
|
||||||
ksu_kernel_read_compat(fp, fileName,
|
ksu_kernel_read_compat(fp, fileName,
|
||||||
header.file_name_length, &pos);
|
header.file_name_length, &pos);
|
||||||
fileName[header.file_name_length] = '\0';
|
fileName[header.file_name_length] = '\0';
|
||||||
|
|
||||||
// Check if the entry matches META-INF/MANIFEST.MF
|
// Check if the entry matches META-INF/MANIFEST.MF
|
||||||
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) ==
|
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) ==
|
||||||
0) {
|
0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Skip the entry file name
|
// Skip the entry file name
|
||||||
pos += header.file_name_length;
|
pos += header.file_name_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip to the next entry
|
// Skip to the next entry
|
||||||
pos += header.extra_field_length + header.compressed_size;
|
pos += header.extra_field_length + header.compressed_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static __always_inline bool check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
|
static __always_inline bool check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
|
||||||
{
|
{
|
||||||
unsigned char buffer[0x11] = { 0 };
|
unsigned char buffer[0x11] = { 0 };
|
||||||
u32 size4;
|
u32 size4;
|
||||||
u64 size8, size_of_block;
|
u64 size8, size_of_block;
|
||||||
|
|
||||||
loff_t pos;
|
loff_t pos;
|
||||||
|
|
||||||
bool v2_signing_valid = false;
|
bool v2_signing_valid = false;
|
||||||
int v2_signing_blocks = 0;
|
int v2_signing_blocks = 0;
|
||||||
bool v3_signing_exist = false;
|
bool v3_signing_exist = false;
|
||||||
bool v3_1_signing_exist = false;
|
bool v3_1_signing_exist = false;
|
||||||
int matched_index = -1;
|
int matched_index = -1;
|
||||||
int i;
|
int i;
|
||||||
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
|
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_err("open %s error.\n", path);
|
pr_err("open %s error.\n", path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
|
// If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
|
||||||
if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
|
if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// disable inotify for this file
|
// disable inotify for this file
|
||||||
fp->f_mode |= FMODE_NONOTIFY;
|
fp->f_mode |= FMODE_NONOTIFY;
|
||||||
|
|
||||||
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
|
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
|
||||||
for (i = 0;; ++i) {
|
for (i = 0;; ++i) {
|
||||||
unsigned short n;
|
unsigned short n;
|
||||||
pos = generic_file_llseek(fp, -i - 2, SEEK_END);
|
pos = generic_file_llseek(fp, -i - 2, SEEK_END);
|
||||||
ksu_kernel_read_compat(fp, &n, 2, &pos);
|
ksu_kernel_read_compat(fp, &n, 2, &pos);
|
||||||
if (n == i) {
|
if (n == i) {
|
||||||
pos -= 22;
|
pos -= 22;
|
||||||
ksu_kernel_read_compat(fp, &size4, 4, &pos);
|
ksu_kernel_read_compat(fp, &size4, 4, &pos);
|
||||||
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
|
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (i == 0xffff) {
|
if (i == 0xffff) {
|
||||||
pr_info("error: cannot find eocd\n");
|
pr_info("error: cannot find eocd\n");
|
||||||
goto clean;
|
goto clean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pos += 12;
|
pos += 12;
|
||||||
// offset
|
// offset
|
||||||
ksu_kernel_read_compat(fp, &size4, 0x4, &pos);
|
ksu_kernel_read_compat(fp, &size4, 0x4, &pos);
|
||||||
pos = size4 - 0x18;
|
pos = size4 - 0x18;
|
||||||
|
|
||||||
ksu_kernel_read_compat(fp, &size8, 0x8, &pos);
|
ksu_kernel_read_compat(fp, &size8, 0x8, &pos);
|
||||||
ksu_kernel_read_compat(fp, buffer, 0x10, &pos);
|
ksu_kernel_read_compat(fp, buffer, 0x10, &pos);
|
||||||
if (strcmp((char *)buffer, "APK Sig Block 42")) {
|
if (strcmp((char *)buffer, "APK Sig Block 42")) {
|
||||||
goto clean;
|
goto clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
pos = size4 - (size8 + 0x8);
|
pos = size4 - (size8 + 0x8);
|
||||||
ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos);
|
ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos);
|
||||||
if (size_of_block != size8) {
|
if (size_of_block != size8) {
|
||||||
goto clean;
|
goto clean;
|
||||||
}
|
}
|
||||||
|
|
||||||
int loop_count = 0;
|
int loop_count = 0;
|
||||||
while (loop_count++ < 10) {
|
while (loop_count++ < 10) {
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
uint32_t offset;
|
uint32_t offset;
|
||||||
ksu_kernel_read_compat(fp, &size8, 0x8,
|
ksu_kernel_read_compat(fp, &size8, 0x8,
|
||||||
&pos); // sequence length
|
&pos); // sequence length
|
||||||
if (size8 == size_of_block) {
|
if (size8 == size_of_block) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id
|
ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id
|
||||||
offset = 4;
|
offset = 4;
|
||||||
if (id == 0x7109871au) {
|
if (id == 0x7109871au) {
|
||||||
v2_signing_blocks++;
|
v2_signing_blocks++;
|
||||||
bool result = check_block(fp, &size4, &pos, &offset, &matched_index);
|
bool result = check_block(fp, &size4, &pos, &offset, &matched_index);
|
||||||
if (result) {
|
if (result) {
|
||||||
v2_signing_valid = true;
|
v2_signing_valid = true;
|
||||||
}
|
}
|
||||||
} else if (id == 0xf05368c0u) {
|
} else if (id == 0xf05368c0u) {
|
||||||
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
|
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
|
||||||
v3_signing_exist = true;
|
v3_signing_exist = true;
|
||||||
} else if (id == 0x1b93ad61u) {
|
} else if (id == 0x1b93ad61u) {
|
||||||
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
|
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
|
||||||
v3_1_signing_exist = true;
|
v3_1_signing_exist = true;
|
||||||
} else {
|
} else {
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_info("Unknown id: 0x%08x\n", id);
|
pr_info("Unknown id: 0x%08x\n", id);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
pos += (size8 - offset);
|
pos += (size8 - offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2_signing_blocks != 1) {
|
if (v2_signing_blocks != 1) {
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_err("Unexpected v2 signature count: %d\n",
|
pr_err("Unexpected v2 signature count: %d\n",
|
||||||
v2_signing_blocks);
|
v2_signing_blocks);
|
||||||
#endif
|
#endif
|
||||||
v2_signing_valid = false;
|
v2_signing_valid = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2_signing_valid) {
|
if (v2_signing_valid) {
|
||||||
int has_v1_signing = has_v1_signature_file(fp);
|
int has_v1_signing = has_v1_signature_file(fp);
|
||||||
if (has_v1_signing) {
|
if (has_v1_signing) {
|
||||||
pr_err("Unexpected v1 signature scheme found!\n");
|
pr_err("Unexpected v1 signature scheme found!\n");
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
clean:
|
clean:
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
|
|
||||||
if (v3_signing_exist || v3_1_signing_exist) {
|
if (v3_signing_exist || v3_1_signing_exist) {
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_err("Unexpected v3 signature scheme found!\n");
|
pr_err("Unexpected v3 signature scheme found!\n");
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (v2_signing_valid) {
|
if (v2_signing_valid) {
|
||||||
if (signature_index) {
|
if (signature_index) {
|
||||||
*signature_index = matched_index;
|
*signature_index = matched_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (check_multi_manager) {
|
if (check_multi_manager) {
|
||||||
// 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign
|
// 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign
|
||||||
if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) {
|
if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) {
|
||||||
pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index);
|
pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
// Common manager check: any valid signature will do
|
// Common manager check: any valid signature will do
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
@@ -401,19 +401,19 @@ int ksu_debug_manager_uid = -1;
|
|||||||
|
|
||||||
static int set_expected_size(const char *val, const struct kernel_param *kp)
|
static int set_expected_size(const char *val, const struct kernel_param *kp)
|
||||||
{
|
{
|
||||||
int rv = param_set_uint(val, kp);
|
int rv = param_set_uint(val, kp);
|
||||||
ksu_set_manager_uid(ksu_debug_manager_uid);
|
ksu_set_manager_uid(ksu_debug_manager_uid);
|
||||||
pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid);
|
pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid);
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct kernel_param_ops expected_size_ops = {
|
static struct kernel_param_ops expected_size_ops = {
|
||||||
.set = set_expected_size,
|
.set = set_expected_size,
|
||||||
.get = param_get_uint,
|
.get = param_get_uint,
|
||||||
};
|
};
|
||||||
|
|
||||||
module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
||||||
&ksu_debug_manager_uid, S_IRUSR | S_IWUSR);
|
&ksu_debug_manager_uid, S_IRUSR | S_IWUSR);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||||
#define PRCTL_SYMBOL "__arm64_sys_prctl"
|
#define PRCTL_SYMBOL "__arm64_sys_prctl"
|
||||||
|
#define REBOOT_SYMBOL "__arm64_sys_reboot"
|
||||||
#define SYS_READ_SYMBOL "__arm64_sys_read"
|
#define SYS_READ_SYMBOL "__arm64_sys_read"
|
||||||
#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat"
|
#define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat"
|
||||||
#define SYS_FSTATAT64_SYMBOL "__arm64_sys_fstatat64"
|
#define SYS_FSTATAT64_SYMBOL "__arm64_sys_fstatat64"
|
||||||
@@ -28,6 +29,7 @@
|
|||||||
#define SYS_EXECVE_COMPAT_SYMBOL "__arm64_compat_sys_execve"
|
#define SYS_EXECVE_COMPAT_SYMBOL "__arm64_compat_sys_execve"
|
||||||
#else
|
#else
|
||||||
#define PRCTL_SYMBOL "sys_prctl"
|
#define PRCTL_SYMBOL "sys_prctl"
|
||||||
|
#define REBOOT_SYMBOL "sys_reboot"
|
||||||
#define SYS_READ_SYMBOL "sys_read"
|
#define SYS_READ_SYMBOL "sys_read"
|
||||||
#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat"
|
#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat"
|
||||||
#define SYS_FSTATAT64_SYMBOL "sys_fstatat64"
|
#define SYS_FSTATAT64_SYMBOL "sys_fstatat64"
|
||||||
@@ -35,6 +37,11 @@
|
|||||||
#define SYS_EXECVE_SYMBOL "sys_execve"
|
#define SYS_EXECVE_SYMBOL "sys_execve"
|
||||||
#define SYS_EXECVE_COMPAT_SYMBOL "compat_sys_execve"
|
#define SYS_EXECVE_COMPAT_SYMBOL "compat_sys_execve"
|
||||||
#endif
|
#endif
|
||||||
|
/*LSM HOOK*/
|
||||||
|
#define SECURITY_TASK_FIX_SETUID_SYMBOL "security_task_fix_setuid"
|
||||||
|
#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__)
|
#elif defined(__x86_64__)
|
||||||
|
|
||||||
@@ -53,6 +60,7 @@
|
|||||||
#define __PT_IP_REG ip
|
#define __PT_IP_REG ip
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
|
||||||
#define PRCTL_SYMBOL "__x64_sys_prctl"
|
#define PRCTL_SYMBOL "__x64_sys_prctl"
|
||||||
|
#define REBOOT_SYMBOL "__x64_sys_reboot"
|
||||||
#define SYS_READ_SYMBOL "__x64_sys_read"
|
#define SYS_READ_SYMBOL "__x64_sys_read"
|
||||||
#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat"
|
#define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat"
|
||||||
#define SYS_FSTATAT64_SYMBOL "__x64_sys_fstatat64"
|
#define SYS_FSTATAT64_SYMBOL "__x64_sys_fstatat64"
|
||||||
@@ -61,6 +69,7 @@
|
|||||||
#define SYS_EXECVE_COMPAT_SYMBOL "__x64_compat_sys_execve"
|
#define SYS_EXECVE_COMPAT_SYMBOL "__x64_compat_sys_execve"
|
||||||
#else
|
#else
|
||||||
#define PRCTL_SYMBOL "sys_prctl"
|
#define PRCTL_SYMBOL "sys_prctl"
|
||||||
|
#define REBOOT_SYMBOL "sys_reboot"
|
||||||
#define SYS_READ_SYMBOL "sys_read"
|
#define SYS_READ_SYMBOL "sys_read"
|
||||||
#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat"
|
#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat"
|
||||||
#define SYS_FSTATAT64_SYMBOL "sys_fstatat64"
|
#define SYS_FSTATAT64_SYMBOL "sys_fstatat64"
|
||||||
@@ -68,6 +77,12 @@
|
|||||||
#define SYS_EXECVE_SYMBOL "sys_execve"
|
#define SYS_EXECVE_SYMBOL "sys_execve"
|
||||||
#define SYS_EXECVE_COMPAT_SYMBOL "compat_sys_execve"
|
#define SYS_EXECVE_COMPAT_SYMBOL "compat_sys_execve"
|
||||||
#endif
|
#endif
|
||||||
|
/*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"
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||||
|
|||||||
2409
kernel/core_hook.c
2409
kernel/core_hook.c
File diff suppressed because it is too large
Load Diff
@@ -7,22 +7,22 @@
|
|||||||
// For sucompat
|
// For sucompat
|
||||||
|
|
||||||
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||||
int *flags);
|
int *flags);
|
||||||
|
|
||||||
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
||||||
|
|
||||||
// For ksud
|
// For ksud
|
||||||
|
|
||||||
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||||
size_t *count_ptr, loff_t **pos);
|
size_t *count_ptr, loff_t **pos);
|
||||||
|
|
||||||
// For ksud and sucompat
|
// For ksud and sucompat
|
||||||
|
|
||||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||||
void *envp, int *flags);
|
void *envp, int *flags);
|
||||||
|
|
||||||
// For volume button
|
// For volume button
|
||||||
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||||
int *value);
|
int *value);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
#include <linux/nsproxy.h>
|
#include <linux/nsproxy.h>
|
||||||
|
#include <linux/filter.h>
|
||||||
|
#include <linux/seccomp.h>
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0)
|
||||||
#include <linux/sched/task.h>
|
#include <linux/sched/task.h>
|
||||||
#else
|
#else
|
||||||
@@ -10,9 +12,9 @@
|
|||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "kernel_compat.h" // Add check Huawei Device
|
#include "kernel_compat.h" // Add check Huawei Device
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||||
defined(CONFIG_IS_HW_HISI) || \
|
defined(CONFIG_IS_HW_HISI) || \
|
||||||
defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
#include <linux/key.h>
|
#include <linux/key.h>
|
||||||
#include <linux/errno.h>
|
#include <linux/errno.h>
|
||||||
#include <linux/cred.h>
|
#include <linux/cred.h>
|
||||||
@@ -20,20 +22,20 @@ struct key *init_session_keyring = NULL;
|
|||||||
|
|
||||||
static inline int install_session_keyring(struct key *keyring)
|
static inline int install_session_keyring(struct key *keyring)
|
||||||
{
|
{
|
||||||
struct cred *new;
|
struct cred *new;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
new = prepare_creds();
|
new = prepare_creds();
|
||||||
if (!new)
|
if (!new)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
ret = install_session_keyring_to_cred(new, keyring);
|
ret = install_session_keyring_to_cred(new, keyring);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
abort_creds(new);
|
abort_creds(new);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
return commit_creds(new);
|
return commit_creds(new);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -41,20 +43,20 @@ extern struct task_struct init_task;
|
|||||||
|
|
||||||
// mnt_ns context switch for environment that android_init->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns, such as WSA
|
// mnt_ns context switch for environment that android_init->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns, such as WSA
|
||||||
struct ksu_ns_fs_saved {
|
struct ksu_ns_fs_saved {
|
||||||
struct nsproxy *ns;
|
struct nsproxy *ns;
|
||||||
struct fs_struct *fs;
|
struct fs_struct *fs;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void ksu_save_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
static void ksu_save_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
||||||
{
|
{
|
||||||
ns_fs_saved->ns = current->nsproxy;
|
ns_fs_saved->ns = current->nsproxy;
|
||||||
ns_fs_saved->fs = current->fs;
|
ns_fs_saved->fs = current->fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ksu_load_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
static void ksu_load_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved)
|
||||||
{
|
{
|
||||||
current->nsproxy = ns_fs_saved->ns;
|
current->nsproxy = ns_fs_saved->ns;
|
||||||
current->fs = ns_fs_saved->fs;
|
current->fs = ns_fs_saved->fs;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool android_context_saved_checked = false;
|
static bool android_context_saved_checked = false;
|
||||||
@@ -63,178 +65,234 @@ static struct ksu_ns_fs_saved android_context_saved;
|
|||||||
|
|
||||||
void ksu_android_ns_fs_check(void)
|
void ksu_android_ns_fs_check(void)
|
||||||
{
|
{
|
||||||
if (android_context_saved_checked)
|
if (android_context_saved_checked)
|
||||||
return;
|
return;
|
||||||
android_context_saved_checked = true;
|
android_context_saved_checked = true;
|
||||||
task_lock(current);
|
task_lock(current);
|
||||||
if (current->nsproxy && current->fs &&
|
if (current->nsproxy && current->fs &&
|
||||||
current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) {
|
current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) {
|
||||||
android_context_saved_enabled = true;
|
android_context_saved_enabled = true;
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n",
|
pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n",
|
||||||
current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns);
|
current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns);
|
||||||
#endif
|
#endif
|
||||||
ksu_save_ns_fs(&android_context_saved);
|
ksu_save_ns_fs(&android_context_saved);
|
||||||
} else {
|
} else {
|
||||||
pr_info("android context saved disabled\n");
|
pr_info("android context saved disabled\n");
|
||||||
}
|
}
|
||||||
task_unlock(current);
|
task_unlock(current);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
|
||||||
{
|
{
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||||
defined(CONFIG_IS_HW_HISI) || \
|
defined(CONFIG_IS_HW_HISI) || \
|
||||||
defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
if (init_session_keyring != NULL && !current_cred()->session_keyring &&
|
if (init_session_keyring != NULL && !current_cred()->session_keyring &&
|
||||||
(current->flags & PF_WQ_WORKER)) {
|
(current->flags & PF_WQ_WORKER)) {
|
||||||
pr_info("installing init session keyring for older kernel\n");
|
pr_info("installing init session keyring for older kernel\n");
|
||||||
install_session_keyring(init_session_keyring);
|
install_session_keyring(init_session_keyring);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
// switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns
|
// switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns
|
||||||
struct ksu_ns_fs_saved saved;
|
struct ksu_ns_fs_saved saved;
|
||||||
if (android_context_saved_enabled) {
|
if (android_context_saved_enabled) {
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_info("start switch current nsproxy and fs to android context\n");
|
pr_info("start switch current nsproxy and fs to android context\n");
|
||||||
#endif
|
#endif
|
||||||
task_lock(current);
|
task_lock(current);
|
||||||
ksu_save_ns_fs(&saved);
|
ksu_save_ns_fs(&saved);
|
||||||
ksu_load_ns_fs(&android_context_saved);
|
ksu_load_ns_fs(&android_context_saved);
|
||||||
task_unlock(current);
|
task_unlock(current);
|
||||||
}
|
}
|
||||||
struct file *fp = filp_open(filename, flags, mode);
|
struct file *fp = filp_open(filename, flags, mode);
|
||||||
if (android_context_saved_enabled) {
|
if (android_context_saved_enabled) {
|
||||||
task_lock(current);
|
task_lock(current);
|
||||||
ksu_load_ns_fs(&saved);
|
ksu_load_ns_fs(&saved);
|
||||||
task_unlock(current);
|
task_unlock(current);
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_info("switch current nsproxy and fs back to saved successfully\n");
|
pr_info("switch current nsproxy and fs back to saved successfully\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return fp;
|
return fp;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||||
loff_t *pos)
|
loff_t *pos)
|
||||||
{
|
{
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || defined(KSU_OPTIONAL_KERNEL_READ)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || defined(KSU_OPTIONAL_KERNEL_READ)
|
||||||
return kernel_read(p, buf, count, pos);
|
return kernel_read(p, buf, count, pos);
|
||||||
#else
|
#else
|
||||||
loff_t offset = pos ? *pos : 0;
|
loff_t offset = pos ? *pos : 0;
|
||||||
ssize_t result = kernel_read(p, offset, (char *)buf, count);
|
ssize_t result = kernel_read(p, offset, (char *)buf, count);
|
||||||
if (pos && result > 0) {
|
if (pos && result > 0) {
|
||||||
*pos = offset + result;
|
*pos = offset + result;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
|
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
|
||||||
loff_t *pos)
|
loff_t *pos)
|
||||||
{
|
{
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || defined(KSU_OPTIONAL_KERNEL_WRITE)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || defined(KSU_OPTIONAL_KERNEL_WRITE)
|
||||||
return kernel_write(p, buf, count, pos);
|
return kernel_write(p, buf, count, pos);
|
||||||
#else
|
#else
|
||||||
loff_t offset = pos ? *pos : 0;
|
loff_t offset = pos ? *pos : 0;
|
||||||
ssize_t result = kernel_write(p, buf, count, offset);
|
ssize_t result = kernel_write(p, buf, count, offset);
|
||||||
if (pos && result > 0) {
|
if (pos && result > 0) {
|
||||||
*pos = offset + result;
|
*pos = offset + result;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || defined(KSU_OPTIONAL_STRNCPY)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || defined(KSU_OPTIONAL_STRNCPY)
|
||||||
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||||
long count)
|
long count)
|
||||||
{
|
{
|
||||||
return strncpy_from_user_nofault(dst, unsafe_addr, count);
|
return strncpy_from_user_nofault(dst, unsafe_addr, count);
|
||||||
}
|
}
|
||||||
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0)
|
||||||
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||||
long count)
|
long count)
|
||||||
{
|
{
|
||||||
return strncpy_from_unsafe_user(dst, unsafe_addr, count);
|
return strncpy_from_unsafe_user(dst, unsafe_addr, count);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// Copied from: https://elixir.bootlin.com/linux/v4.9.337/source/mm/maccess.c#L201
|
// Copied from: https://elixir.bootlin.com/linux/v4.9.337/source/mm/maccess.c#L201
|
||||||
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
||||||
long count)
|
long count)
|
||||||
{
|
{
|
||||||
mm_segment_t old_fs = get_fs();
|
mm_segment_t old_fs = get_fs();
|
||||||
long ret;
|
long ret;
|
||||||
|
|
||||||
if (unlikely(count <= 0))
|
if (unlikely(count <= 0))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
set_fs(USER_DS);
|
set_fs(USER_DS);
|
||||||
pagefault_disable();
|
pagefault_disable();
|
||||||
ret = strncpy_from_user(dst, unsafe_addr, count);
|
ret = strncpy_from_user(dst, unsafe_addr, count);
|
||||||
pagefault_enable();
|
pagefault_enable();
|
||||||
set_fs(old_fs);
|
set_fs(old_fs);
|
||||||
|
|
||||||
if (ret >= count) {
|
if (ret >= count) {
|
||||||
ret = count;
|
ret = count;
|
||||||
dst[ret - 1] = '\0';
|
dst[ret - 1] = '\0';
|
||||||
} else if (ret > 0) {
|
} else if (ret > 0) {
|
||||||
ret++;
|
ret++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
long ksu_strncpy_from_user_retry(char *dst, const void __user *unsafe_addr,
|
long ksu_strncpy_from_user_retry(char *dst, const void __user *unsafe_addr,
|
||||||
long count)
|
long count)
|
||||||
{
|
{
|
||||||
long ret;
|
long ret;
|
||||||
|
|
||||||
ret = ksu_strncpy_from_user_nofault(dst, unsafe_addr, count);
|
ret = ksu_strncpy_from_user_nofault(dst, unsafe_addr, count);
|
||||||
if (likely(ret >= 0))
|
if (likely(ret >= 0))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
// we faulted! fallback to slow path
|
// we faulted! fallback to slow path
|
||||||
if (unlikely(!ksu_access_ok(unsafe_addr, count))) {
|
if (unlikely(!ksu_access_ok(unsafe_addr, count))) {
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_err("%s: faulted!\n", __func__);
|
pr_err("%s: faulted!\n", __func__);
|
||||||
#endif
|
#endif
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// why we don't do like how strncpy_from_user_nofault?
|
// why we don't do like how strncpy_from_user_nofault?
|
||||||
ret = strncpy_from_user(dst, unsafe_addr, count);
|
ret = strncpy_from_user(dst, unsafe_addr, count);
|
||||||
|
|
||||||
if (ret >= count) {
|
if (ret >= count) {
|
||||||
ret = count;
|
ret = count;
|
||||||
dst[ret - 1] = '\0';
|
dst[ret - 1] = '\0';
|
||||||
} else if (likely(ret >= 0)) {
|
} else if (likely(ret >= 0)) {
|
||||||
ret++;
|
ret++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size)
|
long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size)
|
||||||
{
|
{
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
|
||||||
return copy_from_user_nofault(dst, src, size);
|
return copy_from_user_nofault(dst, src, size);
|
||||||
#else
|
#else
|
||||||
// https://elixir.bootlin.com/linux/v5.8/source/mm/maccess.c#L205
|
// https://elixir.bootlin.com/linux/v5.8/source/mm/maccess.c#L205
|
||||||
long ret = -EFAULT;
|
long ret = -EFAULT;
|
||||||
mm_segment_t old_fs = get_fs();
|
mm_segment_t old_fs = get_fs();
|
||||||
|
|
||||||
set_fs(USER_DS);
|
set_fs(USER_DS);
|
||||||
// tweaked to use ksu_access_ok
|
// tweaked to use ksu_access_ok
|
||||||
if (ksu_access_ok(src, size)) {
|
if (ksu_access_ok(src, size)) {
|
||||||
pagefault_disable();
|
pagefault_disable();
|
||||||
ret = __copy_from_user_inatomic(dst, src, size);
|
ret = __copy_from_user_inatomic(dst, src, size);
|
||||||
pagefault_enable();
|
pagefault_enable();
|
||||||
}
|
}
|
||||||
set_fs(old_fs);
|
set_fs(old_fs);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
return 0;
|
return 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
struct action_cache {
|
||||||
|
DECLARE_BITMAP(allow_native, SECCOMP_ARCH_NATIVE_NR);
|
||||||
|
#ifdef SECCOMP_ARCH_COMPAT
|
||||||
|
DECLARE_BITMAP(allow_compat, SECCOMP_ARCH_COMPAT_NR);
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct seccomp_filter {
|
||||||
|
refcount_t refs;
|
||||||
|
refcount_t users;
|
||||||
|
bool log;
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
|
||||||
|
bool wait_killable_recv;
|
||||||
|
#endif
|
||||||
|
struct action_cache cache;
|
||||||
|
struct seccomp_filter *prev;
|
||||||
|
struct bpf_prog *prog;
|
||||||
|
struct notification *notif;
|
||||||
|
struct mutex notify_lock;
|
||||||
|
wait_queue_head_t wqh;
|
||||||
|
};
|
||||||
|
|
||||||
|
void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr)
|
||||||
|
{
|
||||||
|
if (!filter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
|
||||||
|
clear_bit(nr, filter->cache.allow_native);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SECCOMP_ARCH_COMPAT
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
|
||||||
|
clear_bit(nr, filter->cache.allow_compat);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr)
|
||||||
|
{
|
||||||
|
if (!filter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_NATIVE_NR) {
|
||||||
|
set_bit(nr, filter->cache.allow_native);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef SECCOMP_ARCH_COMPAT
|
||||||
|
if (nr >= 0 && nr < SECCOMP_ARCH_COMPAT_NR) {
|
||||||
|
set_bit(nr, filter->cache.allow_compat);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -29,9 +29,9 @@
|
|||||||
* From ss/ebitmap.h
|
* From ss/ebitmap.h
|
||||||
*/
|
*/
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \
|
||||||
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \
|
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \
|
||||||
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \
|
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \
|
||||||
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
|
||||||
#ifdef HISI_SELINUX_EBITMAP_RO
|
#ifdef HISI_SELINUX_EBITMAP_RO
|
||||||
#define CONFIG_IS_HW_HISI
|
#define CONFIG_IS_HW_HISI
|
||||||
#endif
|
#endif
|
||||||
@@ -45,25 +45,25 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern long ksu_strncpy_from_user_nofault(char *dst,
|
extern long ksu_strncpy_from_user_nofault(char *dst,
|
||||||
const void __user *unsafe_addr,
|
const void __user *unsafe_addr,
|
||||||
long count);
|
long count);
|
||||||
extern long ksu_strncpy_from_user_retry(char *dst,
|
extern long ksu_strncpy_from_user_retry(char *dst,
|
||||||
const void __user *unsafe_addr,
|
const void __user *unsafe_addr,
|
||||||
long count);
|
long count);
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
|
||||||
defined(CONFIG_IS_HW_HISI) || \
|
defined(CONFIG_IS_HW_HISI) || \
|
||||||
defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
|
||||||
extern struct key *init_session_keyring;
|
extern struct key *init_session_keyring;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern void ksu_android_ns_fs_check(void);
|
extern void ksu_android_ns_fs_check(void);
|
||||||
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
|
extern struct file *ksu_filp_open_compat(const char *filename, int flags,
|
||||||
umode_t mode);
|
umode_t mode);
|
||||||
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
|
||||||
loff_t *pos);
|
loff_t *pos);
|
||||||
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
|
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
|
||||||
size_t count, loff_t *pos);
|
size_t count, loff_t *pos);
|
||||||
extern long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size);
|
extern long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size);
|
||||||
/*
|
/*
|
||||||
* ksu_copy_from_user_retry
|
* ksu_copy_from_user_retry
|
||||||
@@ -72,20 +72,23 @@ extern long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t
|
|||||||
* 0 = success
|
* 0 = success
|
||||||
*/
|
*/
|
||||||
static long ksu_copy_from_user_retry(void *to,
|
static long ksu_copy_from_user_retry(void *to,
|
||||||
const void __user *from, unsigned long count)
|
const void __user *from, unsigned long count)
|
||||||
{
|
{
|
||||||
long ret = ksu_copy_from_user_nofault(to, from, count);
|
long ret = ksu_copy_from_user_nofault(to, from, count);
|
||||||
if (likely(!ret))
|
if (likely(!ret))
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
// we faulted! fallback to slow path
|
// we faulted! fallback to slow path
|
||||||
return copy_from_user(to, from, count);
|
return copy_from_user(to, from, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0)
|
||||||
#define ksu_access_ok(addr, size) access_ok(addr, size)
|
#define ksu_access_ok(addr, size) access_ok(addr, size)
|
||||||
#else
|
#else
|
||||||
#define ksu_access_ok(addr, size) access_ok(VERIFY_READ, addr, size)
|
#define ksu_access_ok(addr, size) access_ok(VERIFY_READ, addr, size)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr);
|
||||||
|
extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
86
kernel/ksu.c
86
kernel/ksu.c
@@ -23,9 +23,9 @@
|
|||||||
unsigned int enable_kernelsu = 1; // enabled by default
|
unsigned int enable_kernelsu = 1; // enabled by default
|
||||||
static int __init read_kernelsu_state(char *s)
|
static int __init read_kernelsu_state(char *s)
|
||||||
{
|
{
|
||||||
if (s)
|
if (s)
|
||||||
enable_kernelsu = simple_strtoul(s, NULL, 0);
|
enable_kernelsu = simple_strtoul(s, NULL, 0);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
__setup("kernelsu.enabled=", read_kernelsu_state);
|
__setup("kernelsu.enabled=", read_kernelsu_state);
|
||||||
|
|
||||||
@@ -38,21 +38,21 @@ static struct workqueue_struct *ksu_workqueue;
|
|||||||
|
|
||||||
bool ksu_queue_work(struct work_struct *work)
|
bool ksu_queue_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
return queue_work(ksu_workqueue, work);
|
return queue_work(ksu_workqueue, work);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||||
void *argv, void *envp, int *flags);
|
void *argv, void *envp, int *flags);
|
||||||
|
|
||||||
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||||
void *argv, void *envp, int *flags);
|
void *argv, void *envp, int *flags);
|
||||||
|
|
||||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
||||||
void *envp, int *flags)
|
void *envp, int *flags)
|
||||||
{
|
{
|
||||||
ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags);
|
ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags);
|
||||||
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp,
|
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp,
|
||||||
flags);
|
flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void ksu_sucompat_init(void);
|
extern void ksu_sucompat_init(void);
|
||||||
@@ -66,44 +66,44 @@ extern void ksu_trace_unregister();
|
|||||||
|
|
||||||
int __init kernelsu_init(void)
|
int __init kernelsu_init(void)
|
||||||
{
|
{
|
||||||
pr_info("kernelsu.enabled=%d\n",
|
pr_info("kernelsu.enabled=%d\n",
|
||||||
(int)get_ksu_state());
|
(int)get_ksu_state());
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_CMDLINE
|
#ifdef CONFIG_KSU_CMDLINE
|
||||||
if (!get_ksu_state()) {
|
if (!get_ksu_state()) {
|
||||||
pr_info_once("drivers is disabled.");
|
pr_info_once("drivers is disabled.");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
#ifdef CONFIG_KSU_DEBUG
|
||||||
pr_alert("*************************************************************");
|
pr_alert("*************************************************************");
|
||||||
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||||
pr_alert("** **");
|
pr_alert("** **");
|
||||||
pr_alert("** You are running KernelSU in DEBUG mode **");
|
pr_alert("** You are running KernelSU in DEBUG mode **");
|
||||||
pr_alert("** **");
|
pr_alert("** **");
|
||||||
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
|
||||||
pr_alert("*************************************************************");
|
pr_alert("*************************************************************");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_SUSFS
|
#ifdef CONFIG_KSU_SUSFS
|
||||||
susfs_init();
|
susfs_init();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ksu_core_init();
|
ksu_core_init();
|
||||||
|
|
||||||
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0);
|
||||||
|
|
||||||
ksu_allowlist_init();
|
ksu_allowlist_init();
|
||||||
|
|
||||||
ksu_throne_tracker_init();
|
ksu_throne_tracker_init();
|
||||||
|
|
||||||
ksu_sucompat_init();
|
ksu_sucompat_init();
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||||
ksu_ksud_init();
|
ksu_ksud_init();
|
||||||
#else
|
#else
|
||||||
pr_debug("init ksu driver\n");
|
pr_debug("init ksu driver\n");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
|
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
|
||||||
@@ -112,36 +112,38 @@ int __init kernelsu_init(void)
|
|||||||
|
|
||||||
#ifdef MODULE
|
#ifdef MODULE
|
||||||
#ifndef CONFIG_KSU_DEBUG
|
#ifndef CONFIG_KSU_DEBUG
|
||||||
kobject_del(&THIS_MODULE->mkobj.kobj);
|
kobject_del(&THIS_MODULE->mkobj.kobj);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void kernelsu_exit(void)
|
void kernelsu_exit(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KSU_CMDLINE
|
#ifdef CONFIG_KSU_CMDLINE
|
||||||
if (!get_ksu_state()) {
|
if (!get_ksu_state()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
ksu_allowlist_exit();
|
ksu_allowlist_exit();
|
||||||
|
|
||||||
ksu_throne_tracker_exit();
|
ksu_observer_exit();
|
||||||
|
|
||||||
destroy_workqueue(ksu_workqueue);
|
ksu_throne_tracker_exit();
|
||||||
|
|
||||||
|
destroy_workqueue(ksu_workqueue);
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||||
ksu_ksud_exit();
|
ksu_ksud_exit();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
|
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
|
||||||
ksu_trace_unregister();
|
ksu_trace_unregister();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
ksu_sucompat_exit();
|
ksu_sucompat_exit();
|
||||||
|
|
||||||
ksu_core_exit();
|
ksu_core_exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
module_init(kernelsu_init);
|
module_init(kernelsu_init);
|
||||||
|
|||||||
101
kernel/ksu.h
101
kernel/ksu.h
@@ -7,35 +7,12 @@
|
|||||||
#define KERNEL_SU_VERSION KSU_VERSION
|
#define KERNEL_SU_VERSION KSU_VERSION
|
||||||
#define KERNEL_SU_OPTION 0xDEADBEEF
|
#define KERNEL_SU_OPTION 0xDEADBEEF
|
||||||
|
|
||||||
#define CMD_GRANT_ROOT 0
|
extern bool ksu_uid_scanner_enabled;
|
||||||
#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
|
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_MANUAL_SU
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
#define CMD_MANUAL_SU_REQUEST 50
|
#define CMD_MANUAL_SU_REQUEST 50
|
||||||
#endif
|
#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_POST_FS_DATA 1
|
||||||
#define EVENT_BOOT_COMPLETED 2
|
#define EVENT_BOOT_COMPLETED 2
|
||||||
#define EVENT_MODULE_MOUNTED 3
|
#define EVENT_MODULE_MOUNTED 3
|
||||||
@@ -56,6 +33,10 @@
|
|||||||
#define DYNAMIC_MANAGER_OP_GET 1
|
#define DYNAMIC_MANAGER_OP_GET 1
|
||||||
#define DYNAMIC_MANAGER_OP_CLEAR 2
|
#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 {
|
struct dynamic_manager_user_config {
|
||||||
unsigned int operation;
|
unsigned int operation;
|
||||||
unsigned int size;
|
unsigned int size;
|
||||||
@@ -71,67 +52,67 @@ struct manager_list_info {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct root_profile {
|
struct root_profile {
|
||||||
int32_t uid;
|
int32_t uid;
|
||||||
int32_t gid;
|
int32_t gid;
|
||||||
|
|
||||||
int32_t groups_count;
|
int32_t groups_count;
|
||||||
int32_t groups[KSU_MAX_GROUPS];
|
int32_t groups[KSU_MAX_GROUPS];
|
||||||
|
|
||||||
// kernel_cap_t is u32[2] for capabilities v3
|
// kernel_cap_t is u32[2] for capabilities v3
|
||||||
struct {
|
struct {
|
||||||
u64 effective;
|
u64 effective;
|
||||||
u64 permitted;
|
u64 permitted;
|
||||||
u64 inheritable;
|
u64 inheritable;
|
||||||
} capabilities;
|
} capabilities;
|
||||||
|
|
||||||
char selinux_domain[KSU_SELINUX_DOMAIN];
|
char selinux_domain[KSU_SELINUX_DOMAIN];
|
||||||
|
|
||||||
int32_t namespaces;
|
int32_t namespaces;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct non_root_profile {
|
struct non_root_profile {
|
||||||
bool umount_modules;
|
bool umount_modules;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct app_profile {
|
struct app_profile {
|
||||||
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
|
// It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this.
|
||||||
u32 version;
|
u32 version;
|
||||||
|
|
||||||
// this is usually the package of the app, but can be other value for special apps
|
// this is usually the package of the app, but can be other value for special apps
|
||||||
char key[KSU_MAX_PACKAGE_NAME];
|
char key[KSU_MAX_PACKAGE_NAME];
|
||||||
int32_t current_uid;
|
int32_t current_uid;
|
||||||
bool allow_su;
|
bool allow_su;
|
||||||
|
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
bool use_default;
|
bool use_default;
|
||||||
char template_name[KSU_MAX_PACKAGE_NAME];
|
char template_name[KSU_MAX_PACKAGE_NAME];
|
||||||
|
|
||||||
struct root_profile profile;
|
struct root_profile profile;
|
||||||
} rp_config;
|
} rp_config;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool use_default;
|
bool use_default;
|
||||||
|
|
||||||
struct non_root_profile profile;
|
struct non_root_profile profile;
|
||||||
} nrp_config;
|
} nrp_config;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ksu_queue_work(struct work_struct *work);
|
bool ksu_queue_work(struct work_struct *work);
|
||||||
|
|
||||||
static inline int startswith(char *s, char *prefix)
|
static inline int startswith(char *s, char *prefix)
|
||||||
{
|
{
|
||||||
return strncmp(s, prefix, strlen(prefix));
|
return strncmp(s, prefix, strlen(prefix));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int endswith(const char *s, const char *t)
|
static inline int endswith(const char *s, const char *t)
|
||||||
{
|
{
|
||||||
size_t slen = strlen(s);
|
size_t slen = strlen(s);
|
||||||
size_t tlen = strlen(t);
|
size_t tlen = strlen(t);
|
||||||
if (tlen > slen)
|
if (tlen > slen)
|
||||||
return 1;
|
return 1;
|
||||||
return strcmp(s + slen - tlen, t);
|
return strcmp(s + slen - tlen, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -16,7 +16,7 @@ extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
|||||||
void ksu_trace_execveat_hook_callback(void *data, int *fd, struct filename **filename_ptr,
|
void ksu_trace_execveat_hook_callback(void *data, int *fd, struct filename **filename_ptr,
|
||||||
void *argv, void *envp, int *flags)
|
void *argv, void *envp, int *flags)
|
||||||
{
|
{
|
||||||
ksu_handle_execveat(fd, filename_ptr, argv, envp, flags);
|
ksu_handle_execveat(fd, filename_ptr, argv, envp, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_trace_faccessat_hook_callback(void *data, int *dfd, const char __user **filename_user,
|
void ksu_trace_faccessat_hook_callback(void *data, int *dfd, const char __user **filename_user,
|
||||||
@@ -28,8 +28,8 @@ void ksu_trace_faccessat_hook_callback(void *data, int *dfd, const char __user *
|
|||||||
void ksu_trace_sys_read_hook_callback(void *data, unsigned int fd, char __user **buf_ptr,
|
void ksu_trace_sys_read_hook_callback(void *data, unsigned int fd, char __user **buf_ptr,
|
||||||
size_t *count_ptr)
|
size_t *count_ptr)
|
||||||
{
|
{
|
||||||
if (unlikely(ksu_vfs_read_hook))
|
if (unlikely(ksu_vfs_read_hook))
|
||||||
ksu_handle_sys_read(fd, buf_ptr, count_ptr);
|
ksu_handle_sys_read(fd, buf_ptr, count_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_trace_stat_hook_callback(void *data, int *dfd, const char __user **filename_user,
|
void ksu_trace_stat_hook_callback(void *data, int *dfd, const char __user **filename_user,
|
||||||
@@ -41,8 +41,8 @@ void ksu_trace_stat_hook_callback(void *data, int *dfd, const char __user **file
|
|||||||
void ksu_trace_input_hook_callback(void *data, unsigned int *type, unsigned int *code,
|
void ksu_trace_input_hook_callback(void *data, unsigned int *type, unsigned int *code,
|
||||||
int *value)
|
int *value)
|
||||||
{
|
{
|
||||||
if (unlikely(ksu_input_hook))
|
if (unlikely(ksu_input_hook))
|
||||||
ksu_handle_input_handle_event(type, code, value);
|
ksu_handle_input_handle_event(type, code, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
// end tracepoint callback functions
|
// end tracepoint callback functions
|
||||||
|
|||||||
@@ -8,24 +8,24 @@
|
|||||||
#include <linux/tracepoint.h>
|
#include <linux/tracepoint.h>
|
||||||
|
|
||||||
DECLARE_TRACE(ksu_trace_execveat_hook,
|
DECLARE_TRACE(ksu_trace_execveat_hook,
|
||||||
TP_PROTO(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags),
|
TP_PROTO(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags),
|
||||||
TP_ARGS(fd, filename_ptr, argv, envp, flags));
|
TP_ARGS(fd, filename_ptr, argv, envp, flags));
|
||||||
|
|
||||||
DECLARE_TRACE(ksu_trace_faccessat_hook,
|
DECLARE_TRACE(ksu_trace_faccessat_hook,
|
||||||
TP_PROTO(int *dfd, const char __user **filename_user, int *mode, int *flags),
|
TP_PROTO(int *dfd, const char __user **filename_user, int *mode, int *flags),
|
||||||
TP_ARGS(dfd, filename_user, mode, flags));
|
TP_ARGS(dfd, filename_user, mode, flags));
|
||||||
|
|
||||||
DECLARE_TRACE(ksu_trace_sys_read_hook,
|
DECLARE_TRACE(ksu_trace_sys_read_hook,
|
||||||
TP_PROTO(unsigned int fd, char __user **buf_ptr, size_t *count_ptr),
|
TP_PROTO(unsigned int fd, char __user **buf_ptr, size_t *count_ptr),
|
||||||
TP_ARGS(fd, buf_ptr, count_ptr));
|
TP_ARGS(fd, buf_ptr, count_ptr));
|
||||||
|
|
||||||
DECLARE_TRACE(ksu_trace_stat_hook,
|
DECLARE_TRACE(ksu_trace_stat_hook,
|
||||||
TP_PROTO(int *dfd, const char __user **filename_user, int *flags),
|
TP_PROTO(int *dfd, const char __user **filename_user, int *flags),
|
||||||
TP_ARGS(dfd, filename_user, flags));
|
TP_ARGS(dfd, filename_user, flags));
|
||||||
|
|
||||||
DECLARE_TRACE(ksu_trace_input_hook,
|
DECLARE_TRACE(ksu_trace_input_hook,
|
||||||
TP_PROTO(unsigned int *type, unsigned int *code, int *value),
|
TP_PROTO(unsigned int *type, unsigned int *code, int *value),
|
||||||
TP_ARGS(type, code, value));
|
TP_ARGS(type, code, value));
|
||||||
|
|
||||||
#endif /* _KSU_TRACE_H */
|
#endif /* _KSU_TRACE_H */
|
||||||
|
|
||||||
|
|||||||
889
kernel/ksud.c
889
kernel/ksud.c
File diff suppressed because it is too large
Load Diff
@@ -15,28 +15,31 @@ extern int ksu_get_manager_signature_index(uid_t uid);
|
|||||||
|
|
||||||
static inline bool ksu_is_manager_uid_valid(void)
|
static inline bool ksu_is_manager_uid_valid(void)
|
||||||
{
|
{
|
||||||
return ksu_manager_uid != KSU_INVALID_UID;
|
return ksu_manager_uid != KSU_INVALID_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool is_manager(void)
|
static inline bool is_manager(void)
|
||||||
{
|
{
|
||||||
return unlikely(ksu_is_any_manager(current_uid().val) ||
|
return unlikely(ksu_is_any_manager(current_uid().val) ||
|
||||||
(ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val));
|
(ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uid_t ksu_get_manager_uid(void)
|
static inline uid_t ksu_get_manager_uid(void)
|
||||||
{
|
{
|
||||||
return ksu_manager_uid;
|
return ksu_manager_uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ksu_set_manager_uid(uid_t uid)
|
static inline void ksu_set_manager_uid(uid_t uid)
|
||||||
{
|
{
|
||||||
ksu_manager_uid = uid;
|
ksu_manager_uid = uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ksu_invalidate_manager_uid(void)
|
static inline void ksu_invalidate_manager_uid(void)
|
||||||
{
|
{
|
||||||
ksu_manager_uid = KSU_INVALID_UID;
|
ksu_manager_uid = KSU_INVALID_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ksu_observer_init(void);
|
||||||
|
void ksu_observer_exit(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -26,8 +26,8 @@
|
|||||||
#define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000"
|
#define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned size;
|
unsigned size;
|
||||||
const char *sha256;
|
const char *sha256;
|
||||||
} apk_sign_key_t;
|
} apk_sign_key_t;
|
||||||
|
|
||||||
#endif /* MANAGER_SIGN_H */
|
#endif /* MANAGER_SIGN_H */
|
||||||
|
|||||||
@@ -121,9 +121,9 @@ static char* ksu_generate_auth_token(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
strscpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
|
strscpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
|
||||||
#else
|
#else
|
||||||
strlcpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
|
strlcpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
|
||||||
#endif
|
#endif
|
||||||
auth_tokens[token_count].expire_time = jiffies + KSU_TOKEN_EXPIRE_TIME * HZ;
|
auth_tokens[token_count].expire_time = jiffies + KSU_TOKEN_EXPIRE_TIME * HZ;
|
||||||
auth_tokens[token_count].used = false;
|
auth_tokens[token_count].used = false;
|
||||||
@@ -204,9 +204,9 @@ static int handle_token_generation(struct manual_su_request *request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
strscpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
|
strscpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
|
||||||
#else
|
#else
|
||||||
strlcpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
|
strlcpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pr_info("manual_su: auth token generated successfully\n");
|
pr_info("manual_su: auth token generated successfully\n");
|
||||||
|
|||||||
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");
|
||||||
|
}
|
||||||
@@ -20,138 +20,138 @@
|
|||||||
|
|
||||||
static struct policydb *get_policydb(void)
|
static struct policydb *get_policydb(void)
|
||||||
{
|
{
|
||||||
struct policydb *db;
|
struct policydb *db;
|
||||||
// selinux_state does not exists before 4.19
|
// selinux_state does not exists before 4.19
|
||||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||||
#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS
|
#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS
|
||||||
struct selinux_policy *policy = selinux_state.policy;
|
struct selinux_policy *policy = selinux_state.policy;
|
||||||
db = &policy->policydb;
|
db = &policy->policydb;
|
||||||
#else
|
#else
|
||||||
struct selinux_ss *ss = selinux_state.ss;
|
struct selinux_ss *ss = selinux_state.ss;
|
||||||
db = &ss->policydb;
|
db = &ss->policydb;
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
db = &policydb;
|
db = &policydb;
|
||||||
#endif
|
#endif
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
static DEFINE_MUTEX(ksu_rules);
|
static DEFINE_MUTEX(ksu_rules);
|
||||||
void apply_kernelsu_rules(void)
|
void apply_kernelsu_rules(void)
|
||||||
{
|
{
|
||||||
struct policydb *db;
|
struct policydb *db;
|
||||||
|
|
||||||
if (!getenforce()) {
|
if (!getenforce()) {
|
||||||
pr_info("SELinux permissive or disabled, apply rules!\n");
|
pr_info("SELinux permissive or disabled, apply rules!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&ksu_rules);
|
mutex_lock(&ksu_rules);
|
||||||
|
|
||||||
db = get_policydb();
|
db = get_policydb();
|
||||||
|
|
||||||
ksu_permissive(db, KERNEL_SU_DOMAIN);
|
ksu_permissive(db, KERNEL_SU_DOMAIN);
|
||||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
|
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
|
||||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain");
|
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain");
|
||||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain");
|
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain");
|
||||||
|
|
||||||
// Create unconstrained file type
|
// Create unconstrained file type
|
||||||
ksu_type(db, KERNEL_SU_FILE, "file_type");
|
ksu_type(db, KERNEL_SU_FILE, "file_type");
|
||||||
ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject");
|
ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL);
|
ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL);
|
||||||
|
|
||||||
// allow all!
|
// allow all!
|
||||||
ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL);
|
ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL);
|
||||||
|
|
||||||
// allow us do any ioctl
|
// allow us do any ioctl
|
||||||
if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
|
if (db->policyvers >= POLICYDB_VERSION_XPERMS_IOCTL) {
|
||||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "blk_file", ALL);
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "blk_file", ALL);
|
||||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "fifo_file", ALL);
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "fifo_file", ALL);
|
||||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL);
|
||||||
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "file", ALL);
|
ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "file", ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we need to save allowlist in /data/adb/ksu
|
// we need to save allowlist in /data/adb/ksu
|
||||||
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
|
ksu_allow(db, "kernel", "adb_data_file", "dir", ALL);
|
||||||
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
|
ksu_allow(db, "kernel", "adb_data_file", "file", ALL);
|
||||||
// we need to search /data/app
|
// we need to search /data/app
|
||||||
ksu_allow(db, "kernel", "apk_data_file", "file", "open");
|
ksu_allow(db, "kernel", "apk_data_file", "file", "open");
|
||||||
ksu_allow(db, "kernel", "apk_data_file", "dir", "open");
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "open");
|
||||||
ksu_allow(db, "kernel", "apk_data_file", "dir", "read");
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "read");
|
||||||
ksu_allow(db, "kernel", "apk_data_file", "dir", "search");
|
ksu_allow(db, "kernel", "apk_data_file", "dir", "search");
|
||||||
// we may need to do mount on shell
|
// we may need to do mount on shell
|
||||||
ksu_allow(db, "kernel", "shell_data_file", "file", ALL);
|
ksu_allow(db, "kernel", "shell_data_file", "file", ALL);
|
||||||
// we need to read /data/system/packages.list
|
// we need to read /data/system/packages.list
|
||||||
ksu_allow(db, "kernel", "kernel", "capability", "dac_override");
|
ksu_allow(db, "kernel", "kernel", "capability", "dac_override");
|
||||||
// Android 10+:
|
// Android 10+:
|
||||||
// http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512
|
// http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512
|
||||||
ksu_allow(db, "kernel", "packages_list_file", "file", ALL);
|
ksu_allow(db, "kernel", "packages_list_file", "file", ALL);
|
||||||
// Kernel 4.4
|
// Kernel 4.4
|
||||||
ksu_allow(db, "kernel", "packages_list_file", "dir", ALL);
|
ksu_allow(db, "kernel", "packages_list_file", "dir", ALL);
|
||||||
// Android 9-:
|
// Android 9-:
|
||||||
// http://aospxref.com/android-9.0.0_r61/xref/system/sepolicy/private/file_contexts#360
|
// http://aospxref.com/android-9.0.0_r61/xref/system/sepolicy/private/file_contexts#360
|
||||||
ksu_allow(db, "kernel", "system_data_file", "file", ALL);
|
ksu_allow(db, "kernel", "system_data_file", "file", ALL);
|
||||||
ksu_allow(db, "kernel", "system_data_file", "dir", ALL);
|
ksu_allow(db, "kernel", "system_data_file", "dir", ALL);
|
||||||
// our ksud triggered by init
|
// our ksud triggered by init
|
||||||
ksu_allow(db, "init", "adb_data_file", "file", ALL);
|
ksu_allow(db, "init", "adb_data_file", "file", ALL);
|
||||||
ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289
|
ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289
|
||||||
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
|
ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL);
|
||||||
|
|
||||||
// we need to umount modules in zygote
|
// we need to umount modules in zygote
|
||||||
ksu_allow(db, "zygote", "adb_data_file", "dir", "search");
|
ksu_allow(db, "zygote", "adb_data_file", "dir", "search");
|
||||||
|
|
||||||
// copied from Magisk rules
|
// copied from Magisk rules
|
||||||
// suRights
|
// suRights
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "read");
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "dir", "read");
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
||||||
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr");
|
ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "process", "sigchld");
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "process", "sigchld");
|
||||||
|
|
||||||
// allowLog
|
// allowLog
|
||||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "dir", "search");
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "dir", "search");
|
||||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "read");
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "read");
|
||||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "open");
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "open");
|
||||||
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr");
|
ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr");
|
||||||
|
|
||||||
// dumpsys
|
// dumpsys
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fd", "use");
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fd", "use");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "write");
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "write");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "read");
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "read");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "open");
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "open");
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "getattr");
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "getattr");
|
||||||
|
|
||||||
// bootctl
|
// bootctl
|
||||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "dir", "search");
|
||||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "read");
|
||||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "file", "open");
|
||||||
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process",
|
ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process",
|
||||||
"getattr");
|
"getattr");
|
||||||
|
|
||||||
// For mounting loop devices, mirrors, tmpfs
|
// For mounting loop devices, mirrors, tmpfs
|
||||||
ksu_allow(db, "kernel", ALL, "file", "read");
|
ksu_allow(db, "kernel", ALL, "file", "read");
|
||||||
ksu_allow(db, "kernel", ALL, "file", "write");
|
ksu_allow(db, "kernel", ALL, "file", "write");
|
||||||
|
|
||||||
// Allow all binder transactions
|
// Allow all binder transactions
|
||||||
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL);
|
||||||
|
|
||||||
// Allow system server kill su process
|
// Allow system server kill su process
|
||||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
||||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
||||||
|
|
||||||
// https://android-review.googlesource.com/c/platform/system/logging/+/3725346
|
// https://android-review.googlesource.com/c/platform/system/logging/+/3725346
|
||||||
ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");
|
ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_SUSFS
|
#ifdef CONFIG_KSU_SUSFS
|
||||||
// Allow umount in zygote process without installing zygisk
|
// Allow umount in zygote process without installing zygisk
|
||||||
ksu_allow(db, "zygote", "labeledfs", "filesystem", "unmount");
|
ksu_allow(db, "zygote", "labeledfs", "filesystem", "unmount");
|
||||||
susfs_set_kernel_sid();
|
susfs_set_kernel_sid();
|
||||||
susfs_set_init_sid();
|
susfs_set_init_sid();
|
||||||
susfs_set_ksu_sid();
|
susfs_set_ksu_sid();
|
||||||
susfs_set_zygote_sid();
|
susfs_set_zygote_sid();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mutex_unlock(&ksu_rules);
|
mutex_unlock(&ksu_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_SEPOL_LEN 128
|
#define MAX_SEPOL_LEN 128
|
||||||
@@ -171,375 +171,375 @@ extern bool ksu_is_compat __read_mostly;
|
|||||||
|
|
||||||
// armv7l kernel compat
|
// armv7l kernel compat
|
||||||
#ifdef CONFIG_64BIT
|
#ifdef CONFIG_64BIT
|
||||||
#define usize u64
|
#define usize u64
|
||||||
#else
|
#else
|
||||||
#define usize u32
|
#define usize u32
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct sepol_data {
|
struct sepol_data {
|
||||||
u32 cmd;
|
u32 cmd;
|
||||||
u32 subcmd;
|
u32 subcmd;
|
||||||
usize field_sepol1;
|
usize field_sepol1;
|
||||||
usize field_sepol2;
|
usize field_sepol2;
|
||||||
usize field_sepol3;
|
usize field_sepol3;
|
||||||
usize field_sepol4;
|
usize field_sepol4;
|
||||||
usize field_sepol5;
|
usize field_sepol5;
|
||||||
usize field_sepol6;
|
usize field_sepol6;
|
||||||
usize field_sepol7;
|
usize field_sepol7;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ksud 32-bit on arm64 kernel
|
// ksud 32-bit on arm64 kernel
|
||||||
struct __maybe_unused sepol_data_compat {
|
struct __maybe_unused sepol_data_compat {
|
||||||
u32 cmd;
|
u32 cmd;
|
||||||
u32 subcmd;
|
u32 subcmd;
|
||||||
u32 field_sepol1;
|
u32 field_sepol1;
|
||||||
u32 field_sepol2;
|
u32 field_sepol2;
|
||||||
u32 field_sepol3;
|
u32 field_sepol3;
|
||||||
u32 field_sepol4;
|
u32 field_sepol4;
|
||||||
u32 field_sepol5;
|
u32 field_sepol5;
|
||||||
u32 field_sepol6;
|
u32 field_sepol6;
|
||||||
u32 field_sepol7;
|
u32 field_sepol7;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
||||||
char **object)
|
char **object)
|
||||||
{
|
{
|
||||||
if (!user_object) {
|
if (!user_object) {
|
||||||
*object = ALL;
|
*object = ALL;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
|
if (strncpy_from_user(buf, user_object, buf_sz) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
*object = buf;
|
*object = buf;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
||||||
static void reset_avc_cache(void)
|
static void reset_avc_cache(void)
|
||||||
{
|
{
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \
|
||||||
!defined(KSU_COMPAT_USE_SELINUX_STATE)
|
!defined(KSU_COMPAT_USE_SELINUX_STATE)
|
||||||
avc_ss_reset(0);
|
avc_ss_reset(0);
|
||||||
selnl_notify_policyload(0);
|
selnl_notify_policyload(0);
|
||||||
selinux_status_update_policyload(0);
|
selinux_status_update_policyload(0);
|
||||||
#else
|
#else
|
||||||
struct selinux_avc *avc = selinux_state.avc;
|
struct selinux_avc *avc = selinux_state.avc;
|
||||||
avc_ss_reset(avc, 0);
|
avc_ss_reset(avc, 0);
|
||||||
selnl_notify_policyload(0);
|
selnl_notify_policyload(0);
|
||||||
selinux_status_update_policyload(&selinux_state, 0);
|
selinux_status_update_policyload(&selinux_state, 0);
|
||||||
#endif
|
#endif
|
||||||
selinux_xfrm_notify_policyload();
|
selinux_xfrm_notify_policyload();
|
||||||
}
|
}
|
||||||
|
|
||||||
int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||||
{
|
{
|
||||||
struct policydb *db;
|
struct policydb *db;
|
||||||
|
|
||||||
if (!arg4) {
|
if (!arg4) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!getenforce()) {
|
if (!getenforce()) {
|
||||||
pr_info("SELinux permissive or disabled when handle policy!\n");
|
pr_info("SELinux permissive or disabled when handle policy!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 cmd, subcmd;
|
u32 cmd, subcmd;
|
||||||
char __user *sepol1, *sepol2, *sepol3, *sepol4, *sepol5, *sepol6, *sepol7;
|
char __user *sepol1, *sepol2, *sepol3, *sepol4, *sepol5, *sepol6, *sepol7;
|
||||||
|
|
||||||
if (unlikely(ksu_is_compat)) {
|
if (unlikely(ksu_is_compat)) {
|
||||||
struct sepol_data_compat data_compat;
|
struct sepol_data_compat data_compat;
|
||||||
if (copy_from_user(&data_compat, arg4, sizeof(struct sepol_data_compat))) {
|
if (copy_from_user(&data_compat, arg4, sizeof(struct sepol_data_compat))) {
|
||||||
pr_err("sepol: copy sepol_data failed.\n");
|
pr_err("sepol: copy sepol_data failed.\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
pr_info("sepol: running in compat mode!\n");
|
pr_info("sepol: running in compat mode!\n");
|
||||||
sepol1 = compat_ptr(data_compat.field_sepol1);
|
sepol1 = compat_ptr(data_compat.field_sepol1);
|
||||||
sepol2 = compat_ptr(data_compat.field_sepol2);
|
sepol2 = compat_ptr(data_compat.field_sepol2);
|
||||||
sepol3 = compat_ptr(data_compat.field_sepol3);
|
sepol3 = compat_ptr(data_compat.field_sepol3);
|
||||||
sepol4 = compat_ptr(data_compat.field_sepol4);
|
sepol4 = compat_ptr(data_compat.field_sepol4);
|
||||||
sepol5 = compat_ptr(data_compat.field_sepol5);
|
sepol5 = compat_ptr(data_compat.field_sepol5);
|
||||||
sepol6 = compat_ptr(data_compat.field_sepol6);
|
sepol6 = compat_ptr(data_compat.field_sepol6);
|
||||||
sepol7 = compat_ptr(data_compat.field_sepol7);
|
sepol7 = compat_ptr(data_compat.field_sepol7);
|
||||||
cmd = data_compat.cmd;
|
cmd = data_compat.cmd;
|
||||||
subcmd = data_compat.subcmd;
|
subcmd = data_compat.subcmd;
|
||||||
} else {
|
} else {
|
||||||
struct sepol_data data;
|
struct sepol_data data;
|
||||||
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
||||||
pr_err("sepol: copy sepol_data failed.\n");
|
pr_err("sepol: copy sepol_data failed.\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
sepol1 = data.field_sepol1;
|
sepol1 = data.field_sepol1;
|
||||||
sepol2 = data.field_sepol2;
|
sepol2 = data.field_sepol2;
|
||||||
sepol3 = data.field_sepol3;
|
sepol3 = data.field_sepol3;
|
||||||
sepol4 = data.field_sepol4;
|
sepol4 = data.field_sepol4;
|
||||||
sepol5 = data.field_sepol5;
|
sepol5 = data.field_sepol5;
|
||||||
sepol6 = data.field_sepol6;
|
sepol6 = data.field_sepol6;
|
||||||
sepol7 = data.field_sepol7;
|
sepol7 = data.field_sepol7;
|
||||||
cmd = data.cmd;
|
cmd = data.cmd;
|
||||||
subcmd = data.subcmd;
|
subcmd = data.subcmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&ksu_rules);
|
mutex_lock(&ksu_rules);
|
||||||
|
|
||||||
db = get_policydb();
|
db = get_policydb();
|
||||||
|
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
if (cmd == CMD_NORMAL_PERM) {
|
if (cmd == CMD_NORMAL_PERM) {
|
||||||
char src_buf[MAX_SEPOL_LEN];
|
char src_buf[MAX_SEPOL_LEN];
|
||||||
char tgt_buf[MAX_SEPOL_LEN];
|
char tgt_buf[MAX_SEPOL_LEN];
|
||||||
char cls_buf[MAX_SEPOL_LEN];
|
char cls_buf[MAX_SEPOL_LEN];
|
||||||
char perm_buf[MAX_SEPOL_LEN];
|
char perm_buf[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
char *s, *t, *c, *p;
|
char *s, *t, *c, *p;
|
||||||
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
|
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
|
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
|
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy cls failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_object(perm_buf, sepol4, sizeof(perm_buf), &p) <
|
if (get_object(perm_buf, sepol4, sizeof(perm_buf), &p) <
|
||||||
0) {
|
0) {
|
||||||
pr_err("sepol: copy perm failed.\n");
|
pr_err("sepol: copy perm failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (subcmd == 1) {
|
if (subcmd == 1) {
|
||||||
success = ksu_allow(db, s, t, c, p);
|
success = ksu_allow(db, s, t, c, p);
|
||||||
} else if (subcmd == 2) {
|
} else if (subcmd == 2) {
|
||||||
success = ksu_deny(db, s, t, c, p);
|
success = ksu_deny(db, s, t, c, p);
|
||||||
} else if (subcmd == 3) {
|
} else if (subcmd == 3) {
|
||||||
success = ksu_auditallow(db, s, t, c, p);
|
success = ksu_auditallow(db, s, t, c, p);
|
||||||
} else if (subcmd == 4) {
|
} else if (subcmd == 4) {
|
||||||
success = ksu_dontaudit(db, s, t, c, p);
|
success = ksu_dontaudit(db, s, t, c, p);
|
||||||
} else {
|
} else {
|
||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
}
|
}
|
||||||
ret = success ? 0 : -1;
|
ret = success ? 0 : -1;
|
||||||
|
|
||||||
} else if (cmd == CMD_XPERM) {
|
} else if (cmd == CMD_XPERM) {
|
||||||
char src_buf[MAX_SEPOL_LEN];
|
char src_buf[MAX_SEPOL_LEN];
|
||||||
char tgt_buf[MAX_SEPOL_LEN];
|
char tgt_buf[MAX_SEPOL_LEN];
|
||||||
char cls_buf[MAX_SEPOL_LEN];
|
char cls_buf[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
char __maybe_unused
|
char __maybe_unused
|
||||||
operation[MAX_SEPOL_LEN]; // it is always ioctl now!
|
operation[MAX_SEPOL_LEN]; // it is always ioctl now!
|
||||||
char perm_set[MAX_SEPOL_LEN];
|
char perm_set[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
char *s, *t, *c;
|
char *s, *t, *c;
|
||||||
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
|
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
|
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
|
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy cls failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(operation, sepol4,
|
if (strncpy_from_user(operation, sepol4,
|
||||||
sizeof(operation)) < 0) {
|
sizeof(operation)) < 0) {
|
||||||
pr_err("sepol: copy operation failed.\n");
|
pr_err("sepol: copy operation failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(perm_set, sepol5, sizeof(perm_set)) <
|
if (strncpy_from_user(perm_set, sepol5, sizeof(perm_set)) <
|
||||||
0) {
|
0) {
|
||||||
pr_err("sepol: copy perm_set failed.\n");
|
pr_err("sepol: copy perm_set failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (subcmd == 1) {
|
if (subcmd == 1) {
|
||||||
success = ksu_allowxperm(db, s, t, c, perm_set);
|
success = ksu_allowxperm(db, s, t, c, perm_set);
|
||||||
} else if (subcmd == 2) {
|
} else if (subcmd == 2) {
|
||||||
success = ksu_auditallowxperm(db, s, t, c, perm_set);
|
success = ksu_auditallowxperm(db, s, t, c, perm_set);
|
||||||
} else if (subcmd == 3) {
|
} else if (subcmd == 3) {
|
||||||
success = ksu_dontauditxperm(db, s, t, c, perm_set);
|
success = ksu_dontauditxperm(db, s, t, c, perm_set);
|
||||||
} else {
|
} else {
|
||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
}
|
}
|
||||||
ret = success ? 0 : -1;
|
ret = success ? 0 : -1;
|
||||||
} else if (cmd == CMD_TYPE_STATE) {
|
} else if (cmd == CMD_TYPE_STATE) {
|
||||||
char src[MAX_SEPOL_LEN];
|
char src[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (subcmd == 1) {
|
if (subcmd == 1) {
|
||||||
success = ksu_permissive(db, src);
|
success = ksu_permissive(db, src);
|
||||||
} else if (subcmd == 2) {
|
} else if (subcmd == 2) {
|
||||||
success = ksu_enforce(db, src);
|
success = ksu_enforce(db, src);
|
||||||
} else {
|
} else {
|
||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
}
|
}
|
||||||
if (success)
|
if (success)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
|
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
|
||||||
char type[MAX_SEPOL_LEN];
|
char type[MAX_SEPOL_LEN];
|
||||||
char attr[MAX_SEPOL_LEN];
|
char attr[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(type, sepol1, sizeof(type)) < 0) {
|
if (strncpy_from_user(type, sepol1, sizeof(type)) < 0) {
|
||||||
pr_err("sepol: copy type failed.\n");
|
pr_err("sepol: copy type failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(attr, sepol2, sizeof(attr)) < 0) {
|
if (strncpy_from_user(attr, sepol2, sizeof(attr)) < 0) {
|
||||||
pr_err("sepol: copy attr failed.\n");
|
pr_err("sepol: copy attr failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (cmd == CMD_TYPE) {
|
if (cmd == CMD_TYPE) {
|
||||||
success = ksu_type(db, type, attr);
|
success = ksu_type(db, type, attr);
|
||||||
} else {
|
} else {
|
||||||
success = ksu_typeattribute(db, type, attr);
|
success = ksu_typeattribute(db, type, attr);
|
||||||
}
|
}
|
||||||
if (!success) {
|
if (!success) {
|
||||||
pr_err("sepol: %d failed.\n", cmd);
|
pr_err("sepol: %d failed.\n", cmd);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
} else if (cmd == CMD_ATTR) {
|
} else if (cmd == CMD_ATTR) {
|
||||||
char attr[MAX_SEPOL_LEN];
|
char attr[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(attr, sepol1, sizeof(attr)) < 0) {
|
if (strncpy_from_user(attr, sepol1, sizeof(attr)) < 0) {
|
||||||
pr_err("sepol: copy attr failed.\n");
|
pr_err("sepol: copy attr failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (!ksu_attribute(db, attr)) {
|
if (!ksu_attribute(db, attr)) {
|
||||||
pr_err("sepol: %d failed.\n", cmd);
|
pr_err("sepol: %d failed.\n", cmd);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
} else if (cmd == CMD_TYPE_TRANSITION) {
|
} else if (cmd == CMD_TYPE_TRANSITION) {
|
||||||
char src[MAX_SEPOL_LEN];
|
char src[MAX_SEPOL_LEN];
|
||||||
char tgt[MAX_SEPOL_LEN];
|
char tgt[MAX_SEPOL_LEN];
|
||||||
char cls[MAX_SEPOL_LEN];
|
char cls[MAX_SEPOL_LEN];
|
||||||
char default_type[MAX_SEPOL_LEN];
|
char default_type[MAX_SEPOL_LEN];
|
||||||
char object[MAX_SEPOL_LEN];
|
char object[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
|
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
|
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy cls failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(default_type, sepol4,
|
if (strncpy_from_user(default_type, sepol4,
|
||||||
sizeof(default_type)) < 0) {
|
sizeof(default_type)) < 0) {
|
||||||
pr_err("sepol: copy default_type failed.\n");
|
pr_err("sepol: copy default_type failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
char *real_object;
|
char *real_object;
|
||||||
if (sepol5 == NULL) {
|
if (sepol5 == NULL) {
|
||||||
real_object = NULL;
|
real_object = NULL;
|
||||||
} else {
|
} else {
|
||||||
if (strncpy_from_user(object, sepol5,
|
if (strncpy_from_user(object, sepol5,
|
||||||
sizeof(object)) < 0) {
|
sizeof(object)) < 0) {
|
||||||
pr_err("sepol: copy object failed.\n");
|
pr_err("sepol: copy object failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
real_object = object;
|
real_object = object;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = ksu_type_transition(db, src, tgt, cls,
|
bool success = ksu_type_transition(db, src, tgt, cls,
|
||||||
default_type, real_object);
|
default_type, real_object);
|
||||||
if (success)
|
if (success)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
|
||||||
} else if (cmd == CMD_TYPE_CHANGE) {
|
} else if (cmd == CMD_TYPE_CHANGE) {
|
||||||
char src[MAX_SEPOL_LEN];
|
char src[MAX_SEPOL_LEN];
|
||||||
char tgt[MAX_SEPOL_LEN];
|
char tgt[MAX_SEPOL_LEN];
|
||||||
char cls[MAX_SEPOL_LEN];
|
char cls[MAX_SEPOL_LEN];
|
||||||
char default_type[MAX_SEPOL_LEN];
|
char default_type[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
|
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
|
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy cls failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(default_type, sepol4,
|
if (strncpy_from_user(default_type, sepol4,
|
||||||
sizeof(default_type)) < 0) {
|
sizeof(default_type)) < 0) {
|
||||||
pr_err("sepol: copy default_type failed.\n");
|
pr_err("sepol: copy default_type failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (subcmd == 1) {
|
if (subcmd == 1) {
|
||||||
success = ksu_type_change(db, src, tgt, cls,
|
success = ksu_type_change(db, src, tgt, cls,
|
||||||
default_type);
|
default_type);
|
||||||
} else if (subcmd == 2) {
|
} else if (subcmd == 2) {
|
||||||
success = ksu_type_member(db, src, tgt, cls,
|
success = ksu_type_member(db, src, tgt, cls,
|
||||||
default_type);
|
default_type);
|
||||||
} else {
|
} else {
|
||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
}
|
}
|
||||||
if (success)
|
if (success)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
} else if (cmd == CMD_GENFSCON) {
|
} else if (cmd == CMD_GENFSCON) {
|
||||||
char name[MAX_SEPOL_LEN];
|
char name[MAX_SEPOL_LEN];
|
||||||
char path[MAX_SEPOL_LEN];
|
char path[MAX_SEPOL_LEN];
|
||||||
char context[MAX_SEPOL_LEN];
|
char context[MAX_SEPOL_LEN];
|
||||||
if (strncpy_from_user(name, sepol1, sizeof(name)) < 0) {
|
if (strncpy_from_user(name, sepol1, sizeof(name)) < 0) {
|
||||||
pr_err("sepol: copy name failed.\n");
|
pr_err("sepol: copy name failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(path, sepol2, sizeof(path)) < 0) {
|
if (strncpy_from_user(path, sepol2, sizeof(path)) < 0) {
|
||||||
pr_err("sepol: copy path failed.\n");
|
pr_err("sepol: copy path failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(context, sepol3, sizeof(context)) <
|
if (strncpy_from_user(context, sepol3, sizeof(context)) <
|
||||||
0) {
|
0) {
|
||||||
pr_err("sepol: copy context failed.\n");
|
pr_err("sepol: copy context failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ksu_genfscon(db, name, path, context)) {
|
if (!ksu_genfscon(db, name, path, context)) {
|
||||||
pr_err("sepol: %d failed.\n", cmd);
|
pr_err("sepol: %d failed.\n", cmd);
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
} else {
|
} else {
|
||||||
pr_err("sepol: unknown cmd: %d\n", cmd);
|
pr_err("sepol: unknown cmd: %d\n", cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
mutex_unlock(&ksu_rules);
|
mutex_unlock(&ksu_rules);
|
||||||
|
|
||||||
// only allow and xallow needs to reset avc cache, but we cannot do that because
|
// only allow and xallow needs to reset avc cache, but we cannot do that because
|
||||||
// we are in atomic context. so we just reset it every time.
|
// we are in atomic context. so we just reset it every time.
|
||||||
reset_avc_cache();
|
reset_avc_cache();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,232 +16,232 @@ u32 susfs_kernel_sid = 0;
|
|||||||
|
|
||||||
static int transive_to_domain(const char *domain)
|
static int transive_to_domain(const char *domain)
|
||||||
{
|
{
|
||||||
struct cred *cred;
|
struct cred *cred;
|
||||||
struct task_security_struct *tsec;
|
struct task_security_struct *tsec;
|
||||||
u32 sid;
|
u32 sid;
|
||||||
int error;
|
int error;
|
||||||
|
|
||||||
cred = (struct cred *)__task_cred(current);
|
cred = (struct cred *)__task_cred(current);
|
||||||
|
|
||||||
tsec = cred->security;
|
tsec = cred->security;
|
||||||
if (!tsec) {
|
if (!tsec) {
|
||||||
pr_err("tsec == NULL!\n");
|
pr_err("tsec == NULL!\n");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
error = security_secctx_to_secid(domain, strlen(domain), &sid);
|
error = security_secctx_to_secid(domain, strlen(domain), &sid);
|
||||||
if (error) {
|
if (error) {
|
||||||
pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n",
|
pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n",
|
||||||
domain, sid, error);
|
domain, sid, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!error) {
|
if (!error) {
|
||||||
tsec->sid = sid;
|
tsec->sid = sid;
|
||||||
tsec->create_sid = 0;
|
tsec->create_sid = 0;
|
||||||
tsec->keycreate_sid = 0;
|
tsec->keycreate_sid = 0;
|
||||||
tsec->sockcreate_sid = 0;
|
tsec->sockcreate_sid = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 19, 0)
|
#if LINUX_VERSION_CODE <= KERNEL_VERSION(4, 19, 0)
|
||||||
bool __maybe_unused is_ksu_transition(const struct task_security_struct *old_tsec,
|
bool __maybe_unused is_ksu_transition(const struct task_security_struct *old_tsec,
|
||||||
const struct task_security_struct *new_tsec)
|
const struct task_security_struct *new_tsec)
|
||||||
{
|
{
|
||||||
static u32 ksu_sid;
|
static u32 ksu_sid;
|
||||||
char *secdata;
|
char *secdata;
|
||||||
u32 seclen;
|
u32 seclen;
|
||||||
bool allowed = false;
|
bool allowed = false;
|
||||||
|
|
||||||
if (!ksu_sid)
|
if (!ksu_sid)
|
||||||
security_secctx_to_secid(KERNEL_SU_DOMAIN, strlen(KERNEL_SU_DOMAIN), &ksu_sid);
|
security_secctx_to_secid(KERNEL_SU_DOMAIN, strlen(KERNEL_SU_DOMAIN), &ksu_sid);
|
||||||
|
|
||||||
if (security_secid_to_secctx(old_tsec->sid, &secdata, &seclen))
|
if (security_secid_to_secctx(old_tsec->sid, &secdata, &seclen))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
allowed = (!strcmp("u:r:init:s0", secdata) && new_tsec->sid == ksu_sid);
|
allowed = (!strcmp("u:r:init:s0", secdata) && new_tsec->sid == ksu_sid);
|
||||||
security_release_secctx(secdata, seclen);
|
security_release_secctx(secdata, seclen);
|
||||||
return allowed;
|
return allowed;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void setup_selinux(const char *domain)
|
void setup_selinux(const char *domain)
|
||||||
{
|
{
|
||||||
if (transive_to_domain(domain)) {
|
if (transive_to_domain(domain)) {
|
||||||
pr_err("transive domain failed.\n");
|
pr_err("transive domain failed.\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void setenforce(bool enforce)
|
void setenforce(bool enforce)
|
||||||
{
|
{
|
||||||
__setenforce(enforce);
|
__setenforce(enforce);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool getenforce(void)
|
bool getenforce(void)
|
||||||
{
|
{
|
||||||
if (is_selinux_disabled()) {
|
if (is_selinux_disabled()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return __is_selinux_enforcing();
|
return __is_selinux_enforcing();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
|
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
|
||||||
!defined(KSU_COMPAT_HAS_CURRENT_SID)
|
!defined(KSU_COMPAT_HAS_CURRENT_SID)
|
||||||
/*
|
/*
|
||||||
* get the subjective security ID of the current task
|
* get the subjective security ID of the current task
|
||||||
*/
|
*/
|
||||||
static inline u32 current_sid(void)
|
static inline u32 current_sid(void)
|
||||||
{
|
{
|
||||||
const struct task_security_struct *tsec = current_security();
|
const struct task_security_struct *tsec = current_security();
|
||||||
|
|
||||||
return tsec->sid;
|
return tsec->sid;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
bool is_ksu_domain(void)
|
bool is_ksu_domain(void)
|
||||||
{
|
{
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
||||||
struct lsm_context ctx;
|
struct lsm_context ctx;
|
||||||
#else
|
#else
|
||||||
char *domain;
|
char *domain;
|
||||||
u32 seclen;
|
u32 seclen;
|
||||||
#endif
|
#endif
|
||||||
bool result;
|
bool result;
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
||||||
int err = security_secid_to_secctx(current_sid(), &ctx);
|
int err = security_secid_to_secctx(current_sid(), &ctx);
|
||||||
#else
|
#else
|
||||||
int err = security_secid_to_secctx(current_sid(), &domain, &seclen);
|
int err = security_secid_to_secctx(current_sid(), &domain, &seclen);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
||||||
result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0;
|
result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0;
|
||||||
security_release_secctx(&ctx);
|
security_release_secctx(&ctx);
|
||||||
#else
|
#else
|
||||||
result = strncmp(KERNEL_SU_DOMAIN, domain, seclen) == 0;
|
result = strncmp(KERNEL_SU_DOMAIN, domain, seclen) == 0;
|
||||||
security_release_secctx(domain, seclen);
|
security_release_secctx(domain, seclen);
|
||||||
#endif
|
#endif
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_zygote(void *sec)
|
bool is_zygote(void *sec)
|
||||||
{
|
{
|
||||||
struct task_security_struct *tsec = (struct task_security_struct *)sec;
|
struct task_security_struct *tsec = (struct task_security_struct *)sec;
|
||||||
if (!tsec) {
|
if (!tsec) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
||||||
struct lsm_context ctx;
|
struct lsm_context ctx;
|
||||||
#else
|
#else
|
||||||
char *domain;
|
char *domain;
|
||||||
u32 seclen;
|
u32 seclen;
|
||||||
#endif
|
#endif
|
||||||
bool result;
|
bool result;
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
||||||
int err = security_secid_to_secctx(tsec->sid, &ctx);
|
int err = security_secid_to_secctx(tsec->sid, &ctx);
|
||||||
#else
|
#else
|
||||||
int err = security_secid_to_secctx(tsec->sid, &domain, &seclen);
|
int err = security_secid_to_secctx(tsec->sid, &domain, &seclen);
|
||||||
#endif
|
#endif
|
||||||
if (err) {
|
if (err) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0)
|
||||||
result = strncmp("u:r:zygote:s0", ctx.context, ctx.len) == 0;
|
result = strncmp("u:r:zygote:s0", ctx.context, ctx.len) == 0;
|
||||||
security_release_secctx(&ctx);
|
security_release_secctx(&ctx);
|
||||||
#else
|
#else
|
||||||
result = strncmp("u:r:zygote:s0", domain, seclen) == 0;
|
result = strncmp("u:r:zygote:s0", domain, seclen) == 0;
|
||||||
security_release_secctx(domain, seclen);
|
security_release_secctx(domain, seclen);
|
||||||
#endif
|
#endif
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_SUSFS
|
#ifdef CONFIG_KSU_SUSFS
|
||||||
static inline void susfs_set_sid(const char *secctx_name, u32 *out_sid)
|
static inline void susfs_set_sid(const char *secctx_name, u32 *out_sid)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!secctx_name || !out_sid) {
|
if (!secctx_name || !out_sid) {
|
||||||
pr_err("secctx_name || out_sid is NULL\n");
|
pr_err("secctx_name || out_sid is NULL\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
err = security_secctx_to_secid(secctx_name, strlen(secctx_name),
|
err = security_secctx_to_secid(secctx_name, strlen(secctx_name),
|
||||||
out_sid);
|
out_sid);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_err("failed setting sid for '%s', err: %d\n", secctx_name, err);
|
pr_err("failed setting sid for '%s', err: %d\n", secctx_name, err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pr_info("sid '%u' is set for secctx_name '%s'\n", *out_sid, secctx_name);
|
pr_info("sid '%u' is set for secctx_name '%s'\n", *out_sid, secctx_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool susfs_is_sid_equal(void *sec, u32 sid2) {
|
bool susfs_is_sid_equal(void *sec, u32 sid2) {
|
||||||
struct task_security_struct *tsec = (struct task_security_struct *)sec;
|
struct task_security_struct *tsec = (struct task_security_struct *)sec;
|
||||||
if (!tsec) {
|
if (!tsec) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return tsec->sid == sid2;
|
return tsec->sid == sid2;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 susfs_get_sid_from_name(const char *secctx_name)
|
u32 susfs_get_sid_from_name(const char *secctx_name)
|
||||||
{
|
{
|
||||||
u32 out_sid = 0;
|
u32 out_sid = 0;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!secctx_name) {
|
if (!secctx_name) {
|
||||||
pr_err("secctx_name is NULL\n");
|
pr_err("secctx_name is NULL\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
err = security_secctx_to_secid(secctx_name, strlen(secctx_name),
|
err = security_secctx_to_secid(secctx_name, strlen(secctx_name),
|
||||||
&out_sid);
|
&out_sid);
|
||||||
if (err) {
|
if (err) {
|
||||||
pr_err("failed getting sid from secctx_name: %s, err: %d\n", secctx_name, err);
|
pr_err("failed getting sid from secctx_name: %s, err: %d\n", secctx_name, err);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return out_sid;
|
return out_sid;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 susfs_get_current_sid(void) {
|
u32 susfs_get_current_sid(void) {
|
||||||
return current_sid();
|
return current_sid();
|
||||||
}
|
}
|
||||||
|
|
||||||
void susfs_set_zygote_sid(void)
|
void susfs_set_zygote_sid(void)
|
||||||
{
|
{
|
||||||
susfs_set_sid(KERNEL_ZYGOTE_DOMAIN, &susfs_zygote_sid);
|
susfs_set_sid(KERNEL_ZYGOTE_DOMAIN, &susfs_zygote_sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool susfs_is_current_zygote_domain(void) {
|
bool susfs_is_current_zygote_domain(void) {
|
||||||
return unlikely(current_sid() == susfs_zygote_sid);
|
return unlikely(current_sid() == susfs_zygote_sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void susfs_set_ksu_sid(void)
|
void susfs_set_ksu_sid(void)
|
||||||
{
|
{
|
||||||
susfs_set_sid(KERNEL_SU_DOMAIN, &susfs_ksu_sid);
|
susfs_set_sid(KERNEL_SU_DOMAIN, &susfs_ksu_sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool susfs_is_current_ksu_domain(void) {
|
bool susfs_is_current_ksu_domain(void) {
|
||||||
return unlikely(current_sid() == susfs_ksu_sid);
|
return unlikely(current_sid() == susfs_ksu_sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void susfs_set_init_sid(void)
|
void susfs_set_init_sid(void)
|
||||||
{
|
{
|
||||||
susfs_set_sid(KERNEL_INIT_DOMAIN, &susfs_init_sid);
|
susfs_set_sid(KERNEL_INIT_DOMAIN, &susfs_init_sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool susfs_is_current_init_domain(void) {
|
bool susfs_is_current_init_domain(void) {
|
||||||
return unlikely(current_sid() == susfs_init_sid);
|
return unlikely(current_sid() == susfs_init_sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void susfs_set_kernel_sid(void)
|
void susfs_set_kernel_sid(void)
|
||||||
{
|
{
|
||||||
susfs_set_sid(KERNEL_KERNEL_DOMAIN, &susfs_kernel_sid);
|
susfs_set_sid(KERNEL_KERNEL_DOMAIN, &susfs_kernel_sid);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -249,12 +249,12 @@ void susfs_set_kernel_sid(void)
|
|||||||
|
|
||||||
u32 ksu_get_devpts_sid(void)
|
u32 ksu_get_devpts_sid(void)
|
||||||
{
|
{
|
||||||
u32 devpts_sid = 0;
|
u32 devpts_sid = 0;
|
||||||
int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN),
|
int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN),
|
||||||
&devpts_sid);
|
&devpts_sid);
|
||||||
|
|
||||||
if (err)
|
if (err)
|
||||||
pr_info("get devpts sid err %d\n", err);
|
pr_info("get devpts sid err %d\n", err);
|
||||||
|
|
||||||
return devpts_sid;
|
return devpts_sid;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,24 +12,24 @@
|
|||||||
|
|
||||||
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
#ifdef CONFIG_SECURITY_SELINUX_DISABLE
|
||||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||||
#define is_selinux_disabled() (selinux_state.disabled)
|
#define is_selinux_disabled() (selinux_state.disabled)
|
||||||
#else
|
#else
|
||||||
#define is_selinux_disabled() (selinux_disabled)
|
#define is_selinux_disabled() (selinux_disabled)
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#define is_selinux_disabled() (0)
|
#define is_selinux_disabled() (0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
#ifdef CONFIG_SECURITY_SELINUX_DEVELOP
|
||||||
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
#ifdef KSU_COMPAT_USE_SELINUX_STATE
|
||||||
#define __is_selinux_enforcing() (selinux_state.enforcing)
|
#define __is_selinux_enforcing() (selinux_state.enforcing)
|
||||||
#define __setenforce(val) selinux_state.enforcing = val
|
#define __setenforce(val) selinux_state.enforcing = val
|
||||||
#elif defined(SAMSUNG_SELINUX_PORTING) || !defined(KSU_COMPAT_USE_SELINUX_STATE)
|
#elif defined(SAMSUNG_SELINUX_PORTING) || !defined(KSU_COMPAT_USE_SELINUX_STATE)
|
||||||
#define __is_selinux_enforcing() (selinux_enforcing)
|
#define __is_selinux_enforcing() (selinux_enforcing)
|
||||||
#define __setenforce(val) selinux_enforcing = val
|
#define __setenforce(val) selinux_enforcing = val
|
||||||
#endif
|
#endif
|
||||||
#else
|
#else
|
||||||
#define __is_selinux_enforcing() (1)
|
#define __is_selinux_enforcing() (1)
|
||||||
#define __setenforce(val)
|
#define __setenforce(val)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -15,32 +15,32 @@ bool ksu_exists(struct policydb *db, const char *type);
|
|||||||
|
|
||||||
// Access vector rules
|
// Access vector rules
|
||||||
bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *perm);
|
const char *cls, const char *perm);
|
||||||
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *perm);
|
const char *cls, const char *perm);
|
||||||
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *perm);
|
const char *cls, const char *perm);
|
||||||
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *perm);
|
const char *cls, const char *perm);
|
||||||
|
|
||||||
// Extended permissions access vector rules
|
// Extended permissions access vector rules
|
||||||
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *range);
|
const char *cls, const char *range);
|
||||||
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *range);
|
const char *cls, const char *range);
|
||||||
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *range);
|
const char *cls, const char *range);
|
||||||
|
|
||||||
// Type rules
|
// Type rules
|
||||||
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *def, const char *obj);
|
const char *cls, const char *def, const char *obj);
|
||||||
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *def);
|
const char *cls, const char *def);
|
||||||
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
|
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
|
||||||
const char *cls, const char *def);
|
const char *cls, const char *def);
|
||||||
|
|
||||||
// File system labeling
|
// File system labeling
|
||||||
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
|
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
|
||||||
const char *ctx);
|
const char *ctx);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -39,77 +39,77 @@ bool ksu_sucompat_hook_state __read_mostly = true;
|
|||||||
|
|
||||||
static inline void __user *userspace_stack_buffer(const void *d, size_t len)
|
static inline void __user *userspace_stack_buffer(const void *d, size_t len)
|
||||||
{
|
{
|
||||||
/* To avoid having to mmap a page in userspace, just write below the stack
|
/* To avoid having to mmap a page in userspace, just write below the stack
|
||||||
* pointer. */
|
* pointer. */
|
||||||
char __user *p = (void __user *)current_user_stack_pointer() - len;
|
char __user *p = (void __user *)current_user_stack_pointer() - len;
|
||||||
|
|
||||||
return copy_to_user(p, d, len) ? NULL : p;
|
return copy_to_user(p, d, len) ? NULL : p;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline char __user *sh_user_path(void)
|
static inline char __user *sh_user_path(void)
|
||||||
{
|
{
|
||||||
return userspace_stack_buffer(sh_path, sizeof(sh_path));
|
return userspace_stack_buffer(sh_path, sizeof(sh_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline char __user *ksud_user_path(void)
|
static inline char __user *ksud_user_path(void)
|
||||||
{
|
{
|
||||||
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
|
return userspace_stack_buffer(ksud_path, sizeof(ksud_path));
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||||
int *__unused_flags)
|
int *__unused_flags)
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||||
if (!ksu_sucompat_hook_state) {
|
if (!ksu_sucompat_hook_state) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_SUSFS_SUS_SU
|
#ifndef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
if (!ksu_is_allow_uid(current_uid().val)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_SUSFS_SUS_SU
|
#ifdef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
char path[sizeof(su) + 1] = {0};
|
char path[sizeof(su) + 1] = {0};
|
||||||
#else
|
#else
|
||||||
char path[sizeof(su) + 1];
|
char path[sizeof(su) + 1];
|
||||||
memset(path, 0, sizeof(path));
|
memset(path, 0, sizeof(path));
|
||||||
#endif
|
#endif
|
||||||
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
||||||
#if __SULOG_GATE
|
#if __SULOG_GATE
|
||||||
ksu_sulog_report_syscall(current_uid().val, NULL, "faccessat", path);
|
ksu_sulog_report_syscall(current_uid().val, NULL, "faccessat", path);
|
||||||
#endif
|
#endif
|
||||||
pr_info("faccessat su->sh!\n");
|
pr_info("faccessat su->sh!\n");
|
||||||
*filename_user = sh_user_path();
|
*filename_user = sh_user_path();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) && defined(CONFIG_KSU_SUSFS_SUS_SU)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) && defined(CONFIG_KSU_SUSFS_SUS_SU)
|
||||||
struct filename* susfs_ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags) {
|
struct filename* susfs_ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags) {
|
||||||
struct filename *name = getname_flags(*filename_user, getname_statx_lookup_flags(*flags), NULL);
|
struct filename *name = getname_flags(*filename_user, getname_statx_lookup_flags(*flags), NULL);
|
||||||
|
|
||||||
if (unlikely(IS_ERR(name) || name->name == NULL)) {
|
if (unlikely(IS_ERR(name) || name->name == NULL)) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (likely(memcmp(name->name, su, sizeof(su)))) {
|
if (likely(memcmp(name->name, su, sizeof(su)))) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char sh[] = SH_PATH;
|
const char sh[] = SH_PATH;
|
||||||
#if __SULOG_GATE
|
#if __SULOG_GATE
|
||||||
ksu_sulog_report_syscall(current_uid().val, NULL, "vfs_fstatat", sh);
|
ksu_sulog_report_syscall(current_uid().val, NULL, "vfs_fstatat", sh);
|
||||||
#endif
|
#endif
|
||||||
pr_info("vfs_fstatat su->sh!\n");
|
pr_info("vfs_fstatat su->sh!\n");
|
||||||
memcpy((void *)name->name, sh, sizeof(sh));
|
memcpy((void *)name->name, sh, sizeof(sh));
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -117,282 +117,282 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
|||||||
{
|
{
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||||
if (!ksu_sucompat_hook_state) {
|
if (!ksu_sucompat_hook_state) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_SUSFS_SUS_SU
|
#ifndef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
if (!ksu_is_allow_uid(current_uid().val)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (unlikely(!filename_user)) {
|
if (unlikely(!filename_user)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_SUSFS_SUS_SU
|
#ifdef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
char path[sizeof(su) + 1] = {0};
|
char path[sizeof(su) + 1] = {0};
|
||||||
#else
|
#else
|
||||||
char path[sizeof(su) + 1];
|
char path[sizeof(su) + 1];
|
||||||
memset(path, 0, sizeof(path));
|
memset(path, 0, sizeof(path));
|
||||||
#endif
|
#endif
|
||||||
// Remove this later!! we use syscall hook, so this will never happen!!!!!
|
// Remove this later!! we use syscall hook, so this will never happen!!!!!
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0
|
||||||
// it becomes a `struct filename *` after 5.18
|
// it becomes a `struct filename *` after 5.18
|
||||||
// https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216
|
// https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216
|
||||||
const char sh[] = SH_PATH;
|
const char sh[] = SH_PATH;
|
||||||
struct filename *filename = *((struct filename **)filename_user);
|
struct filename *filename = *((struct filename **)filename_user);
|
||||||
if (IS_ERR(filename)) {
|
if (IS_ERR(filename)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (likely(memcmp(filename->name, su, sizeof(su))))
|
if (likely(memcmp(filename->name, su, sizeof(su))))
|
||||||
return 0;
|
return 0;
|
||||||
pr_info("vfs_statx su->sh!\n");
|
pr_info("vfs_statx su->sh!\n");
|
||||||
memcpy((void *)filename->name, sh, sizeof(sh));
|
memcpy((void *)filename->name, sh, sizeof(sh));
|
||||||
#else
|
#else
|
||||||
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
||||||
#if __SULOG_GATE
|
#if __SULOG_GATE
|
||||||
ksu_sulog_report_syscall(current_uid().val, NULL, "newfstatat", path);
|
ksu_sulog_report_syscall(current_uid().val, NULL, "newfstatat", path);
|
||||||
#endif
|
#endif
|
||||||
pr_info("newfstatat su->sh!\n");
|
pr_info("newfstatat su->sh!\n");
|
||||||
*filename_user = sh_user_path();
|
*filename_user = sh_user_path();
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code
|
// the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code
|
||||||
int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||||
void *__never_use_argv, void *__never_use_envp,
|
void *__never_use_argv, void *__never_use_envp,
|
||||||
int *__never_use_flags)
|
int *__never_use_flags)
|
||||||
{
|
{
|
||||||
struct filename *filename;
|
struct filename *filename;
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||||
if (!ksu_sucompat_hook_state) {
|
if (!ksu_sucompat_hook_state) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (unlikely(!filename_ptr))
|
if (unlikely(!filename_ptr))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
filename = *filename_ptr;
|
filename = *filename_ptr;
|
||||||
if (IS_ERR(filename)) {
|
if (IS_ERR(filename)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (likely(memcmp(filename->name, su, sizeof(su))))
|
if (likely(memcmp(filename->name, su, sizeof(su))))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#if __SULOG_GATE
|
#if __SULOG_GATE
|
||||||
ksu_sulog_report_syscall(current_uid().val, NULL, "execve", filename->name);
|
ksu_sulog_report_syscall(current_uid().val, NULL, "execve", filename->name);
|
||||||
#ifndef CONFIG_KSU_SUSFS_SUS_SU
|
#ifndef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
bool is_allowed = ksu_is_allow_uid(current_uid().val);
|
bool is_allowed = ksu_is_allow_uid(current_uid().val);
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_SUSFS_SUS_SU
|
#ifndef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
|
|
||||||
#if __SULOG_GATE
|
#if __SULOG_GATE
|
||||||
if (!is_allowed)
|
if (!is_allowed)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ksu_sulog_report_su_attempt(current_uid().val, NULL, filename->name, is_allowed);
|
ksu_sulog_report_su_attempt(current_uid().val, NULL, filename->name, is_allowed);
|
||||||
#else
|
#else
|
||||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
if (!ksu_is_allow_uid(current_uid().val)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pr_info("do_execveat_common su found\n");
|
pr_info("do_execveat_common su found\n");
|
||||||
memcpy((void *)filename->name, ksud_path, sizeof(ksud_path));
|
memcpy((void *)filename->name, ksud_path, sizeof(ksud_path));
|
||||||
|
|
||||||
escape_to_root();
|
escape_to_root();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
||||||
void *__never_use_argv, void *__never_use_envp,
|
void *__never_use_argv, void *__never_use_envp,
|
||||||
int *__never_use_flags)
|
int *__never_use_flags)
|
||||||
{
|
{
|
||||||
//const char su[] = SU_PATH;
|
//const char su[] = SU_PATH;
|
||||||
#ifdef CONFIG_KSU_SUSFS_SUS_SU
|
#ifdef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
char path[sizeof(su) + 1] = {0};
|
char path[sizeof(su) + 1] = {0};
|
||||||
#else
|
#else
|
||||||
char path[sizeof(su) + 1];
|
char path[sizeof(su) + 1];
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||||
if (!ksu_sucompat_hook_state) {
|
if (!ksu_sucompat_hook_state) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (unlikely(!filename_user))
|
if (unlikely(!filename_user))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* nofault variant fails silently due to pagefault_disable
|
* nofault variant fails silently due to pagefault_disable
|
||||||
* some cpus dont really have that good speculative execution
|
* some cpus dont really have that good speculative execution
|
||||||
* access_ok to substitute set_fs, we check if pointer is accessible
|
* access_ok to substitute set_fs, we check if pointer is accessible
|
||||||
*/
|
*/
|
||||||
if (!ksu_access_ok(*filename_user, sizeof(path)))
|
if (!ksu_access_ok(*filename_user, sizeof(path)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// success = returns number of bytes and should be less than path
|
// success = returns number of bytes and should be less than path
|
||||||
long len = strncpy_from_user(path, *filename_user, sizeof(path));
|
long len = strncpy_from_user(path, *filename_user, sizeof(path));
|
||||||
if (len <= 0 || len > sizeof(path))
|
if (len <= 0 || len > sizeof(path))
|
||||||
return 0;
|
return 0;
|
||||||
// strncpy_from_user_nofault does this too
|
// strncpy_from_user_nofault does this too
|
||||||
path[sizeof(path) - 1] = '\0';
|
path[sizeof(path) - 1] = '\0';
|
||||||
|
|
||||||
if (likely(memcmp(path, su, sizeof(su))))
|
if (likely(memcmp(path, su, sizeof(su))))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#if __SULOG_GATE
|
#if __SULOG_GATE
|
||||||
ksu_sulog_report_syscall(current_uid().val, NULL, "execve", path);
|
ksu_sulog_report_syscall(current_uid().val, NULL, "execve", path);
|
||||||
bool is_allowed = ksu_is_allow_uid(current_uid().val);
|
bool is_allowed = ksu_is_allow_uid(current_uid().val);
|
||||||
if (!is_allowed)
|
if (!is_allowed)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ksu_sulog_report_su_attempt(current_uid().val, NULL, path, is_allowed);
|
ksu_sulog_report_su_attempt(current_uid().val, NULL, path, is_allowed);
|
||||||
#else
|
#else
|
||||||
if (!ksu_is_allow_uid(current_uid().val)) {
|
if (!ksu_is_allow_uid(current_uid().val)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
pr_info("sys_execve su found\n");
|
pr_info("sys_execve su found\n");
|
||||||
*filename_user = ksud_user_path();
|
*filename_user = ksud_user_path();
|
||||||
|
|
||||||
escape_to_root();
|
escape_to_root();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// dummified
|
// dummified
|
||||||
int ksu_handle_devpts(struct inode *inode)
|
int ksu_handle_devpts(struct inode *inode)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int __ksu_handle_devpts(struct inode *inode)
|
int __ksu_handle_devpts(struct inode *inode)
|
||||||
{
|
{
|
||||||
|
|
||||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||||
if (!ksu_sucompat_hook_state)
|
if (!ksu_sucompat_hook_state)
|
||||||
return 0;
|
return 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!current->mm) {
|
if (!current->mm) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
uid_t uid = current_uid().val;
|
uid_t uid = current_uid().val;
|
||||||
if (uid % 100000 < 10000) {
|
if (uid % 100000 < 10000) {
|
||||||
// not untrusted_app, ignore it
|
// not untrusted_app, ignore it
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (likely(!ksu_is_allow_uid(uid)))
|
if (likely(!ksu_is_allow_uid(uid)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || defined(KSU_OPTIONAL_SELINUX_INODE)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || defined(KSU_OPTIONAL_SELINUX_INODE)
|
||||||
struct inode_security_struct *sec = selinux_inode(inode);
|
struct inode_security_struct *sec = selinux_inode(inode);
|
||||||
#else
|
#else
|
||||||
struct inode_security_struct *sec =
|
struct inode_security_struct *sec =
|
||||||
(struct inode_security_struct *)inode->i_security;
|
(struct inode_security_struct *)inode->i_security;
|
||||||
#endif
|
#endif
|
||||||
if (ksu_devpts_sid && sec)
|
if (ksu_devpts_sid && sec)
|
||||||
sec->sid = ksu_devpts_sid;
|
sec->sid = ksu_devpts_sid;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||||
|
|
||||||
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||||
const char __user **filename_user =
|
const char __user **filename_user =
|
||||||
(const char **)&PT_REGS_PARM2(real_regs);
|
(const char **)&PT_REGS_PARM2(real_regs);
|
||||||
int *mode = (int *)&PT_REGS_PARM3(real_regs);
|
int *mode = (int *)&PT_REGS_PARM3(real_regs);
|
||||||
|
|
||||||
return ksu_handle_faccessat(dfd, filename_user, mode, NULL);
|
return ksu_handle_faccessat(dfd, filename_user, mode, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
int *dfd = (int *)&PT_REGS_PARM1(real_regs);
|
||||||
const char __user **filename_user =
|
const char __user **filename_user =
|
||||||
(const char **)&PT_REGS_PARM2(real_regs);
|
(const char **)&PT_REGS_PARM2(real_regs);
|
||||||
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs);
|
int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs);
|
||||||
|
|
||||||
return ksu_handle_stat(dfd, filename_user, flags);
|
return ksu_handle_stat(dfd, filename_user, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
const char __user **filename_user =
|
const char __user **filename_user =
|
||||||
(const char **)&PT_REGS_PARM1(real_regs);
|
(const char **)&PT_REGS_PARM1(real_regs);
|
||||||
|
|
||||||
return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL,
|
return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL,
|
||||||
NULL);
|
NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct kprobe *su_kps[6];
|
static struct kprobe *su_kps[6];
|
||||||
static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
|
static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
|
||||||
struct file *file = (struct file *)PT_REGS_PARM2(regs);
|
struct file *file = (struct file *)PT_REGS_PARM2(regs);
|
||||||
inode = file->f_path.dentry->d_inode;
|
inode = file->f_path.dentry->d_inode;
|
||||||
#else
|
#else
|
||||||
inode = (struct inode *)PT_REGS_PARM2(regs);
|
inode = (struct inode *)PT_REGS_PARM2(regs);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return ksu_handle_devpts(inode);
|
return ksu_handle_devpts(inode);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct kprobe *init_kprobe(const char *name,
|
static struct kprobe *init_kprobe(const char *name,
|
||||||
kprobe_pre_handler_t handler)
|
kprobe_pre_handler_t handler)
|
||||||
{
|
{
|
||||||
struct kprobe *kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
|
struct kprobe *kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL);
|
||||||
if (!kp)
|
if (!kp)
|
||||||
return NULL;
|
return NULL;
|
||||||
kp->symbol_name = name;
|
kp->symbol_name = name;
|
||||||
kp->pre_handler = handler;
|
kp->pre_handler = handler;
|
||||||
|
|
||||||
int ret = register_kprobe(kp);
|
int ret = register_kprobe(kp);
|
||||||
pr_info("sucompat: register_%s kprobe: %d\n", name, ret);
|
pr_info("sucompat: register_%s kprobe: %d\n", name, ret);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
kfree(kp);
|
kfree(kp);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return kp;
|
return kp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void destroy_kprobe(struct kprobe **kp_ptr)
|
static void destroy_kprobe(struct kprobe **kp_ptr)
|
||||||
{
|
{
|
||||||
struct kprobe *kp = *kp_ptr;
|
struct kprobe *kp = *kp_ptr;
|
||||||
if (!kp)
|
if (!kp)
|
||||||
return;
|
return;
|
||||||
unregister_kprobe(kp);
|
unregister_kprobe(kp);
|
||||||
synchronize_rcu();
|
synchronize_rcu();
|
||||||
kfree(kp);
|
kfree(kp);
|
||||||
*kp_ptr = NULL;
|
*kp_ptr = NULL;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -400,28 +400,28 @@ static void destroy_kprobe(struct kprobe **kp_ptr)
|
|||||||
void ksu_sucompat_init(void)
|
void ksu_sucompat_init(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||||
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
||||||
su_kps[1] = init_kprobe(SYS_EXECVE_COMPAT_SYMBOL, execve_handler_pre);
|
su_kps[1] = init_kprobe(SYS_EXECVE_COMPAT_SYMBOL, execve_handler_pre);
|
||||||
su_kps[2] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
|
su_kps[2] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
|
||||||
su_kps[3] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
|
su_kps[3] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
|
||||||
su_kps[4] = init_kprobe(SYS_FSTATAT64_SYMBOL, newfstatat_handler_pre);
|
su_kps[4] = init_kprobe(SYS_FSTATAT64_SYMBOL, newfstatat_handler_pre);
|
||||||
su_kps[5] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre);
|
su_kps[5] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre);
|
||||||
#else
|
#else
|
||||||
ksu_sucompat_hook_state = true;
|
ksu_sucompat_hook_state = true;
|
||||||
pr_info("ksu_sucompat init\n");
|
pr_info("ksu_sucompat init\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_sucompat_exit(void)
|
void ksu_sucompat_exit(void)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
for (i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
||||||
destroy_kprobe(&su_kps[i]);
|
destroy_kprobe(&su_kps[i]);
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
ksu_sucompat_hook_state = false;
|
ksu_sucompat_hook_state = false;
|
||||||
pr_info("ksu_sucompat exit\n");
|
pr_info("ksu_sucompat exit\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -433,34 +433,34 @@ int susfs_sus_su_working_mode = 0;
|
|||||||
|
|
||||||
static bool ksu_is_su_kps_enabled(void) {
|
static bool ksu_is_su_kps_enabled(void) {
|
||||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||||
int i;
|
int i;
|
||||||
for (i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
for (i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
||||||
if (su_kps[i]) {
|
if (su_kps[i]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_susfs_disable_sus_su(void) {
|
void ksu_susfs_disable_sus_su(void) {
|
||||||
susfs_is_sus_su_hooks_enabled = false;
|
susfs_is_sus_su_hooks_enabled = false;
|
||||||
ksu_devpts_hook = false;
|
ksu_devpts_hook = false;
|
||||||
susfs_sus_su_working_mode = SUS_SU_DISABLED;
|
susfs_sus_su_working_mode = SUS_SU_DISABLED;
|
||||||
// Re-enable the su_kps for user, users need to toggle off the kprobe hooks again in ksu manager if they want it disabled.
|
// Re-enable the su_kps for user, users need to toggle off the kprobe hooks again in ksu manager if they want it disabled.
|
||||||
if (!ksu_is_su_kps_enabled()) {
|
if (!ksu_is_su_kps_enabled()) {
|
||||||
ksu_sucompat_init();
|
ksu_sucompat_init();
|
||||||
ksu_su_compat_enabled = true;
|
ksu_su_compat_enabled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_susfs_enable_sus_su(void) {
|
void ksu_susfs_enable_sus_su(void) {
|
||||||
if (ksu_is_su_kps_enabled()) {
|
if (ksu_is_su_kps_enabled()) {
|
||||||
ksu_sucompat_exit();
|
ksu_sucompat_exit();
|
||||||
ksu_su_compat_enabled = false;
|
ksu_su_compat_enabled = false;
|
||||||
}
|
}
|
||||||
susfs_is_sus_su_hooks_enabled = true;
|
susfs_is_sus_su_hooks_enabled = true;
|
||||||
ksu_devpts_hook = true;
|
ksu_devpts_hook = true;
|
||||||
susfs_sus_su_working_mode = SUS_SU_WITH_HOOKS;
|
susfs_sus_su_working_mode = SUS_SU_WITH_HOOKS;
|
||||||
}
|
}
|
||||||
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_SU
|
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_SU
|
||||||
478
kernel/sulog.c
478
kernel/sulog.c
@@ -30,380 +30,380 @@ static bool sulog_enabled = true;
|
|||||||
|
|
||||||
static void get_timestamp(char *buf, size_t len)
|
static void get_timestamp(char *buf, size_t len)
|
||||||
{
|
{
|
||||||
struct timespec64 ts;
|
struct timespec64 ts;
|
||||||
struct tm tm;
|
struct tm tm;
|
||||||
|
|
||||||
ktime_get_real_ts64(&ts);
|
ktime_get_real_ts64(&ts);
|
||||||
|
|
||||||
time64_to_tm(ts.tv_sec - sys_tz.tz_minuteswest * 60, 0, &tm);
|
time64_to_tm(ts.tv_sec - sys_tz.tz_minuteswest * 60, 0, &tm);
|
||||||
|
|
||||||
snprintf(buf, len,
|
snprintf(buf, len,
|
||||||
"%04ld-%02d-%02d %02d:%02d:%02d",
|
"%04ld-%02d-%02d %02d:%02d:%02d",
|
||||||
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
||||||
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ksu_get_cmdline(char *full_comm, const char *comm, size_t buf_len)
|
static void ksu_get_cmdline(char *full_comm, const char *comm, size_t buf_len)
|
||||||
{
|
{
|
||||||
char *kbuf;
|
char *kbuf;
|
||||||
|
|
||||||
if (!full_comm || buf_len <= 0)
|
if (!full_comm || buf_len <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (comm && strlen(comm) > 0) {
|
if (comm && strlen(comm) > 0) {
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
strscpy(full_comm, comm, buf_len);
|
strscpy(full_comm, comm, buf_len);
|
||||||
#else
|
#else
|
||||||
strlcpy(full_comm, comm, buf_len);
|
strlcpy(full_comm, comm, buf_len);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
kbuf = kmalloc(buf_len, GFP_ATOMIC);
|
kbuf = kmalloc(buf_len, GFP_ATOMIC);
|
||||||
if (!kbuf) {
|
if (!kbuf) {
|
||||||
pr_err("sulog: failed to allocate memory for kbuf\n");
|
pr_err("sulog: failed to allocate memory for kbuf\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
int n = get_cmdline(current, kbuf, buf_len);
|
int n = get_cmdline(current, kbuf, buf_len);
|
||||||
|
|
||||||
if (n <= 0) {
|
if (n <= 0) {
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
strscpy(full_comm, current->comm, buf_len);
|
strscpy(full_comm, current->comm, buf_len);
|
||||||
#else
|
#else
|
||||||
strlcpy(full_comm, current->comm, buf_len);
|
strlcpy(full_comm, current->comm, buf_len);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < n; i++) {
|
for (int i = 0; i < n; i++) {
|
||||||
if (kbuf[i] == '\0') kbuf[i] = ' ';
|
if (kbuf[i] == '\0') kbuf[i] = ' ';
|
||||||
}
|
}
|
||||||
kbuf[n < buf_len ? n : buf_len - 1] = '\0';
|
kbuf[n < buf_len ? n : buf_len - 1] = '\0';
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
strscpy(full_comm, kbuf, buf_len);
|
strscpy(full_comm, kbuf, buf_len);
|
||||||
#else
|
#else
|
||||||
strlcpy(full_comm, kbuf, buf_len);
|
strlcpy(full_comm, kbuf, buf_len);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
kfree(kbuf);
|
kfree(kbuf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool dedup_should_print(uid_t uid, u8 type,
|
static bool dedup_should_print(uid_t uid, u8 type,
|
||||||
const char *content, size_t len)
|
const char *content, size_t len)
|
||||||
{
|
{
|
||||||
struct dedup_key key = {
|
struct dedup_key key = {
|
||||||
.crc = dedup_calc_hash(content, len),
|
.crc = dedup_calc_hash(content, len),
|
||||||
.uid = uid,
|
.uid = uid,
|
||||||
.type = type,
|
.type = type,
|
||||||
};
|
};
|
||||||
u64 now = ktime_get_ns();
|
u64 now = ktime_get_ns();
|
||||||
u64 delta_ns = DEDUP_SECS * NSEC_PER_SEC;
|
u64 delta_ns = DEDUP_SECS * NSEC_PER_SEC;
|
||||||
|
|
||||||
u32 idx = key.crc & (SULOG_COMM_LEN - 1);
|
u32 idx = key.crc & (SULOG_COMM_LEN - 1);
|
||||||
spin_lock(&dedup_lock);
|
spin_lock(&dedup_lock);
|
||||||
|
|
||||||
struct dedup_entry *e = &dedup_tbl[idx];
|
struct dedup_entry *e = &dedup_tbl[idx];
|
||||||
if (e->key.crc == key.crc &&
|
if (e->key.crc == key.crc &&
|
||||||
e->key.uid == key.uid &&
|
e->key.uid == key.uid &&
|
||||||
e->key.type == key.type &&
|
e->key.type == key.type &&
|
||||||
(now - e->ts_ns) < delta_ns) {
|
(now - e->ts_ns) < delta_ns) {
|
||||||
spin_unlock(&dedup_lock);
|
spin_unlock(&dedup_lock);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
e->key = key;
|
e->key = key;
|
||||||
e->ts_ns = now;
|
e->ts_ns = now;
|
||||||
spin_unlock(&dedup_lock);
|
spin_unlock(&dedup_lock);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sulog_work_handler(struct work_struct *work)
|
static void sulog_work_handler(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct file *fp;
|
struct file *fp;
|
||||||
struct sulog_entry *entry, *tmp;
|
struct sulog_entry *entry, *tmp;
|
||||||
LIST_HEAD(local_queue);
|
LIST_HEAD(local_queue);
|
||||||
loff_t pos = 0;
|
loff_t pos = 0;
|
||||||
|
|
||||||
mutex_lock(&sulog_mutex);
|
mutex_lock(&sulog_mutex);
|
||||||
list_splice_init(&sulog_queue, &local_queue);
|
list_splice_init(&sulog_queue, &local_queue);
|
||||||
mutex_unlock(&sulog_mutex);
|
mutex_unlock(&sulog_mutex);
|
||||||
|
|
||||||
if (list_empty(&local_queue))
|
if (list_empty(&local_queue))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fp = ksu_filp_open_compat(SULOG_PATH, O_WRONLY | O_CREAT | O_APPEND, 0640);
|
fp = ksu_filp_open_compat(SULOG_PATH, O_WRONLY | O_CREAT | O_APPEND, 0640);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_err("sulog: failed to open log file: %ld\n", PTR_ERR(fp));
|
pr_err("sulog: failed to open log file: %ld\n", PTR_ERR(fp));
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fp->f_inode->i_size > SULOG_MAX_SIZE) {
|
if (fp->f_inode->i_size > SULOG_MAX_SIZE) {
|
||||||
pr_info("sulog: log file exceeds maximum size, clearing...\n");
|
pr_info("sulog: log file exceeds maximum size, clearing...\n");
|
||||||
if (vfs_truncate(&fp->f_path, 0)) {
|
if (vfs_truncate(&fp->f_path, 0)) {
|
||||||
pr_err("sulog: failed to truncate log file\n");
|
pr_err("sulog: failed to truncate log file\n");
|
||||||
}
|
}
|
||||||
pos = 0;
|
pos = 0;
|
||||||
} else {
|
} else {
|
||||||
pos = fp->f_inode->i_size;
|
pos = fp->f_inode->i_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
list_for_each_entry(entry, &local_queue, list) {
|
list_for_each_entry(entry, &local_queue, list) {
|
||||||
ksu_kernel_write_compat(fp, entry->content, strlen(entry->content), &pos);
|
ksu_kernel_write_compat(fp, entry->content, strlen(entry->content), &pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
vfs_fsync(fp, 0);
|
vfs_fsync(fp, 0);
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
list_for_each_entry_safe(entry, tmp, &local_queue, list) {
|
list_for_each_entry_safe(entry, tmp, &local_queue, list) {
|
||||||
list_del(&entry->list);
|
list_del(&entry->list);
|
||||||
kfree(entry);
|
kfree(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sulog_add_entry(const char *content)
|
static void sulog_add_entry(const char *content)
|
||||||
{
|
{
|
||||||
struct sulog_entry *entry;
|
struct sulog_entry *entry;
|
||||||
|
|
||||||
if (!sulog_enabled || !content)
|
if (!sulog_enabled || !content)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
|
entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
|
||||||
if (!entry) {
|
if (!entry) {
|
||||||
pr_err("sulog: failed to allocate memory for log entry\n");
|
pr_err("sulog: failed to allocate memory for log entry\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||||
strscpy(entry->content, content, SULOG_ENTRY_MAX_LEN - 1);
|
strscpy(entry->content, content, SULOG_ENTRY_MAX_LEN - 1);
|
||||||
#else
|
#else
|
||||||
strlcpy(entry->content, content, SULOG_ENTRY_MAX_LEN - 1);
|
strlcpy(entry->content, content, SULOG_ENTRY_MAX_LEN - 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
mutex_lock(&sulog_mutex);
|
mutex_lock(&sulog_mutex);
|
||||||
list_add_tail(&entry->list, &sulog_queue);
|
list_add_tail(&entry->list, &sulog_queue);
|
||||||
mutex_unlock(&sulog_mutex);
|
mutex_unlock(&sulog_mutex);
|
||||||
|
|
||||||
if (sulog_workqueue)
|
if (sulog_workqueue)
|
||||||
queue_work(sulog_workqueue, &sulog_work);
|
queue_work(sulog_workqueue, &sulog_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method)
|
void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method)
|
||||||
{
|
{
|
||||||
char *timestamp, *full_comm, *log_buf;
|
char *timestamp, *full_comm, *log_buf;
|
||||||
|
|
||||||
if (!sulog_enabled)
|
if (!sulog_enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
timestamp = kmalloc(32, GFP_ATOMIC);
|
timestamp = kmalloc(32, GFP_ATOMIC);
|
||||||
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
||||||
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
||||||
|
|
||||||
if (!timestamp || !full_comm || !log_buf) {
|
if (!timestamp || !full_comm || !log_buf) {
|
||||||
pr_err("sulog: failed to allocate memory for su_grant log\n");
|
pr_err("sulog: failed to allocate memory for su_grant log\n");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_timestamp(timestamp, 32);
|
get_timestamp(timestamp, 32);
|
||||||
|
|
||||||
ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN);
|
ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN);
|
||||||
|
|
||||||
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
||||||
"[%s] SU_GRANT: UID=%d COMM=%s METHOD=%s PID=%d\n",
|
"[%s] SU_GRANT: UID=%d COMM=%s METHOD=%s PID=%d\n",
|
||||||
timestamp, uid, full_comm,
|
timestamp, uid, full_comm,
|
||||||
method ? method : "unknown", current->pid);
|
method ? method : "unknown", current->pid);
|
||||||
|
|
||||||
if (!dedup_should_print(uid, DEDUP_SU_GRANT, log_buf, strlen(log_buf)))
|
if (!dedup_should_print(uid, DEDUP_SU_GRANT, log_buf, strlen(log_buf)))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
sulog_add_entry(log_buf);
|
sulog_add_entry(log_buf);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (timestamp) kfree(timestamp);
|
if (timestamp) kfree(timestamp);
|
||||||
if (full_comm) kfree(full_comm);
|
if (full_comm) kfree(full_comm);
|
||||||
if (log_buf) kfree(log_buf);
|
if (log_buf) kfree(log_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success)
|
void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success)
|
||||||
{
|
{
|
||||||
char *timestamp, *full_comm, *log_buf;
|
char *timestamp, *full_comm, *log_buf;
|
||||||
|
|
||||||
if (!sulog_enabled)
|
if (!sulog_enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
timestamp = kmalloc(32, GFP_ATOMIC);
|
timestamp = kmalloc(32, GFP_ATOMIC);
|
||||||
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
||||||
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
||||||
|
|
||||||
if (!timestamp || !full_comm || !log_buf) {
|
if (!timestamp || !full_comm || !log_buf) {
|
||||||
pr_err("sulog: failed to allocate memory for su_attempt log\n");
|
pr_err("sulog: failed to allocate memory for su_attempt log\n");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_timestamp(timestamp, 32);
|
get_timestamp(timestamp, 32);
|
||||||
|
|
||||||
ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN);
|
ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN);
|
||||||
|
|
||||||
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
||||||
"[%s] SU_EXEC: UID=%d COMM=%s TARGET=%s RESULT=%s PID=%d\n",
|
"[%s] SU_EXEC: UID=%d COMM=%s TARGET=%s RESULT=%s PID=%d\n",
|
||||||
timestamp, uid, full_comm,
|
timestamp, uid, full_comm,
|
||||||
target_path ? target_path : "unknown",
|
target_path ? target_path : "unknown",
|
||||||
success ? "SUCCESS" : "DENIED", current->pid);
|
success ? "SUCCESS" : "DENIED", current->pid);
|
||||||
|
|
||||||
if (!dedup_should_print(uid, DEDUP_SU_ATTEMPT, log_buf, strlen(log_buf)))
|
if (!dedup_should_print(uid, DEDUP_SU_ATTEMPT, log_buf, strlen(log_buf)))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
sulog_add_entry(log_buf);
|
sulog_add_entry(log_buf);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (timestamp) kfree(timestamp);
|
if (timestamp) kfree(timestamp);
|
||||||
if (full_comm) kfree(full_comm);
|
if (full_comm) kfree(full_comm);
|
||||||
if (log_buf) kfree(log_buf);
|
if (log_buf) kfree(log_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed)
|
void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed)
|
||||||
{
|
{
|
||||||
char *timestamp, *full_comm, *log_buf;
|
char *timestamp, *full_comm, *log_buf;
|
||||||
|
|
||||||
if (!sulog_enabled)
|
if (!sulog_enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
timestamp = kmalloc(32, GFP_ATOMIC);
|
timestamp = kmalloc(32, GFP_ATOMIC);
|
||||||
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
||||||
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
||||||
|
|
||||||
if (!timestamp || !full_comm || !log_buf) {
|
if (!timestamp || !full_comm || !log_buf) {
|
||||||
pr_err("sulog: failed to allocate memory for permission_check log\n");
|
pr_err("sulog: failed to allocate memory for permission_check log\n");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_timestamp(timestamp, 32);
|
get_timestamp(timestamp, 32);
|
||||||
|
|
||||||
ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN);
|
ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN);
|
||||||
|
|
||||||
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
||||||
"[%s] PERM_CHECK: UID=%d COMM=%s RESULT=%s PID=%d\n",
|
"[%s] PERM_CHECK: UID=%d COMM=%s RESULT=%s PID=%d\n",
|
||||||
timestamp, uid, full_comm,
|
timestamp, uid, full_comm,
|
||||||
allowed ? "ALLOWED" : "DENIED", current->pid);
|
allowed ? "ALLOWED" : "DENIED", current->pid);
|
||||||
|
|
||||||
if (!dedup_should_print(uid, DEDUP_PERM_CHECK, log_buf, strlen(log_buf)))
|
if (!dedup_should_print(uid, DEDUP_PERM_CHECK, log_buf, strlen(log_buf)))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
sulog_add_entry(log_buf);
|
sulog_add_entry(log_buf);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (timestamp) kfree(timestamp);
|
if (timestamp) kfree(timestamp);
|
||||||
if (full_comm) kfree(full_comm);
|
if (full_comm) kfree(full_comm);
|
||||||
if (log_buf) kfree(log_buf);
|
if (log_buf) kfree(log_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid)
|
void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid)
|
||||||
{
|
{
|
||||||
char *timestamp, *full_comm, *log_buf;
|
char *timestamp, *full_comm, *log_buf;
|
||||||
|
|
||||||
if (!sulog_enabled)
|
if (!sulog_enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
timestamp = kmalloc(32, GFP_ATOMIC);
|
timestamp = kmalloc(32, GFP_ATOMIC);
|
||||||
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
||||||
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
||||||
|
|
||||||
if (!timestamp || !full_comm || !log_buf) {
|
if (!timestamp || !full_comm || !log_buf) {
|
||||||
pr_err("sulog: failed to allocate memory for manager_operation log\n");
|
pr_err("sulog: failed to allocate memory for manager_operation log\n");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_timestamp(timestamp, 32);
|
get_timestamp(timestamp, 32);
|
||||||
|
|
||||||
ksu_get_cmdline(full_comm, NULL, SULOG_COMM_LEN);
|
ksu_get_cmdline(full_comm, NULL, SULOG_COMM_LEN);
|
||||||
|
|
||||||
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
||||||
"[%s] MANAGER_OP: OP=%s MANAGER_UID=%d TARGET_UID=%d COMM=%s PID=%d\n",
|
"[%s] MANAGER_OP: OP=%s MANAGER_UID=%d TARGET_UID=%d COMM=%s PID=%d\n",
|
||||||
timestamp, operation ? operation : "unknown",
|
timestamp, operation ? operation : "unknown",
|
||||||
manager_uid, target_uid, full_comm, current->pid);
|
manager_uid, target_uid, full_comm, current->pid);
|
||||||
|
|
||||||
if (!dedup_should_print(manager_uid, DEDUP_MANAGER_OP, log_buf, strlen(log_buf)))
|
if (!dedup_should_print(manager_uid, DEDUP_MANAGER_OP, log_buf, strlen(log_buf)))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
sulog_add_entry(log_buf);
|
sulog_add_entry(log_buf);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (timestamp) kfree(timestamp);
|
if (timestamp) kfree(timestamp);
|
||||||
if (full_comm) kfree(full_comm);
|
if (full_comm) kfree(full_comm);
|
||||||
if (log_buf) kfree(log_buf);
|
if (log_buf) kfree(log_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_sulog_report_syscall(uid_t uid, const char *comm,
|
void ksu_sulog_report_syscall(uid_t uid, const char *comm,
|
||||||
const char *syscall, const char *args)
|
const char *syscall, const char *args)
|
||||||
{
|
{
|
||||||
char *timestamp, *full_comm, *log_buf;
|
char *timestamp, *full_comm, *log_buf;
|
||||||
|
|
||||||
if (!sulog_enabled)
|
if (!sulog_enabled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
timestamp = kmalloc(32, GFP_ATOMIC);
|
timestamp = kmalloc(32, GFP_ATOMIC);
|
||||||
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
||||||
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
||||||
|
|
||||||
if (!timestamp || !full_comm || !log_buf) {
|
if (!timestamp || !full_comm || !log_buf) {
|
||||||
pr_err("sulog: failed to allocate memory for syscall log\n");
|
pr_err("sulog: failed to allocate memory for syscall log\n");
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
}
|
}
|
||||||
|
|
||||||
get_timestamp(timestamp, 32);
|
get_timestamp(timestamp, 32);
|
||||||
|
|
||||||
ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN);
|
ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN);
|
||||||
|
|
||||||
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
||||||
"[%s] SYSCALL: UID=%d COMM=%s SYSCALL=%s ARGS=%s PID=%d\n",
|
"[%s] SYSCALL: UID=%d COMM=%s SYSCALL=%s ARGS=%s PID=%d\n",
|
||||||
timestamp, uid, full_comm,
|
timestamp, uid, full_comm,
|
||||||
syscall ? syscall : "unknown",
|
syscall ? syscall : "unknown",
|
||||||
args ? args : "none",
|
args ? args : "none",
|
||||||
current->pid);
|
current->pid);
|
||||||
|
|
||||||
if (!dedup_should_print(uid, DEDUP_SYSCALL, log_buf, strlen(log_buf)))
|
if (!dedup_should_print(uid, DEDUP_SYSCALL, log_buf, strlen(log_buf)))
|
||||||
goto cleanup;
|
goto cleanup;
|
||||||
|
|
||||||
sulog_add_entry(log_buf);
|
sulog_add_entry(log_buf);
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
if (timestamp) kfree(timestamp);
|
if (timestamp) kfree(timestamp);
|
||||||
if (full_comm) kfree(full_comm);
|
if (full_comm) kfree(full_comm);
|
||||||
if (log_buf) kfree(log_buf);
|
if (log_buf) kfree(log_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_sulog_init(void)
|
int ksu_sulog_init(void)
|
||||||
{
|
{
|
||||||
sulog_workqueue = alloc_workqueue("ksu_sulog", WQ_UNBOUND | WQ_HIGHPRI, 1);
|
sulog_workqueue = alloc_workqueue("ksu_sulog", WQ_UNBOUND | WQ_HIGHPRI, 1);
|
||||||
if (!sulog_workqueue) {
|
if (!sulog_workqueue) {
|
||||||
pr_err("sulog: failed to create workqueue\n");
|
pr_err("sulog: failed to create workqueue\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
INIT_WORK(&sulog_work, sulog_work_handler);
|
INIT_WORK(&sulog_work, sulog_work_handler);
|
||||||
|
|
||||||
pr_info("sulog: initialized successfully\n");
|
pr_info("sulog: initialized successfully\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_sulog_exit(void)
|
void ksu_sulog_exit(void)
|
||||||
{
|
{
|
||||||
struct sulog_entry *entry, *tmp;
|
struct sulog_entry *entry, *tmp;
|
||||||
|
|
||||||
sulog_enabled = false;
|
sulog_enabled = false;
|
||||||
|
|
||||||
if (sulog_workqueue) {
|
if (sulog_workqueue) {
|
||||||
flush_workqueue(sulog_workqueue);
|
flush_workqueue(sulog_workqueue);
|
||||||
destroy_workqueue(sulog_workqueue);
|
destroy_workqueue(sulog_workqueue);
|
||||||
sulog_workqueue = NULL;
|
sulog_workqueue = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
mutex_lock(&sulog_mutex);
|
mutex_lock(&sulog_mutex);
|
||||||
list_for_each_entry_safe(entry, tmp, &sulog_queue, list) {
|
list_for_each_entry_safe(entry, tmp, &sulog_queue, list) {
|
||||||
list_del(&entry->list);
|
list_del(&entry->list);
|
||||||
kfree(entry);
|
kfree(entry);
|
||||||
}
|
}
|
||||||
mutex_unlock(&sulog_mutex);
|
mutex_unlock(&sulog_mutex);
|
||||||
|
|
||||||
pr_info("sulog: cleaned up successfully\n");
|
pr_info("sulog: cleaned up successfully\n");
|
||||||
}
|
}
|
||||||
#endif // __SULOG_GATE
|
#endif // __SULOG_GATE
|
||||||
|
|||||||
@@ -58,8 +58,8 @@ static inline u32 dedup_calc_hash(const char *content, size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct sulog_entry {
|
struct sulog_entry {
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
char content[SULOG_ENTRY_MAX_LEN];
|
char content[SULOG_ENTRY_MAX_LEN];
|
||||||
};
|
};
|
||||||
|
|
||||||
void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method);
|
void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method);
|
||||||
|
|||||||
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
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <linux/workqueue.h>
|
#include <linux/workqueue.h>
|
||||||
#include <linux/version.h>
|
#include <linux/version.h>
|
||||||
|
#include "ksu.h"
|
||||||
|
|
||||||
#include "klog.h"
|
#include "klog.h"
|
||||||
#include "throne_comm.h"
|
#include "throne_comm.h"
|
||||||
@@ -18,198 +19,196 @@ static struct work_struct scan_work;
|
|||||||
static struct work_struct ksu_state_save_work;
|
static struct work_struct ksu_state_save_work;
|
||||||
static struct work_struct ksu_state_load_work;
|
static struct work_struct ksu_state_load_work;
|
||||||
|
|
||||||
extern bool ksu_uid_scanner_enabled;
|
|
||||||
|
|
||||||
// Signal userspace to rescan
|
// Signal userspace to rescan
|
||||||
static bool need_rescan = false;
|
static bool need_rescan = false;
|
||||||
|
|
||||||
static void rescan_work_fn(struct work_struct *work)
|
static void rescan_work_fn(struct work_struct *work)
|
||||||
{
|
{
|
||||||
// Signal userspace through proc interface
|
// Signal userspace through proc interface
|
||||||
need_rescan = true;
|
need_rescan = true;
|
||||||
pr_info("requested userspace uid rescan\n");
|
pr_info("requested userspace uid rescan\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_request_userspace_scan(void)
|
void ksu_request_userspace_scan(void)
|
||||||
{
|
{
|
||||||
if (scanner_wq) {
|
if (scanner_wq) {
|
||||||
queue_work(scanner_wq, &scan_work);
|
queue_work(scanner_wq, &scan_work);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_handle_userspace_update(void)
|
void ksu_handle_userspace_update(void)
|
||||||
{
|
{
|
||||||
// Called when userspace notifies update complete
|
// Called when userspace notifies update complete
|
||||||
need_rescan = false;
|
need_rescan = false;
|
||||||
pr_info("userspace uid list updated\n");
|
pr_info("userspace uid list updated\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void do_save_throne_state(struct work_struct *work)
|
static void do_save_throne_state(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct file *fp;
|
struct file *fp;
|
||||||
char state_char = ksu_uid_scanner_enabled ? '1' : '0';
|
char state_char = ksu_uid_scanner_enabled ? '1' : '0';
|
||||||
loff_t off = 0;
|
loff_t off = 0;
|
||||||
|
|
||||||
fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_err("save_throne_state create file failed: %ld\n", PTR_ERR(fp));
|
pr_err("save_throne_state create file failed: %ld\n", PTR_ERR(fp));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ksu_kernel_write_compat(fp, &state_char, sizeof(state_char), &off) != sizeof(state_char)) {
|
if (ksu_kernel_write_compat(fp, &state_char, sizeof(state_char), &off) != sizeof(state_char)) {
|
||||||
pr_err("save_throne_state write failed\n");
|
pr_err("save_throne_state write failed\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("throne state saved: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled");
|
pr_info("throne state saved: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled");
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void do_load_throne_state(struct work_struct *work)
|
void do_load_throne_state(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct file *fp;
|
struct file *fp;
|
||||||
char state_char;
|
char state_char;
|
||||||
loff_t off = 0;
|
loff_t off = 0;
|
||||||
ssize_t ret;
|
ssize_t ret;
|
||||||
|
|
||||||
fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_RDONLY, 0);
|
fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_RDONLY, 0);
|
||||||
if (IS_ERR(fp)) {
|
if (IS_ERR(fp)) {
|
||||||
pr_info("throne state file not found, using default: disabled\n");
|
pr_info("throne state file not found, using default: disabled\n");
|
||||||
ksu_uid_scanner_enabled = false;
|
ksu_uid_scanner_enabled = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = ksu_kernel_read_compat(fp, &state_char, sizeof(state_char), &off);
|
ret = ksu_kernel_read_compat(fp, &state_char, sizeof(state_char), &off);
|
||||||
if (ret != sizeof(state_char)) {
|
if (ret != sizeof(state_char)) {
|
||||||
pr_err("load_throne_state read err: %zd\n", ret);
|
pr_err("load_throne_state read err: %zd\n", ret);
|
||||||
ksu_uid_scanner_enabled = false;
|
ksu_uid_scanner_enabled = false;
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
ksu_uid_scanner_enabled = (state_char == '1');
|
ksu_uid_scanner_enabled = (state_char == '1');
|
||||||
pr_info("throne state loaded: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled");
|
pr_info("throne state loaded: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled");
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
filp_close(fp, 0);
|
filp_close(fp, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ksu_throne_comm_load_state(void)
|
bool ksu_throne_comm_load_state(void)
|
||||||
{
|
{
|
||||||
return ksu_queue_work(&ksu_state_load_work);
|
return ksu_queue_work(&ksu_state_load_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_throne_comm_save_state(void)
|
void ksu_throne_comm_save_state(void)
|
||||||
{
|
{
|
||||||
ksu_queue_work(&ksu_state_save_work);
|
ksu_queue_work(&ksu_state_save_work);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uid_scanner_show(struct seq_file *m, void *v)
|
static int uid_scanner_show(struct seq_file *m, void *v)
|
||||||
{
|
{
|
||||||
if (need_rescan) {
|
if (need_rescan) {
|
||||||
seq_puts(m, "RESCAN\n");
|
seq_puts(m, "RESCAN\n");
|
||||||
} else {
|
} else {
|
||||||
seq_puts(m, "OK\n");
|
seq_puts(m, "OK\n");
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int uid_scanner_open(struct inode *inode, struct file *file)
|
static int uid_scanner_open(struct inode *inode, struct file *file)
|
||||||
{
|
{
|
||||||
return single_open(file, uid_scanner_show, NULL);
|
return single_open(file, uid_scanner_show, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t uid_scanner_write(struct file *file, const char __user *buffer,
|
static ssize_t uid_scanner_write(struct file *file, const char __user *buffer,
|
||||||
size_t count, loff_t *pos)
|
size_t count, loff_t *pos)
|
||||||
{
|
{
|
||||||
char cmd[16];
|
char cmd[16];
|
||||||
|
|
||||||
if (count >= sizeof(cmd))
|
if (count >= sizeof(cmd))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
if (copy_from_user(cmd, buffer, count))
|
if (copy_from_user(cmd, buffer, count))
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
cmd[count] = '\0';
|
cmd[count] = '\0';
|
||||||
|
|
||||||
// Remove newline if present
|
// Remove newline if present
|
||||||
if (count > 0 && cmd[count-1] == '\n')
|
if (count > 0 && cmd[count-1] == '\n')
|
||||||
cmd[count-1] = '\0';
|
cmd[count-1] = '\0';
|
||||||
|
|
||||||
if (strcmp(cmd, "UPDATED") == 0) {
|
if (strcmp(cmd, "UPDATED") == 0) {
|
||||||
ksu_handle_userspace_update();
|
ksu_handle_userspace_update();
|
||||||
pr_info("received userspace update notification\n");
|
pr_info("received userspace update notification\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef KSU_COMPAT_HAS_PROC_OPS
|
#ifdef KSU_COMPAT_HAS_PROC_OPS
|
||||||
static const struct proc_ops uid_scanner_proc_ops = {
|
static const struct proc_ops uid_scanner_proc_ops = {
|
||||||
.proc_open = uid_scanner_open,
|
.proc_open = uid_scanner_open,
|
||||||
.proc_read = seq_read,
|
.proc_read = seq_read,
|
||||||
.proc_write = uid_scanner_write,
|
.proc_write = uid_scanner_write,
|
||||||
.proc_lseek = seq_lseek,
|
.proc_lseek = seq_lseek,
|
||||||
.proc_release = single_release,
|
.proc_release = single_release,
|
||||||
};
|
};
|
||||||
#else
|
#else
|
||||||
static const struct file_operations uid_scanner_proc_ops = {
|
static const struct file_operations uid_scanner_proc_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.open = uid_scanner_open,
|
.open = uid_scanner_open,
|
||||||
.read = seq_read,
|
.read = seq_read,
|
||||||
.write = uid_scanner_write,
|
.write = uid_scanner_write,
|
||||||
.llseek = seq_lseek,
|
.llseek = seq_lseek,
|
||||||
.release = single_release,
|
.release = single_release,
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int ksu_throne_comm_init(void)
|
int ksu_throne_comm_init(void)
|
||||||
{
|
{
|
||||||
// Create workqueue
|
// Create workqueue
|
||||||
scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1);
|
scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1);
|
||||||
if (!scanner_wq) {
|
if (!scanner_wq) {
|
||||||
pr_err("failed to create scanner workqueue\n");
|
pr_err("failed to create scanner workqueue\n");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
INIT_WORK(&scan_work, rescan_work_fn);
|
INIT_WORK(&scan_work, rescan_work_fn);
|
||||||
|
|
||||||
// Create proc entry
|
// Create proc entry
|
||||||
proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops);
|
proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops);
|
||||||
if (!proc_entry) {
|
if (!proc_entry) {
|
||||||
pr_err("failed to create proc entry\n");
|
pr_err("failed to create proc entry\n");
|
||||||
destroy_workqueue(scanner_wq);
|
destroy_workqueue(scanner_wq);
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("throne communication initialized\n");
|
pr_info("throne communication initialized\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_throne_comm_exit(void)
|
void ksu_throne_comm_exit(void)
|
||||||
{
|
{
|
||||||
if (proc_entry) {
|
if (proc_entry) {
|
||||||
proc_remove(proc_entry);
|
proc_remove(proc_entry);
|
||||||
proc_entry = NULL;
|
proc_entry = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scanner_wq) {
|
if (scanner_wq) {
|
||||||
destroy_workqueue(scanner_wq);
|
destroy_workqueue(scanner_wq);
|
||||||
scanner_wq = NULL;
|
scanner_wq = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
pr_info("throne communication cleaned up\n");
|
pr_info("throne communication cleaned up\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_uid_init(void)
|
int ksu_uid_init(void)
|
||||||
{
|
{
|
||||||
INIT_WORK(&ksu_state_save_work, do_save_throne_state);
|
INIT_WORK(&ksu_state_save_work, do_save_throne_state);
|
||||||
INIT_WORK(&ksu_state_load_work, do_load_throne_state);
|
INIT_WORK(&ksu_state_load_work, do_load_throne_state);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_uid_exit(void)
|
void ksu_uid_exit(void)
|
||||||
{
|
{
|
||||||
do_save_throne_state(NULL);
|
do_save_throne_state(NULL);
|
||||||
}
|
}
|
||||||
@@ -435,6 +435,9 @@ void track_throne(void)
|
|||||||
__maybe_unused loff_t pos = 0;
|
__maybe_unused loff_t pos = 0;
|
||||||
__maybe_unused loff_t line_start = 0;
|
__maybe_unused loff_t line_start = 0;
|
||||||
__maybe_unused char buf[KSU_MAX_PACKAGE_NAME];
|
__maybe_unused char buf[KSU_MAX_PACKAGE_NAME];
|
||||||
|
static bool manager_exist = false;
|
||||||
|
static bool dynamic_manager_exist = false;
|
||||||
|
static int current_manager_uid = ksu_get_manager_uid() % 100000;
|
||||||
|
|
||||||
// init uid list head
|
// init uid list head
|
||||||
INIT_LIST_HEAD(&uid_list);
|
INIT_LIST_HEAD(&uid_list);
|
||||||
@@ -501,9 +504,6 @@ void track_throne(void)
|
|||||||
|
|
||||||
uid_ready:
|
uid_ready:
|
||||||
// first, check if manager_uid exist!
|
// first, check if manager_uid exist!
|
||||||
bool manager_exist = false;
|
|
||||||
int current_manager_uid = ksu_get_manager_uid() % 100000;
|
|
||||||
|
|
||||||
list_for_each_entry(np, &uid_list, list) {
|
list_for_each_entry(np, &uid_list, list) {
|
||||||
if (np->uid == current_manager_uid) {
|
if (np->uid == current_manager_uid) {
|
||||||
manager_exist = true;
|
manager_exist = true;
|
||||||
@@ -519,7 +519,6 @@ uid_ready:
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if the Dynamic Manager exists (only check locked UIDs)
|
// Check if the Dynamic Manager exists (only check locked UIDs)
|
||||||
bool dynamic_manager_exist = false;
|
|
||||||
if (ksu_is_dynamic_manager_enabled() &&
|
if (ksu_is_dynamic_manager_enabled() &&
|
||||||
locked_dynamic_manager_uid != KSU_INVALID_UID) {
|
locked_dynamic_manager_uid != KSU_INVALID_UID) {
|
||||||
list_for_each_entry(np, &uid_list, list) {
|
list_for_each_entry(np, &uid_list, list) {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user