diff --git a/.gitignore b/.gitignore index d4d9525b..b6ce1a41 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea .vscode +CLAUDE.md .DS_Store diff --git a/kernel/.clang-format b/kernel/.clang-format index 10dc5a9a..6453cf96 100644 --- a/kernel/.clang-format +++ b/kernel/.clang-format @@ -56,8 +56,8 @@ ColumnLimit: 80 CommentPragmas: '^ IWYU pragma:' #CompactNamespaces: false # Unknown to clang-format-4.0 ConstructorInitializerAllOnOneLineOrOnePerLine: false -ConstructorInitializerIndentWidth: 8 -ContinuationIndentWidth: 8 +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 Cpp11BracedListStyle: false DerivePointerAlignment: false DisableFormat: false @@ -501,7 +501,7 @@ IncludeCategories: IncludeIsMainRegex: '(Test)?$' IndentCaseLabels: false #IndentPPDirectives: None # Unknown to clang-format-5.0 -IndentWidth: 8 +IndentWidth: 4 IndentWrappedFunctionNames: false JavaScriptQuotes: Leave JavaScriptWrapImports: true @@ -511,7 +511,7 @@ MacroBlockEnd: '' MaxEmptyLinesToKeep: 1 NamespaceIndentation: None #ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 -ObjCBlockIndentWidth: 8 +ObjCBlockIndentWidth: 4 ObjCSpaceAfterProperty: true ObjCSpaceBeforeProtocolList: true @@ -543,6 +543,6 @@ SpacesInCStyleCastParentheses: false SpacesInParentheses: false SpacesInSquareBrackets: false Standard: Cpp03 -TabWidth: 8 -UseTab: Always +TabWidth: 4 +UseTab: Never ... diff --git a/kernel/Kconfig b/kernel/Kconfig index b492fcda..47117816 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -1,38 +1,38 @@ menu "KernelSU" config KSU - tristate "KernelSU function support" - depends on OVERLAY_FS - default y - help - Enable kernel-level root privileges on Android System. - To compile as a module, choose M here: the - module will be called kernelsu. + tristate "KernelSU function support" + depends on OVERLAY_FS + default y + help + Enable kernel-level root privileges on Android System. + To compile as a module, choose M here: the + module will be called kernelsu. config KSU_DEBUG - bool "KernelSU debug mode" - depends on KSU - default n - help - Enable KernelSU debug mode. + bool "KernelSU debug mode" + depends on KSU + default n + help + Enable KernelSU debug mode. config KSU_MANUAL_SU - bool "Use manual su" - depends on KSU - default y - help - Use manual su and authorize the corresponding command line and application via prctl + bool "Use manual su" + depends on KSU + default y + help + Use manual su and authorize the corresponding command line and application via prctl config KPM - bool "Enable SukiSU KPM" - depends on KSU && 64BIT - default n - help - 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. - but it may affect system stability. - select KALLSYMS - select KALLSYMS_ALL + bool "Enable SukiSU KPM" + depends on KSU && 64BIT + default n + help + 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. + but it may affect system stability. + select KALLSYMS + select KALLSYMS_ALL choice prompt "KernelSU hook type" @@ -43,21 +43,21 @@ choice config KSU_KPROBES_HOOK bool "Hook KernelSU with Kprobes" - depends on KPROBES - help - If enabled, Hook required KernelSU syscalls with Kernel-probe. + depends on KPROBES + help + If enabled, Hook required KernelSU syscalls with Kernel-probe. config KSU_TRACEPOINT_HOOK bool "Hook KernelSU with Tracepoint" - depends on TRACEPOINTS - help - If enabled, Hook required KernelSU syscalls with Tracepoint. + depends on TRACEPOINTS + help + If enabled, Hook required KernelSU syscalls with Tracepoint. config KSU_MANUAL_HOOK bool "Hook KernelSU manually" - depends on KSU != m - help - If enabled, Hook required KernelSU syscalls with manually-patched function. + depends on KSU != m + help + If enabled, Hook required KernelSU syscalls with manually-patched function. endchoice diff --git a/kernel/Makefile b/kernel/Makefile index 9d07efdb..1641425e 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -3,8 +3,10 @@ kernelsu-objs += allowlist.o kernelsu-objs += dynamic_manager.o kernelsu-objs += apk_sign.o kernelsu-objs += sucompat.o +kernelsu-objs += pkg_observer.o kernelsu-objs += throne_tracker.o kernelsu-objs += core_hook.o +kernelsu-objs += supercalls.o kernelsu-objs += ksud.o kernelsu-objs += embed_ksud.o kernelsu-objs += kernel_compat.o @@ -134,6 +136,11 @@ else $(info -- KPM is disabled) endif +# Check new vfs_getattr() +ifeq ($(shell grep -A1 "^int vfs_getattr" $(srctree)/fs/stat.c | grep -q "query_flags" ; echo $$?),0) +ccflags-y += -DKSU_HAS_NEW_VFS_GETATTR +endif + ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat ccflags-y += -Wno-declaration-after-statement -Wno-unused-function diff --git a/kernel/allowlist.c b/kernel/allowlist.c index 2bb4f693..6db149eb 100644 --- a/kernel/allowlist.c +++ b/kernel/allowlist.c @@ -8,7 +8,9 @@ #include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) #include +#endif #include "ksu.h" #include "klog.h" // IWYU pragma: keep @@ -34,53 +36,53 @@ static int allow_list_pointer __read_mostly = 0; static void remove_uid_from_arr(uid_t uid) { - int *temp_arr; - int i, j; + int *temp_arr; + int i, j; - if (allow_list_pointer == 0) - return; + if (allow_list_pointer == 0) + return; - temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL); - if (temp_arr == NULL) { - pr_err("%s: unable to allocate memory\n", __func__); - return; - } + temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL); + if (temp_arr == NULL) { + pr_err("%s: unable to allocate memory\n", __func__); + return; + } - for (i = j = 0; i < allow_list_pointer; i++) { - if (allow_list_arr[i] == uid) - continue; - temp_arr[j++] = allow_list_arr[i]; - } + for (i = j = 0; i < allow_list_pointer; i++) { + if (allow_list_arr[i] == uid) + continue; + temp_arr[j++] = allow_list_arr[i]; + } - allow_list_pointer = j; + allow_list_pointer = j; - for (; j < ARRAY_SIZE(allow_list_arr); j++) - temp_arr[j] = -1; + for (; j < ARRAY_SIZE(allow_list_arr); j++) + temp_arr[j] = -1; - memcpy(&allow_list_arr, temp_arr, PAGE_SIZE); - kfree(temp_arr); + memcpy(&allow_list_arr, temp_arr, PAGE_SIZE); + kfree(temp_arr); } -static void init_default_profiles() +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.gid = 0; - default_root_profile.groups_count = 1; - default_root_profile.groups[0] = 0; - memcpy(&default_root_profile.capabilities.effective, &full_cap, - sizeof(default_root_profile.capabilities.effective)); - default_root_profile.namespaces = 0; - strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN); + default_root_profile.uid = 0; + default_root_profile.gid = 0; + default_root_profile.groups_count = 1; + default_root_profile.groups[0] = 0; + memcpy(&default_root_profile.capabilities.effective, &full_cap, + sizeof(default_root_profile.capabilities.effective)); + default_root_profile.namespaces = 0; + strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN); - // This means that we will umount modules by default! - default_non_root_profile.umount_modules = true; + // This means that we will umount modules by default! + default_non_root_profile.umount_modules = true; } struct perm_data { - struct list_head list; - struct app_profile profile; + struct list_head list; + struct app_profile profile; }; static struct list_head allow_list; @@ -93,436 +95,436 @@ static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE); static struct work_struct ksu_save_work; static struct work_struct ksu_load_work; -bool persistent_allow_list(void); +static bool persistent_allow_list(void); void ksu_show_allow_list(void) { - struct perm_data *p = NULL; - struct list_head *pos = NULL; - pr_info("ksu_show_allow_list\n"); - list_for_each (pos, &allow_list) { - p = list_entry(pos, struct perm_data, list); - pr_info("uid :%d, allow: %d\n", p->profile.current_uid, - p->profile.allow_su); - } + struct perm_data *p = NULL; + struct list_head *pos = NULL; + pr_info("ksu_show_allow_list\n"); + list_for_each (pos, &allow_list) { + p = list_entry(pos, struct perm_data, list); + pr_info("uid :%d, allow: %d\n", p->profile.current_uid, + p->profile.allow_su); + } } #ifdef CONFIG_KSU_DEBUG -static void ksu_grant_root_to_shell() +static void ksu_grant_root_to_shell(void) { - struct app_profile profile = { - .version = KSU_APP_PROFILE_VER, - .allow_su = true, - .current_uid = 2000, - }; - strcpy(profile.key, "com.android.shell"); - strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN); - ksu_set_app_profile(&profile, false); + struct app_profile profile = { + .version = KSU_APP_PROFILE_VER, + .allow_su = true, + .current_uid = 2000, + }; + strcpy(profile.key, "com.android.shell"); + strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN); + ksu_set_app_profile(&profile, false); } #endif bool ksu_get_app_profile(struct app_profile *profile) { - struct perm_data *p = NULL; - struct list_head *pos = NULL; - bool found = false; + struct perm_data *p = NULL; + struct list_head *pos = NULL; + bool found = false; - list_for_each (pos, &allow_list) { - p = list_entry(pos, struct perm_data, list); - bool uid_match = profile->current_uid == p->profile.current_uid; - if (uid_match) { - // found it, override it with ours - memcpy(profile, &p->profile, sizeof(*profile)); - found = true; - goto exit; - } - } + list_for_each (pos, &allow_list) { + p = list_entry(pos, struct perm_data, list); + bool uid_match = profile->current_uid == p->profile.current_uid; + if (uid_match) { + // found it, override it with ours + memcpy(profile, &p->profile, sizeof(*profile)); + found = true; + goto exit; + } + } exit: - return found; + return found; } static inline bool forbid_system_uid(uid_t uid) { - #define SHELL_UID 2000 - #define SYSTEM_UID 1000 - return uid < SHELL_UID && uid != SYSTEM_UID; + #define SHELL_UID 2000 + #define SYSTEM_UID 1000 + return uid < SHELL_UID && uid != SYSTEM_UID; } static bool profile_valid(struct app_profile *profile) { - if (!profile) { - return false; - } + if (!profile) { + return false; + } - if (profile->version < KSU_APP_PROFILE_VER) { - pr_info("Unsupported profile version: %d\n", profile->version); - return false; - } + if (profile->version < KSU_APP_PROFILE_VER) { + pr_info("Unsupported profile version: %d\n", profile->version); + return false; + } - if (profile->allow_su) { - if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) { - return false; - } + if (profile->allow_su) { + if (profile->rp_config.profile.groups_count > KSU_MAX_GROUPS) { + return false; + } - if (strlen(profile->rp_config.profile.selinux_domain) == 0) { - return false; - } - } + if (strlen(profile->rp_config.profile.selinux_domain) == 0) { + return false; + } + } - return true; + return true; } bool ksu_set_app_profile(struct app_profile *profile, bool persist) { - struct perm_data *p = NULL; - struct list_head *pos = NULL; - bool result = false; + struct perm_data *p = NULL; + struct list_head *pos = NULL; + bool result = false; - if (!profile_valid(profile)) { - pr_err("Failed to set app profile: invalid profile!\n"); - return false; - } + if (!profile_valid(profile)) { + pr_err("Failed to set app profile: invalid profile!\n"); + return false; + } - list_for_each (pos, &allow_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 - if (profile->current_uid == p->profile.current_uid && - !strcmp(profile->key, p->profile.key)) { - // found it, just override it all! - memcpy(&p->profile, profile, sizeof(*profile)); - result = true; - goto out; - } - } + list_for_each (pos, &allow_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 + if (profile->current_uid == p->profile.current_uid && + !strcmp(profile->key, p->profile.key)) { + // found it, just override it all! + memcpy(&p->profile, profile, sizeof(*profile)); + result = true; + goto out; + } + } - // not found, alloc a new node! - p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL); - if (!p) { - pr_err("ksu_set_app_profile alloc failed\n"); - return false; - } + // not found, alloc a new node! + p = (struct perm_data *)kmalloc(sizeof(struct perm_data), GFP_KERNEL); + if (!p) { + pr_err("ksu_set_app_profile alloc failed\n"); + return false; + } - memcpy(&p->profile, profile, sizeof(*profile)); - if (profile->allow_su) { - pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n", - profile->key, profile->current_uid, - profile->rp_config.profile.gid, - profile->rp_config.profile.selinux_domain); - } else { - pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n", - profile->key, profile->current_uid, - profile->nrp_config.profile.umount_modules); - } - list_add_tail(&p->list, &allow_list); + memcpy(&p->profile, profile, sizeof(*profile)); + if (profile->allow_su) { + pr_info("set root profile, key: %s, uid: %d, gid: %d, context: %s\n", + profile->key, profile->current_uid, + profile->rp_config.profile.gid, + profile->rp_config.profile.selinux_domain); + } else { + pr_info("set app profile, key: %s, uid: %d, umount modules: %d\n", + profile->key, profile->current_uid, + profile->nrp_config.profile.umount_modules); + } + list_add_tail(&p->list, &allow_list); out: - if (profile->current_uid <= BITMAP_UID_MAX) { - if (profile->allow_su) - allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= 1 << (profile->current_uid % BITS_PER_BYTE); - else - allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= ~(1 << (profile->current_uid % BITS_PER_BYTE)); - } else { - if (profile->allow_su) { - /* - * 1024 apps with uid higher than BITMAP_UID_MAX - * registered to request superuser? - */ - if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) { - pr_err("too many apps registered\n"); - WARN_ON(1); - return false; - } - allow_list_arr[allow_list_pointer++] = profile->current_uid; - } else { - remove_uid_from_arr(profile->current_uid); - } - } - result = true; + if (profile->current_uid <= BITMAP_UID_MAX) { + if (profile->allow_su) + allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= 1 << (profile->current_uid % BITS_PER_BYTE); + else + allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= ~(1 << (profile->current_uid % BITS_PER_BYTE)); + } else { + if (profile->allow_su) { + /* + * 1024 apps with uid higher than BITMAP_UID_MAX + * registered to request superuser? + */ + if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) { + pr_err("too many apps registered\n"); + WARN_ON(1); + return false; + } + allow_list_arr[allow_list_pointer++] = profile->current_uid; + } else { + remove_uid_from_arr(profile->current_uid); + } + } + result = true; - // check if the default profiles is changed, cache it to a single struct to accelerate access. - if (unlikely(!strcmp(profile->key, "$"))) { - // set default non root profile - memcpy(&default_non_root_profile, &profile->nrp_config.profile, - sizeof(default_non_root_profile)); - } + // check if the default profiles is changed, cache it to a single struct to accelerate access. + if (unlikely(!strcmp(profile->key, "$"))) { + // set default non root profile + memcpy(&default_non_root_profile, &profile->nrp_config.profile, + sizeof(default_non_root_profile)); + } - if (unlikely(!strcmp(profile->key, "#"))) { - // set default root profile - memcpy(&default_root_profile, &profile->rp_config.profile, - sizeof(default_root_profile)); - } + if (unlikely(!strcmp(profile->key, "#"))) { + // set default root profile + memcpy(&default_root_profile, &profile->rp_config.profile, + sizeof(default_root_profile)); + } - if (persist) - persistent_allow_list(); + if (persist) + persistent_allow_list(); - return result; + return result; } bool __ksu_is_allow_uid(uid_t uid) { - int i; + int i; - if (unlikely(uid == 0)) { - // already root, but only allow our domain. - return is_ksu_domain(); - } + if (unlikely(uid == 0)) { + // already root, but only allow our domain. + return is_ksu_domain(); + } - if (forbid_system_uid(uid)) { - // do not bother going through the list if it's system - return false; - } + if (forbid_system_uid(uid)) { + // do not bother going through the list if it's system + return false; + } - if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) { - // manager is always allowed! - return true; - } + if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) { + // manager is always allowed! + return true; + } - if (likely(uid <= BITMAP_UID_MAX)) { - return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE))); - } else { - for (i = 0; i < allow_list_pointer; i++) { - if (allow_list_arr[i] == uid) - return true; - } - } + if (likely(uid <= BITMAP_UID_MAX)) { + return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE))); + } else { + for (i = 0; i < allow_list_pointer; i++) { + if (allow_list_arr[i] == uid) + return true; + } + } - return false; + return false; } bool ksu_uid_should_umount(uid_t uid) { - struct app_profile profile = { .current_uid = uid }; - if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) { - // we should not umount on manager! - return false; - } - bool found = ksu_get_app_profile(&profile); - if (!found) { - // no app profile found, it must be non root app - return default_non_root_profile.umount_modules; - } - if (profile.allow_su) { - // if found and it is granted to su, we shouldn't umount for it - return false; - } else { - // found an app profile - if (profile.nrp_config.use_default) { - return default_non_root_profile.umount_modules; - } else { - return profile.nrp_config.profile.umount_modules; - } - } + struct app_profile profile = { .current_uid = uid }; + if (likely(ksu_is_manager_uid_valid()) && unlikely(ksu_get_manager_uid() == uid)) { + // we should not umount on manager! + return false; + } + bool found = ksu_get_app_profile(&profile); + if (!found) { + // no app profile found, it must be non root app + return default_non_root_profile.umount_modules; + } + if (profile.allow_su) { + // if found and it is granted to su, we shouldn't umount for it + return false; + } else { + // found an app profile + if (profile.nrp_config.use_default) { + return default_non_root_profile.umount_modules; + } else { + return profile.nrp_config.profile.umount_modules; + } + } } struct root_profile *ksu_get_root_profile(uid_t uid) { - struct perm_data *p = NULL; - struct list_head *pos = NULL; + struct perm_data *p = NULL; + struct list_head *pos = NULL; - list_for_each (pos, &allow_list) { - p = list_entry(pos, struct perm_data, list); - if (uid == p->profile.current_uid && p->profile.allow_su) { - if (!p->profile.rp_config.use_default) { - return &p->profile.rp_config.profile; - } - } - } + list_for_each (pos, &allow_list) { + p = list_entry(pos, struct perm_data, list); + if (uid == p->profile.current_uid && p->profile.allow_su) { + if (!p->profile.rp_config.use_default) { + return &p->profile.rp_config.profile; + } + } + } - // use default profile - return &default_root_profile; + // use default profile + return &default_root_profile; } bool ksu_get_allow_list(int *array, int *length, bool allow) { - struct perm_data *p = NULL; - struct list_head *pos = NULL; - int i = 0; - list_for_each (pos, &allow_list) { - p = list_entry(pos, struct perm_data, list); - // pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow); - if (p->profile.allow_su == allow) { - array[i++] = p->profile.current_uid; - } - } - *length = i; + struct perm_data *p = NULL; + struct list_head *pos = NULL; + int i = 0; + list_for_each (pos, &allow_list) { + p = list_entry(pos, struct perm_data, list); + // pr_info("get_allow_list uid: %d allow: %d\n", p->uid, p->allow); + if (p->profile.allow_su == allow) { + array[i++] = p->profile.current_uid; + } + } + *length = i; - return true; + return true; } -void do_save_allow_list(struct work_struct *work) +static void do_save_allow_list(struct work_struct *work) { - u32 magic = FILE_MAGIC; - u32 version = FILE_FORMAT_VERSION; - struct perm_data *p = NULL; - struct list_head *pos = NULL; - loff_t off = 0; + u32 magic = FILE_MAGIC; + u32 version = FILE_FORMAT_VERSION; + struct perm_data *p = NULL; + struct list_head *pos = NULL; + loff_t off = 0; - struct file *fp = - ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (IS_ERR(fp)) { - pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp)); - return; - } + struct file *fp = + ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (IS_ERR(fp)) { + pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp)); + return; + } - // store magic and version - if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != - sizeof(magic)) { - pr_err("save_allow_list write magic failed.\n"); - goto exit; - } + // store magic and version + if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != + sizeof(magic)) { + pr_err("save_allow_list write magic failed.\n"); + goto exit; + } - if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != - sizeof(version)) { - pr_err("save_allow_list write version failed.\n"); - goto exit; - } + if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != + sizeof(version)) { + pr_err("save_allow_list write version failed.\n"); + goto exit; + } - list_for_each (pos, &allow_list) { - p = list_entry(pos, struct perm_data, list); - pr_info("save allow list, name: %s uid :%d, allow: %d\n", - p->profile.key, p->profile.current_uid, - p->profile.allow_su); + list_for_each (pos, &allow_list) { + p = list_entry(pos, struct perm_data, list); + pr_info("save allow list, name: %s uid: %d, allow: %d\n", + p->profile.key, p->profile.current_uid, + p->profile.allow_su); - ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile), - &off); - } + ksu_kernel_write_compat(fp, &p->profile, sizeof(p->profile), + &off); + } exit: - filp_close(fp, 0); + filp_close(fp, 0); } -void do_load_allow_list(struct work_struct *work) +static void do_load_allow_list(struct work_struct *work) { - loff_t off = 0; - ssize_t ret = 0; - struct file *fp = NULL; - u32 magic; - u32 version; + loff_t off = 0; + ssize_t ret = 0; + struct file *fp = NULL; + u32 magic; + u32 version; #ifdef CONFIG_KSU_DEBUG - // always allow adb shell by default - ksu_grant_root_to_shell(); + // always allow adb shell by default + ksu_grant_root_to_shell(); #endif - // load allowlist now! - fp = ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_RDONLY, 0); - if (IS_ERR(fp)) { - pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp)); - return; - } + // load allowlist now! + fp = ksu_filp_open_compat(KERNEL_SU_ALLOWLIST, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("load_allow_list open file failed: %ld\n", PTR_ERR(fp)); + return; + } - // verify magic - if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != - sizeof(magic) || - magic != FILE_MAGIC) { - pr_err("allowlist file invalid: %d!\n", magic); - goto exit; - } + // verify magic + if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != + sizeof(magic) || + magic != FILE_MAGIC) { + pr_err("allowlist file invalid: %d!\n", magic); + goto exit; + } - if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != - sizeof(version)) { - pr_err("allowlist read version: %d failed\n", version); - goto exit; - } + if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != + sizeof(version)) { + pr_err("allowlist read version: %d failed\n", version); + goto exit; + } - pr_info("allowlist version: %d\n", version); + pr_info("allowlist version: %d\n", version); - while (true) { - struct app_profile profile; + while (true) { + struct app_profile profile; - ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile), - &off); + ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile), + &off); - if (ret <= 0) { - pr_info("load_allow_list read err: %zd\n", ret); - break; - } + if (ret <= 0) { + pr_info("load_allow_list read err: %zd\n", ret); + break; + } - pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n", - profile.key, profile.current_uid, profile.allow_su); - ksu_set_app_profile(&profile, false); - } + pr_info("load_allow_uid, name: %s, uid: %d, allow: %d\n", + profile.key, profile.current_uid, profile.allow_su); + ksu_set_app_profile(&profile, false); + } exit: - ksu_show_allow_list(); - filp_close(fp, 0); + ksu_show_allow_list(); + filp_close(fp, 0); } void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), void *data) { - struct perm_data *np = NULL; - struct perm_data *n = NULL; + struct perm_data *np = NULL; + struct perm_data *n = NULL; - bool modified = false; - // TODO: use RCU! - mutex_lock(&allowlist_mutex); - list_for_each_entry_safe (np, n, &allow_list, list) { - uid_t uid = np->profile.current_uid; - char *package = np->profile.key; - // we use this uid for special cases, don't prune it! - bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID; - if (!is_preserved_uid && !is_uid_valid(uid, package, data)) { - modified = true; - pr_info("prune uid: %d, package: %s\n", uid, package); - list_del(&np->list); - if (likely(uid <= BITMAP_UID_MAX)) { - allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE)); - } - remove_uid_from_arr(uid); - smp_mb(); - kfree(np); - } - } - mutex_unlock(&allowlist_mutex); + bool modified = false; + // TODO: use RCU! + mutex_lock(&allowlist_mutex); + list_for_each_entry_safe (np, n, &allow_list, list) { + uid_t uid = np->profile.current_uid; + char *package = np->profile.key; + // we use this uid for special cases, don't prune it! + bool is_preserved_uid = uid == KSU_APP_PROFILE_PRESERVE_UID; + if (!is_preserved_uid && !is_uid_valid(uid, package, data)) { + modified = true; + pr_info("prune uid: %d, package: %s\n", uid, package); + list_del(&np->list); + if (likely(uid <= BITMAP_UID_MAX)) { + allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE)); + } + remove_uid_from_arr(uid); + smp_mb(); + kfree(np); + } + } + mutex_unlock(&allowlist_mutex); - if (modified) { - persistent_allow_list(); - } + if (modified) { + persistent_allow_list(); + } } // make sure allow list works cross boot -bool persistent_allow_list(void) +static bool persistent_allow_list(void) { - return ksu_queue_work(&ksu_save_work); + return ksu_queue_work(&ksu_save_work); } 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) { - int i; + int i; - BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE); - BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE); + BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE); + BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE); - for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++) - allow_list_arr[i] = -1; + for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++) + 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_load_work, do_load_allow_list); + INIT_WORK(&ksu_save_work, do_save_allow_list); + INIT_WORK(&ksu_load_work, do_load_allow_list); - init_default_profiles(); + init_default_profiles(); } void ksu_allowlist_exit(void) { - struct perm_data *np = NULL; - struct perm_data *n = NULL; + struct perm_data *np = NULL; + struct perm_data *n = NULL; - do_save_allow_list(NULL); + do_save_allow_list(NULL); - // free allowlist - mutex_lock(&allowlist_mutex); - list_for_each_entry_safe (np, n, &allow_list, list) { - list_del(&np->list); - kfree(np); - } - mutex_unlock(&allowlist_mutex); + // free allowlist + mutex_lock(&allowlist_mutex); + list_for_each_entry_safe (np, n, &allow_list, list) { + list_del(&np->list); + kfree(np); + } + mutex_unlock(&allowlist_mutex); } #ifdef CONFIG_KSU_MANUAL_SU @@ -553,7 +555,7 @@ bool ksu_temp_grant_root_once(uid_t uid) 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.groups_count = default_root_profile.groups_count; memcpy(profile.rp_config.profile.groups, default_root_profile.groups, sizeof(default_root_profile.groups)); @@ -601,4 +603,4 @@ void ksu_temp_revoke_root_once(uid_t uid) persistent_allow_list(); pr_info("pending_root: UID=%d removed and persist updated\n", uid); } -#endif +#endif \ No newline at end of file diff --git a/kernel/apk_sign.c b/kernel/apk_sign.c index 2e554580..4f03c2d4 100644 --- a/kernel/apk_sign.c +++ b/kernel/apk_sign.c @@ -21,62 +21,62 @@ #include "manager_sign.h" struct sdesc { - struct shash_desc shash; - char ctx[]; + struct shash_desc shash; + char ctx[]; }; static apk_sign_key_t apk_sign_keys[] = { - {EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // ShirkNeko/SukiSU + {EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // ShirkNeko/SukiSU #ifdef EXPECTED_SIZE - {EXPECTED_SIZE, EXPECTED_HASH}, // Custom + {EXPECTED_SIZE, EXPECTED_HASH}, // Custom #endif }; static struct sdesc *init_sdesc(struct crypto_shash *alg) { - struct sdesc *sdesc; - int size; + struct sdesc *sdesc; + int size; - size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); - sdesc = kmalloc(size, GFP_KERNEL); - if (!sdesc) - return ERR_PTR(-ENOMEM); - sdesc->shash.tfm = alg; - return sdesc; + size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); + sdesc = kmalloc(size, GFP_KERNEL); + if (!sdesc) + return ERR_PTR(-ENOMEM); + sdesc->shash.tfm = alg; + return sdesc; } 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; - int ret; + struct sdesc *sdesc; + int ret; - sdesc = init_sdesc(alg); - if (IS_ERR(sdesc)) { - pr_info("can't alloc sdesc\n"); - return PTR_ERR(sdesc); - } + sdesc = init_sdesc(alg); + if (IS_ERR(sdesc)) { + pr_info("can't alloc sdesc\n"); + return PTR_ERR(sdesc); + } - ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest); - kfree(sdesc); - return ret; + ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest); + kfree(sdesc); + return ret; } static int ksu_sha256(const unsigned char *data, unsigned int datalen, - unsigned char *digest) + unsigned char *digest) { - struct crypto_shash *alg; - char *hash_alg_name = "sha256"; - int ret; + struct crypto_shash *alg; + char *hash_alg_name = "sha256"; + int ret; - alg = crypto_alloc_shash(hash_alg_name, 0, 0); - if (IS_ERR(alg)) { - pr_info("can't alloc alg %s\n", hash_alg_name); - return PTR_ERR(alg); - } - ret = calc_hash(alg, data, datalen, digest); - crypto_free_shash(alg); - return ret; + alg = crypto_alloc_shash(hash_alg_name, 0, 0); + if (IS_ERR(alg)) { + pr_info("can't alloc alg %s\n", hash_alg_name); + return PTR_ERR(alg); + } + ret = calc_hash(alg, data, datalen, digest); + crypto_free_shash(alg); + return ret; } @@ -84,304 +84,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) { - struct dynamic_sign_key current_dynamic_key = dynamic_sign; - - 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", - current_dynamic_key.size, current_dynamic_key.hash); - } - - if (size4 != current_dynamic_key.size) { - return false; - } + struct dynamic_sign_key current_dynamic_key = dynamic_sign; + + 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", + current_dynamic_key.size, current_dynamic_key.hash); + } + + if (size4 != current_dynamic_key.size) { + return false; + } #define CERT_MAX_LENGTH 1024 - char cert[CERT_MAX_LENGTH]; - if (size4 > CERT_MAX_LENGTH) { - pr_info("cert length overlimit\n"); - return false; - } - - ksu_kernel_read_compat(fp, cert, size4, pos); - - unsigned char digest[SHA256_DIGEST_SIZE]; - if (ksu_sha256(cert, size4, digest) < 0) { - pr_info("sha256 error\n"); - return false; - } + char cert[CERT_MAX_LENGTH]; + if (size4 > CERT_MAX_LENGTH) { + pr_info("cert length overlimit\n"); + return false; + } + + ksu_kernel_read_compat(fp, cert, size4, pos); + + unsigned char digest[SHA256_DIGEST_SIZE]; + if (ksu_sha256(cert, size4, digest) < 0) { + pr_info("sha256 error\n"); + return false; + } - char hash_str[SHA256_DIGEST_SIZE * 2 + 1]; - hash_str[SHA256_DIGEST_SIZE * 2] = '\0'; - bin2hex(hash_str, digest, SHA256_DIGEST_SIZE); - - 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 (matched_index) { - *matched_index = DYNAMIC_SIGN_INDEX; - } - return true; - } - - return false; + char hash_str[SHA256_DIGEST_SIZE * 2 + 1]; + hash_str[SHA256_DIGEST_SIZE * 2] = '\0'; + bin2hex(hash_str, digest, SHA256_DIGEST_SIZE); + + 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 (matched_index) { + *matched_index = DYNAMIC_SIGN_INDEX; + } + return true; + } + + return false; } static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, int *matched_index) { - int i; - apk_sign_key_t sign_key; - bool signature_valid = false; + int i; + apk_sign_key_t sign_key; + bool signature_valid = false; - 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); // signed data 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); // 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; - *offset += 0x4 + *size4; + *pos += *size4; + *offset += 0x4 + *size4; - ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length - ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length - *offset += 0x4 * 2; + ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length + ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length + *offset += 0x4 * 2; - if (ksu_is_dynamic_manager_enabled()) { - loff_t temp_pos = *pos; - if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) { - *pos = temp_pos; - *offset += *size4; - return true; - } - } + if (ksu_is_dynamic_manager_enabled()) { + loff_t temp_pos = *pos; + if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) { + *pos = temp_pos; + *offset += *size4; + return true; + } + } - for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) { - sign_key = apk_sign_keys[i]; + for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) { + sign_key = apk_sign_keys[i]; - if (*size4 != sign_key.size) - continue; - *offset += *size4; + if (*size4 != sign_key.size) + continue; + *offset += *size4; #define CERT_MAX_LENGTH 1024 - char cert[CERT_MAX_LENGTH]; - if (*size4 > CERT_MAX_LENGTH) { - pr_info("cert length overlimit\n"); - return false; - } - ksu_kernel_read_compat(fp, cert, *size4, pos); - unsigned char digest[SHA256_DIGEST_SIZE]; - if (IS_ERR(ksu_sha256(cert, *size4, digest))) { - pr_info("sha256 error\n"); - return false; - } + char cert[CERT_MAX_LENGTH]; + if (*size4 > CERT_MAX_LENGTH) { + pr_info("cert length overlimit\n"); + return false; + } + ksu_kernel_read_compat(fp, cert, *size4, pos); + unsigned char digest[SHA256_DIGEST_SIZE]; + if (ksu_sha256(cert, *size4, digest) < 0 ) { + pr_info("sha256 error\n"); + return false; + } - char hash_str[SHA256_DIGEST_SIZE * 2 + 1]; - hash_str[SHA256_DIGEST_SIZE * 2] = '\0'; + char hash_str[SHA256_DIGEST_SIZE * 2 + 1]; + hash_str[SHA256_DIGEST_SIZE * 2] = '\0'; - bin2hex(hash_str, digest, SHA256_DIGEST_SIZE); - pr_info("sha256: %s, expected: %s, index: %d\n", hash_str, sign_key.sha256, i); - - if (strcmp(sign_key.sha256, hash_str) == 0) { - signature_valid = true; - if (matched_index) { - *matched_index = i; - } - break; - } - } - return signature_valid; + bin2hex(hash_str, digest, SHA256_DIGEST_SIZE); + pr_info("sha256: %s, expected: %s, index: %d\n", hash_str, sign_key.sha256, i); + + if (strcmp(sign_key.sha256, hash_str) == 0) { + signature_valid = true; + if (matched_index) { + *matched_index = i; + } + break; + } + } + return signature_valid; } struct zip_entry_header { - uint32_t signature; - uint16_t version; - uint16_t flags; - uint16_t compression; - uint16_t mod_time; - uint16_t mod_date; - uint32_t crc32; - uint32_t compressed_size; - uint32_t uncompressed_size; - uint16_t file_name_length; - uint16_t extra_field_length; + uint32_t signature; + uint16_t version; + uint16_t flags; + uint16_t compression; + uint16_t mod_time; + uint16_t mod_date; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t file_name_length; + uint16_t extra_field_length; } __attribute__((packed)); // This is a necessary but not sufficient condition, but it is enough for us static bool has_v1_signature_file(struct file *fp) { - struct zip_entry_header header; - const char MANIFEST[] = "META-INF/MANIFEST.MF"; + struct zip_entry_header header; + const char MANIFEST[] = "META-INF/MANIFEST.MF"; - loff_t pos = 0; + loff_t pos = 0; - while (ksu_kernel_read_compat(fp, &header, - sizeof(struct zip_entry_header), &pos) == - sizeof(struct zip_entry_header)) { - if (header.signature != 0x04034b50) { - // ZIP magic: 'PK' - return false; - } - // Read the entry file name - if (header.file_name_length == sizeof(MANIFEST) - 1) { - char fileName[sizeof(MANIFEST)]; - ksu_kernel_read_compat(fp, fileName, - header.file_name_length, &pos); - fileName[header.file_name_length] = '\0'; + while (ksu_kernel_read_compat(fp, &header, + sizeof(struct zip_entry_header), &pos) == + sizeof(struct zip_entry_header)) { + if (header.signature != 0x04034b50) { + // ZIP magic: 'PK' + return false; + } + // Read the entry file name + if (header.file_name_length == sizeof(MANIFEST) - 1) { + char fileName[sizeof(MANIFEST)]; + ksu_kernel_read_compat(fp, fileName, + header.file_name_length, &pos); + fileName[header.file_name_length] = '\0'; - // Check if the entry matches META-INF/MANIFEST.MF - if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) == 0) { - return true; - } - } else { - // Skip the entry file name - pos += header.file_name_length; - } + // Check if the entry matches META-INF/MANIFEST.MF + if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) == + 0) { + return true; + } + } else { + // Skip the entry file name + pos += header.file_name_length; + } - // Skip to the next entry - pos += header.extra_field_length + header.compressed_size; - } + // Skip to the next entry + 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) { - unsigned char buffer[0x11] = { 0 }; - u32 size4; - u64 size8, size_of_block; - loff_t pos; - bool v2_signing_valid = false; - int v2_signing_blocks = 0; - bool v3_signing_exist = false; - bool v3_1_signing_exist = false; - int matched_index = -1; - int i; - struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0); - if (IS_ERR(fp)) { - pr_err("open %s error.\n", path); - return false; - } + unsigned char buffer[0x11] = { 0 }; + u32 size4; + u64 size8, size_of_block; - // 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()) { - filp_close(fp, 0); - return 0; - } + loff_t pos; - // disable inotify for this file - fp->f_mode |= FMODE_NONOTIFY; + bool v2_signing_valid = false; + int v2_signing_blocks = 0; + bool v3_signing_exist = false; + bool v3_1_signing_exist = false; + int matched_index = -1; + int i; + struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_err("open %s error.\n", path); + return false; + } - // https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD) - for (i = 0;; ++i) { - unsigned short n; - pos = generic_file_llseek(fp, -i - 2, SEEK_END); - ksu_kernel_read_compat(fp, &n, 2, &pos); - if (n == i) { - pos -= 22; - ksu_kernel_read_compat(fp, &size4, 4, &pos); - if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) { - break; - } - } - if (i == 0xffff) { - pr_info("error: cannot find eocd\n"); - goto clean; - } - } + // 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()) { + filp_close(fp, 0); + return 0; + } - pos += 12; - // offset - ksu_kernel_read_compat(fp, &size4, 0x4, &pos); - pos = size4 - 0x18; + // disable inotify for this file + fp->f_mode |= FMODE_NONOTIFY; - ksu_kernel_read_compat(fp, &size8, 0x8, &pos); - ksu_kernel_read_compat(fp, buffer, 0x10, &pos); - if (strcmp((char *)buffer, "APK Sig Block 42")) { - goto clean; - } + // https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD) + for (i = 0;; ++i) { + unsigned short n; + pos = generic_file_llseek(fp, -i - 2, SEEK_END); + ksu_kernel_read_compat(fp, &n, 2, &pos); + if (n == i) { + pos -= 22; + ksu_kernel_read_compat(fp, &size4, 4, &pos); + if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) { + break; + } + } + if (i == 0xffff) { + pr_info("error: cannot find eocd\n"); + goto clean; + } + } - pos = size4 - (size8 + 0x8); - ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos); - if (size_of_block != size8) { - goto clean; - } + pos += 12; + // offset + ksu_kernel_read_compat(fp, &size4, 0x4, &pos); + pos = size4 - 0x18; - int loop_count = 0; - while (loop_count++ < 10) { - uint32_t id; - uint32_t offset; - ksu_kernel_read_compat(fp, &size8, 0x8, - &pos); // sequence length - if (size8 == size_of_block) { - break; - } - ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id - offset = 4; - if (id == 0x7109871au) { - v2_signing_blocks++; - bool result = check_block(fp, &size4, &pos, &offset, &matched_index); - if (result) { - v2_signing_valid = true; - } - } else if (id == 0xf05368c0u) { - // http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73 - v3_signing_exist = true; - } else if (id == 0x1b93ad61u) { - // http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74 - v3_1_signing_exist = true; - } else { + ksu_kernel_read_compat(fp, &size8, 0x8, &pos); + ksu_kernel_read_compat(fp, buffer, 0x10, &pos); + if (strcmp((char *)buffer, "APK Sig Block 42")) { + goto clean; + } + + pos = size4 - (size8 + 0x8); + ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos); + if (size_of_block != size8) { + goto clean; + } + + int loop_count = 0; + while (loop_count++ < 10) { + uint32_t id; + uint32_t offset; + ksu_kernel_read_compat(fp, &size8, 0x8, + &pos); // sequence length + if (size8 == size_of_block) { + break; + } + ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id + offset = 4; + if (id == 0x7109871au) { + v2_signing_blocks++; + bool result = check_block(fp, &size4, &pos, &offset, &matched_index); + if (result) { + v2_signing_valid = true; + } + } else if (id == 0xf05368c0u) { + // http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73 + v3_signing_exist = true; + } else if (id == 0x1b93ad61u) { + // http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74 + v3_1_signing_exist = true; + } else { #ifdef CONFIG_KSU_DEBUG - pr_info("Unknown id: 0x%08x\n", id); + pr_info("Unknown id: 0x%08x\n", id); #endif - } - pos += (size8 - offset); - } + } + pos += (size8 - offset); + } - if (v2_signing_blocks != 1) { + if (v2_signing_blocks != 1) { #ifdef CONFIG_KSU_DEBUG - pr_err("Unexpected v2 signature count: %d\n", - v2_signing_blocks); + pr_err("Unexpected v2 signature count: %d\n", + v2_signing_blocks); #endif - v2_signing_valid = false; - } + v2_signing_valid = false; + } - if (v2_signing_valid) { - int has_v1_signing = has_v1_signature_file(fp); - if (has_v1_signing) { - pr_err("Unexpected v1 signature scheme found!\n"); - filp_close(fp, 0); - return false; - } - } + if (v2_signing_valid) { + int has_v1_signing = has_v1_signature_file(fp); + if (has_v1_signing) { + pr_err("Unexpected v1 signature scheme found!\n"); + filp_close(fp, 0); + return false; + } + } 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 - pr_err("Unexpected v3 signature scheme found!\n"); + pr_err("Unexpected v3 signature scheme found!\n"); #endif - return false; - } + return false; + } - if (v2_signing_valid) { - if (signature_index) { - *signature_index = matched_index; - } - - if (check_multi_manager) { - // 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign - if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) { - pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index); - return true; - } - return false; - } else { - // Common manager check: any valid signature will do - return true; - } - } - return false; + if (v2_signing_valid) { + if (signature_index) { + *signature_index = matched_index; + } + + if (check_multi_manager) { + // 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign + if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) { + pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index); + return true; + } + return false; + } else { + // Common manager check: any valid signature will do + return true; + } + } + return false; } #ifdef CONFIG_KSU_DEBUG @@ -392,19 +395,19 @@ int ksu_debug_manager_uid = -1; static int set_expected_size(const char *val, const struct kernel_param *kp) { - int rv = param_set_uint(val, kp); - ksu_set_manager_uid(ksu_debug_manager_uid); - pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid); - return rv; + int rv = param_set_uint(val, kp); + ksu_set_manager_uid(ksu_debug_manager_uid); + pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid); + return rv; } static struct kernel_param_ops expected_size_ops = { - .set = set_expected_size, - .get = param_get_uint, + .set = set_expected_size, + .get = param_get_uint, }; 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 diff --git a/kernel/arch.h b/kernel/arch.h index eec38c28..0e1a2e09 100644 --- a/kernel/arch.h +++ b/kernel/arch.h @@ -19,10 +19,17 @@ #define __PT_IP_REG pc #define PRCTL_SYMBOL "__arm64_sys_prctl" +#define REBOOT_SYMBOL "__arm64_sys_reboot" #define SYS_READ_SYMBOL "__arm64_sys_read" #define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat" #define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat" #define SYS_EXECVE_SYMBOL "__arm64_sys_execve" +/*LSM HOOK*/ +#define SECURITY_TASK_FIX_SETUID_SYMBOL "security_task_fix_setuid" +#define PRCTL_SYMBOL "__arm64_sys_prctl" +#define INODE_PERMISSION_SYMBOL "security_inode_permission" +#define BPRM_CHECK_SECURITY_SYMBOL "security_bprm_check" +#define TASK_ALLOC_SYMBOL "security_task_alloc" #elif defined(__x86_64__) @@ -40,10 +47,17 @@ #define __PT_SP_REG sp #define __PT_IP_REG ip #define PRCTL_SYMBOL "__x64_sys_prctl" +#define REBOOT_SYMBOL "__x64_sys_reboot" #define SYS_READ_SYMBOL "__x64_sys_read" #define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat" #define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat" #define SYS_EXECVE_SYMBOL "__x64_sys_execve" +/*LSM HOOK*/ +#define SECURITY_TASK_FIX_SETUID_SYMBOL "security_task_fix_setuid" +#define PRCTL_SYMBOL "__x64_sys_prctl" +#define INODE_PERMISSION_SYMBOL "security_inode_permission" +#define BPRM_CHECK_SECURITY_SYMBOL "security_bprm_check" +#define TASK_ALLOC_SYMBOL "security_task_alloc" #else #error "Unsupported arch" diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 263d748a..dc487a27 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -1,14 +1,17 @@ +#include +#include #include #include #include #include +#include #include #include -#include #include #include -#include #include +#include +#include #include #include #include @@ -20,21 +23,10 @@ #include #include #include -#include +#include #include #include -#include -#include - -#ifdef MODULE -#include -#include -#include -#include -#include -#endif - #include "allowlist.h" #include "arch.h" #include "core_hook.h" @@ -43,10 +35,8 @@ #include "ksud.h" #include "manager.h" #include "selinux/selinux.h" -#include "throne_tracker.h" -#include "throne_comm.h" #include "kernel_compat.h" -#include "dynamic_manager.h" +#include "supercalls.h" #include "sulog.h" #ifdef CONFIG_KSU_MANUAL_SU @@ -57,153 +47,172 @@ #include "kpm/kpm.h" #endif -static bool ksu_module_mounted = false; -bool ksu_uid_scanner_enabled = false; +bool ksu_module_mounted = false; -extern int handle_sepolicy(unsigned long arg3, void __user *arg4); +#ifdef CONFIG_COMPAT +bool ksu_is_compat __read_mostly = false; +#endif -static bool ksu_su_compat_enabled = true; -extern void ksu_sucompat_init(); -extern void ksu_sucompat_exit(); +#ifndef DEVPTS_SUPER_MAGIC +#define DEVPTS_SUPER_MAGIC 0x1cd1 +#endif + +extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c + +#ifdef CONFIG_KSU_MANUAL_SU +static void ksu_try_escalate_for_uid(uid_t uid) +{ + if (!is_pending_root(uid)) + return; + + pr_info("pending_root: UID=%d temporarily allowed\n", uid); + remove_pending_root(uid); +} +#endif + +static int (*original_cap_task_fix_setuid)(struct cred *new, const struct cred *old, int flags); +static struct security_hook_list *ksu_hooked_security_hook; static inline bool is_allow_su() { - if (is_manager()) { - // we are manager, allow! - return true; - } - return ksu_is_allow_uid(current_uid().val); + if (is_manager()) { + // we are manager, allow! + return true; + } + return ksu_is_allow_uid(current_uid().val); } static inline bool is_unsupported_uid(uid_t uid) { #define LAST_APPLICATION_UID 19999 - uid_t appid = uid % 100000; - return appid > LAST_APPLICATION_UID; + uid_t appid = uid % 100000; + return appid > LAST_APPLICATION_UID; } #if LINUX_VERSION_CODE >= KERNEL_VERSION (6, 7, 0) - static struct group_info root_groups = { .usage = REFCOUNT_INIT(2), }; + static struct group_info root_groups = { .usage = REFCOUNT_INIT(2), }; #else - static struct group_info root_groups = { .usage = ATOMIC_INIT(2) }; + static struct group_info root_groups = { .usage = ATOMIC_INIT(2) }; #endif static void setup_groups(struct root_profile *profile, struct cred *cred) { - if (profile->groups_count > KSU_MAX_GROUPS) { - pr_warn("Failed to setgroups, too large group: %d!\n", - profile->uid); - return; - } + if (profile->groups_count > KSU_MAX_GROUPS) { + pr_warn("Failed to setgroups, too large group: %d!\n", + profile->uid); + return; + } - if (profile->groups_count == 1 && profile->groups[0] == 0) { - // setgroup to root and return early. - if (cred->group_info) - put_group_info(cred->group_info); - cred->group_info = get_group_info(&root_groups); - return; - } + if (profile->groups_count == 1 && profile->groups[0] == 0) { + // setgroup to root and return early. + if (cred->group_info) + put_group_info(cred->group_info); + cred->group_info = get_group_info(&root_groups); + return; + } - u32 ngroups = profile->groups_count; - struct group_info *group_info = groups_alloc(ngroups); - if (!group_info) { - pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid); - return; - } + u32 ngroups = profile->groups_count; + struct group_info *group_info = groups_alloc(ngroups); + if (!group_info) { + pr_warn("Failed to setgroups, ENOMEM for: %d\n", profile->uid); + return; + } - int i; - for (i = 0; i < ngroups; i++) { - gid_t gid = profile->groups[i]; - kgid_t kgid = make_kgid(current_user_ns(), gid); - if (!gid_valid(kgid)) { - pr_warn("Failed to setgroups, invalid gid: %d\n", gid); - put_group_info(group_info); - return; - } - group_info->gid[i] = kgid; - } + int i; + for (i = 0; i < ngroups; i++) { + gid_t gid = profile->groups[i]; + kgid_t kgid = make_kgid(current_user_ns(), gid); + if (!gid_valid(kgid)) { + pr_warn("Failed to setgroups, invalid gid: %d\n", gid); + put_group_info(group_info); + return; + } + group_info->gid[i] = kgid; + } - groups_sort(group_info); - set_groups(cred, group_info); - put_group_info(group_info); + groups_sort(group_info); + set_groups(cred, group_info); + put_group_info(group_info); } static void disable_seccomp() { - assert_spin_locked(¤t->sighand->siglock); - // disable seccomp + assert_spin_locked(¤t->sighand->siglock); + // disable seccomp #if defined(CONFIG_GENERIC_ENTRY) && \ - LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) - clear_syscall_work(SECCOMP); + LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + clear_syscall_work(SECCOMP); #else - clear_thread_flag(TIF_SECCOMP); + clear_thread_flag(TIF_SECCOMP); #endif #ifdef CONFIG_SECCOMP - current->seccomp.mode = 0; - current->seccomp.filter = NULL; + current->seccomp.mode = 0; + current->seccomp.filter = NULL; #else #endif } void escape_to_root(void) { - struct cred *cred; + struct cred *cred; - cred = prepare_creds(); - if (!cred) { - pr_warn("prepare_creds failed!\n"); - return; - } + cred = prepare_creds(); + if (!cred) { + pr_warn("prepare_creds failed!\n"); + return; + } - if (cred->euid.val == 0) { - pr_warn("Already root, don't escape!\n"); - ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root_failed"); - abort_creds(cred); - return; - } - - struct root_profile *profile = ksu_get_root_profile(cred->uid.val); - - cred->uid.val = profile->uid; - cred->suid.val = profile->uid; - cred->euid.val = profile->uid; - cred->fsuid.val = profile->uid; - - cred->gid.val = profile->gid; - cred->fsgid.val = profile->gid; - cred->sgid.val = profile->gid; - cred->egid.val = profile->gid; - cred->securebits = 0; - - BUILD_BUG_ON(sizeof(profile->capabilities.effective) != - sizeof(kernel_cap_t)); - - // setup capabilities - // we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process - // we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec! - u64 cap_for_ksud = - profile->capabilities.effective | CAP_DAC_READ_SEARCH; - memcpy(&cred->cap_effective, &cap_for_ksud, - sizeof(cred->cap_effective)); - memcpy(&cred->cap_permitted, &profile->capabilities.effective, - sizeof(cred->cap_permitted)); - memcpy(&cred->cap_bset, &profile->capabilities.effective, - sizeof(cred->cap_bset)); - - setup_groups(profile, cred); - - commit_creds(cred); - - // Refer to kernel/seccomp.c: seccomp_set_mode_strict - // When disabling Seccomp, ensure that current->sighand->siglock is held during the operation. - spin_lock_irq(¤t->sighand->siglock); - disable_seccomp(); - spin_unlock_irq(¤t->sighand->siglock); - - setup_selinux(profile->selinux_domain); + if (cred->euid.val == 0) { + pr_warn("Already root, don't escape!\n"); #if __SULOG_GATE - ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root"); + ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root_failed"); +#endif + abort_creds(cred); + return; + } + + struct root_profile *profile = ksu_get_root_profile(cred->uid.val); + + cred->uid.val = profile->uid; + cred->suid.val = profile->uid; + cred->euid.val = profile->uid; + cred->fsuid.val = profile->uid; + + cred->gid.val = profile->gid; + cred->fsgid.val = profile->gid; + cred->sgid.val = profile->gid; + cred->egid.val = profile->gid; + cred->securebits = 0; + + BUILD_BUG_ON(sizeof(profile->capabilities.effective) != + sizeof(kernel_cap_t)); + + // setup capabilities + // we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process + // we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec! + u64 cap_for_ksud = + profile->capabilities.effective | CAP_DAC_READ_SEARCH; + memcpy(&cred->cap_effective, &cap_for_ksud, + sizeof(cred->cap_effective)); + memcpy(&cred->cap_permitted, &profile->capabilities.effective, + sizeof(cred->cap_permitted)); + memcpy(&cred->cap_bset, &profile->capabilities.effective, + sizeof(cred->cap_bset)); + + setup_groups(profile, cred); + + commit_creds(cred); + + // Refer to kernel/seccomp.c: seccomp_set_mode_strict + // When disabling Seccomp, ensure that current->sighand->siglock is held during the operation. + spin_lock_irq(¤t->sighand->siglock); + disable_seccomp(); + spin_unlock_irq(¤t->sighand->siglock); + + setup_selinux(profile->selinux_domain); +#if __SULOG_GATE + ksu_sulog_report_su_grant(current_euid().val, NULL, "escape_to_root"); #endif } @@ -211,738 +220,241 @@ void escape_to_root(void) static void disable_seccomp_for_task(struct task_struct *tsk) { - if (!tsk->seccomp.filter && tsk->seccomp.mode == SECCOMP_MODE_DISABLED) - return; + if (!tsk->seccomp.filter && tsk->seccomp.mode == SECCOMP_MODE_DISABLED) + return; - if (WARN_ON(!spin_is_locked(&tsk->sighand->siglock))) - return; + if (WARN_ON(!spin_is_locked(&tsk->sighand->siglock))) + return; #ifdef CONFIG_SECCOMP - tsk->seccomp.mode = 0; - if (tsk->seccomp.filter) { + tsk->seccomp.mode = 0; + if (tsk->seccomp.filter) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) - seccomp_filter_release(tsk); - atomic_set(&tsk->seccomp.filter_count, 0); + seccomp_filter_release(tsk); + atomic_set(&tsk->seccomp.filter_count, 0); #else - // for 6.11+ kernel support? + // for 6.11+ kernel support? #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) - put_seccomp_filter(tsk); + put_seccomp_filter(tsk); #endif - tsk->seccomp.filter = NULL; + tsk->seccomp.filter = NULL; #endif - } + } #endif } void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid) { - struct cred *newcreds; - struct task_struct *target_task; + struct cred *newcreds; + struct task_struct *target_task; - pr_info("cmd_su: escape_to_root_for_cmd_su called for UID: %d, PID: %d\n", target_uid, target_pid); + pr_info("cmd_su: escape_to_root_for_cmd_su called for UID: %d, PID: %d\n", target_uid, target_pid); - // Find target task by PID - rcu_read_lock(); - target_task = pid_task(find_vpid(target_pid), PIDTYPE_PID); - if (!target_task) { - rcu_read_unlock(); - pr_err("cmd_su: target task not found for PID: %d\n", target_pid); + // Find target task by PID + rcu_read_lock(); + target_task = pid_task(find_vpid(target_pid), PIDTYPE_PID); + if (!target_task) { + rcu_read_unlock(); + pr_err("cmd_su: target task not found for PID: %d\n", target_pid); #if __SULOG_GATE - ksu_sulog_report_su_grant(target_uid, "cmd_su", "target_not_found"); + ksu_sulog_report_su_grant(target_uid, "cmd_su", "target_not_found"); #endif - return; - } - get_task_struct(target_task); - rcu_read_unlock(); + return; + } + get_task_struct(target_task); + rcu_read_unlock(); - if (task_uid(target_task).val == 0) { - pr_warn("cmd_su: target task is already root, PID: %d\n", target_pid); - put_task_struct(target_task); - return; - } + if (task_uid(target_task).val == 0) { + pr_warn("cmd_su: target task is already root, PID: %d\n", target_pid); + put_task_struct(target_task); + return; + } - newcreds = prepare_kernel_cred(target_task); - if (newcreds == NULL) { - pr_err("cmd_su: failed to allocate new cred for PID: %d\n", target_pid); + newcreds = prepare_kernel_cred(target_task); + if (newcreds == NULL) { + pr_err("cmd_su: failed to allocate new cred for PID: %d\n", target_pid); #if __SULOG_GATE - ksu_sulog_report_su_grant(target_uid, "cmd_su", "cred_alloc_failed"); + ksu_sulog_report_su_grant(target_uid, "cmd_su", "cred_alloc_failed"); #endif - put_task_struct(target_task); - return; - } + put_task_struct(target_task); + return; + } - struct root_profile *profile = ksu_get_root_profile(target_uid); + struct root_profile *profile = ksu_get_root_profile(target_uid); - newcreds->uid.val = profile->uid; - newcreds->suid.val = profile->uid; - newcreds->euid.val = profile->uid; - newcreds->fsuid.val = profile->uid; + newcreds->uid.val = profile->uid; + newcreds->suid.val = profile->uid; + newcreds->euid.val = profile->uid; + newcreds->fsuid.val = profile->uid; - newcreds->gid.val = profile->gid; - newcreds->fsgid.val = profile->gid; - newcreds->sgid.val = profile->gid; - newcreds->egid.val = profile->gid; - newcreds->securebits = 0; + newcreds->gid.val = profile->gid; + newcreds->fsgid.val = profile->gid; + newcreds->sgid.val = profile->gid; + newcreds->egid.val = profile->gid; + newcreds->securebits = 0; - u64 cap_for_cmd_su = profile->capabilities.effective | CAP_DAC_READ_SEARCH | CAP_SETUID | CAP_SETGID; - memcpy(&newcreds->cap_effective, &cap_for_cmd_su, sizeof(newcreds->cap_effective)); - memcpy(&newcreds->cap_permitted, &profile->capabilities.effective, sizeof(newcreds->cap_permitted)); - memcpy(&newcreds->cap_bset, &profile->capabilities.effective, sizeof(newcreds->cap_bset)); + u64 cap_for_cmd_su = profile->capabilities.effective | CAP_DAC_READ_SEARCH | CAP_SETUID | CAP_SETGID; + memcpy(&newcreds->cap_effective, &cap_for_cmd_su, sizeof(newcreds->cap_effective)); + memcpy(&newcreds->cap_permitted, &profile->capabilities.effective, sizeof(newcreds->cap_permitted)); + memcpy(&newcreds->cap_bset, &profile->capabilities.effective, sizeof(newcreds->cap_bset)); - setup_groups(profile, newcreds); - task_lock(target_task); + setup_groups(profile, newcreds); + task_lock(target_task); - const struct cred *old_creds = get_task_cred(target_task); + const struct cred *old_creds = get_task_cred(target_task); - rcu_assign_pointer(target_task->real_cred, newcreds); - rcu_assign_pointer(target_task->cred, get_cred(newcreds)); - task_unlock(target_task); + rcu_assign_pointer(target_task->real_cred, newcreds); + rcu_assign_pointer(target_task->cred, get_cred(newcreds)); + task_unlock(target_task); - if (target_task->sighand) { - spin_lock_irq(&target_task->sighand->siglock); - disable_seccomp_for_task(target_task); - spin_unlock_irq(&target_task->sighand->siglock); - } + if (target_task->sighand) { + spin_lock_irq(&target_task->sighand->siglock); + disable_seccomp_for_task(target_task); + spin_unlock_irq(&target_task->sighand->siglock); + } - setup_selinux(profile->selinux_domain); - put_cred(old_creds); - wake_up_process(target_task); + setup_selinux(profile->selinux_domain); + put_cred(old_creds); + wake_up_process(target_task); - if (target_task->signal->tty) { - struct inode *inode = target_task->signal->tty->driver_data; - if (inode && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) { - __ksu_handle_devpts(inode); - } - } + if (target_task->signal->tty) { + struct inode *inode = target_task->signal->tty->driver_data; + if (inode && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) { + __ksu_handle_devpts(inode); + } + } - put_task_struct(target_task); + put_task_struct(target_task); #if __SULOG_GATE - ksu_sulog_report_su_grant(target_uid, "cmd_su", "manual_escalation"); + ksu_sulog_report_su_grant(target_uid, "cmd_su", "manual_escalation"); #endif - pr_info("cmd_su: privilege escalation completed for UID: %d, PID: %d\n", target_uid, target_pid); + pr_info("cmd_su: privilege escalation completed for UID: %d, PID: %d\n", target_uid, target_pid); } #endif -int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry) -{ - if (!current->mm) { - // skip kernel threads - return 0; - } - - if (current_uid().val != 1000) { - // skip non system uid - return 0; - } - - if (!old_dentry || !new_dentry) { - return 0; - } - - // /data/system/packages.list.tmp -> /data/system/packages.list - if (strcmp(new_dentry->d_iname, "packages.list")) { - return 0; - } - - char path[128]; - char *buf = dentry_path_raw(new_dentry, path, sizeof(path)); - if (IS_ERR(buf)) { - pr_err("dentry_path_raw failed.\n"); - return 0; - } - - if (!strstr(buf, "/system/packages.list")) { - return 0; - } - pr_info("renameat: %s -> %s, new path: %s\n", old_dentry->d_iname, - new_dentry->d_iname, buf); - - if (ksu_uid_scanner_enabled) { - ksu_request_userspace_scan(); - } - - track_throne(); - - return 0; -} #ifdef CONFIG_EXT4_FS -static void nuke_ext4_sysfs() { - struct path path; - int err = kern_path("/data/adb/modules", 0, &path); - if (err) { - pr_err("nuke path err: %d\n", err); - return; - } +void nuke_ext4_sysfs(void) +{ + struct path path; + int err = kern_path("/data/adb/modules", 0, &path); + if (err) { + pr_err("nuke path err: %d\n", err); + return; + } - struct super_block* sb = path.dentry->d_inode->i_sb; - const char* name = sb->s_type->name; - if (strcmp(name, "ext4") != 0) { - pr_info("nuke but module aren't mounted\n"); - return; - } + struct super_block *sb = path.dentry->d_inode->i_sb; + const char *name = sb->s_type->name; + if (strcmp(name, "ext4") != 0) { + pr_info("nuke but module aren't mounted\n"); + return; + } - ext4_unregister_sysfs(sb); - path_put(&path); + ext4_unregister_sysfs(sb); + path_put(&path); } #else -static inline void nuke_ext4_sysfs() { } +inline void nuke_ext4_sysfs(void) +{ + +} #endif -static bool is_system_bin_su() +bool is_system_uid(void) { - if (!current->mm || current->in_execve) { - return 0; - } - - // quick af check - return (current->mm->exe_file && !strcmp(current->mm->exe_file->f_path.dentry->d_name.name, "su")); -} - -static bool is_system_uid(void) -{ - if (!current->mm || current->in_execve) { - return 0; - } - - uid_t caller_uid = current_uid().val; - return caller_uid <= 2000; -} - -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); - } - } + if (!current->mm || current->in_execve) { + return 0; + } + + uid_t caller_uid = current_uid().val; + return caller_uid <= 2000; } #if __SULOG_GATE static void sulog_prctl_cmd(uid_t uid, unsigned long cmd) { - const char *name = NULL; + const char *name = NULL; - switch (cmd) { - case CMD_GRANT_ROOT: name = "prctl_grant_root"; break; - case CMD_BECOME_MANAGER: name = "prctl_become_manager"; break; - case CMD_GET_VERSION: name = "prctl_get_version"; break; - case CMD_GET_FULL_VERSION: name = "prctl_get_full_version"; break; - case CMD_SET_SEPOLICY: name = "prctl_set_sepolicy"; break; - case CMD_CHECK_SAFEMODE: name = "prctl_check_safemode"; break; - case CMD_GET_ALLOW_LIST: name = "prctl_get_allow_list"; break; - case CMD_GET_DENY_LIST: name = "prctl_get_deny_list"; break; - case CMD_UID_GRANTED_ROOT: name = "prctl_uid_granted_root"; break; - case CMD_UID_SHOULD_UMOUNT: name = "prctl_uid_should_umount"; break; - case CMD_IS_SU_ENABLED: name = "prctl_is_su_enabled"; break; - case CMD_ENABLE_SU: name = "prctl_enable_su"; break; -#ifdef CONFIG_KPM - case CMD_ENABLE_KPM: name = "prctl_enable_kpm"; break; -#endif - case CMD_HOOK_TYPE: name = "prctl_hook_type"; break; - case CMD_DYNAMIC_MANAGER: name = "prctl_dynamic_manager"; break; - case CMD_GET_MANAGERS: name = "prctl_get_managers"; break; - case CMD_ENABLE_UID_SCANNER: name = "prctl_enable_uid_scanner"; break; - case CMD_REPORT_EVENT: name = "prctl_report_event"; break; - case CMD_SET_APP_PROFILE: name = "prctl_set_app_profile"; break; - case CMD_GET_APP_PROFILE: name = "prctl_get_app_profile"; break; + switch (cmd) { #ifdef CONFIG_KSU_MANUAL_SU - case CMD_MANUAL_SU_REQUEST: name = "prctl_manual_su_request"; break; + case CMD_MANUAL_SU_REQUEST: name = "prctl_manual_su_request"; break; #endif - default: name = "prctl_unknown"; break; - } + default: name = "prctl_unknown"; break; + } - ksu_sulog_report_syscall(uid, NULL, name, NULL); + ksu_sulog_report_syscall(uid, NULL, name, NULL); } #endif int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, - unsigned long arg4, unsigned long arg5) + unsigned long arg4, unsigned long arg5) { - // if success, we modify the arg5 as result! - bool is_manual_su_cmd = false; - u32 *result = (u32 *)arg5; - u32 reply_ok = KERNEL_SU_OPTION; - uid_t current_uid_val = current_uid().val; -if (likely(ksu_is_current_proc_umounted())) { // prevent side channel attack in ksu side - return 0; - } + // if success, we modify the arg5 as result! + u32 *result = (u32 *)arg5; + u32 reply_ok = KERNEL_SU_OPTION; -#ifdef CONFIG_KSU_MANUAL_SU - is_manual_su_cmd = (arg2 == CMD_MANUAL_SU_REQUEST); -#endif + if (likely(ksu_is_current_proc_umounted())) + return 0; // prevent side channel attack in ksu side - // skip this private space support if uid below 100k - if (current_uid_val < 100000) - goto skip_check; - - uid_t manager_uid = ksu_get_manager_uid(); - if (current_uid_val != manager_uid && - current_uid_val % 100000 == manager_uid) { - ksu_set_manager_uid(current_uid_val); - } - -skip_check: - // yes this causes delay, but this keeps the delay consistent, which is what we want - // with a barrier for safety as the compiler might try to do something smart. - DONT_GET_SMART(); - if (!is_allow_su() && !is_system_uid()) - return 0; - - // we move it after uid check here so they cannot - // compare 0xdeadbeef call to a non-0xdeadbeef call - if (KERNEL_SU_OPTION != option) - return 0; - - // just continue old logic - bool from_root = !current_uid().val; - bool from_manager = is_manager(); - + if (KERNEL_SU_OPTION != option) + return 0; + #if __SULOG_GATE - sulog_prctl_cmd(current_uid().val, arg2); + sulog_prctl_cmd(current_uid().val, arg2); #endif - if (!from_root && !from_manager - && !(is_manual_su_cmd ? is_system_uid(): - (is_allow_su() && is_system_bin_su()))) { - // only root or manager can access this interface - return 0; - } + if (!is_system_uid()) { + return 0; + } #ifdef CONFIG_KSU_DEBUG - pr_info("option: 0x%x, cmd: %ld\n", option, arg2); + pr_info("option: 0x%x, cmd: %ld\n", option, arg2); #endif - if (arg2 == CMD_BECOME_MANAGER) { - if (from_manager) { - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("become_manager: prctl reply error\n"); - } - return 0; - } - return 0; - } +#ifdef CONFIG_KPM + if(sukisu_is_kpm_control_code(arg2)) { + int res; - if (arg2 == CMD_GRANT_ROOT) { -#if __SULOG_GATE - bool is_allowed = is_allow_su(); - ksu_sulog_report_permission_check(current_uid().val, current->comm, is_allowed); - if (is_allowed) { -#else - if (is_allow_su()) { + pr_info("KPM: calling before arg2=%d\n", (int) arg2); + + res = sukisu_handle_kpm(arg2, arg3, arg4, arg5); + + return 0; + } #endif - pr_info("allow root for: %d\n", current_uid().val); - escape_to_root(); - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("grant_root: prctl reply error\n"); - } - } - return 0; - } - - // Both root manager and root processes should be allowed to get version - if (arg2 == CMD_GET_VERSION) { - u32 version = KERNEL_SU_VERSION; - if (copy_to_user(arg3, &version, sizeof(version))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - u32 version_flags = 2; -#ifdef MODULE - version_flags |= 0x1; -#endif - if (arg4 && - copy_to_user(arg4, &version_flags, sizeof(version_flags))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - return 0; - } - - // Allow root manager to get full version strings - if (arg2 == CMD_GET_FULL_VERSION) { - char ksu_version_full[KSU_FULL_VERSION_STRING] = {0}; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) - strscpy(ksu_version_full, KSU_VERSION_FULL, KSU_FULL_VERSION_STRING); -#else - strlcpy(ksu_version_full, KSU_VERSION_FULL, KSU_FULL_VERSION_STRING); -#endif - if (copy_to_user((void __user *)arg3, ksu_version_full, KSU_FULL_VERSION_STRING)) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - return -EFAULT; - } - return 0; - } - - // Allow the root manager to configure dynamic manageratures - if (arg2 == CMD_DYNAMIC_MANAGER) { - if (!from_root && !from_manager) { - return 0; - } - - struct dynamic_manager_user_config config; - - if (copy_from_user(&config, (void __user *)arg3, sizeof(config))) { - pr_err("copy dynamic manager config failed\n"); - return 0; - } - - int ret = ksu_handle_dynamic_manager(&config); - - if (ret == 0 && config.operation == DYNAMIC_MANAGER_OP_GET) { - if (copy_to_user((void __user *)arg3, &config, sizeof(config))) { - pr_err("copy dynamic manager config back failed\n"); - return 0; - } - } - - if (ret == 0) { - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("dynamic_manager: prctl reply error\n"); - } - } - return 0; - } - - // Allow root manager to get active managers - if (arg2 == CMD_GET_MANAGERS) { - if (!from_root && !from_manager) { - return 0; - } - - struct manager_list_info manager_info; - int ret = ksu_get_active_managers(&manager_info); - - if (ret == 0) { - if (copy_to_user((void __user *)arg3, &manager_info, sizeof(manager_info))) { - pr_err("copy manager list failed\n"); - return 0; - } - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("get_managers: prctl reply error\n"); - } - } - return 0; - } - - if (arg2 == CMD_REPORT_EVENT) { - if (!from_root) { - return 0; - } - switch (arg3) { - 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(); -#if __SULOG_GATE - ksu_sulog_init(); -#endif - // Initialize UID scanner if enabled - init_uid_scanner(); - // Initializing Dynamic Signatures - ksu_dynamic_manager_init(); - pr_info("Dynamic sign config loaded during post-fs-data\n"); - } - 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; - } - - if (arg2 == CMD_SET_SEPOLICY) { - if (!from_root) { - return 0; - } - if (!handle_sepolicy(arg3, arg4)) { - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("sepolicy: prctl reply error\n"); - } - } - - return 0; - } - - if (arg2 == CMD_CHECK_SAFEMODE) { - if (ksu_is_safe_mode()) { - pr_warn("safemode enabled!\n"); - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("safemode: prctl reply error\n"); - } - } - return 0; - } - - if (arg2 == CMD_GET_ALLOW_LIST || arg2 == CMD_GET_DENY_LIST) { - u32 array[128]; - u32 array_length; - bool success = ksu_get_allow_list(array, &array_length, - arg2 == CMD_GET_ALLOW_LIST); - if (success) { - if (!copy_to_user(arg4, &array_length, - sizeof(array_length)) && - !copy_to_user(arg3, array, - sizeof(u32) * array_length)) { - if (copy_to_user(result, &reply_ok, - sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", - arg2); - } - } else { - pr_err("prctl copy allowlist error\n"); - } - } - return 0; - } - - if (arg2 == CMD_UID_GRANTED_ROOT || arg2 == CMD_UID_SHOULD_UMOUNT) { - uid_t target_uid = (uid_t)arg3; - bool allow = false; - if (arg2 == CMD_UID_GRANTED_ROOT) { - allow = ksu_is_allow_uid(target_uid); - } else if (arg2 == CMD_UID_SHOULD_UMOUNT) { - allow = ksu_uid_should_umount(target_uid); - } else { - pr_err("unknown cmd: %lu\n", arg2); - } - if (!copy_to_user(arg4, &allow, sizeof(allow))) { - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - } else { - pr_err("prctl copy err, cmd: %lu\n", arg2); - } - return 0; - } - - if (arg2 == CMD_ENABLE_SU) { - bool enabled = (arg3 != 0); - if (enabled == ksu_su_compat_enabled) { - pr_info("cmd enable su but no need to change.\n"); - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {// return the reply_ok directly - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - return 0; - } - - if (enabled) { - ksu_sucompat_init(); - } else { - ksu_sucompat_exit(); - } - ksu_su_compat_enabled = enabled; - - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - - return 0; - } - - #ifdef CONFIG_KPM - // ADD: 添加KPM模块控制 - if(sukisu_is_kpm_control_code(arg2)) { - int res; - - pr_info("KPM: calling before arg2=%d\n", (int) arg2); - - res = sukisu_handle_kpm(arg2, arg3, arg4, arg5); - - return 0; - } - #endif - - if (arg2 == CMD_ENABLE_KPM) { - bool KPM_Enabled = IS_ENABLED(CONFIG_KPM); - if (copy_to_user((void __user *)arg3, &KPM_Enabled, sizeof(KPM_Enabled))) - pr_info("KPM: copy_to_user() failed\n"); - return 0; - } - - // Checking hook usage - if (arg2 == CMD_HOOK_TYPE) { - const char *hook_type = "Kprobes"; -#if defined(CONFIG_KSU_TRACEPOINT_HOOK) - hook_type = "Tracepoint"; -#elif defined(CONFIG_KSU_MANUAL_HOOK) - hook_type = "Manual"; -#endif - - size_t len = strlen(hook_type) + 1; - if (copy_to_user((void __user *)arg3, hook_type, len)) { - pr_err("hook_type: copy_to_user failed\n"); - return 0; - } - - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("hook_type: prctl reply error\n"); - } - return 0; - } #ifdef CONFIG_KSU_MANUAL_SU - if (arg2 == CMD_MANUAL_SU_REQUEST) { - struct manual_su_request request; - int su_option = (int)arg3; - - if (copy_from_user(&request, (void __user *)arg4, sizeof(request))) { - pr_err("manual_su: failed to copy request from user\n"); - return 0; - } + if (arg2 == CMD_MANUAL_SU_REQUEST) { + struct manual_su_request request; + int su_option = (int)arg3; + + if (copy_from_user(&request, (void __user *)arg4, sizeof(request))) { + pr_err("manual_su: failed to copy request from user\n"); + return 0; + } - int ret = ksu_handle_manual_su_request(su_option, &request); + int ret = ksu_handle_manual_su_request(su_option, &request); - // Copy back result for token generation - if (ret == 0 && su_option == MANUAL_SU_OP_GENERATE_TOKEN) { - if (copy_to_user((void __user *)arg4, &request, sizeof(request))) { - pr_err("manual_su: failed to copy request back to user\n"); - return 0; - } - } - - if (ret == 0) { - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("manual_su: prctl reply error\n"); - } - } - return 0; - } + // Copy back result for token generation + if (ret == 0 && su_option == MANUAL_SU_OP_GENERATE_TOKEN) { + if (copy_to_user((void __user *)arg4, &request, sizeof(request))) { + pr_err("manual_su: failed to copy request back to user\n"); + return 0; + } + } + + if (ret == 0) { + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("manual_su: prctl reply error\n"); + } + } + return 0; + } #endif - - // all other cmds are for 'root manager' - if (!from_manager) { - return 0; - } - - // we are already manager - if (arg2 == CMD_GET_APP_PROFILE) { - struct app_profile profile; - if (copy_from_user(&profile, arg3, sizeof(profile))) { - pr_err("copy profile failed\n"); - return 0; - } - - bool success = ksu_get_app_profile(&profile); - if (success) { - if (copy_to_user(arg3, &profile, sizeof(profile))) { - pr_err("copy profile failed\n"); - return 0; - } - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - } - return 0; - } - - if (arg2 == CMD_SET_APP_PROFILE) { - struct app_profile profile; - if (copy_from_user(&profile, arg3, sizeof(profile))) { - pr_err("copy profile failed\n"); - return 0; - } - - // todo: validate the params - if (ksu_set_app_profile(&profile, true)) { -#if __SULOG_GATE - ksu_sulog_report_manager_operation("SET_APP_PROFILE", - current_uid().val, profile.current_uid); -#endif - - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - } - return 0; - } - - if (arg2 == CMD_IS_SU_ENABLED) { - if (copy_to_user(arg3, &ksu_su_compat_enabled, - sizeof(ksu_su_compat_enabled))) { - pr_err("copy su compat failed\n"); - return 0; - } - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("prctl reply error, cmd: %lu\n", arg2); - } - return 0; - } - - // UID Scanner control command - if (arg2 == CMD_ENABLE_UID_SCANNER) { - if (arg3 == 0) { - // Get current status - bool status = ksu_uid_scanner_enabled; - if (copy_to_user((void __user *)arg4, &status, sizeof(status))) { - pr_err("uid_scanner: copy status failed\n"); - return 0; - } - } else if (arg3 == 1) { - // Enable/Disable toggle - bool enabled = (arg4 != 0); - - if (enabled == ksu_uid_scanner_enabled) { - pr_info("uid_scanner: no need to change, already %s\n", - enabled ? "enabled" : "disabled"); - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("uid_scanner: prctl reply error\n"); - } - return 0; - } - - if (enabled) { - // Enable UID scanner - int ret = ksu_throne_comm_init(); - if (ret != 0) { - pr_err("uid_scanner: failed to initialize: %d\n", ret); - return 0; - } - pr_info("uid_scanner: enabled\n"); - } else { - // Disable UID scanner - ksu_throne_comm_exit(); - pr_info("uid_scanner: disabled\n"); - } - - ksu_uid_scanner_enabled = enabled; - ksu_throne_comm_save_state(); - } else if (arg3 == 2) { - // Clear environment (force exit) - ksu_throne_comm_exit(); - ksu_uid_scanner_enabled = false; - ksu_throne_comm_save_state(); - pr_info("uid_scanner: environment cleared\n"); - } - - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("uid_scanner: prctl reply error\n"); - } - return 0; - } - - return 0; + return 0; } static bool is_appuid(kuid_t uid) @@ -951,504 +463,421 @@ static bool is_appuid(kuid_t uid) #define FIRST_APPLICATION_UID 10000 #define LAST_APPLICATION_UID 19999 - uid_t appid = uid.val % PER_USER_RANGE; - return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID; + uid_t appid = uid.val % PER_USER_RANGE; + return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID; } static bool should_umount(struct path *path) { - if (!path) { - return false; - } + if (!path) { + return false; + } - if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) { - pr_info("ignore global mnt namespace process: %d\n", - current_uid().val); - return false; - } + if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) { + pr_info("ignore global mnt namespace process: %d\n", + current_uid().val); + return false; + } - if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) { - const char *fstype = path->mnt->mnt_sb->s_type->name; - return strcmp(fstype, "overlay") == 0; - } - return false; + if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) { + const char *fstype = path->mnt->mnt_sb->s_type->name; + return strcmp(fstype, "overlay") == 0; + } + return false; } static void ksu_umount_mnt(struct path *path, int flags) { - int err = path_umount(path, flags); - if (err) { - pr_info("umount %s failed: %d\n", path->dentry->d_iname, err); - } + int err = path_umount(path, flags); + if (err) { + pr_info("umount %s failed: %d\n", path->dentry->d_iname, err); + } } static void try_umount(const char *mnt, bool check_mnt, int flags) { - struct path path; - int err = kern_path(mnt, 0, &path); - if (err) { - return; - } + struct path path; + int err = kern_path(mnt, 0, &path); + if (err) { + return; + } - if (path.dentry != path.mnt->mnt_root) { - // it is not root mountpoint, maybe umounted by others already. - path_put(&path); - return; - } + if (path.dentry != path.mnt->mnt_root) { + // it is not root mountpoint, maybe umounted by others already. + path_put(&path); + return; + } - // we are only interest in some specific mounts - if (check_mnt && !should_umount(&path)) { - path_put(&path); - return; - } + // we are only interest in some specific mounts + if (check_mnt && !should_umount(&path)) { + path_put(&path); + return; + } - ksu_umount_mnt(&path, flags); + ksu_umount_mnt(&path, flags); } int ksu_handle_setuid(struct cred *new, const struct cred *old) { - // this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it! - if (!ksu_module_mounted) { - return 0; - } + if (!new || !old) { + return 0; + } - if (!new || !old) { - return 0; - } + kuid_t new_uid = new->uid; + kuid_t old_uid = old->uid; - kuid_t new_uid = new->uid; - kuid_t old_uid = old->uid; + if (0 != old_uid.val) { + // old process is not root, ignore it. + return 0; + } - if (0 != old_uid.val) { - // old process is not root, ignore it. - return 0; - } + if (!is_appuid(new_uid) || is_unsupported_uid(new_uid.val)) { + // pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid.val); + return 0; + } - if (!is_appuid(new_uid) || is_unsupported_uid(new_uid.val)) { - // pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid.val); - return 0; - } + if (ksu_get_manager_uid() == new_uid.val) { + pr_info("install fd for: %d\n", new_uid.val); - if (ksu_is_allow_uid(new_uid.val)) { - // pr_info("handle setuid ignore allowed application: %d\n", new_uid.val); - return 0; - } + ksu_install_fd(); + spin_lock_irq(¤t->sighand->siglock); + ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot); + spin_unlock_irq(¤t->sighand->siglock); + return 0; + } - if (!ksu_uid_should_umount(new_uid.val)) { - return 0; - } else { + if (ksu_is_allow_uid(new_uid.val)) { + if (current->seccomp.mode == SECCOMP_MODE_FILTER && + current->seccomp.filter) { + spin_lock_irq(¤t->sighand->siglock); + ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot); + spin_unlock_irq(¤t->sighand->siglock); + } + } + + // this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it! + if (!ksu_module_mounted) { + return 0; + } + + if (!ksu_uid_should_umount(new_uid.val)) { + return 0; + } else { #ifdef CONFIG_KSU_DEBUG - pr_info("uid: %d should not umount!\n", current_uid().val); + pr_info("uid: %d should not umount!\n", current_uid().val); #endif - } + } - // check old process's selinux context, if it is not zygote, ignore it! - // because some su apps may setuid to untrusted_app but they are in global mount namespace - // when we umount for such process, that is a disaster! - bool is_zygote_child = is_zygote(old->security); - if (!is_zygote_child) { - pr_info("handle umount ignore non zygote child: %d\n", - current->pid); - return 0; - } + // check old process's selinux context, if it is not zygote, ignore it! + // because some su apps may setuid to untrusted_app but they are in global mount namespace + // when we umount for such process, that is a disaster! + bool is_zygote_child = is_zygote(old->security); + if (!is_zygote_child) { + pr_info("handle umount ignore non zygote child: %d\n", current->pid); + return 0; + } + #if __SULOG_GATE - ksu_sulog_report_syscall(new_uid.val, NULL, "setuid", NULL); + ksu_sulog_report_syscall(new_uid.val, NULL, "setuid", NULL); #endif + #ifdef CONFIG_KSU_DEBUG - // umount the target mnt - pr_info("handle umount for uid: %d, pid: %d\n", new_uid.val, - current->pid); + // umount the target mnt + pr_info("handle umount for uid: %d, pid: %d\n", new_uid.val, current->pid); #endif - // fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and - // filter the mountpoint whose target is `/data/adb` - try_umount("/system", true, 0); - try_umount("/vendor", true, 0); - try_umount("/product", true, 0); - try_umount("/system_ext", true, 0); - try_umount("/data/adb/modules", false, MNT_DETACH); + // fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and + // filter the mountpoint whose target is `/data/adb` + try_umount("/system", true, 0); + try_umount("/vendor", true, 0); + try_umount("/product", true, 0); + try_umount("/system_ext", true, 0); + try_umount("/data/adb/modules", false, MNT_DETACH); - // try umount ksu temp path - try_umount("/debug_ramdisk", false, MNT_DETACH); + // try umount ksu temp path + try_umount("/debug_ramdisk", false, MNT_DETACH); - get_task_struct(current); // delay fix - ksu_set_current_proc_umounted(); - put_task_struct(current); + get_task_struct(current); // delay fix + ksu_set_current_proc_umounted(); + put_task_struct(current); - return 0; + return 0; } -// Init functons +static int ksu_task_fix_setuid_hook(struct cred *new, const struct cred *old, int flags) +{ + ksu_handle_setuid(new, old); + if (original_cap_task_fix_setuid) { + return original_cap_task_fix_setuid(new, old, flags); + } + + return 0; +} + +// Init functons - kprobe hooks + +// 1. Reboot hook for installing fd +static int reboot_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + int magic1 = (int)PT_REGS_PARM1(real_regs); + int magic2 = (int)PT_REGS_PARM2(real_regs); + unsigned long arg4; + + // Check if this is a request to install KSU fd + if (magic1 == KSU_INSTALL_MAGIC1 && magic2 == KSU_INSTALL_MAGIC2) { + int fd = ksu_install_fd(); + pr_info("[%d] install ksu fd: %d\n", current->pid, fd); + + arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs); + if (copy_to_user((int *)arg4, &fd, sizeof(fd))) { + pr_err("install ksu fd reply err\n"); + } + } + + return 0; +} + +static struct kprobe reboot_kp = { + .symbol_name = REBOOT_SYMBOL, + .pre_handler = reboot_handler_pre, +}; + + +// 3. prctl hook for handling ksu prctl commands static int handler_pre(struct kprobe *p, struct pt_regs *regs) { - struct pt_regs *real_regs = PT_REAL_REGS(regs); - int option = (int)PT_REGS_PARM1(real_regs); - unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs); - unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs); - // PRCTL_SYMBOL is the arch-specificed one, which receive raw pt_regs from syscall - unsigned long arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs); - unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs); + struct pt_regs *real_regs = PT_REAL_REGS(regs); + int option = (int)PT_REGS_PARM1(real_regs); + unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs); + unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs); + // PRCTL_SYMBOL is the arch-specificed one, which receive raw pt_regs from syscall + unsigned long arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs); + unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs); - return ksu_handle_prctl(option, arg2, arg3, arg4, arg5); + return ksu_handle_prctl(option, arg2, arg3, arg4, arg5); } static struct kprobe prctl_kp = { - .symbol_name = PRCTL_SYMBOL, - .pre_handler = handler_pre, + .symbol_name = PRCTL_SYMBOL, + .pre_handler = handler_pre, }; -static int renameat_handler_pre(struct kprobe *p, struct pt_regs *regs) +// 4.inode_permission hook for handling devpts +static int ksu_inode_permission_handler_pre(struct kprobe *p, struct pt_regs *regs) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0) - // https://elixir.bootlin.com/linux/v5.12-rc1/source/include/linux/fs.h - struct renamedata *rd = PT_REGS_PARM1(regs); - struct dentry *old_entry = rd->old_dentry; - struct dentry *new_entry = rd->new_dentry; -#else - struct dentry *old_entry = (struct dentry *)PT_REGS_PARM2(regs); - struct dentry *new_entry = (struct dentry *)PT_REGS_CCALL_PARM4(regs); -#endif + struct inode *inode = (struct inode *)PT_REGS_PARM1(regs); - return ksu_handle_rename(old_entry, new_entry); + if (inode && inode->i_sb && unlikely(inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) { + // pr_info("%s: handling devpts for: %s \n", __func__, current->comm); + __ksu_handle_devpts(inode); + } + + return 0; } -static struct kprobe renameat_kp = { - .symbol_name = "vfs_rename", - .pre_handler = renameat_handler_pre, +static struct kprobe ksu_inode_permission_kp = { + .symbol_name = INODE_PERMISSION_SYMBOL, + .pre_handler = ksu_inode_permission_handler_pre, }; + +// 5. bprm_check_security hook for handling ksud compatibility +static int ksu_bprm_check_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct linux_binprm *bprm = (struct linux_binprm *)PT_REGS_PARM1(regs); + char *filename = (char *)bprm->filename; + + if (likely(!ksu_execveat_hook)) + return 0; + +#ifdef CONFIG_COMPAT + static bool compat_check_done __read_mostly = false; + if (unlikely(!compat_check_done) && unlikely(!strcmp(filename, "/data/adb/ksud")) + && !memcmp(bprm->buf, "\x7f\x45\x4c\x46", 4)) { + if (bprm->buf[4] == 0x01) + ksu_is_compat = true; + + pr_info("%s: %s ELF magic found! ksu_is_compat: %d \n", __func__, filename, ksu_is_compat); + compat_check_done = true; + } +#endif + + ksu_handle_pre_ksud(filename); + +#ifdef CONFIG_KSU_MANUAL_SU + ksu_try_escalate_for_uid(current_uid().val); +#endif + + return 0; +} + +static struct kprobe ksu_bprm_check_kp = { + .symbol_name = BPRM_CHECK_SECURITY_SYMBOL, + .pre_handler = ksu_bprm_check_handler_pre, +}; + +#ifdef CONFIG_KSU_MANUAL_SU +// 6. task_alloc hook for handling manual su escalation +static int ksu_task_alloc_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + struct task_struct *task = (struct task_struct *)PT_REGS_PARM1(regs); + + ksu_try_escalate_for_uid(task_uid(task).val); + return 0; +} + +static struct kprobe ksu_task_alloc_kp = { + .symbol_name = TASK_ALLOC_SYMBOL, + .pre_handler = ksu_task_alloc_handler_pre, +}; +#endif + +#define GET_SYMBOL_ADDR(sym) \ + ({ \ + void *addr = kallsyms_lookup_name(#sym ".cfi_jt"); \ + if (!addr) { \ + addr = kallsyms_lookup_name(#sym); \ + } \ + addr; \ + }) + + +void ksu_lsm_hook_init(void) +{ + void *cap_setuid = GET_SYMBOL_ADDR(cap_task_fix_setuid); + if (!cap_setuid) { + pr_err("Failed to find cap_task_fix_setuid symbol\n"); + return; + } + + + struct security_hook_list *hp; + hlist_for_each_entry(hp, &security_hook_heads.task_fix_setuid, list) { + if (hp->hook.task_fix_setuid == cap_setuid) { + pr_info("Found original task_fix_setuid LSM hook, patching it\n"); + + original_cap_task_fix_setuid = cap_setuid; + ksu_hooked_security_hook = hp; + + u64 page_addr = (u64)&hp->hook.task_fix_setuid & PAGE_MASK; + set_memory_rw(page_addr, 1); + hp->hook.task_fix_setuid = ksu_task_fix_setuid_hook; + set_memory_ro(page_addr, 1); + + pr_info("LSM hook patched successfully\n"); + break; + } + } + + smp_mb(); +} + +void ksu_lsm_hook_exit(void) +{ + if (!ksu_hooked_security_hook || !original_cap_task_fix_setuid) { + pr_info("No LSM hook to restore\n"); + return; + } + + pr_info("Restoring original task_fix_setuid LSM hook\n"); + + u64 page_addr = (u64)&ksu_hooked_security_hook->hook.task_fix_setuid & PAGE_MASK; + set_memory_rw(page_addr, 1); + ksu_hooked_security_hook->hook.task_fix_setuid = original_cap_task_fix_setuid; + set_memory_ro(page_addr, 1); + + smp_mb(); + + original_cap_task_fix_setuid = NULL; + ksu_hooked_security_hook = NULL; + + pr_info("LSM hook restored successfully\n"); +} + __maybe_unused int ksu_kprobe_init(void) { - int rc = 0; - rc = register_kprobe(&prctl_kp); + int rc = 0; - if (rc) { - pr_info("prctl kprobe failed: %d.\n", rc); - return rc; - } + // Register reboot kprobe + rc = register_kprobe(&reboot_kp); + if (rc) { + pr_err("reboot kprobe failed: %d\n", rc); + } else { + pr_info("reboot kprobe registered successfully\n"); + } - rc = register_kprobe(&renameat_kp); - pr_info("renameat kp: %d\n", rc); + // Register prctl kprobe + rc = register_kprobe(&prctl_kp); + if (rc) { + pr_info("prctl kprobe failed: %d.\n", rc); + } else { + pr_info("prctl kprobe registered successfully.\n"); + } - return rc; + // Register inode_permission kprobe + rc = register_kprobe(&ksu_inode_permission_kp); + if (rc) { + pr_err("inode_permission kprobe failed: %d\n", rc); + } else { + pr_info("inode_permission kprobe registered successfully\n"); + } + + // Register bprm_check_security kprobe + rc = register_kprobe(&ksu_bprm_check_kp); + if (rc) { + pr_err("bprm_check_security kprobe failed: %d\n", rc); + } else { + pr_info("bprm_check_security kprobe registered successfully\n"); + } + +#ifdef CONFIG_KSU_MANUAL_SU + // Register task_alloc kprobe + rc = register_kprobe(&ksu_task_alloc_kp); + if (rc) { + pr_err("task_alloc kprobe failed: %d\n", rc); + } else { + pr_info("task_alloc kprobe registered successfully\n"); + } +#endif + + return 0; } __maybe_unused int ksu_kprobe_exit(void) { - unregister_kprobe(&prctl_kp); - unregister_kprobe(&renameat_kp); - return 0; -} - -#ifndef DEVPTS_SUPER_MAGIC -#define DEVPTS_SUPER_MAGIC 0x1cd1 -#endif - -extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c - -int ksu_inode_permission(struct inode *inode, int mask) -{ - if (inode && inode->i_sb - && unlikely(inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) { - //pr_info("%s: handling devpts for: %s \n", __func__, current->comm); - __ksu_handle_devpts(inode); - } - return 0; -} - + unregister_kprobe(&reboot_kp); + unregister_kprobe(&prctl_kp); + unregister_kprobe(&ksu_inode_permission_kp); + unregister_kprobe(&ksu_bprm_check_kp); #ifdef CONFIG_KSU_MANUAL_SU -static void ksu_try_escalate_for_uid(uid_t uid) -{ - if (!is_pending_root(uid)) - return; - - pr_info("pending_root: UID=%d temporarily allowed\n", uid); - remove_pending_root(uid); -} + unregister_kprobe(&ksu_task_alloc_kp); #endif - -#ifdef CONFIG_COMPAT -bool ksu_is_compat __read_mostly = false; -#endif - -int ksu_bprm_check(struct linux_binprm *bprm) -{ - char *filename = (char *)bprm->filename; - - if (likely(!ksu_execveat_hook)) - return 0; - -#ifdef CONFIG_COMPAT - static bool compat_check_done __read_mostly = false; - if ( unlikely(!compat_check_done) && unlikely(!strcmp(filename, "/data/adb/ksud")) - && !memcmp(bprm->buf, "\x7f\x45\x4c\x46", 4) ) { - if (bprm->buf[4] == 0x01 ) - ksu_is_compat = true; - - pr_info("%s: %s ELF magic found! ksu_is_compat: %d \n", __func__, filename, ksu_is_compat); - compat_check_done = true; - } -#endif - - ksu_handle_pre_ksud(filename); - -#ifdef CONFIG_KSU_MANUAL_SU - ksu_try_escalate_for_uid(current_uid().val); -#endif - - return 0; - + return 0; } -static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3, - unsigned long arg4, unsigned long arg5) -{ - ksu_handle_prctl(option, arg2, arg3, arg4, arg5); - return -ENOSYS; -} - -#ifdef CONFIG_KSU_MANUAL_SU -static int ksu_task_alloc(struct task_struct *task, - unsigned long clone_flags) -{ - ksu_try_escalate_for_uid(task_uid(task).val); - return 0; -} -#endif - -static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry, - struct inode *new_inode, struct dentry *new_dentry) -{ - return ksu_handle_rename(old_dentry, new_dentry); -} - -static int ksu_task_fix_setuid(struct cred *new, const struct cred *old, - int flags) -{ - return ksu_handle_setuid(new, old); -} - -#ifndef MODULE -static struct security_hook_list ksu_hooks[] = { - LSM_HOOK_INIT(task_prctl, ksu_task_prctl), - LSM_HOOK_INIT(inode_rename, ksu_inode_rename), - LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid), - LSM_HOOK_INIT(inode_permission, ksu_inode_permission), -#ifdef CONFIG_KSU_MANUAL_SU - LSM_HOOK_INIT(task_alloc, ksu_task_alloc), -#endif -#ifndef CONFIG_KSU_KPROBES_HOOK - LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check), -#endif -}; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) -const struct lsm_id ksu_lsmid = { - .name = "ksu", - .id = 912, -}; -#endif - -void __init ksu_lsm_hook_init(void) -{ - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) - // https://elixir.bootlin.com/linux/v6.8/source/include/linux/lsm_hooks.h#L120 - security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), &ksu_lsmid); -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) - security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu"); -#else - // https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892 - security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks)); -#endif -} - -#else -static int override_security_head(void *head, const void *new_head, size_t len) -{ - unsigned long base = (unsigned long)head & PAGE_MASK; - unsigned long offset = offset_in_page(head); - - // this is impossible for our case because the page alignment - // but be careful for other cases! - BUG_ON(offset + len > PAGE_SIZE); - struct page *page = phys_to_page(__pa(base)); - if (!page) { - return -EFAULT; - } - - void *addr = vmap(&page, 1, VM_MAP, PAGE_KERNEL); - if (!addr) { - return -ENOMEM; - } - local_irq_disable(); - memcpy(addr + offset, new_head, len); - local_irq_enable(); - vunmap(addr); - return 0; -} - -static void free_security_hook_list(struct hlist_head *head) -{ - struct hlist_node *temp; - struct security_hook_list *entry; - - if (!head) - return; - - hlist_for_each_entry_safe (entry, temp, head, list) { - hlist_del(&entry->list); - kfree(entry); - } - - kfree(head); -} - -struct hlist_head *copy_security_hlist(struct hlist_head *orig) -{ - struct hlist_head *new_head = kmalloc(sizeof(*new_head), GFP_KERNEL); - if (!new_head) - return NULL; - - INIT_HLIST_HEAD(new_head); - - struct security_hook_list *entry; - struct security_hook_list *new_entry; - - hlist_for_each_entry (entry, orig, list) { - new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); - if (!new_entry) { - free_security_hook_list(new_head); - return NULL; - } - - *new_entry = *entry; - - hlist_add_tail_rcu(&new_entry->list, new_head); - } - - return new_head; -} - -#define LSM_SEARCH_MAX 180 // This should be enough to iterate -static void *find_head_addr(void *security_ptr, int *index) -{ - if (!security_ptr) { - return NULL; - } - struct hlist_head *head_start = - (struct hlist_head *)&security_hook_heads; - - for (int i = 0; i < LSM_SEARCH_MAX; i++) { - struct hlist_head *head = head_start + i; - struct security_hook_list *pos; - hlist_for_each_entry (pos, head, list) { - if (pos->hook.capget == security_ptr) { - if (index) { - *index = i; - } - return head; - } - } - } - - return NULL; -} - -#define GET_SYMBOL_ADDR(sym) \ - ({ \ - void *addr = kallsyms_lookup_name(#sym ".cfi_jt"); \ - if (!addr) { \ - addr = kallsyms_lookup_name(#sym); \ - } \ - addr; \ - }) - -#define KSU_LSM_HOOK_HACK_INIT(head_ptr, name, func) \ - do { \ - static struct security_hook_list hook = { \ - .hook = { .name = func } \ - }; \ - hook.head = head_ptr; \ - hook.lsm = "ksu"; \ - struct hlist_head *new_head = copy_security_hlist(hook.head); \ - if (!new_head) { \ - pr_err("Failed to copy security list: %s\n", #name); \ - break; \ - } \ - hlist_add_tail_rcu(&hook.list, new_head); \ - if (override_security_head(hook.head, new_head, \ - sizeof(*new_head))) { \ - free_security_hook_list(new_head); \ - pr_err("Failed to hack lsm for: %s\n", #name); \ - } \ - } while (0) - -void __init ksu_lsm_hook_init(void) -{ - void *cap_prctl = GET_SYMBOL_ADDR(cap_task_prctl); - void *prctl_head = find_head_addr(cap_prctl, NULL); - if (prctl_head) { - if (prctl_head != &security_hook_heads.task_prctl) { - pr_warn("prctl's address has shifted!\n"); - } - KSU_LSM_HOOK_HACK_INIT(prctl_head, task_prctl, ksu_task_prctl); - } else { - pr_warn("Failed to find task_prctl!\n"); - } - - int inode_killpriv_index = -1; - void *cap_killpriv = GET_SYMBOL_ADDR(cap_inode_killpriv); - find_head_addr(cap_killpriv, &inode_killpriv_index); - if (inode_killpriv_index < 0) { - pr_warn("Failed to find inode_rename, use kprobe instead!\n"); - register_kprobe(&renameat_kp); - } else { - int inode_rename_index = inode_killpriv_index + - &security_hook_heads.inode_rename - - &security_hook_heads.inode_killpriv; - struct hlist_head *head_start = - (struct hlist_head *)&security_hook_heads; - void *inode_rename_head = head_start + inode_rename_index; - if (inode_rename_head != &security_hook_heads.inode_rename) { - pr_warn("inode_rename's address has shifted!\n"); - } - KSU_LSM_HOOK_HACK_INIT(inode_rename_head, inode_rename, - ksu_inode_rename); - } - void *cap_setuid = GET_SYMBOL_ADDR(cap_task_fix_setuid); - void *setuid_head = find_head_addr(cap_setuid, NULL); - if (setuid_head) { - if (setuid_head != &security_hook_heads.task_fix_setuid) { - pr_warn("setuid's address has shifted!\n"); - } - KSU_LSM_HOOK_HACK_INIT(setuid_head, task_fix_setuid, - ksu_task_fix_setuid); - } else { - pr_warn("Failed to find task_fix_setuid!\n"); - } - smp_mb(); -} -#endif - void __init ksu_core_init(void) { - ksu_lsm_hook_init(); + ksu_lsm_hook_init(); +#ifdef CONFIG_KPROBES + int rc = ksu_kprobe_init(); + if (rc) { + pr_err("ksu_kprobe_init failed: %d\n", rc); + } +#endif } void ksu_core_exit(void) { - ksu_uid_exit(); - ksu_throne_comm_exit(); - + ksu_uid_exit(); + ksu_throne_comm_exit(); #if __SULOG_GATE - ksu_sulog_exit(); + ksu_sulog_exit(); #endif - -#ifdef CONFIG_KPROBE - pr_info("ksu_core_kprobe_exit\n"); - // we dont use this now - // ksu_kprobe_exit(); + pr_info("ksu_core_exit\n"); + ksu_lsm_hook_exit(); +#ifdef CONFIG_KPROBES + ksu_kprobe_exit(); #endif -} +} \ No newline at end of file diff --git a/kernel/core_hook.h b/kernel/core_hook.h index 5f121ae3..ce926b42 100644 --- a/kernel/core_hook.h +++ b/kernel/core_hook.h @@ -11,11 +11,11 @@ void ksu_core_exit(void); #define KSU_PROC_UMOUNT 50 static inline bool ksu_is_current_proc_umounted(void) { - return test_ti_thread_flag(¤t->thread_info, KSU_PROC_UMOUNT); + return test_ti_thread_flag(¤t->thread_info, KSU_PROC_UMOUNT); } static inline void ksu_set_current_proc_umounted(void) { - set_ti_thread_flag(¤t->thread_info, KSU_PROC_UMOUNT); + set_ti_thread_flag(¤t->thread_info, KSU_PROC_UMOUNT); } #endif diff --git a/kernel/include/ksu_hook.h b/kernel/include/ksu_hook.h index ea0b04d3..fb0eebc8 100644 --- a/kernel/include/ksu_hook.h +++ b/kernel/include/ksu_hook.h @@ -7,22 +7,22 @@ // For sucompat 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); // For ksud 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 int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, - void *envp, int *flags); + void *envp, int *flags); // For volume button int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, - int *value); + int *value); #endif diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c index c49ac125..c8d3f4b9 100644 --- a/kernel/kernel_compat.c +++ b/kernel/kernel_compat.c @@ -3,6 +3,8 @@ #include #include #include +#include +#include #include "klog.h" // IWYU pragma: keep #include "kernel_compat.h" @@ -10,20 +12,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 struct ksu_ns_fs_saved { - struct nsproxy *ns; - struct fs_struct *fs; + struct nsproxy *ns; + struct fs_struct *fs; }; static void ksu_save_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved) { - ns_fs_saved->ns = current->nsproxy; - ns_fs_saved->fs = current->fs; + ns_fs_saved->ns = current->nsproxy; + ns_fs_saved->fs = current->fs; } static void ksu_load_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved) { - current->nsproxy = ns_fs_saved->ns; - current->fs = ns_fs_saved->fs; + current->nsproxy = ns_fs_saved->ns; + current->fs = ns_fs_saved->fs; } static bool android_context_saved_checked = false; @@ -32,64 +34,119 @@ static struct ksu_ns_fs_saved android_context_saved; void ksu_android_ns_fs_check() { - if (android_context_saved_checked) - return; - android_context_saved_checked = true; - task_lock(current); - if (current->nsproxy && current->fs && - current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) { - android_context_saved_enabled = true; + if (android_context_saved_checked) + return; + android_context_saved_checked = true; + task_lock(current); + if (current->nsproxy && current->fs && + current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) { + android_context_saved_enabled = true; #ifdef CONFIG_KSU_DEBUG - pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n", - current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns); + pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n", + current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns); #endif - ksu_save_ns_fs(&android_context_saved); - } else { - pr_info("android context saved disabled\n"); - } - task_unlock(current); + ksu_save_ns_fs(&android_context_saved); + } else { + pr_info("android context saved disabled\n"); + } + task_unlock(current); } struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode) { - // switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns - struct ksu_ns_fs_saved saved; - if (android_context_saved_enabled) { + // switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns + struct ksu_ns_fs_saved saved; + if (android_context_saved_enabled) { #ifdef CONFIG_KSU_DEBUG - pr_info("start switch current nsproxy and fs to android context\n"); + pr_info("start switch current nsproxy and fs to android context\n"); #endif - task_lock(current); - ksu_save_ns_fs(&saved); - ksu_load_ns_fs(&android_context_saved); - task_unlock(current); - } - struct file *fp = filp_open(filename, flags, mode); - if (android_context_saved_enabled) { - task_lock(current); - ksu_load_ns_fs(&saved); - task_unlock(current); + task_lock(current); + ksu_save_ns_fs(&saved); + ksu_load_ns_fs(&android_context_saved); + task_unlock(current); + } + struct file *fp = filp_open(filename, flags, mode); + if (android_context_saved_enabled) { + task_lock(current); + ksu_load_ns_fs(&saved); + task_unlock(current); #ifdef CONFIG_KSU_DEBUG - pr_info("switch current nsproxy and fs back to saved successfully\n"); + pr_info("switch current nsproxy and fs back to saved successfully\n"); #endif - } - return fp; + } + return fp; } ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, - loff_t *pos) + loff_t *pos) { - return kernel_read(p, buf, count, pos); + return kernel_read(p, buf, count, pos); } ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, - loff_t *pos) + loff_t *pos) { - return kernel_write(p, buf, count, pos); + return kernel_write(p, buf, count, pos); } 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); } +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 +} diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h index 11ee9023..ef57c291 100644 --- a/kernel/kernel_compat.h +++ b/kernel/kernel_compat.h @@ -48,7 +48,7 @@ static inline __maybe_unused size_t list_count_nodes(const struct list_head *hea list_for_each(pos, head) count++; - return count; + return count; } #endif @@ -58,25 +58,25 @@ static inline __maybe_unused size_t list_count_nodes(const struct list_head *hea * From ss/ebitmap.h */ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \ - (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \ - (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \ - (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)) + (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \ + (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \ + (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)) #ifdef HISI_SELINUX_EBITMAP_RO #define CONFIG_IS_HW_HISI #endif #endif extern long ksu_strncpy_from_user_nofault(char *dst, - const void __user *unsafe_addr, - long count); + const void __user *unsafe_addr, + long count); extern void ksu_android_ns_fs_check(); 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, - loff_t *pos); + loff_t *pos); 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); /* * ksu_copy_from_user_retry * try nofault copy first, if it fails, try with plain @@ -84,14 +84,17 @@ extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, * 0 = success */ 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 = copy_from_user_nofault(to, from, count); - if (likely(!ret)) - return ret; + long ret = copy_from_user_nofault(to, from, count); + if (likely(!ret)) + return ret; - // we faulted! fallback to slow path - return copy_from_user(to, from, count); + // we faulted! fallback to slow path + return copy_from_user(to, from, count); } +extern void ksu_seccomp_clear_cache(struct seccomp_filter *filter, int nr); +extern void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr); + #endif diff --git a/kernel/ksu.c b/kernel/ksu.c index d4c87df4..0f657826 100644 --- a/kernel/ksu.c +++ b/kernel/ksu.c @@ -16,11 +16,11 @@ static struct workqueue_struct *ksu_workqueue; 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, - void *argv, void *envp, int *flags); + void *argv, void *envp, int *flags); extern void ksu_sucompat_init(); extern void ksu_sucompat_exit(); @@ -34,27 +34,27 @@ extern void ksu_trace_unregister(); int __init kernelsu_init(void) { #ifdef CONFIG_KSU_DEBUG - pr_alert("*************************************************************"); - pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); - pr_alert("** **"); - pr_alert("** You are running KernelSU in DEBUG mode **"); - pr_alert("** **"); - pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); - pr_alert("*************************************************************"); + pr_alert("*************************************************************"); + pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); + pr_alert("** **"); + pr_alert("** You are running KernelSU in DEBUG mode **"); + pr_alert("** **"); + pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); + pr_alert("*************************************************************"); #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(); #ifdef CONFIG_KSU_KPROBES_HOOK - ksu_sucompat_init(); - ksu_ksud_init(); + ksu_sucompat_init(); + ksu_ksud_init(); #else - pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html"); + pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html"); #endif #ifdef CONFIG_KSU_TRACEPOINT_HOOK @@ -63,30 +63,32 @@ int __init kernelsu_init(void) #ifdef MODULE #ifndef CONFIG_KSU_DEBUG - kobject_del(&THIS_MODULE->mkobj.kobj); + kobject_del(&THIS_MODULE->mkobj.kobj); #endif #endif - return 0; + return 0; } void kernelsu_exit(void) { - 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 - ksu_ksud_exit(); - ksu_sucompat_exit(); + ksu_ksud_exit(); + ksu_sucompat_exit(); #endif #ifdef CONFIG_KSU_TRACEPOINT_HOOK ksu_trace_unregister(); #endif - ksu_core_exit(); + ksu_core_exit(); } module_init(kernelsu_init); diff --git a/kernel/ksu.h b/kernel/ksu.h index 72d60b29..f9d93d4a 100644 --- a/kernel/ksu.h +++ b/kernel/ksu.h @@ -5,37 +5,14 @@ #include #define KERNEL_SU_VERSION KSU_VERSION -#define KERNEL_SU_OPTION 0xDEADBEEF +#define KERNEL_SU_OPTION 0xBADC0DE -#define CMD_GRANT_ROOT 0 -#define CMD_BECOME_MANAGER 1 -#define CMD_GET_VERSION 2 -#define CMD_ALLOW_SU 3 -#define CMD_DENY_SU 4 -#define CMD_GET_ALLOW_LIST 5 -#define CMD_GET_DENY_LIST 6 -#define CMD_REPORT_EVENT 7 -#define CMD_SET_SEPOLICY 8 -#define CMD_CHECK_SAFEMODE 9 -#define CMD_GET_APP_PROFILE 10 -#define CMD_SET_APP_PROFILE 11 -#define CMD_UID_GRANTED_ROOT 12 -#define CMD_UID_SHOULD_UMOUNT 13 -#define CMD_IS_SU_ENABLED 14 -#define CMD_ENABLE_SU 15 +extern bool ksu_uid_scanner_enabled; #ifdef CONFIG_KSU_MANUAL_SU #define CMD_MANUAL_SU_REQUEST 50 #endif -#define CMD_GET_FULL_VERSION 0xC0FFEE1A - -#define CMD_ENABLE_KPM 100 -#define CMD_HOOK_TYPE 101 -#define CMD_DYNAMIC_MANAGER 103 -#define CMD_GET_MANAGERS 104 -#define CMD_ENABLE_UID_SCANNER 105 - #define EVENT_POST_FS_DATA 1 #define EVENT_BOOT_COMPLETED 2 #define EVENT_MODULE_MOUNTED 3 @@ -56,6 +33,10 @@ #define DYNAMIC_MANAGER_OP_GET 1 #define DYNAMIC_MANAGER_OP_CLEAR 2 +#define UID_SCANNER_OP_GET_STATUS 0 +#define UID_SCANNER_OP_TOGGLE 1 +#define UID_SCANNER_OP_CLEAR_ENV 2 + struct dynamic_manager_user_config { unsigned int operation; unsigned int size; @@ -71,67 +52,67 @@ struct manager_list_info { }; struct root_profile { - int32_t uid; - int32_t gid; + int32_t uid; + int32_t gid; - int32_t groups_count; - int32_t groups[KSU_MAX_GROUPS]; + int32_t groups_count; + int32_t groups[KSU_MAX_GROUPS]; - // kernel_cap_t is u32[2] for capabilities v3 - struct { - u64 effective; - u64 permitted; - u64 inheritable; - } capabilities; + // kernel_cap_t is u32[2] for capabilities v3 + struct { + u64 effective; + u64 permitted; + u64 inheritable; + } capabilities; - char selinux_domain[KSU_SELINUX_DOMAIN]; + char selinux_domain[KSU_SELINUX_DOMAIN]; - int32_t namespaces; + int32_t namespaces; }; struct non_root_profile { - bool umount_modules; + bool umount_modules; }; struct app_profile { - // It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this. - u32 version; + // It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this. + u32 version; - // this is usually the package of the app, but can be other value for special apps - char key[KSU_MAX_PACKAGE_NAME]; - int32_t current_uid; - bool allow_su; + // this is usually the package of the app, but can be other value for special apps + char key[KSU_MAX_PACKAGE_NAME]; + int32_t current_uid; + bool allow_su; - union { - struct { - bool use_default; - char template_name[KSU_MAX_PACKAGE_NAME]; + union { + struct { + bool use_default; + char template_name[KSU_MAX_PACKAGE_NAME]; - struct root_profile profile; - } rp_config; + struct root_profile profile; + } rp_config; - struct { - bool use_default; + struct { + bool use_default; - struct non_root_profile profile; - } nrp_config; - }; + struct non_root_profile profile; + } nrp_config; + }; }; bool ksu_queue_work(struct work_struct *work); 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) { - size_t slen = strlen(s); - size_t tlen = strlen(t); - if (tlen > slen) - return 1; - return strcmp(s + slen - tlen, t); + size_t slen = strlen(s); + size_t tlen = strlen(t); + if (tlen > slen) + return 1; + return strcmp(s + slen - tlen, t); } #endif diff --git a/kernel/ksu_trace.c b/kernel/ksu_trace.c index 9276b752..e49c0a00 100644 --- a/kernel/ksu_trace.c +++ b/kernel/ksu_trace.c @@ -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 *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, @@ -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, size_t *count_ptr) { - if (unlikely(ksu_vfs_read_hook)) - ksu_handle_sys_read(fd, buf_ptr, count_ptr); + if (unlikely(ksu_vfs_read_hook)) + ksu_handle_sys_read(fd, buf_ptr, count_ptr); } 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, int *value) { - if (unlikely(ksu_input_hook)) - ksu_handle_input_handle_event(type, code, value); + if (unlikely(ksu_input_hook)) + ksu_handle_input_handle_event(type, code, value); } // end tracepoint callback functions diff --git a/kernel/ksu_trace.h b/kernel/ksu_trace.h index 06159b3c..77425638 100644 --- a/kernel/ksu_trace.h +++ b/kernel/ksu_trace.h @@ -8,24 +8,24 @@ #include DECLARE_TRACE(ksu_trace_execveat_hook, - TP_PROTO(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags), - TP_ARGS(fd, filename_ptr, argv, envp, flags)); + TP_PROTO(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags), + TP_ARGS(fd, filename_ptr, argv, envp, flags)); DECLARE_TRACE(ksu_trace_faccessat_hook, - TP_PROTO(int *dfd, const char __user **filename_user, int *mode, int *flags), - TP_ARGS(dfd, filename_user, mode, flags)); + TP_PROTO(int *dfd, const char __user **filename_user, int *mode, int *flags), + TP_ARGS(dfd, filename_user, mode, flags)); DECLARE_TRACE(ksu_trace_sys_read_hook, - TP_PROTO(unsigned int fd, char __user **buf_ptr, size_t *count_ptr), - TP_ARGS(fd, buf_ptr, count_ptr)); + TP_PROTO(unsigned int fd, char __user **buf_ptr, size_t *count_ptr), + TP_ARGS(fd, buf_ptr, count_ptr)); DECLARE_TRACE(ksu_trace_stat_hook, - TP_PROTO(int *dfd, const char __user **filename_user, int *flags), - TP_ARGS(dfd, filename_user, flags)); + TP_PROTO(int *dfd, const char __user **filename_user, int *flags), + TP_ARGS(dfd, filename_user, flags)); DECLARE_TRACE(ksu_trace_input_hook, - TP_PROTO(unsigned int *type, unsigned int *code, int *value), - TP_ARGS(type, code, value)); + TP_PROTO(unsigned int *type, unsigned int *code, int *value), + TP_ARGS(type, code, value)); #endif /* _KSU_TRACE_H */ diff --git a/kernel/ksud.c b/kernel/ksud.c index f121b623..fe153900 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -1,3 +1,4 @@ +#include "manager.h" #include #include #include @@ -22,27 +23,27 @@ static const char KERNEL_SU_RC[] = - "\n" + "\n" - "on post-fs-data\n" - " start logd\n" - // We should wait for the post-fs-data finish - " exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n" - "\n" + "on post-fs-data\n" + " start logd\n" + // We should wait for the post-fs-data finish + " exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n" + "\n" - "on nonencrypted\n" - " exec u:r:su:s0 root -- " KSUD_PATH " services\n" - "\n" + "on nonencrypted\n" + " exec u:r:su:s0 root -- " KSUD_PATH " services\n" + "\n" - "on property:vold.decrypt=trigger_restart_framework\n" - " exec u:r:su:s0 root -- " KSUD_PATH " services\n" - "\n" + "on property:vold.decrypt=trigger_restart_framework\n" + " exec u:r:su:s0 root -- " KSUD_PATH " services\n" + "\n" - "on property:sys.boot_completed=1\n" - " exec u:r:su:s0 root -- " KSUD_PATH " boot-completed\n" - "\n" - - "\n"; + "on property:sys.boot_completed=1\n" + " exec u:r:su:s0 root -- " KSUD_PATH " boot-completed\n" + "\n" + + "\n"; static void stop_vfs_read_hook(); static void stop_execve_hook(); @@ -65,21 +66,22 @@ static bool is_boot_phase = true; void on_post_fs_data(void) { - static bool done = false; - if (done) { - pr_info("on_post_fs_data already done\n"); - return; - } - done = true; - pr_info("on_post_fs_data!\n"); - ksu_load_allow_list(); - // sanity check, this may influence the performance - stop_input_hook(); + static bool done = false; + if (done) { + pr_info("on_post_fs_data already done\n"); + return; + } + done = true; + pr_info("on_post_fs_data!\n"); + ksu_load_allow_list(); + ksu_observer_init(); + // sanity check, this may influence the performance + stop_input_hook(); - ksu_devpts_sid = ksu_get_devpts_sid(); - pr_info("devpts sid: %d\n", ksu_devpts_sid); + ksu_devpts_sid = ksu_get_devpts_sid(); + pr_info("devpts sid: %d\n", ksu_devpts_sid); - // End of boot state + // End of boot state is_boot_phase = false; } @@ -88,149 +90,149 @@ void on_post_fs_data(void) // adapted from ksu_handle_execveat_ksud static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const char *envp, size_t envp_len) { - static const char app_process[] = "/system/bin/app_process"; - static bool first_app_process = true; + static const char app_process[] = "/system/bin/app_process"; + static bool first_app_process = true; - /* This applies to versions Android 10+ */ - static const char system_bin_init[] = "/system/bin/init"; - /* This applies to versions between Android 6 ~ 9 */ - static const char old_system_init[] = "/init"; - static bool init_second_stage_executed = false; + /* This applies to versions Android 10+ */ + static const char system_bin_init[] = "/system/bin/init"; + /* This applies to versions between Android 6 ~ 9 */ + static const char old_system_init[] = "/init"; + static bool init_second_stage_executed = false; - // return early when disabled - if (!ksu_execveat_hook) - return 0; + // return early when disabled + if (!ksu_execveat_hook) + return 0; - if (!filename) - return 0; + if (!filename) + return 0; - // debug! remove me! - pr_info("%s: filename: %s argv1: %s envp_len: %zu\n", __func__, filename, argv1, envp_len); + // debug! remove me! + pr_info("%s: filename: %s argv1: %s envp_len: %zu\n", __func__, filename, argv1, envp_len); #ifdef CONFIG_KSU_DEBUG - const char *envp_n = envp; - unsigned int envc = 1; - do { - pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n); - envp_n += strlen(envp_n) + 1; - envc++; - } while (envp_n < envp + 256); + const char *envp_n = envp; + unsigned int envc = 1; + do { + pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n); + envp_n += strlen(envp_n) + 1; + envc++; + } while (envp_n < envp + 256); #endif - if (init_second_stage_executed) - goto first_app_process; + if (init_second_stage_executed) + goto first_app_process; - // /system/bin/init with argv1 - if (!init_second_stage_executed - && (!memcmp(filename, system_bin_init, sizeof(system_bin_init) - 1))) { - if (argv1 && !strcmp(argv1, "second_stage")) { - pr_info("%s: /system/bin/init second_stage executed\n", __func__); - apply_kernelsu_rules(); - init_second_stage_executed = true; - ksu_android_ns_fs_check(); - } - } + // /system/bin/init with argv1 + if (!init_second_stage_executed + && (!memcmp(filename, system_bin_init, sizeof(system_bin_init) - 1))) { + if (argv1 && !strcmp(argv1, "second_stage")) { + pr_info("%s: /system/bin/init second_stage executed\n", __func__); + apply_kernelsu_rules(); + init_second_stage_executed = true; + ksu_android_ns_fs_check(); + } + } - // /init with argv1 - if (!init_second_stage_executed - && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) { - if (argv1 && !strcmp(argv1, "--second-stage")) { - pr_info("%s: /init --second-stage executed\n", __func__); - apply_kernelsu_rules(); - init_second_stage_executed = true; - ksu_android_ns_fs_check(); - } - } + // /init with argv1 + if (!init_second_stage_executed + && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) { + if (argv1 && !strcmp(argv1, "--second-stage")) { + pr_info("%s: /init --second-stage executed\n", __func__); + apply_kernelsu_rules(); + init_second_stage_executed = true; + ksu_android_ns_fs_check(); + } + } - if (!envp || !envp_len) - goto first_app_process; + if (!envp || !envp_len) + goto first_app_process; - // /init without argv1/useless-argv1 but usable envp - // untested! TODO: test and debug me! - if (!init_second_stage_executed && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) { - - // we hunt for "INIT_SECOND_STAGE" - const char *envp_n = envp; - unsigned int envc = 1; - do { - if (strstarts(envp_n, "INIT_SECOND_STAGE")) - break; - envp_n += strlen(envp_n) + 1; - envc++; - } while (envp_n < envp + envp_len); - pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n); - - if (!strcmp(envp_n, "INIT_SECOND_STAGE=1") - || !strcmp(envp_n, "INIT_SECOND_STAGE=true") ) { - pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__); - apply_kernelsu_rules(); - init_second_stage_executed = true; - ksu_android_ns_fs_check(); - } - } + // /init without argv1/useless-argv1 but usable envp + // untested! TODO: test and debug me! + if (!init_second_stage_executed && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) { + + // we hunt for "INIT_SECOND_STAGE" + const char *envp_n = envp; + unsigned int envc = 1; + do { + if (strstarts(envp_n, "INIT_SECOND_STAGE")) + break; + envp_n += strlen(envp_n) + 1; + envc++; + } while (envp_n < envp + envp_len); + pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n); + + if (!strcmp(envp_n, "INIT_SECOND_STAGE=1") + || !strcmp(envp_n, "INIT_SECOND_STAGE=true") ) { + pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__); + apply_kernelsu_rules(); + init_second_stage_executed = true; + ksu_android_ns_fs_check(); + } + } first_app_process: - if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) { - first_app_process = false; - pr_info("%s: exec app_process, /data prepared, second_stage: %d\n", __func__, init_second_stage_executed); - on_post_fs_data(); - stop_execve_hook(); - } + if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) { + first_app_process = false; + pr_info("%s: exec app_process, /data prepared, second_stage: %d\n", __func__, init_second_stage_executed); + on_post_fs_data(); + stop_execve_hook(); + } - return 0; + return 0; } int ksu_handle_pre_ksud(const char *filename) { - if (likely(!ksu_execveat_hook)) - return 0; + if (likely(!ksu_execveat_hook)) + return 0; - // not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy) - // return 0; - if (likely(strcmp(filename, "/system/bin/init") && strcmp(filename, "/init") - && !strstarts(filename, "/system/bin/app_process") )) - return 0; + // not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy) + // return 0; + if (likely(strcmp(filename, "/system/bin/init") && strcmp(filename, "/init") + && !strstarts(filename, "/system/bin/app_process") )) + return 0; - if (!current || !current->mm) - return 0; + if (!current || !current->mm) + return 0; - // https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429 - // unsigned long arg_start, arg_end, env_start, env_end; - unsigned long arg_start = current->mm->arg_start; - unsigned long arg_end = current->mm->arg_end; - unsigned long env_start = current->mm->env_start; - unsigned long env_end = current->mm->env_end; + // https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429 + // unsigned long arg_start, arg_end, env_start, env_end; + unsigned long arg_start = current->mm->arg_start; + unsigned long arg_end = current->mm->arg_end; + unsigned long env_start = current->mm->env_start; + unsigned long env_end = current->mm->env_end; - size_t arg_len = arg_end - arg_start; - size_t envp_len = env_end - env_start; + size_t arg_len = arg_end - arg_start; + size_t envp_len = env_end - env_start; - if (arg_len <= 0 || envp_len <= 0) // this wont make sense, filter it - return 0; + if (arg_len <= 0 || envp_len <= 0) // this wont make sense, filter it + return 0; - #define ARGV_MAX 32 // this is enough for argv1 - #define ENVP_MAX 256 // this is enough for INIT_SECOND_STAGE - char args[ARGV_MAX]; - size_t argv_copy_len = (arg_len > ARGV_MAX) ? ARGV_MAX : arg_len; - char envp[ENVP_MAX]; - size_t envp_copy_len = (envp_len > ENVP_MAX) ? ENVP_MAX : envp_len; + #define ARGV_MAX 32 // this is enough for argv1 + #define ENVP_MAX 256 // this is enough for INIT_SECOND_STAGE + char args[ARGV_MAX]; + size_t argv_copy_len = (arg_len > ARGV_MAX) ? ARGV_MAX : arg_len; + char envp[ENVP_MAX]; + size_t envp_copy_len = (envp_len > ENVP_MAX) ? ENVP_MAX : envp_len; - // we cant use strncpy on here, else it will truncate once it sees \0 - if (ksu_copy_from_user_retry(args, (void __user *)arg_start, argv_copy_len)) - return 0; + // we cant use strncpy on here, else it will truncate once it sees \0 + if (ksu_copy_from_user_retry(args, (void __user *)arg_start, argv_copy_len)) + return 0; - if (ksu_copy_from_user_retry(envp, (void __user *)env_start, envp_copy_len)) - return 0; + if (ksu_copy_from_user_retry(envp, (void __user *)env_start, envp_copy_len)) + return 0; - args[ARGV_MAX - 1] = '\0'; - envp[ENVP_MAX - 1] = '\0'; + args[ARGV_MAX - 1] = '\0'; + envp[ENVP_MAX - 1] = '\0'; - // we only need argv1 ! - // abuse strlen here since it only gets length up to \0 - char *argv1 = args + strlen(args) + 1; - if (argv1 >= args + argv_copy_len) // out of bounds! - argv1 = ""; + // we only need argv1 ! + // abuse strlen here since it only gets length up to \0 + char *argv1 = args + strlen(args) + 1; + if (argv1 >= args + argv_copy_len) // out of bounds! + argv1 = ""; - return ksu_handle_bprm_ksud(filename, argv1, envp, envp_copy_len); + return ksu_handle_bprm_ksud(filename, argv1, envp, envp_copy_len); } static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *); @@ -239,394 +241,394 @@ static struct file_operations fops_proxy; static ssize_t read_count_append = 0; static ssize_t read_proxy(struct file *file, char __user *buf, size_t count, - loff_t *pos) + loff_t *pos) { - bool first_read = file->f_pos == 0; - ssize_t ret = orig_read(file, buf, count, pos); - if (first_read) { - pr_info("read_proxy append %ld + %ld\n", ret, - read_count_append); - ret += read_count_append; - } - return ret; + bool first_read = file->f_pos == 0; + ssize_t ret = orig_read(file, buf, count, pos); + if (first_read) { + pr_info("read_proxy append %ld + %ld\n", ret, + read_count_append); + ret += read_count_append; + } + return ret; } static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to) { - bool first_read = iocb->ki_pos == 0; - ssize_t ret = orig_read_iter(iocb, to); - if (first_read) { - pr_info("read_iter_proxy append %ld + %ld\n", ret, - read_count_append); - ret += read_count_append; - } - return ret; + bool first_read = iocb->ki_pos == 0; + ssize_t ret = orig_read_iter(iocb, to); + if (first_read) { + pr_info("read_iter_proxy append %ld + %ld\n", ret, + read_count_append); + ret += read_count_append; + } + return ret; } int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, - size_t *count_ptr, loff_t **pos) + size_t *count_ptr, loff_t **pos) { #ifndef CONFIG_KSU_KPROBES_HOOK - if (!ksu_vfs_read_hook) { - return 0; - } + if (!ksu_vfs_read_hook) { + return 0; + } #endif - struct file *file; - char __user *buf; - size_t count; + struct file *file; + char __user *buf; + size_t count; - if (strcmp(current->comm, "init")) { - // we are only interest in `init` process - return 0; - } + if (strcmp(current->comm, "init")) { + // we are only interest in `init` process + return 0; + } - file = *file_ptr; - if (IS_ERR(file)) { - return 0; - } + file = *file_ptr; + if (IS_ERR(file)) { + return 0; + } - if (!d_is_reg(file->f_path.dentry)) { - return 0; - } + if (!d_is_reg(file->f_path.dentry)) { + return 0; + } - const char *short_name = file->f_path.dentry->d_name.name; - if (strcmp(short_name, "atrace.rc")) { - // we are only interest `atrace.rc` file name file - return 0; - } - char path[256]; - char *dpath = d_path(&file->f_path, path, sizeof(path)); + const char *short_name = file->f_path.dentry->d_name.name; + if (strcmp(short_name, "atrace.rc")) { + // we are only interest `atrace.rc` file name file + return 0; + } + char path[256]; + char *dpath = d_path(&file->f_path, path, sizeof(path)); - if (IS_ERR(dpath)) { - return 0; - } + if (IS_ERR(dpath)) { + return 0; + } - if (strcmp(dpath, "/system/etc/init/atrace.rc")) { - return 0; - } + if (strcmp(dpath, "/system/etc/init/atrace.rc")) { + return 0; + } - // we only process the first read - static bool rc_inserted = false; - if (rc_inserted) { - // we don't need this kprobe, unregister it! - stop_vfs_read_hook(); - return 0; - } - rc_inserted = true; + // we only process the first read + static bool rc_inserted = false; + if (rc_inserted) { + // we don't need this kprobe, unregister it! + stop_vfs_read_hook(); + return 0; + } + rc_inserted = true; - // now we can sure that the init process is reading - // `/system/etc/init/atrace.rc` - buf = *buf_ptr; - count = *count_ptr; + // now we can sure that the init process is reading + // `/system/etc/init/atrace.rc` + buf = *buf_ptr; + count = *count_ptr; - size_t rc_count = strlen(KERNEL_SU_RC); + size_t rc_count = strlen(KERNEL_SU_RC); - pr_info("vfs_read: %s, comm: %s, count: %zu, rc_count: %zu\n", dpath, - current->comm, count, rc_count); + pr_info("vfs_read: %s, comm: %s, count: %zu, rc_count: %zu\n", dpath, + current->comm, count, rc_count); - if (count < rc_count) { - pr_err("count: %zu < rc_count: %zu\n", count, rc_count); - return 0; - } + if (count < rc_count) { + pr_err("count: %zu < rc_count: %zu\n", count, rc_count); + return 0; + } - size_t ret = copy_to_user(buf, KERNEL_SU_RC, rc_count); - if (ret) { - pr_err("copy ksud.rc failed: %zu\n", ret); - return 0; - } + size_t ret = copy_to_user(buf, KERNEL_SU_RC, rc_count); + if (ret) { + pr_err("copy ksud.rc failed: %zu\n", ret); + return 0; + } - // we've succeed to insert ksud.rc, now we need to proxy the read and modify the result! - // But, we can not modify the file_operations directly, because it's in read-only memory. - // We just replace the whole file_operations with a proxy one. - memcpy(&fops_proxy, file->f_op, sizeof(struct file_operations)); - orig_read = file->f_op->read; - if (orig_read) { - fops_proxy.read = read_proxy; - } - orig_read_iter = file->f_op->read_iter; - if (orig_read_iter) { - fops_proxy.read_iter = read_iter_proxy; - } - // replace the file_operations - file->f_op = &fops_proxy; - read_count_append = rc_count; + // we've succeed to insert ksud.rc, now we need to proxy the read and modify the result! + // But, we can not modify the file_operations directly, because it's in read-only memory. + // We just replace the whole file_operations with a proxy one. + memcpy(&fops_proxy, file->f_op, sizeof(struct file_operations)); + orig_read = file->f_op->read; + if (orig_read) { + fops_proxy.read = read_proxy; + } + orig_read_iter = file->f_op->read_iter; + if (orig_read_iter) { + fops_proxy.read_iter = read_iter_proxy; + } + // replace the file_operations + file->f_op = &fops_proxy; + read_count_append = rc_count; - *buf_ptr = buf + rc_count; - *count_ptr = count - rc_count; + *buf_ptr = buf + rc_count; + *count_ptr = count - rc_count; - return 0; + return 0; } int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, - size_t *count_ptr) + size_t *count_ptr) { - struct file *file = fget(fd); - if (!file) { - return 0; - } - int result = ksu_handle_vfs_read(&file, buf_ptr, count_ptr, NULL); - fput(file); - return result; + struct file *file = fget(fd); + if (!file) { + return 0; + } + int result = ksu_handle_vfs_read(&file, buf_ptr, count_ptr, NULL); + fput(file); + return result; } static unsigned int volumedown_pressed_count = 0; static bool is_volumedown_enough(unsigned int count) { - return count >= 3; + return count >= 3; } int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, - int *value) + int *value) { #ifndef CONFIG_KSU_KPROBES_HOOK - if (!ksu_input_hook) { - return 0; - } + if (!ksu_input_hook) { + return 0; + } #endif - if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) { - int val = *value; - pr_info("KEY_VOLUMEDOWN val: %d\n", val); - if (val && is_boot_phase) { - // key pressed, count it - volumedown_pressed_count += 1; - if (is_volumedown_enough(volumedown_pressed_count)) { - stop_input_hook(); - } - } - } + if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) { + int val = *value; + pr_info("KEY_VOLUMEDOWN val: %d\n", val); + if (val && is_boot_phase) { + // key pressed, count it + volumedown_pressed_count += 1; + if (is_volumedown_enough(volumedown_pressed_count)) { + stop_input_hook(); + } + } + } - return 0; + return 0; } bool ksu_is_safe_mode() { - static bool safe_mode = false; - if (safe_mode) { - // don't need to check again, userspace may call multiple times - return true; - } + static bool safe_mode = false; + if (safe_mode) { + // don't need to check again, userspace may call multiple times + return true; + } - // stop hook first! - stop_input_hook(); + // stop hook first! + stop_input_hook(); - pr_info("volumedown_pressed_count: %d\n", volumedown_pressed_count); - if (is_volumedown_enough(volumedown_pressed_count)) { - // pressed over 3 times - pr_info("KEY_VOLUMEDOWN pressed max times, safe mode detected!\n"); - safe_mode = true; - return true; - } + pr_info("volumedown_pressed_count: %d\n", volumedown_pressed_count); + if (is_volumedown_enough(volumedown_pressed_count)) { + // pressed over 3 times + pr_info("KEY_VOLUMEDOWN pressed max times, safe mode detected!\n"); + safe_mode = true; + return true; + } - return false; + return false; } #ifdef CONFIG_KSU_KPROBES_HOOK static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) { - /* - asmlinkage int sys_execve(const char __user *filenamei, - const char __user *const __user *argv, - const char __user *const __user *envp, struct pt_regs *regs) - */ - struct pt_regs *real_regs = PT_REAL_REGS(regs); - const char __user *filename_user = (const char __user *)PT_REGS_PARM1(real_regs); - const char __user *const __user *__argv = (const char __user *const __user *)PT_REGS_PARM2(real_regs); - const char __user *const __user *__envp = (const char __user *const __user *)PT_REGS_PARM3(real_regs); - char path[32]; + /* + asmlinkage int sys_execve(const char __user *filenamei, + const char __user *const __user *argv, + const char __user *const __user *envp, struct pt_regs *regs) + */ + struct pt_regs *real_regs = PT_REAL_REGS(regs); + const char __user *filename_user = (const char __user *)PT_REGS_PARM1(real_regs); + const char __user *const __user *__argv = (const char __user *const __user *)PT_REGS_PARM2(real_regs); + const char __user *const __user *__envp = (const char __user *const __user *)PT_REGS_PARM3(real_regs); + char path[32]; - if (!filename_user) - return 0; + if (!filename_user) + return 0; // filename stage - if (ksu_copy_from_user_retry(path, filename_user, sizeof(path))) - return 0; + if (ksu_copy_from_user_retry(path, filename_user, sizeof(path))) + return 0; - path[sizeof(path) - 1] = '\0'; + path[sizeof(path) - 1] = '\0'; - // not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy) - // we dont care !! - if (likely(strcmp(path, "/system/bin/init") && strcmp(path, "/init") - && !strstarts(path, "/system/bin/app_process") )) - return 0; + // not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy) + // we dont care !! + if (likely(strcmp(path, "/system/bin/init") && strcmp(path, "/init") + && !strstarts(path, "/system/bin/app_process") )) + return 0; // argv stage - char argv1[32] = {0}; - // memzero_explicit(argv1, 32); - if (__argv) { - const char __user *arg1_user = NULL; - // grab argv[1] pointer - // this looks like - /* - * 0x1000 ./program << this is __argv - * 0x1001 -o - * 0x1002 arg - */ - if (ksu_copy_from_user_retry(&arg1_user, __argv + 1, sizeof(arg1_user))) - goto no_argv1; // copy argv[1] pointer fail, probably no argv1 !! + char argv1[32] = {0}; + // memzero_explicit(argv1, 32); + if (__argv) { + const char __user *arg1_user = NULL; + // grab argv[1] pointer + // this looks like + /* + * 0x1000 ./program << this is __argv + * 0x1001 -o + * 0x1002 arg + */ + if (ksu_copy_from_user_retry(&arg1_user, __argv + 1, sizeof(arg1_user))) + goto no_argv1; // copy argv[1] pointer fail, probably no argv1 !! - if (arg1_user) - ksu_copy_from_user_retry(argv1, arg1_user, sizeof(argv1)); - } + if (arg1_user) + ksu_copy_from_user_retry(argv1, arg1_user, sizeof(argv1)); + } no_argv1: - argv1[sizeof(argv1) - 1] = '\0'; + argv1[sizeof(argv1) - 1] = '\0'; // envp stage - #define ENVP_MAX 256 - char envp[ENVP_MAX] = {0}; - char *dst = envp; - size_t envp_len = 0; - int i = 0; // to track user pointer offset from __envp + #define ENVP_MAX 256 + char envp[ENVP_MAX] = {0}; + char *dst = envp; + size_t envp_len = 0; + int i = 0; // to track user pointer offset from __envp - // memzero_explicit(envp, ENVP_MAX); + // memzero_explicit(envp, ENVP_MAX); - if (__envp) { - do { - const char __user *env_entry_user = NULL; - // this is also like argv above - /* - * 0x1001 PATH=/bin - * 0x1002 VARIABLE=value - * 0x1002 some_more_env_var=1 - */ + if (__envp) { + do { + const char __user *env_entry_user = NULL; + // this is also like argv above + /* + * 0x1001 PATH=/bin + * 0x1002 VARIABLE=value + * 0x1002 some_more_env_var=1 + */ - // check if pointer exists - if (ksu_copy_from_user_retry(&env_entry_user, __envp + i, sizeof(env_entry_user))) - break; + // check if pointer exists + if (ksu_copy_from_user_retry(&env_entry_user, __envp + i, sizeof(env_entry_user))) + break; - // check if no more env entry - if (!env_entry_user) - break; - - // probably redundant to while condition but ok - if (envp_len >= ENVP_MAX - 1) - break; + // check if no more env entry + if (!env_entry_user) + break; + + // probably redundant to while condition but ok + if (envp_len >= ENVP_MAX - 1) + break; - // copy strings from env_entry_user pointer that we collected - // also break if failed - if (ksu_copy_from_user_retry(dst, env_entry_user, ENVP_MAX - envp_len)) - break; + // copy strings from env_entry_user pointer that we collected + // also break if failed + if (ksu_copy_from_user_retry(dst, env_entry_user, ENVP_MAX - envp_len)) + break; - // get the length of that new copy above - // get lngth of dst as far as ENVP_MAX - current collected envp_len - size_t len = strnlen(dst, ENVP_MAX - envp_len); - if (envp_len + len + 1 > ENVP_MAX) - break; // if more than 255 bytes, bail + // get the length of that new copy above + // get lngth of dst as far as ENVP_MAX - current collected envp_len + size_t len = strnlen(dst, ENVP_MAX - envp_len); + if (envp_len + len + 1 > ENVP_MAX) + break; // if more than 255 bytes, bail - dst[len] = '\0'; - // collect total number of copied strings - envp_len = envp_len + len + 1; - // increment dst address since we need to put something on next iter - dst = dst + len + 1; - // pointer walk, __envp + i - i++; - } while (envp_len < ENVP_MAX); - } + dst[len] = '\0'; + // collect total number of copied strings + envp_len = envp_len + len + 1; + // increment dst address since we need to put something on next iter + dst = dst + len + 1; + // pointer walk, __envp + i + i++; + } while (envp_len < ENVP_MAX); + } - /* - at this point, we shoul've collected envp from - * 0x1001 PATH=/bin - * 0x1002 VARIABLE=value - * 0x1002 some_more_env_var=1 - to - * 0x1234 PATH=/bin\0VARIABLE=value\0some_more_env_var=1\0\0\0\0 - */ + /* + at this point, we shoul've collected envp from + * 0x1001 PATH=/bin + * 0x1002 VARIABLE=value + * 0x1002 some_more_env_var=1 + to + * 0x1234 PATH=/bin\0VARIABLE=value\0some_more_env_var=1\0\0\0\0 + */ - envp[ENVP_MAX - 1] = '\0'; + envp[ENVP_MAX - 1] = '\0'; #ifdef CONFIG_KSU_DEBUG - pr_info("%s: filename: %s argv[1]:%s envp_len: %zu\n", __func__, path, argv1, envp_len); + pr_info("%s: filename: %s argv[1]:%s envp_len: %zu\n", __func__, path, argv1, envp_len); #endif - return ksu_handle_bprm_ksud(path, argv1, envp, envp_len); + return ksu_handle_bprm_ksud(path, argv1, envp, envp_len); } static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs) { - struct pt_regs *real_regs = PT_REAL_REGS(regs); - unsigned int fd = PT_REGS_PARM1(real_regs); - char __user **buf_ptr = (char __user **)&PT_REGS_PARM2(real_regs); - size_t count_ptr = (size_t *)&PT_REGS_PARM3(real_regs); + struct pt_regs *real_regs = PT_REAL_REGS(regs); + unsigned int fd = PT_REGS_PARM1(real_regs); + char __user **buf_ptr = (char __user **)&PT_REGS_PARM2(real_regs); + size_t count_ptr = (size_t *)&PT_REGS_PARM3(real_regs); - return ksu_handle_sys_read(fd, buf_ptr, count_ptr); + return ksu_handle_sys_read(fd, buf_ptr, count_ptr); } static int input_handle_event_handler_pre(struct kprobe *p, - struct pt_regs *regs) + struct pt_regs *regs) { - unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs); - unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs); - int *value = (int *)&PT_REGS_CCALL_PARM4(regs); - return ksu_handle_input_handle_event(type, code, value); + unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs); + unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs); + int *value = (int *)&PT_REGS_CCALL_PARM4(regs); + return ksu_handle_input_handle_event(type, code, value); } static struct kprobe execve_kp = { - .symbol_name = SYS_EXECVE_SYMBOL, - .pre_handler = sys_execve_handler_pre, + .symbol_name = SYS_EXECVE_SYMBOL, + .pre_handler = sys_execve_handler_pre, }; static struct kprobe vfs_read_kp = { - .symbol_name = SYS_READ_SYMBOL, - .pre_handler = sys_read_handler_pre, + .symbol_name = SYS_READ_SYMBOL, + .pre_handler = sys_read_handler_pre, }; static struct kprobe input_event_kp = { - .symbol_name = "input_event", - .pre_handler = input_handle_event_handler_pre, + .symbol_name = "input_event", + .pre_handler = input_handle_event_handler_pre, }; static void do_stop_vfs_read_hook(struct work_struct *work) { - unregister_kprobe(&vfs_read_kp); + unregister_kprobe(&vfs_read_kp); } static void do_stop_execve_hook(struct work_struct *work) { - unregister_kprobe(&execve_kp); + unregister_kprobe(&execve_kp); } static void do_stop_input_hook(struct work_struct *work) { - unregister_kprobe(&input_event_kp); + unregister_kprobe(&input_event_kp); } #endif static void stop_vfs_read_hook() { #ifdef CONFIG_KSU_KPROBES_HOOK - bool ret = schedule_work(&stop_vfs_read_work); - pr_info("unregister vfs_read kprobe: %d!\n", ret); + bool ret = schedule_work(&stop_vfs_read_work); + pr_info("unregister vfs_read kprobe: %d!\n", ret); #else - ksu_vfs_read_hook = false; - pr_info("stop vfs_read_hook\n"); + ksu_vfs_read_hook = false; + pr_info("stop vfs_read_hook\n"); #endif } static void stop_execve_hook() { #ifdef CONFIG_KSU_KPROBES_HOOK - bool ret = schedule_work(&stop_execve_hook_work); - pr_info("unregister execve kprobe: %d!\n", ret); + bool ret = schedule_work(&stop_execve_hook_work); + pr_info("unregister execve kprobe: %d!\n", ret); #else - pr_info("stop execve_hook\n"); + pr_info("stop execve_hook\n"); #endif - ksu_execveat_hook = false; + ksu_execveat_hook = false; } static void stop_input_hook() { - static bool input_hook_stopped = false; - if (input_hook_stopped) { - return; - } - input_hook_stopped = true; + static bool input_hook_stopped = false; + if (input_hook_stopped) { + return; + } + input_hook_stopped = true; #ifdef CONFIG_KSU_KPROBES_HOOK - bool ret = schedule_work(&stop_input_hook_work); - pr_info("unregister input kprobe: %d!\n", ret); + bool ret = schedule_work(&stop_input_hook_work); + pr_info("unregister input kprobe: %d!\n", ret); #else - ksu_input_hook = false; - pr_info("stop input_hook\n"); + ksu_input_hook = false; + pr_info("stop input_hook\n"); #endif } @@ -634,31 +636,31 @@ static void stop_input_hook() void ksu_ksud_init() { #ifdef CONFIG_KSU_KPROBES_HOOK - int ret; + int ret; - ret = register_kprobe(&execve_kp); - pr_info("ksud: execve_kp: %d\n", ret); + ret = register_kprobe(&execve_kp); + pr_info("ksud: execve_kp: %d\n", ret); - ret = register_kprobe(&vfs_read_kp); - pr_info("ksud: vfs_read_kp: %d\n", ret); + ret = register_kprobe(&vfs_read_kp); + pr_info("ksud: vfs_read_kp: %d\n", ret); - ret = register_kprobe(&input_event_kp); - pr_info("ksud: input_event_kp: %d\n", ret); + ret = register_kprobe(&input_event_kp); + pr_info("ksud: input_event_kp: %d\n", ret); - INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook); - INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook); - INIT_WORK(&stop_input_hook_work, do_stop_input_hook); + INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook); + INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook); + INIT_WORK(&stop_input_hook_work, do_stop_input_hook); #endif } void ksu_ksud_exit() { #ifdef CONFIG_KSU_KPROBES_HOOK - unregister_kprobe(&execve_kp); - // this should be done before unregister vfs_read_kp - // unregister_kprobe(&vfs_read_kp); - unregister_kprobe(&input_event_kp); + unregister_kprobe(&execve_kp); + // this should be done before unregister vfs_read_kp + // unregister_kprobe(&vfs_read_kp); + unregister_kprobe(&input_event_kp); #endif - is_boot_phase = false; + is_boot_phase = false; } diff --git a/kernel/manager.h b/kernel/manager.h index f27afbbf..4c9e9570 100644 --- a/kernel/manager.h +++ b/kernel/manager.h @@ -13,30 +13,33 @@ extern void ksu_add_manager(uid_t uid, int signature_index); extern void ksu_remove_manager(uid_t uid); extern int ksu_get_manager_signature_index(uid_t uid); -static inline bool ksu_is_manager_uid_valid() +static inline bool ksu_is_manager_uid_valid(void) { - return ksu_manager_uid != KSU_INVALID_UID; + return ksu_manager_uid != KSU_INVALID_UID; } -static inline bool is_manager() +static inline bool is_manager(void) { - return unlikely(ksu_is_any_manager(current_uid().val) || - (ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val)); + return unlikely(ksu_is_any_manager(current_uid().val) || + (ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val)); } -static inline uid_t ksu_get_manager_uid() +static inline uid_t ksu_get_manager_uid(void) { - return ksu_manager_uid; + return ksu_manager_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() +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 \ No newline at end of file diff --git a/kernel/manager_sign.h b/kernel/manager_sign.h index 7de89937..265ef35c 100644 --- a/kernel/manager_sign.h +++ b/kernel/manager_sign.h @@ -10,8 +10,8 @@ #define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000" typedef struct { - unsigned size; - const char *sha256; + unsigned size; + const char *sha256; } apk_sign_key_t; #endif /* MANAGER_SIGN_H */ diff --git a/kernel/manual_su.c b/kernel/manual_su.c index 55623594..20fce170 100644 --- a/kernel/manual_su.c +++ b/kernel/manual_su.c @@ -120,9 +120,9 @@ static char* ksu_generate_auth_token(void) } #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 - 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 auth_tokens[token_count].expire_time = jiffies + KSU_TOKEN_EXPIRE_TIME * HZ; auth_tokens[token_count].used = false; @@ -203,9 +203,9 @@ static int handle_token_generation(struct manual_su_request *request) } #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 - strlcpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1); + strlcpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1); #endif pr_info("manual_su: auth token generated successfully\n"); @@ -344,4 +344,4 @@ static void add_pending_root(uid_t uid) pending_uids[pending_cnt++] = (struct pending_uid){uid, 0}; ksu_temp_grant_root_once(uid); pr_info("pending_root: cached UID %d\n", uid); -} +} \ No newline at end of file diff --git a/kernel/manual_su.h b/kernel/manual_su.h index dfc6521f..c39c5426 100644 --- a/kernel/manual_su.h +++ b/kernel/manual_su.h @@ -3,6 +3,11 @@ #include #include +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0) +#define mmap_lock mmap_sem +#endif #define ksu_task_is_dead(t) ((t)->exit_state != 0) diff --git a/kernel/pkg_observer.c b/kernel/pkg_observer.c new file mode 100644 index 00000000..e0641332 --- /dev/null +++ b/kernel/pkg_observer.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#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"); +} \ No newline at end of file diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index c60159c0..c8db9fc3 100644 --- a/kernel/selinux/rules.c +++ b/kernel/selinux/rules.c @@ -18,119 +18,119 @@ static struct policydb *get_policydb(void) { - struct policydb *db; - struct selinux_policy *policy = selinux_state.policy; - db = &policy->policydb; - return db; + struct policydb *db; + struct selinux_policy *policy = selinux_state.policy; + db = &policy->policydb; + return db; } static DEFINE_MUTEX(ksu_rules); void apply_kernelsu_rules() { - struct policydb *db; + struct policydb *db; - if (!getenforce()) { - pr_info("SELinux permissive or disabled, apply rules!\n"); - } + if (!getenforce()) { + 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_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject"); - ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain"); - ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain"); + ksu_permissive(db, KERNEL_SU_DOMAIN); + ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject"); + ksu_typeattribute(db, KERNEL_SU_DOMAIN, "netdomain"); + ksu_typeattribute(db, KERNEL_SU_DOMAIN, "bluetoothdomain"); - // Create unconstrained file type - ksu_type(db, KERNEL_SU_FILE, "file_type"); - ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject"); - ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL); + // Create unconstrained file type + ksu_type(db, KERNEL_SU_FILE, "file_type"); + ksu_typeattribute(db, KERNEL_SU_FILE, "mlstrustedobject"); + ksu_allow(db, ALL, KERNEL_SU_FILE, ALL, ALL); - // allow all! - ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL); + // allow all! + ksu_allow(db, KERNEL_SU_DOMAIN, ALL, ALL, ALL); - // allow us do any 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, "fifo_file", ALL); - ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL); - ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "file", ALL); - } + // allow us do any 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, "fifo_file", ALL); + ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "chr_file", ALL); + ksu_allowxperm(db, KERNEL_SU_DOMAIN, ALL, "file", ALL); + } - // 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", "file", ALL); - // we need to search /data/app - 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", "read"); - ksu_allow(db, "kernel", "apk_data_file", "dir", "search"); - // we may need to do mount on shell - ksu_allow(db, "kernel", "shell_data_file", "file", ALL); - // we need to read /data/system/packages.list - ksu_allow(db, "kernel", "kernel", "capability", "dac_override"); - // Android 10+: - // http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512 - ksu_allow(db, "kernel", "packages_list_file", "file", ALL); - // Kernel 4.4 - ksu_allow(db, "kernel", "packages_list_file", "dir", ALL); - // Android 9-: - // 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", "dir", ALL); - // our ksud triggered by init - ksu_allow(db, "init", "adb_data_file", "file", ALL); - ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289 - ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL); - // we need to umount modules in zygote - ksu_allow(db, "zygote", "adb_data_file", "dir", "search"); + // 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", "file", ALL); + // we need to search /data/app + 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", "read"); + ksu_allow(db, "kernel", "apk_data_file", "dir", "search"); + // we may need to do mount on shell + ksu_allow(db, "kernel", "shell_data_file", "file", ALL); + // we need to read /data/system/packages.list + ksu_allow(db, "kernel", "kernel", "capability", "dac_override"); + // Android 10+: + // http://aospxref.com/android-12.0.0_r3/xref/system/sepolicy/private/file_contexts#512 + ksu_allow(db, "kernel", "packages_list_file", "file", ALL); + // Kernel 4.4 + ksu_allow(db, "kernel", "packages_list_file", "dir", ALL); + // Android 9-: + // 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", "dir", ALL); + // our ksud triggered by init + ksu_allow(db, "init", "adb_data_file", "file", ALL); + ksu_allow(db, "init", "adb_data_file", "dir", ALL); // #1289 + ksu_allow(db, "init", KERNEL_SU_DOMAIN, ALL, ALL); + // we need to umount modules in zygote + ksu_allow(db, "zygote", "adb_data_file", "dir", "search"); - // copied from Magisk rules - // suRights - 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, "file", "open"); - ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read"); - ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr"); - ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "process", "sigchld"); + // copied from Magisk rules + // suRights + 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, "file", "open"); + ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "file", "read"); + ksu_allow(db, "servicemanager", KERNEL_SU_DOMAIN, "process", "getattr"); + ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "process", "sigchld"); - // allowLog - 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", "open"); - ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr"); + // allowLog + 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", "open"); + ksu_allow(db, "logd", KERNEL_SU_DOMAIN, "file", "getattr"); - // dumpsys - 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", "read"); - ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "open"); - ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "getattr"); + // dumpsys + 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", "read"); + ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "open"); + ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "fifo_file", "getattr"); - // bootctl - 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", "open"); - ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process", - "getattr"); + // bootctl + 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", "open"); + ksu_allow(db, "hwservicemanager", KERNEL_SU_DOMAIN, "process", + "getattr"); - // For mounting loop devices, mirrors, tmpfs - ksu_allow(db, "kernel", ALL, "file", "read"); - ksu_allow(db, "kernel", ALL, "file", "write"); + // For mounting loop devices, mirrors, tmpfs + ksu_allow(db, "kernel", ALL, "file", "read"); + ksu_allow(db, "kernel", ALL, "file", "write"); - // Allow all binder transactions - ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL); + // Allow all binder transactions + ksu_allow(db, ALL, KERNEL_SU_DOMAIN, "binder", ALL); // 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", "sigkill"); - // https://android-review.googlesource.com/c/platform/system/logging/+/3725346 - ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr"); + // https://android-review.googlesource.com/c/platform/system/logging/+/3725346 + ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr"); - mutex_unlock(&ksu_rules); + mutex_unlock(&ksu_rules); } #define MAX_SEPOL_LEN 128 @@ -147,399 +147,399 @@ void apply_kernelsu_rules() #ifdef CONFIG_64BIT struct sepol_data { - u32 cmd; - u32 subcmd; - u64 field_sepol1; - u64 field_sepol2; - u64 field_sepol3; - u64 field_sepol4; - u64 field_sepol5; - u64 field_sepol6; - u64 field_sepol7; + u32 cmd; + u32 subcmd; + u64 field_sepol1; + u64 field_sepol2; + u64 field_sepol3; + u64 field_sepol4; + u64 field_sepol5; + u64 field_sepol6; + u64 field_sepol7; }; #ifdef CONFIG_COMPAT extern bool ksu_is_compat __read_mostly; struct sepol_compat_data { - u32 cmd; - u32 subcmd; - u32 field_sepol1; - u32 field_sepol2; - u32 field_sepol3; - u32 field_sepol4; - u32 field_sepol5; - u32 field_sepol6; - u32 field_sepol7; + u32 cmd; + u32 subcmd; + u32 field_sepol1; + u32 field_sepol2; + u32 field_sepol3; + u32 field_sepol4; + u32 field_sepol5; + u32 field_sepol6; + u32 field_sepol7; }; #endif // CONFIG_COMPAT #else struct sepol_data { - u32 cmd; - u32 subcmd; - u32 field_sepol1; - u32 field_sepol2; - u32 field_sepol3; - u32 field_sepol4; - u32 field_sepol5; - u32 field_sepol6; - u32 field_sepol7; + u32 cmd; + u32 subcmd; + u32 field_sepol1; + u32 field_sepol2; + u32 field_sepol3; + u32 field_sepol4; + u32 field_sepol5; + u32 field_sepol6; + u32 field_sepol7; }; #endif // CONFIG_64BIT static int get_object(char *buf, char __user *user_object, size_t buf_sz, - char **object) + char **object) { - if (!user_object) { - *object = ALL; - return 0; - } + if (!user_object) { + *object = ALL; + return 0; + } - if (strncpy_from_user(buf, user_object, buf_sz) < 0) { - return -1; - } + if (strncpy_from_user(buf, user_object, buf_sz) < 0) { + 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 static void reset_avc_cache() { #if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)) - avc_ss_reset(0); - selnl_notify_policyload(0); - selinux_status_update_policyload(0); + avc_ss_reset(0); + selnl_notify_policyload(0); + selinux_status_update_policyload(0); #else - struct selinux_avc *avc = selinux_state.avc; - avc_ss_reset(avc, 0); - selnl_notify_policyload(0); - selinux_status_update_policyload(&selinux_state, 0); + struct selinux_avc *avc = selinux_state.avc; + avc_ss_reset(avc, 0); + selnl_notify_policyload(0); + selinux_status_update_policyload(&selinux_state, 0); #endif - selinux_xfrm_notify_policyload(); + selinux_xfrm_notify_policyload(); } int handle_sepolicy(unsigned long arg3, void __user *arg4) { - struct policydb *db; + struct policydb *db; - if (!arg4) { - return -1; - } + if (!arg4) { + return -1; + } - if (!getenforce()) { - pr_info("SELinux permissive or disabled when handle policy!\n"); - } - - u32 cmd, subcmd; - char __user *sepol1, *sepol2, *sepol3, *sepol4, *sepol5, *sepol6, *sepol7; + if (!getenforce()) { + pr_info("SELinux permissive or disabled when handle policy!\n"); + } + + u32 cmd, subcmd; + char __user *sepol1, *sepol2, *sepol3, *sepol4, *sepol5, *sepol6, *sepol7; #if defined(CONFIG_64BIT) && defined(CONFIG_COMPAT) - if (unlikely(ksu_is_compat)) { - struct sepol_compat_data compat_data; - if (copy_from_user(&compat_data, arg4, sizeof(struct sepol_compat_data))) { - pr_err("sepol: copy sepol_data failed.\n"); - return -1; - } - sepol1 = compat_ptr(compat_data.field_sepol1); - sepol2 = compat_ptr(compat_data.field_sepol2); - sepol3 = compat_ptr(compat_data.field_sepol3); - sepol4 = compat_ptr(compat_data.field_sepol4); - sepol5 = compat_ptr(compat_data.field_sepol5); - sepol6 = compat_ptr(compat_data.field_sepol6); - sepol7 = compat_ptr(compat_data.field_sepol7); - cmd = compat_data.cmd; - subcmd = compat_data.subcmd; - } else { - struct sepol_data data; - if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) { - pr_err("sepol: copy sepol_data failed.\n"); - return -1; - } - sepol1 = data.field_sepol1; - sepol2 = data.field_sepol2; - sepol3 = data.field_sepol3; - sepol4 = data.field_sepol4; - sepol5 = data.field_sepol5; - sepol6 = data.field_sepol6; - sepol7 = data.field_sepol7; - cmd = data.cmd; - subcmd = data.subcmd; - } + if (unlikely(ksu_is_compat)) { + struct sepol_compat_data compat_data; + if (copy_from_user(&compat_data, arg4, sizeof(struct sepol_compat_data))) { + pr_err("sepol: copy sepol_data failed.\n"); + return -1; + } + sepol1 = compat_ptr(compat_data.field_sepol1); + sepol2 = compat_ptr(compat_data.field_sepol2); + sepol3 = compat_ptr(compat_data.field_sepol3); + sepol4 = compat_ptr(compat_data.field_sepol4); + sepol5 = compat_ptr(compat_data.field_sepol5); + sepol6 = compat_ptr(compat_data.field_sepol6); + sepol7 = compat_ptr(compat_data.field_sepol7); + cmd = compat_data.cmd; + subcmd = compat_data.subcmd; + } else { + struct sepol_data data; + if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) { + pr_err("sepol: copy sepol_data failed.\n"); + return -1; + } + sepol1 = data.field_sepol1; + sepol2 = data.field_sepol2; + sepol3 = data.field_sepol3; + sepol4 = data.field_sepol4; + sepol5 = data.field_sepol5; + sepol6 = data.field_sepol6; + sepol7 = data.field_sepol7; + cmd = data.cmd; + subcmd = data.subcmd; + } #else - // basically for full native, say (64BIT=y COMPAT=n) || (64BIT=n) - struct sepol_data data; - if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) { - pr_err("sepol: copy sepol_data failed.\n"); - return -1; - } - sepol1 = data.field_sepol1; - sepol2 = data.field_sepol2; - sepol3 = data.field_sepol3; - sepol4 = data.field_sepol4; - sepol5 = data.field_sepol5; - sepol6 = data.field_sepol6; - sepol7 = data.field_sepol7; - cmd = data.cmd; - subcmd = data.subcmd; + // basically for full native, say (64BIT=y COMPAT=n) || (64BIT=n) + struct sepol_data data; + if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) { + pr_err("sepol: copy sepol_data failed.\n"); + return -1; + } + sepol1 = data.field_sepol1; + sepol2 = data.field_sepol2; + sepol3 = data.field_sepol3; + sepol4 = data.field_sepol4; + sepol5 = data.field_sepol5; + sepol6 = data.field_sepol6; + sepol7 = data.field_sepol7; + cmd = data.cmd; + subcmd = data.subcmd; #endif - mutex_lock(&ksu_rules); + mutex_lock(&ksu_rules); - db = get_policydb(); + db = get_policydb(); - int ret = -1; - if (cmd == CMD_NORMAL_PERM) { - char src_buf[MAX_SEPOL_LEN]; - char tgt_buf[MAX_SEPOL_LEN]; - char cls_buf[MAX_SEPOL_LEN]; - char perm_buf[MAX_SEPOL_LEN]; + int ret = -1; + if (cmd == CMD_NORMAL_PERM) { + char src_buf[MAX_SEPOL_LEN]; + char tgt_buf[MAX_SEPOL_LEN]; + char cls_buf[MAX_SEPOL_LEN]; + char perm_buf[MAX_SEPOL_LEN]; - char *s, *t, *c, *p; - if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) { - pr_err("sepol: copy src failed.\n"); - goto exit; - } + char *s, *t, *c, *p; + if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) { + pr_err("sepol: copy src failed.\n"); + goto exit; + } - if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) { - pr_err("sepol: copy tgt failed.\n"); - goto exit; - } + if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) { + pr_err("sepol: copy tgt failed.\n"); + goto exit; + } - if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) { - pr_err("sepol: copy cls failed.\n"); - goto exit; - } + if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) { + pr_err("sepol: copy cls failed.\n"); + goto exit; + } - if (get_object(perm_buf, sepol4, sizeof(perm_buf), &p) < - 0) { - pr_err("sepol: copy perm failed.\n"); - goto exit; - } + if (get_object(perm_buf, sepol4, sizeof(perm_buf), &p) < + 0) { + pr_err("sepol: copy perm failed.\n"); + goto exit; + } - bool success = false; - if (subcmd == 1) { - success = ksu_allow(db, s, t, c, p); - } else if (subcmd == 2) { - success = ksu_deny(db, s, t, c, p); - } else if (subcmd == 3) { - success = ksu_auditallow(db, s, t, c, p); - } else if (subcmd == 4) { - success = ksu_dontaudit(db, s, t, c, p); - } else { - pr_err("sepol: unknown subcmd: %d\n", subcmd); - } - ret = success ? 0 : -1; + bool success = false; + if (subcmd == 1) { + success = ksu_allow(db, s, t, c, p); + } else if (subcmd == 2) { + success = ksu_deny(db, s, t, c, p); + } else if (subcmd == 3) { + success = ksu_auditallow(db, s, t, c, p); + } else if (subcmd == 4) { + success = ksu_dontaudit(db, s, t, c, p); + } else { + pr_err("sepol: unknown subcmd: %d\n", subcmd); + } + ret = success ? 0 : -1; - } else if (cmd == CMD_XPERM) { - char src_buf[MAX_SEPOL_LEN]; - char tgt_buf[MAX_SEPOL_LEN]; - char cls_buf[MAX_SEPOL_LEN]; + } else if (cmd == CMD_XPERM) { + char src_buf[MAX_SEPOL_LEN]; + char tgt_buf[MAX_SEPOL_LEN]; + char cls_buf[MAX_SEPOL_LEN]; - char __maybe_unused - operation[MAX_SEPOL_LEN]; // it is always ioctl now! - char perm_set[MAX_SEPOL_LEN]; + char __maybe_unused + operation[MAX_SEPOL_LEN]; // it is always ioctl now! + char perm_set[MAX_SEPOL_LEN]; - char *s, *t, *c; - if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) { - pr_err("sepol: copy src failed.\n"); - goto exit; - } - if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) { - pr_err("sepol: copy tgt failed.\n"); - goto exit; - } - if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) { - pr_err("sepol: copy cls failed.\n"); - goto exit; - } - if (strncpy_from_user(operation, sepol4, - sizeof(operation)) < 0) { - pr_err("sepol: copy operation failed.\n"); - goto exit; - } - if (strncpy_from_user(perm_set, sepol5, sizeof(perm_set)) < - 0) { - pr_err("sepol: copy perm_set failed.\n"); - goto exit; - } + char *s, *t, *c; + if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) { + pr_err("sepol: copy src failed.\n"); + goto exit; + } + if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) { + pr_err("sepol: copy tgt failed.\n"); + goto exit; + } + if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) { + pr_err("sepol: copy cls failed.\n"); + goto exit; + } + if (strncpy_from_user(operation, sepol4, + sizeof(operation)) < 0) { + pr_err("sepol: copy operation failed.\n"); + goto exit; + } + if (strncpy_from_user(perm_set, sepol5, sizeof(perm_set)) < + 0) { + pr_err("sepol: copy perm_set failed.\n"); + goto exit; + } - bool success = false; - if (subcmd == 1) { - success = ksu_allowxperm(db, s, t, c, perm_set); - } else if (subcmd == 2) { - success = ksu_auditallowxperm(db, s, t, c, perm_set); - } else if (subcmd == 3) { - success = ksu_dontauditxperm(db, s, t, c, perm_set); - } else { - pr_err("sepol: unknown subcmd: %d\n", subcmd); - } - ret = success ? 0 : -1; - } else if (cmd == CMD_TYPE_STATE) { - char src[MAX_SEPOL_LEN]; + bool success = false; + if (subcmd == 1) { + success = ksu_allowxperm(db, s, t, c, perm_set); + } else if (subcmd == 2) { + success = ksu_auditallowxperm(db, s, t, c, perm_set); + } else if (subcmd == 3) { + success = ksu_dontauditxperm(db, s, t, c, perm_set); + } else { + pr_err("sepol: unknown subcmd: %d\n", subcmd); + } + ret = success ? 0 : -1; + } else if (cmd == CMD_TYPE_STATE) { + char src[MAX_SEPOL_LEN]; - if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) { - pr_err("sepol: copy src failed.\n"); - goto exit; - } + if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) { + pr_err("sepol: copy src failed.\n"); + goto exit; + } - bool success = false; - if (subcmd == 1) { - success = ksu_permissive(db, src); - } else if (subcmd == 2) { - success = ksu_enforce(db, src); - } else { - pr_err("sepol: unknown subcmd: %d\n", subcmd); - } - if (success) - ret = 0; + bool success = false; + if (subcmd == 1) { + success = ksu_permissive(db, src); + } else if (subcmd == 2) { + success = ksu_enforce(db, src); + } else { + pr_err("sepol: unknown subcmd: %d\n", subcmd); + } + if (success) + ret = 0; - } else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) { - char type[MAX_SEPOL_LEN]; - char attr[MAX_SEPOL_LEN]; + } else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) { + char type[MAX_SEPOL_LEN]; + char attr[MAX_SEPOL_LEN]; - if (strncpy_from_user(type, sepol1, sizeof(type)) < 0) { - pr_err("sepol: copy type failed.\n"); - goto exit; - } - if (strncpy_from_user(attr, sepol2, sizeof(attr)) < 0) { - pr_err("sepol: copy attr failed.\n"); - goto exit; - } + if (strncpy_from_user(type, sepol1, sizeof(type)) < 0) { + pr_err("sepol: copy type failed.\n"); + goto exit; + } + if (strncpy_from_user(attr, sepol2, sizeof(attr)) < 0) { + pr_err("sepol: copy attr failed.\n"); + goto exit; + } - bool success = false; - if (cmd == CMD_TYPE) { - success = ksu_type(db, type, attr); - } else { - success = ksu_typeattribute(db, type, attr); - } - if (!success) { - pr_err("sepol: %d failed.\n", cmd); - goto exit; - } - ret = 0; + bool success = false; + if (cmd == CMD_TYPE) { + success = ksu_type(db, type, attr); + } else { + success = ksu_typeattribute(db, type, attr); + } + if (!success) { + pr_err("sepol: %d failed.\n", cmd); + goto exit; + } + ret = 0; - } else if (cmd == CMD_ATTR) { - char attr[MAX_SEPOL_LEN]; + } else if (cmd == CMD_ATTR) { + char attr[MAX_SEPOL_LEN]; - if (strncpy_from_user(attr, sepol1, sizeof(attr)) < 0) { - pr_err("sepol: copy attr failed.\n"); - goto exit; - } - if (!ksu_attribute(db, attr)) { - pr_err("sepol: %d failed.\n", cmd); - goto exit; - } - ret = 0; + if (strncpy_from_user(attr, sepol1, sizeof(attr)) < 0) { + pr_err("sepol: copy attr failed.\n"); + goto exit; + } + if (!ksu_attribute(db, attr)) { + pr_err("sepol: %d failed.\n", cmd); + goto exit; + } + ret = 0; - } else if (cmd == CMD_TYPE_TRANSITION) { - char src[MAX_SEPOL_LEN]; - char tgt[MAX_SEPOL_LEN]; - char cls[MAX_SEPOL_LEN]; - char default_type[MAX_SEPOL_LEN]; - char object[MAX_SEPOL_LEN]; + } else if (cmd == CMD_TYPE_TRANSITION) { + char src[MAX_SEPOL_LEN]; + char tgt[MAX_SEPOL_LEN]; + char cls[MAX_SEPOL_LEN]; + char default_type[MAX_SEPOL_LEN]; + char object[MAX_SEPOL_LEN]; - if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) { - pr_err("sepol: copy src failed.\n"); - goto exit; - } - if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) { - pr_err("sepol: copy tgt failed.\n"); - goto exit; - } - if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) { - pr_err("sepol: copy cls failed.\n"); - goto exit; - } - if (strncpy_from_user(default_type, sepol4, - sizeof(default_type)) < 0) { - pr_err("sepol: copy default_type failed.\n"); - goto exit; - } - char *real_object; - if (sepol5 == NULL) { - real_object = NULL; - } else { - if (strncpy_from_user(object, sepol5, - sizeof(object)) < 0) { - pr_err("sepol: copy object failed.\n"); - goto exit; - } - real_object = object; - } + if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) { + pr_err("sepol: copy src failed.\n"); + goto exit; + } + if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) { + pr_err("sepol: copy tgt failed.\n"); + goto exit; + } + if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) { + pr_err("sepol: copy cls failed.\n"); + goto exit; + } + if (strncpy_from_user(default_type, sepol4, + sizeof(default_type)) < 0) { + pr_err("sepol: copy default_type failed.\n"); + goto exit; + } + char *real_object; + if (sepol5 == NULL) { + real_object = NULL; + } else { + if (strncpy_from_user(object, sepol5, + sizeof(object)) < 0) { + pr_err("sepol: copy object failed.\n"); + goto exit; + } + real_object = object; + } - bool success = ksu_type_transition(db, src, tgt, cls, - default_type, real_object); - if (success) - ret = 0; + bool success = ksu_type_transition(db, src, tgt, cls, + default_type, real_object); + if (success) + ret = 0; - } else if (cmd == CMD_TYPE_CHANGE) { - char src[MAX_SEPOL_LEN]; - char tgt[MAX_SEPOL_LEN]; - char cls[MAX_SEPOL_LEN]; - char default_type[MAX_SEPOL_LEN]; + } else if (cmd == CMD_TYPE_CHANGE) { + char src[MAX_SEPOL_LEN]; + char tgt[MAX_SEPOL_LEN]; + char cls[MAX_SEPOL_LEN]; + char default_type[MAX_SEPOL_LEN]; - if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) { - pr_err("sepol: copy src failed.\n"); - goto exit; - } - if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) { - pr_err("sepol: copy tgt failed.\n"); - goto exit; - } - if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) { - pr_err("sepol: copy cls failed.\n"); - goto exit; - } - if (strncpy_from_user(default_type, sepol4, - sizeof(default_type)) < 0) { - pr_err("sepol: copy default_type failed.\n"); - goto exit; - } - bool success = false; - if (subcmd == 1) { - success = ksu_type_change(db, src, tgt, cls, - default_type); - } else if (subcmd == 2) { - success = ksu_type_member(db, src, tgt, cls, - default_type); - } else { - pr_err("sepol: unknown subcmd: %d\n", subcmd); - } - if (success) - ret = 0; - } else if (cmd == CMD_GENFSCON) { - char name[MAX_SEPOL_LEN]; - char path[MAX_SEPOL_LEN]; - char context[MAX_SEPOL_LEN]; - if (strncpy_from_user(name, sepol1, sizeof(name)) < 0) { - pr_err("sepol: copy name failed.\n"); - goto exit; - } - if (strncpy_from_user(path, sepol2, sizeof(path)) < 0) { - pr_err("sepol: copy path failed.\n"); - goto exit; - } - if (strncpy_from_user(context, sepol3, sizeof(context)) < - 0) { - pr_err("sepol: copy context failed.\n"); - goto exit; - } + if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) { + pr_err("sepol: copy src failed.\n"); + goto exit; + } + if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) { + pr_err("sepol: copy tgt failed.\n"); + goto exit; + } + if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) { + pr_err("sepol: copy cls failed.\n"); + goto exit; + } + if (strncpy_from_user(default_type, sepol4, + sizeof(default_type)) < 0) { + pr_err("sepol: copy default_type failed.\n"); + goto exit; + } + bool success = false; + if (subcmd == 1) { + success = ksu_type_change(db, src, tgt, cls, + default_type); + } else if (subcmd == 2) { + success = ksu_type_member(db, src, tgt, cls, + default_type); + } else { + pr_err("sepol: unknown subcmd: %d\n", subcmd); + } + if (success) + ret = 0; + } else if (cmd == CMD_GENFSCON) { + char name[MAX_SEPOL_LEN]; + char path[MAX_SEPOL_LEN]; + char context[MAX_SEPOL_LEN]; + if (strncpy_from_user(name, sepol1, sizeof(name)) < 0) { + pr_err("sepol: copy name failed.\n"); + goto exit; + } + if (strncpy_from_user(path, sepol2, sizeof(path)) < 0) { + pr_err("sepol: copy path failed.\n"); + goto exit; + } + if (strncpy_from_user(context, sepol3, sizeof(context)) < + 0) { + pr_err("sepol: copy context failed.\n"); + goto exit; + } - if (!ksu_genfscon(db, name, path, context)) { - pr_err("sepol: %d failed.\n", cmd); - goto exit; - } - ret = 0; - } else { - pr_err("sepol: unknown cmd: %d\n", cmd); - } + if (!ksu_genfscon(db, name, path, context)) { + pr_err("sepol: %d failed.\n", cmd); + goto exit; + } + ret = 0; + } else { + pr_err("sepol: unknown cmd: %d\n", cmd); + } exit: - mutex_unlock(&ksu_rules); + mutex_unlock(&ksu_rules); - // 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. - reset_avc_cache(); + // 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. + reset_avc_cache(); - return ret; + return ret; } diff --git a/kernel/selinux/selinux.c b/kernel/selinux/selinux.c index e4b5a5aa..c95952a0 100644 --- a/kernel/selinux/selinux.c +++ b/kernel/selinux/selinux.c @@ -7,41 +7,41 @@ static int transive_to_domain(const char *domain) { - struct cred *cred; - struct task_security_struct *tsec; - u32 sid; - int error; + struct cred *cred; + struct task_security_struct *tsec; + u32 sid; + int error; - cred = (struct cred *)__task_cred(current); + cred = (struct cred *)__task_cred(current); - tsec = cred->security; - if (!tsec) { - pr_err("tsec == NULL!\n"); - return -1; - } + tsec = cred->security; + if (!tsec) { + pr_err("tsec == NULL!\n"); + return -1; + } - error = security_secctx_to_secid(domain, strlen(domain), &sid); - if (error) { - pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n", - domain, sid, error); - } - if (!error) { - tsec->sid = sid; - tsec->create_sid = 0; - tsec->keycreate_sid = 0; - tsec->sockcreate_sid = 0; - } - return error; + error = security_secctx_to_secid(domain, strlen(domain), &sid); + if (error) { + pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n", + domain, sid, error); + } + if (!error) { + tsec->sid = sid; + tsec->create_sid = 0; + tsec->keycreate_sid = 0; + tsec->sockcreate_sid = 0; + } + return error; } void setup_selinux(const char *domain) { - if (transive_to_domain(domain)) { - pr_err("transive domain failed.\n"); - return; - } + if (transive_to_domain(domain)) { + pr_err("transive domain failed.\n"); + return; + } - /* we didn't need this now, we have change selinux rules when boot! + /* we didn't need this now, we have change selinux rules when boot! if (!is_domain_permissive) { if (set_domain_permissive() == 0) { is_domain_permissive = true; @@ -52,109 +52,109 @@ if (!is_domain_permissive) { void setenforce(bool enforce) { #ifdef CONFIG_SECURITY_SELINUX_DEVELOP - selinux_state.enforcing = enforce; + selinux_state.enforcing = enforce; #endif } bool getenforce() { #ifdef CONFIG_SECURITY_SELINUX_DISABLE - if (selinux_state.disabled) { - return false; - } + if (selinux_state.disabled) { + return false; + } #endif #ifdef CONFIG_SECURITY_SELINUX_DEVELOP - return selinux_state.enforcing; + return selinux_state.enforcing; #else - return true; + return true; #endif } #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 */ 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 bool is_ksu_domain() { #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0) - struct lsm_context ctx; + struct lsm_context ctx; #else - char *domain; - u32 seclen; + char *domain; + u32 seclen; #endif - bool result; + bool result; #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 - int err = security_secid_to_secctx(current_sid(), &domain, &seclen); + int err = security_secid_to_secctx(current_sid(), &domain, &seclen); #endif - if (err) { - return false; - } + if (err) { + return false; + } #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0) - result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0; - security_release_secctx(&ctx); + result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0; + security_release_secctx(&ctx); #else - result = strncmp(KERNEL_SU_DOMAIN, domain, seclen) == 0; - security_release_secctx(domain, seclen); + result = strncmp(KERNEL_SU_DOMAIN, domain, seclen) == 0; + security_release_secctx(domain, seclen); #endif - return result; + return result; } bool is_zygote(void *sec) { - struct task_security_struct *tsec = (struct task_security_struct *)sec; - if (!tsec) { - return false; - } + struct task_security_struct *tsec = (struct task_security_struct *)sec; + if (!tsec) { + return false; + } #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0) - struct lsm_context ctx; + struct lsm_context ctx; #else - char *domain; - u32 seclen; + char *domain; + u32 seclen; #endif - bool result; + bool result; #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 - int err = security_secid_to_secctx(tsec->sid, &domain, &seclen); + int err = security_secid_to_secctx(tsec->sid, &domain, &seclen); #endif - if (err) { - return false; - } + if (err) { + return false; + } #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0) - result = strncmp("u:r:zygote:s0", ctx.context, ctx.len) == 0; - security_release_secctx(&ctx); + result = strncmp("u:r:zygote:s0", ctx.context, ctx.len) == 0; + security_release_secctx(&ctx); #else - result = strncmp("u:r:zygote:s0", domain, seclen) == 0; - security_release_secctx(domain, seclen); + result = strncmp("u:r:zygote:s0", domain, seclen) == 0; + security_release_secctx(domain, seclen); #endif - return result; + return result; } #define DEVPTS_DOMAIN "u:object_r:ksu_file:s0" u32 ksu_get_devpts_sid() { - u32 devpts_sid = 0; - int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN), - &devpts_sid); - if (err) { - pr_info("get devpts sid err %d\n", err); - } - return devpts_sid; + u32 devpts_sid = 0; + int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN), + &devpts_sid); + if (err) { + pr_info("get devpts sid err %d\n", err); + } + return devpts_sid; } diff --git a/kernel/selinux/sepolicy.c b/kernel/selinux/sepolicy.c index b7997de8..8d128c1f 100644 --- a/kernel/selinux/sepolicy.c +++ b/kernel/selinux/sepolicy.c @@ -15,44 +15,44 @@ ////////////////////////////////////////////////////// static struct avtab_node *get_avtab_node(struct policydb *db, - struct avtab_key *key, - struct avtab_extended_perms *xperms); + struct avtab_key *key, + struct avtab_extended_perms *xperms); static bool add_rule(struct policydb *db, const char *s, const char *t, - const char *c, const char *p, int effect, bool invert); + const char *c, const char *p, int effect, bool invert); static void add_rule_raw(struct policydb *db, struct type_datum *src, - struct type_datum *tgt, struct class_datum *cls, - struct perm_datum *perm, int effect, bool invert); + struct type_datum *tgt, struct class_datum *cls, + struct perm_datum *perm, int effect, bool invert); static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src, - struct type_datum *tgt, struct class_datum *cls, - uint16_t low, uint16_t high, int effect, - bool invert); + struct type_datum *tgt, struct class_datum *cls, + uint16_t low, uint16_t high, int effect, + bool invert); static bool add_xperm_rule(struct policydb *db, const char *s, const char *t, - const char *c, const char *range, int effect, - bool invert); + const char *c, const char *range, int effect, + bool invert); static bool add_type_rule(struct policydb *db, const char *s, const char *t, - const char *c, const char *d, int effect); + const char *c, const char *d, int effect); static bool add_filename_trans(struct policydb *db, const char *s, - const char *t, const char *c, const char *d, - const char *o); + const char *t, const char *c, const char *d, + const char *o); static bool add_genfscon(struct policydb *db, const char *fs_name, - const char *path, const char *context); + const char *path, const char *context); static bool add_type(struct policydb *db, const char *type_name, bool attr); static bool set_type_state(struct policydb *db, const char *type_name, - bool permissive); + bool permissive); static void add_typeattribute_raw(struct policydb *db, struct type_datum *type, - struct type_datum *attr); + struct type_datum *attr); static bool add_typeattribute(struct policydb *db, const char *type, - const char *attr); + const char *attr); ////////////////////////////////////////////////////// // Implementation @@ -63,18 +63,18 @@ static bool add_typeattribute(struct policydb *db, const char *type, #define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert) #define ksu_hash_for_each(node_ptr, n_slot, cur) \ - int i; \ - for (i = 0; i < n_slot; ++i) \ - for (cur = node_ptr[i]; cur; cur = cur->next) + int i; \ + for (i = 0; i < n_slot; ++i) \ + for (cur = node_ptr[i]; cur; cur = cur->next) // htable is a struct instead of pointer above 5.8.0: // https://elixir.bootlin.com/linux/v5.8-rc1/source/security/selinux/ss/symtab.h #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) #define ksu_hashtab_for_each(htab, cur) \ - ksu_hash_for_each(htab.htable, htab.size, cur) + ksu_hash_for_each(htab.htable, htab.size, cur) #else #define ksu_hashtab_for_each(htab, cur) \ - ksu_hash_for_each(htab->htable, htab->size, cur) + ksu_hash_for_each(htab->htable, htab->size, cur) #endif // symtab_search is introduced on 5.9.0: @@ -85,186 +85,186 @@ static bool add_typeattribute(struct policydb *db, const char *type, #endif #define avtab_for_each(avtab, cur) \ - ksu_hash_for_each(avtab.htable, avtab.nslot, cur); + ksu_hash_for_each(avtab.htable, avtab.nslot, cur); static struct avtab_node *get_avtab_node(struct policydb *db, - struct avtab_key *key, - struct avtab_extended_perms *xperms) + struct avtab_key *key, + struct avtab_extended_perms *xperms) { - struct avtab_node *node; + struct avtab_node *node; - /* AVTAB_XPERMS entries are not necessarily unique */ - if (key->specified & AVTAB_XPERMS) { - bool match = false; - node = avtab_search_node(&db->te_avtab, key); - while (node) { - if ((node->datum.u.xperms->specified == - xperms->specified) && - (node->datum.u.xperms->driver == xperms->driver)) { - match = true; - break; - } - node = avtab_search_node_next(node, key->specified); - } - if (!match) - node = NULL; - } else { - node = avtab_search_node(&db->te_avtab, key); - } + /* AVTAB_XPERMS entries are not necessarily unique */ + if (key->specified & AVTAB_XPERMS) { + bool match = false; + node = avtab_search_node(&db->te_avtab, key); + while (node) { + if ((node->datum.u.xperms->specified == + xperms->specified) && + (node->datum.u.xperms->driver == xperms->driver)) { + match = true; + break; + } + node = avtab_search_node_next(node, key->specified); + } + if (!match) + node = NULL; + } else { + node = avtab_search_node(&db->te_avtab, key); + } - if (!node) { - struct avtab_datum avdatum = {}; - /* + if (!node) { + struct avtab_datum avdatum = {}; + /* * AUDITDENY, aka DONTAUDIT, are &= assigned, versus |= for * others. Initialize the data accordingly. */ - if (key->specified & AVTAB_XPERMS) { - avdatum.u.xperms = xperms; - } else { - avdatum.u.data = - key->specified == AVTAB_AUDITDENY ? ~0U : 0U; - } - /* this is used to get the node - insertion is actually unique */ - node = avtab_insert_nonunique(&db->te_avtab, key, &avdatum); + if (key->specified & AVTAB_XPERMS) { + avdatum.u.xperms = xperms; + } else { + avdatum.u.data = + key->specified == AVTAB_AUDITDENY ? ~0U : 0U; + } + /* this is used to get the node - insertion is actually unique */ + node = avtab_insert_nonunique(&db->te_avtab, key, &avdatum); - int grow_size = sizeof(struct avtab_key); - grow_size += sizeof(struct avtab_datum); - if (key->specified & AVTAB_XPERMS) { - grow_size += sizeof(u8); - grow_size += sizeof(u8); - grow_size += sizeof(u32) * - ARRAY_SIZE(avdatum.u.xperms->perms.p); - } - db->len += grow_size; - } + int grow_size = sizeof(struct avtab_key); + grow_size += sizeof(struct avtab_datum); + if (key->specified & AVTAB_XPERMS) { + grow_size += sizeof(u8); + grow_size += sizeof(u8); + grow_size += sizeof(u32) * + ARRAY_SIZE(avdatum.u.xperms->perms.p); + } + db->len += grow_size; + } - return node; + return node; } static bool add_rule(struct policydb *db, const char *s, const char *t, - const char *c, const char *p, int effect, bool invert) + const char *c, const char *p, int effect, bool invert) { - struct type_datum *src = NULL, *tgt = NULL; - struct class_datum *cls = NULL; - struct perm_datum *perm = NULL; + struct type_datum *src = NULL, *tgt = NULL; + struct class_datum *cls = NULL; + struct perm_datum *perm = NULL; - if (s) { - src = symtab_search(&db->p_types, s); - if (src == NULL) { - pr_info("source type %s does not exist\n", s); - return false; - } - } + if (s) { + src = symtab_search(&db->p_types, s); + if (src == NULL) { + pr_info("source type %s does not exist\n", s); + return false; + } + } - if (t) { - tgt = symtab_search(&db->p_types, t); - if (tgt == NULL) { - pr_info("target type %s does not exist\n", t); - return false; - } - } + if (t) { + tgt = symtab_search(&db->p_types, t); + if (tgt == NULL) { + pr_info("target type %s does not exist\n", t); + return false; + } + } - if (c) { - cls = symtab_search(&db->p_classes, c); - if (cls == NULL) { - pr_info("class %s does not exist\n", c); - return false; - } - } + if (c) { + cls = symtab_search(&db->p_classes, c); + if (cls == NULL) { + pr_info("class %s does not exist\n", c); + return false; + } + } - if (p) { - if (c == NULL) { - pr_info("No class is specified, cannot add perm [%s] \n", - p); - return false; - } + if (p) { + if (c == NULL) { + pr_info("No class is specified, cannot add perm [%s] \n", + p); + return false; + } - perm = symtab_search(&cls->permissions, p); - if (perm == NULL && cls->comdatum != NULL) { - perm = symtab_search(&cls->comdatum->permissions, p); - } - if (perm == NULL) { - pr_info("perm %s does not exist in class %s\n", p, c); - return false; - } - } - add_rule_raw(db, src, tgt, cls, perm, effect, invert); - return true; + perm = symtab_search(&cls->permissions, p); + if (perm == NULL && cls->comdatum != NULL) { + perm = symtab_search(&cls->comdatum->permissions, p); + } + if (perm == NULL) { + pr_info("perm %s does not exist in class %s\n", p, c); + return false; + } + } + add_rule_raw(db, src, tgt, cls, perm, effect, invert); + return true; } static void add_rule_raw(struct policydb *db, struct type_datum *src, - struct type_datum *tgt, struct class_datum *cls, - struct perm_datum *perm, int effect, bool invert) + struct type_datum *tgt, struct class_datum *cls, + struct perm_datum *perm, int effect, bool invert) { - if (src == NULL) { - struct hashtab_node *node; - if (strip_av(effect, invert)) { - ksu_hashtab_for_each(db->p_types.table, node) - { - add_rule_raw(db, - (struct type_datum *)node->datum, - tgt, cls, perm, effect, invert); - }; - } else { - ksu_hashtab_for_each(db->p_types.table, node) - { - struct type_datum *type = - (struct type_datum *)(node->datum); - if (type->attribute) { - add_rule_raw(db, type, tgt, cls, perm, - effect, invert); - } - }; - } - } else if (tgt == NULL) { - struct hashtab_node *node; - if (strip_av(effect, invert)) { - ksu_hashtab_for_each(db->p_types.table, node) - { - add_rule_raw(db, src, - (struct type_datum *)node->datum, - cls, perm, effect, invert); - }; - } else { - ksu_hashtab_for_each(db->p_types.table, node) - { - struct type_datum *type = - (struct type_datum *)(node->datum); - if (type->attribute) { - add_rule_raw(db, src, type, cls, perm, - effect, invert); - } - }; - } - } else if (cls == NULL) { - struct hashtab_node *node; - ksu_hashtab_for_each(db->p_classes.table, node) - { - add_rule_raw(db, src, tgt, - (struct class_datum *)node->datum, perm, - effect, invert); - } - } else { - struct avtab_key key; - key.source_type = src->value; - key.target_type = tgt->value; - key.target_class = cls->value; - key.specified = effect; + if (src == NULL) { + struct hashtab_node *node; + if (strip_av(effect, invert)) { + ksu_hashtab_for_each(db->p_types.table, node) + { + add_rule_raw(db, + (struct type_datum *)node->datum, + tgt, cls, perm, effect, invert); + }; + } else { + ksu_hashtab_for_each(db->p_types.table, node) + { + struct type_datum *type = + (struct type_datum *)(node->datum); + if (type->attribute) { + add_rule_raw(db, type, tgt, cls, perm, + effect, invert); + } + }; + } + } else if (tgt == NULL) { + struct hashtab_node *node; + if (strip_av(effect, invert)) { + ksu_hashtab_for_each(db->p_types.table, node) + { + add_rule_raw(db, src, + (struct type_datum *)node->datum, + cls, perm, effect, invert); + }; + } else { + ksu_hashtab_for_each(db->p_types.table, node) + { + struct type_datum *type = + (struct type_datum *)(node->datum); + if (type->attribute) { + add_rule_raw(db, src, type, cls, perm, + effect, invert); + } + }; + } + } else if (cls == NULL) { + struct hashtab_node *node; + ksu_hashtab_for_each(db->p_classes.table, node) + { + add_rule_raw(db, src, tgt, + (struct class_datum *)node->datum, perm, + effect, invert); + } + } else { + struct avtab_key key; + key.source_type = src->value; + key.target_type = tgt->value; + key.target_class = cls->value; + key.specified = effect; - struct avtab_node *node = get_avtab_node(db, &key, NULL); - if (invert) { - if (perm) - node->datum.u.data &= - ~(1U << (perm->value - 1)); - else - node->datum.u.data = 0U; - } else { - if (perm) - node->datum.u.data |= 1U << (perm->value - 1); - else - node->datum.u.data = ~0U; - } - } + struct avtab_node *node = get_avtab_node(db, &key, NULL); + if (invert) { + if (perm) + node->datum.u.data &= + ~(1U << (perm->value - 1)); + else + node->datum.u.data = 0U; + } else { + if (perm) + node->datum.u.data |= 1U << (perm->value - 1); + else + node->datum.u.data = ~0U; + } + } } #define ioctl_driver(x) (x >> 8 & 0xFF) @@ -275,183 +275,183 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src, #define xperm_clear(x, p) (p[x >> 5] &= ~(1 << (x & 0x1f))) static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src, - struct type_datum *tgt, struct class_datum *cls, - uint16_t low, uint16_t high, int effect, - bool invert) + struct type_datum *tgt, struct class_datum *cls, + uint16_t low, uint16_t high, int effect, + bool invert) { - if (src == NULL) { - struct hashtab_node *node; - ksu_hashtab_for_each(db->p_types.table, node) - { - struct type_datum *type = - (struct type_datum *)(node->datum); - if (type->attribute) { - add_xperm_rule_raw(db, type, tgt, cls, low, - high, effect, invert); - } - }; - } else if (tgt == NULL) { - struct hashtab_node *node; - ksu_hashtab_for_each(db->p_types.table, node) - { - struct type_datum *type = - (struct type_datum *)(node->datum); - if (type->attribute) { - add_xperm_rule_raw(db, src, type, cls, low, - high, effect, invert); - } - }; - } else if (cls == NULL) { - struct hashtab_node *node; - ksu_hashtab_for_each(db->p_classes.table, node) - { - add_xperm_rule_raw(db, src, tgt, - (struct class_datum *)(node->datum), - low, high, effect, invert); - }; - } else { - struct avtab_key key; - key.source_type = src->value; - key.target_type = tgt->value; - key.target_class = cls->value; - key.specified = effect; + if (src == NULL) { + struct hashtab_node *node; + ksu_hashtab_for_each(db->p_types.table, node) + { + struct type_datum *type = + (struct type_datum *)(node->datum); + if (type->attribute) { + add_xperm_rule_raw(db, type, tgt, cls, low, + high, effect, invert); + } + }; + } else if (tgt == NULL) { + struct hashtab_node *node; + ksu_hashtab_for_each(db->p_types.table, node) + { + struct type_datum *type = + (struct type_datum *)(node->datum); + if (type->attribute) { + add_xperm_rule_raw(db, src, type, cls, low, + high, effect, invert); + } + }; + } else if (cls == NULL) { + struct hashtab_node *node; + ksu_hashtab_for_each(db->p_classes.table, node) + { + add_xperm_rule_raw(db, src, tgt, + (struct class_datum *)(node->datum), + low, high, effect, invert); + }; + } else { + struct avtab_key key; + key.source_type = src->value; + key.target_type = tgt->value; + key.target_class = cls->value; + key.specified = effect; - struct avtab_datum *datum; - struct avtab_node *node; - struct avtab_extended_perms xperms; + struct avtab_datum *datum; + struct avtab_node *node; + struct avtab_extended_perms xperms; - memset(&xperms, 0, sizeof(xperms)); - if (ioctl_driver(low) != ioctl_driver(high)) { - xperms.specified = AVTAB_XPERMS_IOCTLDRIVER; - xperms.driver = 0; - } else { - xperms.specified = AVTAB_XPERMS_IOCTLFUNCTION; - xperms.driver = ioctl_driver(low); - } - int i; - if (xperms.specified == AVTAB_XPERMS_IOCTLDRIVER) { - for (i = ioctl_driver(low); i <= ioctl_driver(high); - ++i) { - if (invert) - xperm_clear(i, xperms.perms.p); - else - xperm_set(i, xperms.perms.p); - } - } else { - for (i = ioctl_func(low); i <= ioctl_func(high); ++i) { - if (invert) - xperm_clear(i, xperms.perms.p); - else - xperm_set(i, xperms.perms.p); - } - } + memset(&xperms, 0, sizeof(xperms)); + if (ioctl_driver(low) != ioctl_driver(high)) { + xperms.specified = AVTAB_XPERMS_IOCTLDRIVER; + xperms.driver = 0; + } else { + xperms.specified = AVTAB_XPERMS_IOCTLFUNCTION; + xperms.driver = ioctl_driver(low); + } + int i; + if (xperms.specified == AVTAB_XPERMS_IOCTLDRIVER) { + for (i = ioctl_driver(low); i <= ioctl_driver(high); + ++i) { + if (invert) + xperm_clear(i, xperms.perms.p); + else + xperm_set(i, xperms.perms.p); + } + } else { + for (i = ioctl_func(low); i <= ioctl_func(high); ++i) { + if (invert) + xperm_clear(i, xperms.perms.p); + else + xperm_set(i, xperms.perms.p); + } + } - node = get_avtab_node(db, &key, &xperms); - if (!node) { - pr_warn("add_xperm_rule_raw cannot found node!\n"); - return; - } - datum = &node->datum; + node = get_avtab_node(db, &key, &xperms); + if (!node) { + pr_warn("add_xperm_rule_raw cannot found node!\n"); + return; + } + datum = &node->datum; - if (datum->u.xperms == NULL) { - datum->u.xperms = - (struct avtab_extended_perms *)(kmalloc( - sizeof(xperms), GFP_KERNEL)); - if (!datum->u.xperms) { - pr_err("alloc xperms failed\n"); - return; - } - memcpy(datum->u.xperms, &xperms, sizeof(xperms)); - } - } + if (datum->u.xperms == NULL) { + datum->u.xperms = + (struct avtab_extended_perms *)(kmalloc( + sizeof(xperms), GFP_KERNEL)); + if (!datum->u.xperms) { + pr_err("alloc xperms failed\n"); + return; + } + memcpy(datum->u.xperms, &xperms, sizeof(xperms)); + } + } } static bool add_xperm_rule(struct policydb *db, const char *s, const char *t, - const char *c, const char *range, int effect, - bool invert) + const char *c, const char *range, int effect, + bool invert) { - struct type_datum *src = NULL, *tgt = NULL; - struct class_datum *cls = NULL; + struct type_datum *src = NULL, *tgt = NULL; + struct class_datum *cls = NULL; - if (s) { - src = symtab_search(&db->p_types, s); - if (src == NULL) { - pr_info("source type %s does not exist\n", s); - return false; - } - } + if (s) { + src = symtab_search(&db->p_types, s); + if (src == NULL) { + pr_info("source type %s does not exist\n", s); + return false; + } + } - if (t) { - tgt = symtab_search(&db->p_types, t); - if (tgt == NULL) { - pr_info("target type %s does not exist\n", t); - return false; - } - } + if (t) { + tgt = symtab_search(&db->p_types, t); + if (tgt == NULL) { + pr_info("target type %s does not exist\n", t); + return false; + } + } - if (c) { - cls = symtab_search(&db->p_classes, c); - if (cls == NULL) { - pr_info("class %s does not exist\n", c); - return false; - } - } + if (c) { + cls = symtab_search(&db->p_classes, c); + if (cls == NULL) { + pr_info("class %s does not exist\n", c); + return false; + } + } - u16 low, high; + u16 low, high; - if (range) { - if (strchr(range, '-')) { - sscanf(range, "%hx-%hx", &low, &high); - } else { - sscanf(range, "%hx", &low); - high = low; - } - } else { - low = 0; - high = 0xFFFF; - } + if (range) { + if (strchr(range, '-')) { + sscanf(range, "%hx-%hx", &low, &high); + } else { + sscanf(range, "%hx", &low); + high = low; + } + } else { + low = 0; + high = 0xFFFF; + } - add_xperm_rule_raw(db, src, tgt, cls, low, high, effect, invert); - return true; + add_xperm_rule_raw(db, src, tgt, cls, low, high, effect, invert); + return true; } static bool add_type_rule(struct policydb *db, const char *s, const char *t, - const char *c, const char *d, int effect) + const char *c, const char *d, int effect) { - struct type_datum *src, *tgt, *def; - struct class_datum *cls; + struct type_datum *src, *tgt, *def; + struct class_datum *cls; - src = symtab_search(&db->p_types, s); - if (src == NULL) { - pr_info("source type %s does not exist\n", s); - return false; - } - tgt = symtab_search(&db->p_types, t); - if (tgt == NULL) { - pr_info("target type %s does not exist\n", t); - return false; - } - cls = symtab_search(&db->p_classes, c); - if (cls == NULL) { - pr_info("class %s does not exist\n", c); - return false; - } - def = symtab_search(&db->p_types, d); - if (def == NULL) { - pr_info("default type %s does not exist\n", d); - return false; - } + src = symtab_search(&db->p_types, s); + if (src == NULL) { + pr_info("source type %s does not exist\n", s); + return false; + } + tgt = symtab_search(&db->p_types, t); + if (tgt == NULL) { + pr_info("target type %s does not exist\n", t); + return false; + } + cls = symtab_search(&db->p_classes, c); + if (cls == NULL) { + pr_info("class %s does not exist\n", c); + return false; + } + def = symtab_search(&db->p_types, d); + if (def == NULL) { + pr_info("default type %s does not exist\n", d); + return false; + } - struct avtab_key key; - key.source_type = src->value; - key.target_type = tgt->value; - key.target_class = cls->value; - key.specified = effect; + struct avtab_key key; + key.source_type = src->value; + key.target_type = tgt->value; + key.target_class = cls->value; + key.specified = effect; - struct avtab_node *node = get_avtab_node(db, &key, NULL); - node->datum.u.data = def->value; + struct avtab_node *node = get_avtab_node(db, &key, NULL); + node->datum.u.data = def->value; - return true; + return true; } // 5.9.0 : static inline int hashtab_insert(struct hashtab *h, void *key, void @@ -460,287 +460,287 @@ static bool add_type_rule(struct policydb *db, const char *s, const char *t, #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) static u32 filenametr_hash(const void *k) { - const struct filename_trans_key *ft = k; - unsigned long hash; - unsigned int byte_num; - unsigned char focus; + const struct filename_trans_key *ft = k; + unsigned long hash; + unsigned int byte_num; + unsigned char focus; - hash = ft->ttype ^ ft->tclass; + hash = ft->ttype ^ ft->tclass; - byte_num = 0; - while ((focus = ft->name[byte_num++])) - hash = partial_name_hash(focus, hash); - return hash; + byte_num = 0; + while ((focus = ft->name[byte_num++])) + hash = partial_name_hash(focus, hash); + return hash; } static int filenametr_cmp(const void *k1, const void *k2) { - const struct filename_trans_key *ft1 = k1; - const struct filename_trans_key *ft2 = k2; - int v; + const struct filename_trans_key *ft1 = k1; + const struct filename_trans_key *ft2 = k2; + int v; - v = ft1->ttype - ft2->ttype; - if (v) - return v; + v = ft1->ttype - ft2->ttype; + if (v) + return v; - v = ft1->tclass - ft2->tclass; - if (v) - return v; + v = ft1->tclass - ft2->tclass; + if (v) + return v; - return strcmp(ft1->name, ft2->name); + return strcmp(ft1->name, ft2->name); } static const struct hashtab_key_params filenametr_key_params = { - .hash = filenametr_hash, - .cmp = filenametr_cmp, + .hash = filenametr_hash, + .cmp = filenametr_cmp, }; #endif static bool add_filename_trans(struct policydb *db, const char *s, - const char *t, const char *c, const char *d, - const char *o) + const char *t, const char *c, const char *d, + const char *o) { - struct type_datum *src, *tgt, *def; - struct class_datum *cls; + struct type_datum *src, *tgt, *def; + struct class_datum *cls; - src = symtab_search(&db->p_types, s); - if (src == NULL) { - pr_warn("source type %s does not exist\n", s); - return false; - } - tgt = symtab_search(&db->p_types, t); - if (tgt == NULL) { - pr_warn("target type %s does not exist\n", t); - return false; - } - cls = symtab_search(&db->p_classes, c); - if (cls == NULL) { - pr_warn("class %s does not exist\n", c); - return false; - } - def = symtab_search(&db->p_types, d); - if (def == NULL) { - pr_warn("default type %s does not exist\n", d); - return false; - } + src = symtab_search(&db->p_types, s); + if (src == NULL) { + pr_warn("source type %s does not exist\n", s); + return false; + } + tgt = symtab_search(&db->p_types, t); + if (tgt == NULL) { + pr_warn("target type %s does not exist\n", t); + return false; + } + cls = symtab_search(&db->p_classes, c); + if (cls == NULL) { + pr_warn("class %s does not exist\n", c); + return false; + } + def = symtab_search(&db->p_types, d); + if (def == NULL) { + pr_warn("default type %s does not exist\n", d); + return false; + } - struct filename_trans_key key; - key.ttype = tgt->value; - key.tclass = cls->value; - key.name = (char *)o; + struct filename_trans_key key; + key.ttype = tgt->value; + key.tclass = cls->value; + key.name = (char *)o; - struct filename_trans_datum *last = NULL; + struct filename_trans_datum *last = NULL; - struct filename_trans_datum *trans = - policydb_filenametr_search(db, &key); - while (trans) { - if (ebitmap_get_bit(&trans->stypes, src->value - 1)) { - // Duplicate, overwrite existing data and return - trans->otype = def->value; - return true; - } - if (trans->otype == def->value) - break; - last = trans; - trans = trans->next; - } + struct filename_trans_datum *trans = + policydb_filenametr_search(db, &key); + while (trans) { + if (ebitmap_get_bit(&trans->stypes, src->value - 1)) { + // Duplicate, overwrite existing data and return + trans->otype = def->value; + return true; + } + if (trans->otype == def->value) + break; + last = trans; + trans = trans->next; + } - if (trans == NULL) { - trans = (struct filename_trans_datum *)kcalloc(1 ,sizeof(*trans), - GFP_ATOMIC); - struct filename_trans_key *new_key = - (struct filename_trans_key *)kmalloc(sizeof(*new_key), - GFP_ATOMIC); - *new_key = key; - new_key->name = kstrdup(key.name, GFP_ATOMIC); - trans->next = last; - trans->otype = def->value; - hashtab_insert(&db->filename_trans, new_key, trans, - filenametr_key_params); - } + if (trans == NULL) { + trans = (struct filename_trans_datum *)kcalloc(1 ,sizeof(*trans), + GFP_ATOMIC); + struct filename_trans_key *new_key = + (struct filename_trans_key *)kmalloc(sizeof(*new_key), + GFP_ATOMIC); + *new_key = key; + new_key->name = kstrdup(key.name, GFP_ATOMIC); + trans->next = last; + trans->otype = def->value; + hashtab_insert(&db->filename_trans, new_key, trans, + filenametr_key_params); + } - db->compat_filename_trans_count++; - return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0; + db->compat_filename_trans_count++; + return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0; } static bool add_genfscon(struct policydb *db, const char *fs_name, - const char *path, const char *context) + const char *path, const char *context) { - return false; + return false; } static void *ksu_realloc(void *old, size_t new_size, size_t old_size) { - // we can't use krealloc, because it may be read-only - void *new = kzalloc(new_size, GFP_ATOMIC); - if (!new) { - return NULL; - } - if (old_size) { - memcpy(new, old, old_size); - } - // we can't use kfree, because it may be read-only - // there maybe some leaks, maybe we can check ptr_write, but it's not a big deal - // kfree(old); - return new; + // we can't use krealloc, because it may be read-only + void *new = kzalloc(new_size, GFP_ATOMIC); + if (!new) { + return NULL; + } + if (old_size) { + memcpy(new, old, old_size); + } + // we can't use kfree, because it may be read-only + // there maybe some leaks, maybe we can check ptr_write, but it's not a big deal + // kfree(old); + return new; } static bool add_type(struct policydb *db, const char *type_name, bool attr) { - struct type_datum *type = symtab_search(&db->p_types, type_name); - if (type) { - pr_warn("Type %s already exists\n", type_name); - return true; - } + struct type_datum *type = symtab_search(&db->p_types, type_name); + if (type) { + pr_warn("Type %s already exists\n", type_name); + return true; + } - u32 value = ++db->p_types.nprim; - type = (struct type_datum *)kzalloc(sizeof(struct type_datum), - GFP_ATOMIC); - if (!type) { - pr_err("add_type: alloc type_datum failed.\n"); - return false; - } + u32 value = ++db->p_types.nprim; + type = (struct type_datum *)kzalloc(sizeof(struct type_datum), + GFP_ATOMIC); + if (!type) { + pr_err("add_type: alloc type_datum failed.\n"); + return false; + } - type->primary = 1; - type->value = value; - type->attribute = attr; + type->primary = 1; + type->value = value; + type->attribute = attr; - char *key = kstrdup(type_name, GFP_ATOMIC); - if (!key) { - pr_err("add_type: alloc key failed.\n"); - return false; - } + char *key = kstrdup(type_name, GFP_ATOMIC); + if (!key) { + pr_err("add_type: alloc key failed.\n"); + return false; + } - if (symtab_insert(&db->p_types, key, type)) { - pr_err("add_type: insert symtab failed.\n"); - return false; - } + if (symtab_insert(&db->p_types, key, type)) { + pr_err("add_type: insert symtab failed.\n"); + return false; + } - struct ebitmap *new_type_attr_map_array = - ksu_realloc(db->type_attr_map_array, - value * sizeof(struct ebitmap), - (value - 1) * sizeof(struct ebitmap)); + struct ebitmap *new_type_attr_map_array = + ksu_realloc(db->type_attr_map_array, + value * sizeof(struct ebitmap), + (value - 1) * sizeof(struct ebitmap)); - if (!new_type_attr_map_array) { - pr_err("add_type: alloc type_attr_map_array failed\n"); - return false; - } + if (!new_type_attr_map_array) { + pr_err("add_type: alloc type_attr_map_array failed\n"); + return false; + } - struct type_datum **new_type_val_to_struct = - ksu_realloc(db->type_val_to_struct, - sizeof(*db->type_val_to_struct) * value, - sizeof(*db->type_val_to_struct) * (value - 1)); + struct type_datum **new_type_val_to_struct = + ksu_realloc(db->type_val_to_struct, + sizeof(*db->type_val_to_struct) * value, + sizeof(*db->type_val_to_struct) * (value - 1)); - if (!new_type_val_to_struct) { - pr_err("add_type: alloc type_val_to_struct failed\n"); - return false; - } + if (!new_type_val_to_struct) { + pr_err("add_type: alloc type_val_to_struct failed\n"); + return false; + } - char **new_val_to_name_types = - ksu_realloc(db->sym_val_to_name[SYM_TYPES], - sizeof(char *) * value, - sizeof(char *) * (value - 1)); - if (!new_val_to_name_types) { - pr_err("add_type: alloc val_to_name failed\n"); - return false; - } + char **new_val_to_name_types = + ksu_realloc(db->sym_val_to_name[SYM_TYPES], + sizeof(char *) * value, + sizeof(char *) * (value - 1)); + if (!new_val_to_name_types) { + pr_err("add_type: alloc val_to_name failed\n"); + return false; + } - db->type_attr_map_array = new_type_attr_map_array; - ebitmap_init(&db->type_attr_map_array[value - 1]); - ebitmap_set_bit(&db->type_attr_map_array[value - 1], value - 1, 1); + db->type_attr_map_array = new_type_attr_map_array; + ebitmap_init(&db->type_attr_map_array[value - 1]); + ebitmap_set_bit(&db->type_attr_map_array[value - 1], value - 1, 1); - db->type_val_to_struct = new_type_val_to_struct; - db->type_val_to_struct[value - 1] = type; + db->type_val_to_struct = new_type_val_to_struct; + db->type_val_to_struct[value - 1] = type; - db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types; - db->sym_val_to_name[SYM_TYPES][value - 1] = key; + db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types; + db->sym_val_to_name[SYM_TYPES][value - 1] = key; - int i; - for (i = 0; i < db->p_roles.nprim; ++i) { - ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1, - 1); - } + int i; + for (i = 0; i < db->p_roles.nprim; ++i) { + ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1, + 1); + } - return true; + return true; } static bool set_type_state(struct policydb *db, const char *type_name, - bool permissive) + bool permissive) { - struct type_datum *type; - if (type_name == NULL) { - struct hashtab_node *node; - ksu_hashtab_for_each(db->p_types.table, node) - { - type = (struct type_datum *)(node->datum); - if (ebitmap_set_bit(&db->permissive_map, type->value, - permissive)) - pr_info("Could not set bit in permissive map\n"); - }; - } else { - type = (struct type_datum *)symtab_search(&db->p_types, - type_name); - if (type == NULL) { - pr_info("type %s does not exist\n", type_name); - return false; - } - if (ebitmap_set_bit(&db->permissive_map, type->value, - permissive)) { - pr_info("Could not set bit in permissive map\n"); - return false; - } - } - return true; + struct type_datum *type; + if (type_name == NULL) { + struct hashtab_node *node; + ksu_hashtab_for_each(db->p_types.table, node) + { + type = (struct type_datum *)(node->datum); + if (ebitmap_set_bit(&db->permissive_map, type->value, + permissive)) + pr_info("Could not set bit in permissive map\n"); + }; + } else { + type = (struct type_datum *)symtab_search(&db->p_types, + type_name); + if (type == NULL) { + pr_info("type %s does not exist\n", type_name); + return false; + } + if (ebitmap_set_bit(&db->permissive_map, type->value, + permissive)) { + pr_info("Could not set bit in permissive map\n"); + return false; + } + } + return true; } static void add_typeattribute_raw(struct policydb *db, struct type_datum *type, - struct type_datum *attr) + struct type_datum *attr) { - struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1]; - ebitmap_set_bit(sattr, attr->value - 1, 1); + struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1]; + ebitmap_set_bit(sattr, attr->value - 1, 1); - struct hashtab_node *node; - struct constraint_node *n; - struct constraint_expr *e; - ksu_hashtab_for_each(db->p_classes.table, node) - { - struct class_datum *cls = (struct class_datum *)(node->datum); - for (n = cls->constraints; n; n = n->next) { - for (e = n->expr; e; e = e->next) { - if (e->expr_type == CEXPR_NAMES && - ebitmap_get_bit(&e->type_names->types, - attr->value - 1)) { - ebitmap_set_bit(&e->names, - type->value - 1, 1); - } - } - } - }; + struct hashtab_node *node; + struct constraint_node *n; + struct constraint_expr *e; + ksu_hashtab_for_each(db->p_classes.table, node) + { + struct class_datum *cls = (struct class_datum *)(node->datum); + for (n = cls->constraints; n; n = n->next) { + for (e = n->expr; e; e = e->next) { + if (e->expr_type == CEXPR_NAMES && + ebitmap_get_bit(&e->type_names->types, + attr->value - 1)) { + ebitmap_set_bit(&e->names, + type->value - 1, 1); + } + } + } + }; } static bool add_typeattribute(struct policydb *db, const char *type, - const char *attr) + const char *attr) { - struct type_datum *type_d = symtab_search(&db->p_types, type); - if (type_d == NULL) { - pr_info("type %s does not exist\n", type); - return false; - } else if (type_d->attribute) { - pr_info("type %s is an attribute\n", attr); - return false; - } + struct type_datum *type_d = symtab_search(&db->p_types, type); + if (type_d == NULL) { + pr_info("type %s does not exist\n", type); + return false; + } else if (type_d->attribute) { + pr_info("type %s is an attribute\n", attr); + return false; + } - struct type_datum *attr_d = symtab_search(&db->p_types, attr); - if (attr_d == NULL) { - pr_info("attribute %s does not exist\n", type); - return false; - } else if (!attr_d->attribute) { - pr_info("type %s is not an attribute \n", attr); - return false; - } + struct type_datum *attr_d = symtab_search(&db->p_types, attr); + if (attr_d == NULL) { + pr_info("attribute %s does not exist\n", type); + return false; + } else if (!attr_d->attribute) { + pr_info("type %s is not an attribute \n", attr); + return false; + } - add_typeattribute_raw(db, type_d, attr_d); - return true; + add_typeattribute_raw(db, type_d, attr_d); + return true; } ////////////////////////////////////////////////////////////////////////// @@ -748,106 +748,106 @@ static bool add_typeattribute(struct policydb *db, const char *type, // Operation on types bool ksu_type(struct policydb *db, const char *name, const char *attr) { - return add_type(db, name, false) && add_typeattribute(db, name, attr); + return add_type(db, name, false) && add_typeattribute(db, name, attr); } bool ksu_attribute(struct policydb *db, const char *name) { - return add_type(db, name, true); + return add_type(db, name, true); } bool ksu_permissive(struct policydb *db, const char *type) { - return set_type_state(db, type, true); + return set_type_state(db, type, true); } bool ksu_enforce(struct policydb *db, const char *type) { - return set_type_state(db, type, false); + return set_type_state(db, type, false); } bool ksu_typeattribute(struct policydb *db, const char *type, const char *attr) { - return add_typeattribute(db, type, attr); + return add_typeattribute(db, type, attr); } bool ksu_exists(struct policydb *db, const char *type) { - return symtab_search(&db->p_types, type) != NULL; + return symtab_search(&db->p_types, type) != NULL; } // Access vector rules bool ksu_allow(struct policydb *db, const char *src, const char *tgt, - const char *cls, const char *perm) + const char *cls, const char *perm) { - return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, false); + return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, false); } bool ksu_deny(struct policydb *db, const char *src, const char *tgt, - const char *cls, const char *perm) + const char *cls, const char *perm) { - return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, true); + return add_rule(db, src, tgt, cls, perm, AVTAB_ALLOWED, true); } bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt, - const char *cls, const char *perm) + const char *cls, const char *perm) { - return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITALLOW, false); + return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITALLOW, false); } bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt, - const char *cls, const char *perm) + const char *cls, const char *perm) { - return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITDENY, true); + return add_rule(db, src, tgt, cls, perm, AVTAB_AUDITDENY, true); } // Extended permissions access vector rules bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt, - const char *cls, const char *range) + const char *cls, const char *range) { - return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_ALLOWED, - false); + return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_ALLOWED, + false); } bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt, - const char *cls, const char *range) + const char *cls, const char *range) { - return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_AUDITALLOW, - false); + return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_AUDITALLOW, + false); } bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt, - const char *cls, const char *range) + const char *cls, const char *range) { - return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_DONTAUDIT, - false); + return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_DONTAUDIT, + false); } // Type rules 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) { - if (obj) { - return add_filename_trans(db, src, tgt, cls, def, obj); - } else { - return add_type_rule(db, src, tgt, cls, def, AVTAB_TRANSITION); - } + if (obj) { + return add_filename_trans(db, src, tgt, cls, def, obj); + } else { + return add_type_rule(db, src, tgt, cls, def, AVTAB_TRANSITION); + } } 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) { - return add_type_rule(db, src, tgt, cls, def, AVTAB_CHANGE); + return add_type_rule(db, src, tgt, cls, def, AVTAB_CHANGE); } 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) { - return add_type_rule(db, src, tgt, cls, def, AVTAB_MEMBER); + return add_type_rule(db, src, tgt, cls, def, AVTAB_MEMBER); } // File system labeling bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path, - const char *ctx) + const char *ctx) { - return add_genfscon(db, fs_name, path, ctx); + return add_genfscon(db, fs_name, path, ctx); } diff --git a/kernel/selinux/sepolicy.h b/kernel/selinux/sepolicy.h index 675d1499..fd062ce6 100644 --- a/kernel/selinux/sepolicy.h +++ b/kernel/selinux/sepolicy.h @@ -15,32 +15,32 @@ bool ksu_exists(struct policydb *db, const char *type); // Access vector rules 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, - const char *cls, const char *perm); + const char *cls, const char *perm); 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, - const char *cls, const char *perm); + const char *cls, const char *perm); // Extended permissions access vector rules 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, - const char *cls, const char *range); + const char *cls, const char *range); 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 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, - 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, - const char *cls, const char *def); + const char *cls, const char *def); // File system labeling bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path, - const char *ctx); + const char *ctx); #endif diff --git a/kernel/sucompat.c b/kernel/sucompat.c index 7cdcf018..d935dafe 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -29,312 +29,312 @@ static bool ksu_sucompat_hook_state __read_mostly = true; static void __user *userspace_stack_buffer(const void *d, size_t len) { - /* To avoid having to mmap a page in userspace, just write below the stack + /* To avoid having to mmap a page in userspace, just write below the stack * pointer. */ - 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 char __user *sh_user_path(void) { - static const char sh_path[] = "/system/bin/sh"; + static const char sh_path[] = "/system/bin/sh"; - return userspace_stack_buffer(sh_path, sizeof(sh_path)); + return userspace_stack_buffer(sh_path, sizeof(sh_path)); } static char __user *ksud_user_path(void) { - static const char ksud_path[] = KSUD_PATH; + static const char ksud_path[] = KSUD_PATH; - 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 *__unused_flags) + int *__unused_flags) { - const char su[] = SU_PATH; + const char su[] = SU_PATH; #ifndef CONFIG_KSU_KPROBES_HOOK - if (!ksu_sucompat_hook_state) { - return 0; - } + if (!ksu_sucompat_hook_state) { + return 0; + } #endif - if (!ksu_is_allow_uid(current_uid().val)) { - return 0; - } + if (!ksu_is_allow_uid(current_uid().val)) { + return 0; + } - char path[sizeof(su) + 1]; - memset(path, 0, sizeof(path)); - ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path)); + char path[sizeof(su) + 1]; + memset(path, 0, 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 - ksu_sulog_report_syscall(current_uid().val, NULL, "faccessat", path); + ksu_sulog_report_syscall(current_uid().val, NULL, "faccessat", path); #endif - pr_info("faccessat su->sh!\n"); - *filename_user = sh_user_path(); - } + pr_info("faccessat su->sh!\n"); + *filename_user = sh_user_path(); + } - return 0; + return 0; } int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags) { - // const char sh[] = SH_PATH; - const char su[] = SU_PATH; + // const char sh[] = SH_PATH; + const char su[] = SU_PATH; #ifndef CONFIG_KSU_KPROBES_HOOK - if (!ksu_sucompat_hook_state) { - return 0; - } + if (!ksu_sucompat_hook_state) { + return 0; + } #endif - if (!ksu_is_allow_uid(current_uid().val)) { - return 0; - } + if (!ksu_is_allow_uid(current_uid().val)) { + return 0; + } - if (unlikely(!filename_user)) { - return 0; - } + if (unlikely(!filename_user)) { + return 0; + } - char path[sizeof(su) + 1]; - memset(path, 0, sizeof(path)); + char path[sizeof(su) + 1]; + memset(path, 0, sizeof(path)); // Remove this later!! we use syscall hook, so this will never happen!!!!! #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0 - // it becomes a `struct filename *` after 5.18 - // https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216 - const char sh[] = SH_PATH; - struct filename *filename = *((struct filename **)filename_user); - if (IS_ERR(filename)) { - return 0; - } - if (likely(memcmp(filename->name, su, sizeof(su)))) - return 0; - pr_info("vfs_statx su->sh!\n"); - memcpy((void *)filename->name, sh, sizeof(sh)); + // it becomes a `struct filename *` after 5.18 + // https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216 + const char sh[] = SH_PATH; + struct filename *filename = *((struct filename **)filename_user); + if (IS_ERR(filename)) { + return 0; + } + if (likely(memcmp(filename->name, su, sizeof(su)))) + return 0; + pr_info("vfs_statx su->sh!\n"); + memcpy((void *)filename->name, sh, sizeof(sh)); #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 - ksu_sulog_report_syscall(current_uid().val, NULL, "newfstatat", path); + ksu_sulog_report_syscall(current_uid().val, NULL, "newfstatat", path); #endif - pr_info("newfstatat su->sh!\n"); - *filename_user = sh_user_path(); - } + pr_info("newfstatat su->sh!\n"); + *filename_user = sh_user_path(); + } #endif - return 0; + return 0; } int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, - void *envp, int *flags) + void *envp, int *flags) { - return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, flags); + return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, flags); } // 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, - void *__never_use_argv, void *__never_use_envp, - int *__never_use_flags) + void *__never_use_argv, void *__never_use_envp, + int *__never_use_flags) { - struct filename *filename; - const char sh[] = KSUD_PATH; - const char su[] = SU_PATH; + struct filename *filename; + const char sh[] = KSUD_PATH; + const char su[] = SU_PATH; #ifndef CONFIG_KSU_KPROBES_HOOK - if (!ksu_sucompat_hook_state) { - return 0; - } + if (!ksu_sucompat_hook_state) { + return 0; + } #endif - if (unlikely(!filename_ptr)) - return 0; + if (unlikely(!filename_ptr)) + return 0; - filename = *filename_ptr; - if (IS_ERR(filename)) { - return 0; - } + filename = *filename_ptr; + if (IS_ERR(filename)) { + return 0; + } - if (likely(memcmp(filename->name, su, sizeof(su)))) - return 0; + if (likely(memcmp(filename->name, su, sizeof(su)))) + return 0; #if __SULOG_GATE - bool is_allowed = ksu_is_allow_uid(current_uid().val); - ksu_sulog_report_syscall(current_uid().val, NULL, "execve", filename->name); + bool is_allowed = ksu_is_allow_uid(current_uid().val); + ksu_sulog_report_syscall(current_uid().val, NULL, "execve", filename->name); - if (!is_allowed) { - return 0; - } + if (!is_allowed) { + 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 - if (!ksu_is_allow_uid(current_uid().val)) { - return 0; - } + if (!ksu_is_allow_uid(current_uid().val)) { + return 0; + } #endif - pr_info("do_execveat_common su found\n"); - memcpy((void *)filename->name, sh, sizeof(sh)); + pr_info("do_execveat_common su found\n"); + memcpy((void *)filename->name, sh, sizeof(sh)); - escape_to_root(); + escape_to_root(); - return 0; + return 0; } int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user, - void *__never_use_argv, void *__never_use_envp, - int *__never_use_flags) + void *__never_use_argv, void *__never_use_envp, + int *__never_use_flags) { - const char su[] = SU_PATH; - char path[sizeof(su) + 1]; + const char su[] = SU_PATH; + char path[sizeof(su) + 1]; #ifndef CONFIG_KSU_KPROBES_HOOK - if (!ksu_sucompat_hook_state){ - return 0; - } + if (!ksu_sucompat_hook_state){ + return 0; + } #endif - if (unlikely(!filename_user)) - return 0; + if (unlikely(!filename_user)) + return 0; - memset(path, 0, sizeof(path)); - ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path)); + memset(path, 0, sizeof(path)); + ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path)); - if (likely(memcmp(path, su, sizeof(su)))) - return 0; + if (likely(memcmp(path, su, sizeof(su)))) + return 0; #if __SULOG_GATE - bool is_allowed = ksu_is_allow_uid(current_uid().val); - ksu_sulog_report_syscall(current_uid().val, NULL, "execve", path); - - if (!is_allowed) - return 0; + bool is_allowed = ksu_is_allow_uid(current_uid().val); + ksu_sulog_report_syscall(current_uid().val, NULL, "execve", path); + + if (!is_allowed) + 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 - if (!ksu_is_allow_uid(current_uid().val)) { - return 0; - } + if (!ksu_is_allow_uid(current_uid().val)) { + return 0; + } #endif - pr_info("sys_execve su found\n"); - *filename_user = ksud_user_path(); + pr_info("sys_execve su found\n"); + *filename_user = ksud_user_path(); - escape_to_root(); + escape_to_root(); - return 0; + return 0; } // dummified int ksu_handle_devpts(struct inode *inode) { - return 0; + return 0; } int __ksu_handle_devpts(struct inode *inode) { #ifndef CONFIG_KSU_KPROBES_HOOK - if (!ksu_sucompat_hook_state) - return 0; + if (!ksu_sucompat_hook_state) + return 0; #endif - if (!current->mm) { - return 0; - } + if (!current->mm) { + return 0; + } - uid_t uid = current_uid().val; - if (uid % 100000 < 10000) { - // not untrusted_app, ignore it - return 0; - } + uid_t uid = current_uid().val; + if (uid % 100000 < 10000) { + // not untrusted_app, ignore it + return 0; + } - if (likely(!ksu_is_allow_uid(uid))) - return 0; + if (likely(!ksu_is_allow_uid(uid))) + return 0; - struct inode_security_struct *sec = selinux_inode(inode); + struct inode_security_struct *sec = selinux_inode(inode); - if (ksu_devpts_sid && sec) - sec->sid = ksu_devpts_sid; + if (ksu_devpts_sid && sec) + sec->sid = ksu_devpts_sid; - return 0; + return 0; } #ifdef CONFIG_KSU_KPROBES_HOOK static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) { - struct pt_regs *real_regs = PT_REAL_REGS(regs); - int *dfd = (int *)&PT_REGS_PARM1(real_regs); - const char __user **filename_user = - (const char **)&PT_REGS_PARM2(real_regs); - int *mode = (int *)&PT_REGS_PARM3(real_regs); + struct pt_regs *real_regs = PT_REAL_REGS(regs); + int *dfd = (int *)&PT_REGS_PARM1(real_regs); + const char __user **filename_user = + (const char **)&PT_REGS_PARM2(real_regs); + int *mode = (int *)&PT_REGS_PARM3(real_regs); - 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) { - struct pt_regs *real_regs = PT_REAL_REGS(regs); - int *dfd = (int *)&PT_REGS_PARM1(real_regs); - const char __user **filename_user = - (const char **)&PT_REGS_PARM2(real_regs); - int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs); + struct pt_regs *real_regs = PT_REAL_REGS(regs); + int *dfd = (int *)&PT_REGS_PARM1(real_regs); + const char __user **filename_user = + (const char **)&PT_REGS_PARM2(real_regs); + int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs); - return ksu_handle_stat(dfd, filename_user, flags); + return ksu_handle_stat(dfd, filename_user, flags); } static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) { - struct pt_regs *real_regs = PT_REAL_REGS(regs); - const char __user **filename_user = - (const char **)&PT_REGS_PARM1(real_regs); + struct pt_regs *real_regs = PT_REAL_REGS(regs); + const char __user **filename_user = + (const char **)&PT_REGS_PARM1(real_regs); - return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL, - NULL); + return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL, + NULL); } static struct kprobe *su_kps[4]; 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) - struct file *file = (struct file *)PT_REGS_PARM2(regs); - inode = file->f_path.dentry->d_inode; + struct file *file = (struct file *)PT_REGS_PARM2(regs); + inode = file->f_path.dentry->d_inode; #else - inode = (struct inode *)PT_REGS_PARM2(regs); + inode = (struct inode *)PT_REGS_PARM2(regs); #endif - return ksu_handle_devpts(inode); + return ksu_handle_devpts(inode); } 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); - if (!kp) - return NULL; - kp->symbol_name = name; - kp->pre_handler = handler; + struct kprobe *kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL); + if (!kp) + return NULL; + kp->symbol_name = name; + kp->pre_handler = handler; - int ret = register_kprobe(kp); - pr_info("sucompat: register_%s kprobe: %d\n", name, ret); - if (ret) { - kfree(kp); - return NULL; - } + int ret = register_kprobe(kp); + pr_info("sucompat: register_%s kprobe: %d\n", name, ret); + if (ret) { + kfree(kp); + return NULL; + } - return kp; + return kp; } static void destroy_kprobe(struct kprobe **kp_ptr) { - struct kprobe *kp = *kp_ptr; - if (!kp) - return; - unregister_kprobe(kp); - synchronize_rcu(); - kfree(kp); - *kp_ptr = NULL; + struct kprobe *kp = *kp_ptr; + if (!kp) + return; + unregister_kprobe(kp); + synchronize_rcu(); + kfree(kp); + *kp_ptr = NULL; } #endif @@ -343,25 +343,25 @@ static void destroy_kprobe(struct kprobe **kp_ptr) void ksu_sucompat_init() { #ifdef CONFIG_KSU_KPROBES_HOOK - su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre); - su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre); - su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre); - su_kps[3] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre); + su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre); + su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre); + su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre); + su_kps[3] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre); #else - ksu_sucompat_hook_state = true; - pr_info("ksu_sucompat_init: hooks enabled: execve/execveat_su, faccessat, stat\n"); + ksu_sucompat_hook_state = true; + pr_info("ksu_sucompat_init: hooks enabled: execve/execveat_su, faccessat, stat\n"); #endif } void ksu_sucompat_exit() { #ifdef CONFIG_KSU_KPROBES_HOOK - int i; - for (i = 0; i < ARRAY_SIZE(su_kps); i++) { - destroy_kprobe(&su_kps[i]); - } + int i; + for (i = 0; i < ARRAY_SIZE(su_kps); i++) { + destroy_kprobe(&su_kps[i]); + } #else - ksu_sucompat_hook_state = false; - pr_info("ksu_sucompat_exit: hooks disabled: execve/execveat_su, faccessat, stat\n"); + ksu_sucompat_hook_state = false; + pr_info("ksu_sucompat_exit: hooks disabled: execve/execveat_su, faccessat, stat\n"); #endif } diff --git a/kernel/sulog.c b/kernel/sulog.c index ed67e3a2..0ccf78cf 100644 --- a/kernel/sulog.c +++ b/kernel/sulog.c @@ -30,380 +30,380 @@ static bool sulog_enabled = true; static void get_timestamp(char *buf, size_t len) { - struct timespec64 ts; - struct tm tm; + struct timespec64 ts; + 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, - "%04ld-%02d-%02d %02d:%02d:%02d", - tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, - tm.tm_hour, tm.tm_min, tm.tm_sec); + snprintf(buf, len, + "%04ld-%02d-%02d %02d:%02d:%02d", + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, + tm.tm_hour, tm.tm_min, tm.tm_sec); } 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) - return; + if (!full_comm || buf_len <= 0) + return; - if (comm && strlen(comm) > 0) { + if (comm && strlen(comm) > 0) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) - strscpy(full_comm, comm, buf_len); + strscpy(full_comm, comm, buf_len); #else - strlcpy(full_comm, comm, buf_len); + strlcpy(full_comm, comm, buf_len); #endif - } else { - kbuf = kmalloc(buf_len, GFP_ATOMIC); - if (!kbuf) { - pr_err("sulog: failed to allocate memory for kbuf\n"); - return; - } + } else { + kbuf = kmalloc(buf_len, GFP_ATOMIC); + if (!kbuf) { + pr_err("sulog: failed to allocate memory for kbuf\n"); + 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) - strscpy(full_comm, current->comm, buf_len); + strscpy(full_comm, current->comm, buf_len); #else - strlcpy(full_comm, current->comm, buf_len); + strlcpy(full_comm, current->comm, buf_len); #endif - } else { - for (int i = 0; i < n; i++) { - if (kbuf[i] == '\0') kbuf[i] = ' '; - } - kbuf[n < buf_len ? n : buf_len - 1] = '\0'; + } else { + for (int i = 0; i < n; i++) { + if (kbuf[i] == '\0') kbuf[i] = ' '; + } + kbuf[n < buf_len ? n : buf_len - 1] = '\0'; #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) - strscpy(full_comm, kbuf, buf_len); + strscpy(full_comm, kbuf, buf_len); #else - strlcpy(full_comm, kbuf, buf_len); + strlcpy(full_comm, kbuf, buf_len); #endif - } + } - kfree(kbuf); - } + kfree(kbuf); + } } 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 = { - .crc = dedup_calc_hash(content, len), - .uid = uid, - .type = type, - }; - u64 now = ktime_get_ns(); - u64 delta_ns = DEDUP_SECS * NSEC_PER_SEC; + struct dedup_key key = { + .crc = dedup_calc_hash(content, len), + .uid = uid, + .type = type, + }; + u64 now = ktime_get_ns(); + u64 delta_ns = DEDUP_SECS * NSEC_PER_SEC; - u32 idx = key.crc & (SULOG_COMM_LEN - 1); - spin_lock(&dedup_lock); + u32 idx = key.crc & (SULOG_COMM_LEN - 1); + spin_lock(&dedup_lock); - struct dedup_entry *e = &dedup_tbl[idx]; - if (e->key.crc == key.crc && - e->key.uid == key.uid && - e->key.type == key.type && - (now - e->ts_ns) < delta_ns) { - spin_unlock(&dedup_lock); - return false; - } + struct dedup_entry *e = &dedup_tbl[idx]; + if (e->key.crc == key.crc && + e->key.uid == key.uid && + e->key.type == key.type && + (now - e->ts_ns) < delta_ns) { + spin_unlock(&dedup_lock); + return false; + } - e->key = key; - e->ts_ns = now; - spin_unlock(&dedup_lock); - return true; + e->key = key; + e->ts_ns = now; + spin_unlock(&dedup_lock); + return true; } static void sulog_work_handler(struct work_struct *work) { - struct file *fp; - struct sulog_entry *entry, *tmp; - LIST_HEAD(local_queue); - loff_t pos = 0; - - mutex_lock(&sulog_mutex); - list_splice_init(&sulog_queue, &local_queue); - mutex_unlock(&sulog_mutex); - - if (list_empty(&local_queue)) - return; - - fp = ksu_filp_open_compat(SULOG_PATH, O_WRONLY | O_CREAT | O_APPEND, 0640); - if (IS_ERR(fp)) { - pr_err("sulog: failed to open log file: %ld\n", PTR_ERR(fp)); - goto cleanup; - } - - if (fp->f_inode->i_size > SULOG_MAX_SIZE) { - pr_info("sulog: log file exceeds maximum size, clearing...\n"); - if (vfs_truncate(&fp->f_path, 0)) { - pr_err("sulog: failed to truncate log file\n"); - } - pos = 0; - } else { - pos = fp->f_inode->i_size; - } - - list_for_each_entry(entry, &local_queue, list) { - ksu_kernel_write_compat(fp, entry->content, strlen(entry->content), &pos); - } - - vfs_fsync(fp, 0); - filp_close(fp, 0); - + struct file *fp; + struct sulog_entry *entry, *tmp; + LIST_HEAD(local_queue); + loff_t pos = 0; + + mutex_lock(&sulog_mutex); + list_splice_init(&sulog_queue, &local_queue); + mutex_unlock(&sulog_mutex); + + if (list_empty(&local_queue)) + return; + + fp = ksu_filp_open_compat(SULOG_PATH, O_WRONLY | O_CREAT | O_APPEND, 0640); + if (IS_ERR(fp)) { + pr_err("sulog: failed to open log file: %ld\n", PTR_ERR(fp)); + goto cleanup; + } + + if (fp->f_inode->i_size > SULOG_MAX_SIZE) { + pr_info("sulog: log file exceeds maximum size, clearing...\n"); + if (vfs_truncate(&fp->f_path, 0)) { + pr_err("sulog: failed to truncate log file\n"); + } + pos = 0; + } else { + pos = fp->f_inode->i_size; + } + + list_for_each_entry(entry, &local_queue, list) { + ksu_kernel_write_compat(fp, entry->content, strlen(entry->content), &pos); + } + + vfs_fsync(fp, 0); + filp_close(fp, 0); + cleanup: - list_for_each_entry_safe(entry, tmp, &local_queue, list) { - list_del(&entry->list); - kfree(entry); - } + list_for_each_entry_safe(entry, tmp, &local_queue, list) { + list_del(&entry->list); + kfree(entry); + } } static void sulog_add_entry(const char *content) { - struct sulog_entry *entry; - - if (!sulog_enabled || !content) - return; - - entry = kmalloc(sizeof(*entry), GFP_ATOMIC); - if (!entry) { - pr_err("sulog: failed to allocate memory for log entry\n"); - return; - } - + struct sulog_entry *entry; + + if (!sulog_enabled || !content) + return; + + entry = kmalloc(sizeof(*entry), GFP_ATOMIC); + if (!entry) { + pr_err("sulog: failed to allocate memory for log entry\n"); + return; + } + #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 - strlcpy(entry->content, content, SULOG_ENTRY_MAX_LEN - 1); + strlcpy(entry->content, content, SULOG_ENTRY_MAX_LEN - 1); #endif - - mutex_lock(&sulog_mutex); - list_add_tail(&entry->list, &sulog_queue); - mutex_unlock(&sulog_mutex); - - if (sulog_workqueue) - queue_work(sulog_workqueue, &sulog_work); + + mutex_lock(&sulog_mutex); + list_add_tail(&entry->list, &sulog_queue); + mutex_unlock(&sulog_mutex); + + if (sulog_workqueue) + queue_work(sulog_workqueue, &sulog_work); } void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method) { - char *timestamp, *full_comm, *log_buf; - - if (!sulog_enabled) - return; - - timestamp = kmalloc(32, GFP_ATOMIC); - full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC); - log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC); - - if (!timestamp || !full_comm || !log_buf) { - pr_err("sulog: failed to allocate memory for su_grant log\n"); - goto cleanup; - } - - get_timestamp(timestamp, 32); - - ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN); - - snprintf(log_buf, SULOG_ENTRY_MAX_LEN, - "[%s] SU_GRANT: UID=%d COMM=%s METHOD=%s PID=%d\n", - timestamp, uid, full_comm, - method ? method : "unknown", current->pid); + char *timestamp, *full_comm, *log_buf; + + if (!sulog_enabled) + return; + + timestamp = kmalloc(32, GFP_ATOMIC); + full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC); + log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC); + + if (!timestamp || !full_comm || !log_buf) { + pr_err("sulog: failed to allocate memory for su_grant log\n"); + goto cleanup; + } + + get_timestamp(timestamp, 32); + + ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN); + + snprintf(log_buf, SULOG_ENTRY_MAX_LEN, + "[%s] SU_GRANT: UID=%d COMM=%s METHOD=%s PID=%d\n", + timestamp, uid, full_comm, + method ? method : "unknown", current->pid); - if (!dedup_should_print(uid, DEDUP_SU_GRANT, log_buf, strlen(log_buf))) - goto cleanup; - - sulog_add_entry(log_buf); - + if (!dedup_should_print(uid, DEDUP_SU_GRANT, log_buf, strlen(log_buf))) + goto cleanup; + + sulog_add_entry(log_buf); + cleanup: - if (timestamp) kfree(timestamp); - if (full_comm) kfree(full_comm); - if (log_buf) kfree(log_buf); + if (timestamp) kfree(timestamp); + if (full_comm) kfree(full_comm); + if (log_buf) kfree(log_buf); } void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success) { - char *timestamp, *full_comm, *log_buf; - - if (!sulog_enabled) - return; - - timestamp = kmalloc(32, GFP_ATOMIC); - full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC); - log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC); - - if (!timestamp || !full_comm || !log_buf) { - pr_err("sulog: failed to allocate memory for su_attempt log\n"); - goto cleanup; - } - - get_timestamp(timestamp, 32); - - ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN); - - snprintf(log_buf, SULOG_ENTRY_MAX_LEN, - "[%s] SU_EXEC: UID=%d COMM=%s TARGET=%s RESULT=%s PID=%d\n", - timestamp, uid, full_comm, - target_path ? target_path : "unknown", - success ? "SUCCESS" : "DENIED", current->pid); + char *timestamp, *full_comm, *log_buf; + + if (!sulog_enabled) + return; + + timestamp = kmalloc(32, GFP_ATOMIC); + full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC); + log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC); + + if (!timestamp || !full_comm || !log_buf) { + pr_err("sulog: failed to allocate memory for su_attempt log\n"); + goto cleanup; + } + + get_timestamp(timestamp, 32); + + ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN); + + snprintf(log_buf, SULOG_ENTRY_MAX_LEN, + "[%s] SU_EXEC: UID=%d COMM=%s TARGET=%s RESULT=%s PID=%d\n", + timestamp, uid, full_comm, + target_path ? target_path : "unknown", + success ? "SUCCESS" : "DENIED", current->pid); - if (!dedup_should_print(uid, DEDUP_SU_ATTEMPT, log_buf, strlen(log_buf))) - goto cleanup; - - sulog_add_entry(log_buf); - + if (!dedup_should_print(uid, DEDUP_SU_ATTEMPT, log_buf, strlen(log_buf))) + goto cleanup; + + sulog_add_entry(log_buf); + cleanup: - if (timestamp) kfree(timestamp); - if (full_comm) kfree(full_comm); - if (log_buf) kfree(log_buf); + if (timestamp) kfree(timestamp); + if (full_comm) kfree(full_comm); + if (log_buf) kfree(log_buf); } void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed) { - char *timestamp, *full_comm, *log_buf; - - if (!sulog_enabled) - return; - - timestamp = kmalloc(32, GFP_ATOMIC); - full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC); - log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC); - - if (!timestamp || !full_comm || !log_buf) { - pr_err("sulog: failed to allocate memory for permission_check log\n"); - goto cleanup; - } - - get_timestamp(timestamp, 32); - - ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN); - - snprintf(log_buf, SULOG_ENTRY_MAX_LEN, - "[%s] PERM_CHECK: UID=%d COMM=%s RESULT=%s PID=%d\n", - timestamp, uid, full_comm, - allowed ? "ALLOWED" : "DENIED", current->pid); + char *timestamp, *full_comm, *log_buf; + + if (!sulog_enabled) + return; + + timestamp = kmalloc(32, GFP_ATOMIC); + full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC); + log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC); + + if (!timestamp || !full_comm || !log_buf) { + pr_err("sulog: failed to allocate memory for permission_check log\n"); + goto cleanup; + } + + get_timestamp(timestamp, 32); + + ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN); + + snprintf(log_buf, SULOG_ENTRY_MAX_LEN, + "[%s] PERM_CHECK: UID=%d COMM=%s RESULT=%s PID=%d\n", + timestamp, uid, full_comm, + allowed ? "ALLOWED" : "DENIED", current->pid); - if (!dedup_should_print(uid, DEDUP_PERM_CHECK, log_buf, strlen(log_buf))) - goto cleanup; - - sulog_add_entry(log_buf); - + if (!dedup_should_print(uid, DEDUP_PERM_CHECK, log_buf, strlen(log_buf))) + goto cleanup; + + sulog_add_entry(log_buf); + cleanup: - if (timestamp) kfree(timestamp); - if (full_comm) kfree(full_comm); - if (log_buf) kfree(log_buf); + if (timestamp) kfree(timestamp); + if (full_comm) kfree(full_comm); + if (log_buf) kfree(log_buf); } void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid) { - char *timestamp, *full_comm, *log_buf; - - if (!sulog_enabled) - return; - - timestamp = kmalloc(32, GFP_ATOMIC); - full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC); - log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC); - - if (!timestamp || !full_comm || !log_buf) { - pr_err("sulog: failed to allocate memory for manager_operation log\n"); - goto cleanup; - } - - get_timestamp(timestamp, 32); + char *timestamp, *full_comm, *log_buf; + + if (!sulog_enabled) + return; + + timestamp = kmalloc(32, GFP_ATOMIC); + full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC); + log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC); + + if (!timestamp || !full_comm || !log_buf) { + pr_err("sulog: failed to allocate memory for manager_operation log\n"); + goto cleanup; + } + + get_timestamp(timestamp, 32); - ksu_get_cmdline(full_comm, NULL, SULOG_COMM_LEN); - - snprintf(log_buf, SULOG_ENTRY_MAX_LEN, - "[%s] MANAGER_OP: OP=%s MANAGER_UID=%d TARGET_UID=%d COMM=%s PID=%d\n", - timestamp, operation ? operation : "unknown", - manager_uid, target_uid, full_comm, current->pid); + ksu_get_cmdline(full_comm, NULL, SULOG_COMM_LEN); + + snprintf(log_buf, SULOG_ENTRY_MAX_LEN, + "[%s] MANAGER_OP: OP=%s MANAGER_UID=%d TARGET_UID=%d COMM=%s PID=%d\n", + timestamp, operation ? operation : "unknown", + manager_uid, target_uid, full_comm, current->pid); - if (!dedup_should_print(manager_uid, DEDUP_MANAGER_OP, log_buf, strlen(log_buf))) - goto cleanup; - - sulog_add_entry(log_buf); - + if (!dedup_should_print(manager_uid, DEDUP_MANAGER_OP, log_buf, strlen(log_buf))) + goto cleanup; + + sulog_add_entry(log_buf); + cleanup: - if (timestamp) kfree(timestamp); - if (full_comm) kfree(full_comm); - if (log_buf) kfree(log_buf); + if (timestamp) kfree(timestamp); + if (full_comm) kfree(full_comm); + if (log_buf) kfree(log_buf); } 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) - return; + if (!sulog_enabled) + return; - timestamp = kmalloc(32, GFP_ATOMIC); - full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC); - log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC); + timestamp = kmalloc(32, GFP_ATOMIC); + full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC); + log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC); - if (!timestamp || !full_comm || !log_buf) { - pr_err("sulog: failed to allocate memory for syscall log\n"); - goto cleanup; - } + if (!timestamp || !full_comm || !log_buf) { + pr_err("sulog: failed to allocate memory for syscall log\n"); + goto cleanup; + } - get_timestamp(timestamp, 32); - - ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN); + get_timestamp(timestamp, 32); + + ksu_get_cmdline(full_comm, comm, SULOG_COMM_LEN); - snprintf(log_buf, SULOG_ENTRY_MAX_LEN, - "[%s] SYSCALL: UID=%d COMM=%s SYSCALL=%s ARGS=%s PID=%d\n", - timestamp, uid, full_comm, - syscall ? syscall : "unknown", - args ? args : "none", - current->pid); + snprintf(log_buf, SULOG_ENTRY_MAX_LEN, + "[%s] SYSCALL: UID=%d COMM=%s SYSCALL=%s ARGS=%s PID=%d\n", + timestamp, uid, full_comm, + syscall ? syscall : "unknown", + args ? args : "none", + current->pid); - if (!dedup_should_print(uid, DEDUP_SYSCALL, log_buf, strlen(log_buf))) - goto cleanup; + if (!dedup_should_print(uid, DEDUP_SYSCALL, log_buf, strlen(log_buf))) + goto cleanup; - sulog_add_entry(log_buf); + sulog_add_entry(log_buf); cleanup: - if (timestamp) kfree(timestamp); - if (full_comm) kfree(full_comm); - if (log_buf) kfree(log_buf); + if (timestamp) kfree(timestamp); + if (full_comm) kfree(full_comm); + if (log_buf) kfree(log_buf); } int ksu_sulog_init(void) { - sulog_workqueue = alloc_workqueue("ksu_sulog", WQ_UNBOUND | WQ_HIGHPRI, 1); - if (!sulog_workqueue) { - pr_err("sulog: failed to create workqueue\n"); - return -ENOMEM; - } - - INIT_WORK(&sulog_work, sulog_work_handler); - - pr_info("sulog: initialized successfully\n"); - return 0; + sulog_workqueue = alloc_workqueue("ksu_sulog", WQ_UNBOUND | WQ_HIGHPRI, 1); + if (!sulog_workqueue) { + pr_err("sulog: failed to create workqueue\n"); + return -ENOMEM; + } + + INIT_WORK(&sulog_work, sulog_work_handler); + + pr_info("sulog: initialized successfully\n"); + return 0; } void ksu_sulog_exit(void) { - struct sulog_entry *entry, *tmp; - - sulog_enabled = false; - - if (sulog_workqueue) { - flush_workqueue(sulog_workqueue); - destroy_workqueue(sulog_workqueue); - sulog_workqueue = NULL; - } - - mutex_lock(&sulog_mutex); - list_for_each_entry_safe(entry, tmp, &sulog_queue, list) { - list_del(&entry->list); - kfree(entry); - } - mutex_unlock(&sulog_mutex); - - pr_info("sulog: cleaned up successfully\n"); + struct sulog_entry *entry, *tmp; + + sulog_enabled = false; + + if (sulog_workqueue) { + flush_workqueue(sulog_workqueue); + destroy_workqueue(sulog_workqueue); + sulog_workqueue = NULL; + } + + mutex_lock(&sulog_mutex); + list_for_each_entry_safe(entry, tmp, &sulog_queue, list) { + list_del(&entry->list); + kfree(entry); + } + mutex_unlock(&sulog_mutex); + + pr_info("sulog: cleaned up successfully\n"); } #endif // __SULOG_GATE \ No newline at end of file diff --git a/kernel/sulog.h b/kernel/sulog.h index d2646270..59d2dafa 100644 --- a/kernel/sulog.h +++ b/kernel/sulog.h @@ -15,6 +15,23 @@ extern struct timezone sys_tz; #define SULOG_COMM_LEN 256 #define DEDUP_SECS 10 +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) +#include + +static inline void time64_to_tm(time64_t totalsecs, int offset, struct tm *result) +{ + struct rtc_time rtc_tm; + rtc_time64_to_tm(totalsecs, &rtc_tm); + + result->tm_sec = rtc_tm.tm_sec; + result->tm_min = rtc_tm.tm_min; + result->tm_hour = rtc_tm.tm_hour; + result->tm_mday = rtc_tm.tm_mday; + result->tm_mon = rtc_tm.tm_mon; + result->tm_year = rtc_tm.tm_year; +} +#endif + struct dedup_key { u32 crc; uid_t uid; @@ -41,8 +58,8 @@ static inline u32 dedup_calc_hash(const char *content, size_t len) } struct sulog_entry { - struct list_head list; - char content[SULOG_ENTRY_MAX_LEN]; + struct list_head list; + char content[SULOG_ENTRY_MAX_LEN]; }; void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method); diff --git a/kernel/supercalls.c b/kernel/supercalls.c new file mode 100644 index 00000000..627a916f --- /dev/null +++ b/kernel/supercalls.c @@ -0,0 +1,609 @@ +#include "supercalls.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} \ No newline at end of file diff --git a/kernel/supercalls.h b/kernel/supercalls.h new file mode 100644 index 00000000..738aef66 --- /dev/null +++ b/kernel/supercalls.h @@ -0,0 +1,142 @@ +#ifndef __KSU_H_SUPERCALLS +#define __KSU_H_SUPERCALLS + +#include +#include +#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 \ No newline at end of file diff --git a/kernel/throne_comm.c b/kernel/throne_comm.c index 79acb6bd..c3c82b3d 100644 --- a/kernel/throne_comm.c +++ b/kernel/throne_comm.c @@ -8,6 +8,7 @@ #include "klog.h" #include "throne_comm.h" #include "kernel_compat.h" +#include "ksu.h" #define PROC_UID_SCANNER "ksu_uid_scanner" #define UID_SCANNER_STATE_FILE "/data/adb/ksu/.uid_scanner" @@ -18,187 +19,197 @@ static struct work_struct scan_work; static struct work_struct ksu_state_save_work; static struct work_struct ksu_state_load_work; -extern bool ksu_uid_scanner_enabled; // Signal userspace to rescan static bool need_rescan = false; static void rescan_work_fn(struct work_struct *work) { - // Signal userspace through proc interface - need_rescan = true; - pr_info("requested userspace uid rescan\n"); + // Signal userspace through proc interface + need_rescan = true; + pr_info("requested userspace uid rescan\n"); } void ksu_request_userspace_scan(void) { - if (scanner_wq) { - queue_work(scanner_wq, &scan_work); - } + if (scanner_wq) { + queue_work(scanner_wq, &scan_work); + } } void ksu_handle_userspace_update(void) { - // Called when userspace notifies update complete - need_rescan = false; - pr_info("userspace uid list updated\n"); + // Called when userspace notifies update complete + need_rescan = false; + pr_info("userspace uid list updated\n"); } static void do_save_throne_state(struct work_struct *work) { - struct file *fp; - char state_char = ksu_uid_scanner_enabled ? '1' : '0'; - loff_t off = 0; + struct file *fp; + char state_char = ksu_uid_scanner_enabled ? '1' : '0'; + loff_t off = 0; - fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644); - if (IS_ERR(fp)) { - pr_err("save_throne_state create file failed: %ld\n", PTR_ERR(fp)); - return; - } + fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (IS_ERR(fp)) { + pr_err("save_throne_state create file failed: %ld\n", PTR_ERR(fp)); + return; + } - if (ksu_kernel_write_compat(fp, &state_char, sizeof(state_char), &off) != sizeof(state_char)) { - pr_err("save_throne_state write failed\n"); - goto exit; - } + if (ksu_kernel_write_compat(fp, &state_char, sizeof(state_char), &off) != sizeof(state_char)) { + pr_err("save_throne_state write failed\n"); + 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: - filp_close(fp, 0); + filp_close(fp, 0); } void do_load_throne_state(struct work_struct *work) { - struct file *fp; - char state_char; - loff_t off = 0; - ssize_t ret; + struct file *fp; + char state_char; + loff_t off = 0; + ssize_t ret; - fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_RDONLY, 0); - if (IS_ERR(fp)) { - pr_info("throne state file not found, using default: disabled\n"); - ksu_uid_scanner_enabled = false; - return; - } + fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_RDONLY, 0); + if (IS_ERR(fp)) { + pr_info("throne state file not found, using default: disabled\n"); + ksu_uid_scanner_enabled = false; + return; + } - ret = ksu_kernel_read_compat(fp, &state_char, sizeof(state_char), &off); - if (ret != sizeof(state_char)) { - pr_err("load_throne_state read err: %zd\n", ret); - ksu_uid_scanner_enabled = false; - goto exit; - } + ret = ksu_kernel_read_compat(fp, &state_char, sizeof(state_char), &off); + if (ret != sizeof(state_char)) { + pr_err("load_throne_state read err: %zd\n", ret); + ksu_uid_scanner_enabled = false; + goto exit; + } - ksu_uid_scanner_enabled = (state_char == '1'); - pr_info("throne state loaded: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled"); + ksu_uid_scanner_enabled = (state_char == '1'); + pr_info("throne state loaded: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled"); exit: - filp_close(fp, 0); + filp_close(fp, 0); } 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) { - 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) { - if (need_rescan) { - seq_puts(m, "RESCAN\n"); - } else { - seq_puts(m, "OK\n"); - } - return 0; + if (need_rescan) { + seq_puts(m, "RESCAN\n"); + } else { + seq_puts(m, "OK\n"); + } + return 0; } 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, size_t count, loff_t *pos) { - char cmd[16]; - - if (count >= sizeof(cmd)) - return -EINVAL; - - if (copy_from_user(cmd, buffer, count)) - return -EFAULT; - - cmd[count] = '\0'; - - // Remove newline if present - if (count > 0 && cmd[count-1] == '\n') - cmd[count-1] = '\0'; - - if (strcmp(cmd, "UPDATED") == 0) { - ksu_handle_userspace_update(); - pr_info("received userspace update notification\n"); - } - - return count; + char cmd[16]; + + if (count >= sizeof(cmd)) + return -EINVAL; + + if (copy_from_user(cmd, buffer, count)) + return -EFAULT; + + cmd[count] = '\0'; + + // Remove newline if present + if (count > 0 && cmd[count-1] == '\n') + cmd[count-1] = '\0'; + + if (strcmp(cmd, "UPDATED") == 0) { + ksu_handle_userspace_update(); + pr_info("received userspace update notification\n"); + } + + return count; } +#ifdef KSU_COMPAT_HAS_PROC_OPS static const struct proc_ops uid_scanner_proc_ops = { .proc_open = uid_scanner_open, .proc_read = seq_read, - .proc_write = uid_scanner_write, + .proc_write = uid_scanner_write, .proc_lseek = seq_lseek, .proc_release = single_release, }; +#else +static const struct file_operations uid_scanner_proc_ops = { + .owner = THIS_MODULE, + .open = uid_scanner_open, + .read = seq_read, + .write = uid_scanner_write, + .llseek = seq_lseek, + .release = single_release, +}; +#endif int ksu_throne_comm_init(void) { - // Create workqueue - scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1); - if (!scanner_wq) { - pr_err("failed to create scanner workqueue\n"); - return -ENOMEM; - } - - INIT_WORK(&scan_work, rescan_work_fn); - - // Create proc entry - proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops); - if (!proc_entry) { - pr_err("failed to create proc entry\n"); - destroy_workqueue(scanner_wq); - return -ENOMEM; - } - - pr_info("throne communication initialized\n"); - return 0; + // Create workqueue + scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1); + if (!scanner_wq) { + pr_err("failed to create scanner workqueue\n"); + return -ENOMEM; + } + + INIT_WORK(&scan_work, rescan_work_fn); + + // Create proc entry + proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops); + if (!proc_entry) { + pr_err("failed to create proc entry\n"); + destroy_workqueue(scanner_wq); + return -ENOMEM; + } + + pr_info("throne communication initialized\n"); + return 0; } void ksu_throne_comm_exit(void) { - if (proc_entry) { - proc_remove(proc_entry); - proc_entry = NULL; - } - - if (scanner_wq) { - destroy_workqueue(scanner_wq); - scanner_wq = NULL; - } - - pr_info("throne communication cleaned up\n"); + if (proc_entry) { + proc_remove(proc_entry); + proc_entry = NULL; + } + + if (scanner_wq) { + destroy_workqueue(scanner_wq); + scanner_wq = NULL; + } + + pr_info("throne communication cleaned up\n"); } int ksu_uid_init(void) { - INIT_WORK(&ksu_state_save_work, do_save_throne_state); - INIT_WORK(&ksu_state_load_work, do_load_throne_state); - return 0; + INIT_WORK(&ksu_state_save_work, do_save_throne_state); + INIT_WORK(&ksu_state_load_work, do_load_throne_state); + return 0; } void ksu_uid_exit(void) { - do_save_throne_state(NULL); + do_save_throne_state(NULL); } \ No newline at end of file diff --git a/kernel/throne_tracker.c b/kernel/throne_tracker.c index ba5b43de..e0e4d230 100644 --- a/kernel/throne_tracker.c +++ b/kernel/throne_tracker.c @@ -26,203 +26,203 @@ static uid_t locked_dynamic_manager_uid = KSU_INVALID_UID; #define USER_DATA_PATH_LEN 288 struct uid_data { - struct list_head list; - u32 uid; - char package[KSU_MAX_PACKAGE_NAME]; + struct list_head list; + u32 uid; + char package[KSU_MAX_PACKAGE_NAME]; }; // Try read /data/misc/user_uid/uid_list static int uid_from_um_list(struct list_head *uid_list) { - struct file *fp; - char *buf = NULL; - loff_t size, pos = 0; - ssize_t nr; - int cnt = 0; + struct file *fp; + char *buf = NULL; + loff_t size, pos = 0; + ssize_t nr; + int cnt = 0; - fp = ksu_filp_open_compat(KSU_UID_LIST_PATH, O_RDONLY, 0); - if (IS_ERR(fp)) - return -ENOENT; + fp = ksu_filp_open_compat(KSU_UID_LIST_PATH, O_RDONLY, 0); + if (IS_ERR(fp)) + return -ENOENT; - size = fp->f_inode->i_size; - if (size <= 0) { - filp_close(fp, NULL); - return -ENODATA; - } + size = fp->f_inode->i_size; + if (size <= 0) { + filp_close(fp, NULL); + return -ENODATA; + } - buf = kzalloc(size + 1, GFP_ATOMIC); - if (!buf) { - pr_err("uid_list: OOM %lld B\n", size); - filp_close(fp, NULL); - return -ENOMEM; - } + buf = kzalloc(size + 1, GFP_ATOMIC); + if (!buf) { + pr_err("uid_list: OOM %lld B\n", size); + filp_close(fp, NULL); + return -ENOMEM; + } - nr = ksu_kernel_read_compat(fp, buf, size, &pos); - filp_close(fp, NULL); - if (nr != size) { - pr_err("uid_list: short read %zd/%lld\n", nr, size); - kfree(buf); - return -EIO; - } - buf[size] = '\0'; + nr = ksu_kernel_read_compat(fp, buf, size, &pos); + filp_close(fp, NULL); + if (nr != size) { + pr_err("uid_list: short read %zd/%lld\n", nr, size); + kfree(buf); + return -EIO; + } + buf[size] = '\0'; - for (char *line = buf, *next; line; line = next) { - next = strchr(line, '\n'); - if (next) *next++ = '\0'; + for (char *line = buf, *next; line; line = next) { + next = strchr(line, '\n'); + if (next) *next++ = '\0'; - while (*line == ' ' || *line == '\t' || *line == '\r') ++line; - if (!*line) continue; + while (*line == ' ' || *line == '\t' || *line == '\r') ++line; + if (!*line) continue; - char *uid_str = strsep(&line, " \t"); - char *pkg = line; - if (!pkg) continue; - while (*pkg == ' ' || *pkg == '\t') ++pkg; - if (!*pkg) continue; + char *uid_str = strsep(&line, " \t"); + char *pkg = line; + if (!pkg) continue; + while (*pkg == ' ' || *pkg == '\t') ++pkg; + if (!*pkg) continue; - u32 uid; - if (kstrtou32(uid_str, 10, &uid)) { - pr_warn_once("uid_list: bad uid <%s>\n", uid_str); - continue; - } + u32 uid; + if (kstrtou32(uid_str, 10, &uid)) { + pr_warn_once("uid_list: bad uid <%s>\n", uid_str); + continue; + } - struct uid_data *d = kzalloc(sizeof(*d), GFP_ATOMIC); - if (unlikely(!d)) { - pr_err("uid_list: OOM uid=%u\n", uid); - continue; - } + struct uid_data *d = kzalloc(sizeof(*d), GFP_ATOMIC); + if (unlikely(!d)) { + pr_err("uid_list: OOM uid=%u\n", uid); + continue; + } - d->uid = uid; - strscpy(d->package, pkg, KSU_MAX_PACKAGE_NAME); - list_add_tail(&d->list, uid_list); - ++cnt; - } + d->uid = uid; + strscpy(d->package, pkg, KSU_MAX_PACKAGE_NAME); + list_add_tail(&d->list, uid_list); + ++cnt; + } - kfree(buf); - pr_info("uid_list: loaded %d entries\n", cnt); - return cnt > 0 ? 0 : -ENODATA; + kfree(buf); + pr_info("uid_list: loaded %d entries\n", cnt); + return cnt > 0 ? 0 : -ENODATA; } static int get_pkg_from_apk_path(char *pkg, const char *path) { - int len = strlen(path); - if (len >= KSU_MAX_PACKAGE_NAME || len < 1) - return -1; + int len = strlen(path); + if (len >= KSU_MAX_PACKAGE_NAME || len < 1) + return -1; - const char *last_slash = NULL; - const char *second_last_slash = NULL; + const char *last_slash = NULL; + const char *second_last_slash = NULL; - int i; - for (i = len - 1; i >= 0; i--) { - if (path[i] == '/') { - if (!last_slash) { - last_slash = &path[i]; - } else { - second_last_slash = &path[i]; - break; - } - } - } + int i; + for (i = len - 1; i >= 0; i--) { + if (path[i] == '/') { + if (!last_slash) { + last_slash = &path[i]; + } else { + second_last_slash = &path[i]; + break; + } + } + } - if (!last_slash || !second_last_slash) - return -1; + if (!last_slash || !second_last_slash) + return -1; - const char *last_hyphen = strchr(second_last_slash, '-'); - if (!last_hyphen || last_hyphen > last_slash) - return -1; + const char *last_hyphen = strchr(second_last_slash, '-'); + if (!last_hyphen || last_hyphen > last_slash) + return -1; - int pkg_len = last_hyphen - second_last_slash - 1; - if (pkg_len >= KSU_MAX_PACKAGE_NAME || pkg_len <= 0) - return -1; + int pkg_len = last_hyphen - second_last_slash - 1; + if (pkg_len >= KSU_MAX_PACKAGE_NAME || pkg_len <= 0) + return -1; - // Copying the package name - strncpy(pkg, second_last_slash + 1, pkg_len); - pkg[pkg_len] = '\0'; + // Copying the package name + strncpy(pkg, second_last_slash + 1, pkg_len); + pkg[pkg_len] = '\0'; - return 0; + return 0; } static void crown_manager(const char *apk, struct list_head *uid_data, int signature_index) { - char pkg[KSU_MAX_PACKAGE_NAME]; - if (get_pkg_from_apk_path(pkg, apk) < 0) { - pr_err("Failed to get package name from apk path: %s\n", apk); - return; - } + char pkg[KSU_MAX_PACKAGE_NAME]; + if (get_pkg_from_apk_path(pkg, apk) < 0) { + pr_err("Failed to get package name from apk path: %s\n", apk); + return; + } - pr_info("manager pkg: %s, signature_index: %d\n", pkg, signature_index); + pr_info("manager pkg: %s, signature_index: %d\n", pkg, signature_index); #ifdef KSU_MANAGER_PACKAGE - // pkg is `/` - if (strncmp(pkg, KSU_MANAGER_PACKAGE, sizeof(KSU_MANAGER_PACKAGE))) { - pr_info("manager package is inconsistent with kernel build: %s\n", - KSU_MANAGER_PACKAGE); - return; - } + // pkg is `/` + if (strncmp(pkg, KSU_MANAGER_PACKAGE, sizeof(KSU_MANAGER_PACKAGE))) { + pr_info("manager package is inconsistent with kernel build: %s\n", + KSU_MANAGER_PACKAGE); + return; + } #endif - struct uid_data *np; + struct uid_data *np; - list_for_each_entry(np, uid_data, list) { - if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) { - bool is_dynamic = (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2); + list_for_each_entry(np, uid_data, list) { + if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) { + bool is_dynamic = (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2); - if (is_dynamic) { - if (locked_dynamic_manager_uid != KSU_INVALID_UID && locked_dynamic_manager_uid != np->uid) { - pr_info("Unlocking previous dynamic manager UID: %d\n", locked_dynamic_manager_uid); - ksu_remove_manager(locked_dynamic_manager_uid); - locked_dynamic_manager_uid = KSU_INVALID_UID; - } - } else { - if (locked_manager_uid != KSU_INVALID_UID && locked_manager_uid != np->uid) { - pr_info("Unlocking previous manager UID: %d\n", locked_manager_uid); - ksu_invalidate_manager_uid(); // unlock old one - locked_manager_uid = KSU_INVALID_UID; - } - } + if (is_dynamic) { + if (locked_dynamic_manager_uid != KSU_INVALID_UID && locked_dynamic_manager_uid != np->uid) { + pr_info("Unlocking previous dynamic manager UID: %d\n", locked_dynamic_manager_uid); + ksu_remove_manager(locked_dynamic_manager_uid); + locked_dynamic_manager_uid = KSU_INVALID_UID; + } + } else { + if (locked_manager_uid != KSU_INVALID_UID && locked_manager_uid != np->uid) { + pr_info("Unlocking previous manager UID: %d\n", locked_manager_uid); + ksu_invalidate_manager_uid(); // unlock old one + locked_manager_uid = KSU_INVALID_UID; + } + } - pr_info("Crowning %s manager: %s (uid=%d, signature_index=%d)\n", - is_dynamic ? "dynamic" : "traditional", pkg, np->uid, signature_index); + pr_info("Crowning %s manager: %s (uid=%d, signature_index=%d)\n", + is_dynamic ? "dynamic" : "traditional", pkg, np->uid, signature_index); - if (is_dynamic) { - ksu_add_manager(np->uid, signature_index); - locked_dynamic_manager_uid = np->uid; + if (is_dynamic) { + ksu_add_manager(np->uid, signature_index); + locked_dynamic_manager_uid = np->uid; - // If there is no traditional manager, set it to the current UID - if (!ksu_is_manager_uid_valid()) { - ksu_set_manager_uid(np->uid); - locked_manager_uid = np->uid; - } - } else { - ksu_set_manager_uid(np->uid); // throne new UID - locked_manager_uid = np->uid; // store locked UID - } - break; - } - } + // If there is no traditional manager, set it to the current UID + if (!ksu_is_manager_uid_valid()) { + ksu_set_manager_uid(np->uid); + locked_manager_uid = np->uid; + } + } else { + ksu_set_manager_uid(np->uid); // throne new UID + locked_manager_uid = np->uid; // store locked UID + } + break; + } + } } #define DATA_PATH_LEN 384 // 384 is enough for /data/app//base.apk struct data_path { - char dirpath[DATA_PATH_LEN]; - int depth; - struct list_head list; + char dirpath[DATA_PATH_LEN]; + int depth; + struct list_head list; }; struct apk_path_hash { - unsigned int hash; - bool exists; - struct list_head list; + unsigned int hash; + bool exists; + struct list_head list; }; static struct list_head apk_path_hash_list = LIST_HEAD_INIT(apk_path_hash_list); struct my_dir_context { - struct dir_context ctx; - struct list_head *data_path_list; - char *parent_dir; - void *private_data; - int depth; - int *stop; + struct dir_context ctx; + struct list_head *data_path_list; + char *parent_dir; + void *private_data; + int depth; + int *stop; }; // https://docs.kernel.org/filesystems/porting.html // filldir_t (readdir callbacks) calling conventions have changed. Instead of returning 0 or -E... it returns bool now. false means "no more" (as -E... used to) and true - "keep going" (as 0 in old calling conventions). Rationale: callers never looked at specific -E... values anyway. -> iterate_shared() instances require no changes at all, all filldir_t ones in the tree converted. @@ -237,411 +237,426 @@ struct my_dir_context { #endif struct uid_scan_stats { - size_t total_found; - size_t errors_encountered; + size_t total_found; + size_t errors_encountered; }; struct user_data_context { - struct dir_context ctx; - struct list_head *uid_list; - struct uid_scan_stats *stats; + struct dir_context ctx; + struct list_head *uid_list; + struct uid_scan_stats *stats; }; FILLDIR_RETURN_TYPE user_data_actor(struct dir_context *ctx, const char *name, - int namelen, loff_t off, u64 ino, - unsigned int d_type) + int namelen, loff_t off, u64 ino, + unsigned int d_type) { - struct user_data_context *my_ctx = - container_of(ctx, struct user_data_context, ctx); - - if (!my_ctx || !my_ctx->uid_list) { - return FILLDIR_ACTOR_STOP; - } + struct user_data_context *my_ctx = + container_of(ctx, struct user_data_context, ctx); + + if (!my_ctx || !my_ctx->uid_list) { + return FILLDIR_ACTOR_STOP; + } - if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen)) - return FILLDIR_ACTOR_CONTINUE; + if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen)) + return FILLDIR_ACTOR_CONTINUE; - if (d_type != DT_DIR) - return FILLDIR_ACTOR_CONTINUE; + if (d_type != DT_DIR) + return FILLDIR_ACTOR_CONTINUE; - if (namelen >= KSU_MAX_PACKAGE_NAME) { - pr_warn("Package name too long: %.*s\n", namelen, name); - if (my_ctx->stats) - my_ctx->stats->errors_encountered++; - return FILLDIR_ACTOR_CONTINUE; - } + if (namelen >= KSU_MAX_PACKAGE_NAME) { + pr_warn("Package name too long: %.*s\n", namelen, name); + if (my_ctx->stats) + my_ctx->stats->errors_encountered++; + return FILLDIR_ACTOR_CONTINUE; + } - char package_path[USER_DATA_PATH_LEN]; - if (snprintf(package_path, sizeof(package_path), "%s/%.*s", - USER_DATA_PATH, namelen, name) >= sizeof(package_path)) { - pr_err("Path too long for package: %.*s\n", namelen, name); - if (my_ctx->stats) - my_ctx->stats->errors_encountered++; - return FILLDIR_ACTOR_CONTINUE; - } + char package_path[USER_DATA_PATH_LEN]; + if (snprintf(package_path, sizeof(package_path), "%s/%.*s", + USER_DATA_PATH, namelen, name) >= sizeof(package_path)) { + pr_err("Path too long for package: %.*s\n", namelen, name); + if (my_ctx->stats) + my_ctx->stats->errors_encountered++; + return FILLDIR_ACTOR_CONTINUE; + } - struct path path; - int err = kern_path(package_path, LOOKUP_FOLLOW, &path); - if (err) { - pr_debug("Package path lookup failed: %s (err: %d)\n", package_path, err); - if (my_ctx->stats) - my_ctx->stats->errors_encountered++; - return FILLDIR_ACTOR_CONTINUE; - } + struct path path; + int err = kern_path(package_path, LOOKUP_FOLLOW, &path); + if (err) { + pr_debug("Package path lookup failed: %s (err: %d)\n", package_path, err); + if (my_ctx->stats) + my_ctx->stats->errors_encountered++; + return FILLDIR_ACTOR_CONTINUE; + } - struct kstat stat; - err = vfs_getattr(&path, &stat, STATX_UID, AT_STATX_SYNC_AS_STAT); - path_put(&path); - - if (err) { - pr_info("Failed to get attributes for: %s (err: %d)\n", package_path, err); - if (my_ctx->stats) - my_ctx->stats->errors_encountered++; - return FILLDIR_ACTOR_CONTINUE; - } +/* +4.11, also backported on lineage common kernel 4.9 !! +int vfs_getattr(const struct path *path, struct kstat *stat, + u32 request_mask, unsigned int query_flags) - uid_t uid = from_kuid(&init_user_ns, stat.uid); - if (uid == (uid_t)-1) { - pr_warn("Invalid UID for package: %.*s\n", namelen, name); - if (my_ctx->stats) - my_ctx->stats->errors_encountered++; - return FILLDIR_ACTOR_CONTINUE; - } +4.10 +int vfs_getattr(struct path *path, struct kstat *stat) - struct uid_data *data = kzalloc(sizeof(struct uid_data), GFP_ATOMIC); - if (!data) { - pr_err("Failed to allocate memory for package: %.*s\n", namelen, name); - if (my_ctx->stats) - my_ctx->stats->errors_encountered++; - return FILLDIR_ACTOR_CONTINUE; - } +basically no mask and flags for =< 4.10 - data->uid = uid; - size_t copy_len = min(namelen, KSU_MAX_PACKAGE_NAME - 1); - strncpy(data->package, name, copy_len); - data->package[copy_len] = '\0'; - - list_add_tail(&data->list, my_ctx->uid_list); - - if (my_ctx->stats) - my_ctx->stats->total_found++; - - pr_info("UserDE UID: Found package: %s, uid: %u\n", data->package, data->uid); - - return FILLDIR_ACTOR_CONTINUE; +*/ + struct kstat stat; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0) || defined(KSU_HAS_NEW_VFS_GETATTR) + err = vfs_getattr(&path, &stat, STATX_UID, AT_STATX_SYNC_AS_STAT); +#else + err = vfs_getattr(&path, &stat); +#endif + path_put(&path); + + if (err) { + pr_info("Failed to get attributes for: %s (err: %d)\n", package_path, err); + if (my_ctx->stats) + my_ctx->stats->errors_encountered++; + return FILLDIR_ACTOR_CONTINUE; + } + + uid_t uid = from_kuid(&init_user_ns, stat.uid); + if (uid == (uid_t)-1) { + pr_warn("Invalid UID for package: %.*s\n", namelen, name); + if (my_ctx->stats) + my_ctx->stats->errors_encountered++; + return FILLDIR_ACTOR_CONTINUE; + } + + struct uid_data *data = kzalloc(sizeof(struct uid_data), GFP_ATOMIC); + if (!data) { + pr_err("Failed to allocate memory for package: %.*s\n", namelen, name); + if (my_ctx->stats) + my_ctx->stats->errors_encountered++; + return FILLDIR_ACTOR_CONTINUE; + } + + data->uid = uid; + size_t copy_len = min(namelen, KSU_MAX_PACKAGE_NAME - 1); + strncpy(data->package, name, copy_len); + data->package[copy_len] = '\0'; + + list_add_tail(&data->list, my_ctx->uid_list); + + if (my_ctx->stats) + my_ctx->stats->total_found++; + + pr_info("UserDE UID: Found package: %s, uid: %u\n", data->package, data->uid); + + return FILLDIR_ACTOR_CONTINUE; } static int scan_user_data_for_uids(struct list_head *uid_list) { - struct file *dir_file; - struct uid_scan_stats stats = {0}; - int ret = 0; - - if (!uid_list) { - return -EINVAL; - } + struct file *dir_file; + struct uid_scan_stats stats = {0}; + int ret = 0; + + if (!uid_list) { + return -EINVAL; + } - dir_file = ksu_filp_open_compat(USER_DATA_PATH, O_RDONLY, 0); - if (IS_ERR(dir_file)) { - pr_err("UserDE UID: Failed to open %s, err: (%ld)\n", USER_DATA_PATH, PTR_ERR(dir_file)); - return PTR_ERR(dir_file); - } + dir_file = ksu_filp_open_compat(USER_DATA_PATH, O_RDONLY, 0); + if (IS_ERR(dir_file)) { + pr_err("UserDE UID: Failed to open %s, err: (%ld)\n", USER_DATA_PATH, PTR_ERR(dir_file)); + return PTR_ERR(dir_file); + } - struct user_data_context ctx = { - .ctx.actor = user_data_actor, - .uid_list = uid_list, - .stats = &stats - }; + struct user_data_context ctx = { + .ctx.actor = user_data_actor, + .uid_list = uid_list, + .stats = &stats + }; - ret = iterate_dir(dir_file, &ctx.ctx); - filp_close(dir_file, NULL); + ret = iterate_dir(dir_file, &ctx.ctx); + filp_close(dir_file, NULL); - if (stats.errors_encountered > 0) { - pr_warn("Encountered %zu errors while scanning user data directory\n", - stats.errors_encountered); - } + if (stats.errors_encountered > 0) { + pr_warn("Encountered %zu errors while scanning user data directory\n", + stats.errors_encountered); + } - pr_info("UserDE UID: Scanned %s directory with %zu errors\n", - USER_DATA_PATH, stats.errors_encountered); + pr_info("UserDE UID: Scanned %s directory with %zu errors\n", + USER_DATA_PATH, stats.errors_encountered); - return ret; + return ret; } FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name, - int namelen, loff_t off, u64 ino, - unsigned int d_type) + int namelen, loff_t off, u64 ino, + unsigned int d_type) { - struct my_dir_context *my_ctx = - container_of(ctx, struct my_dir_context, ctx); - char dirpath[DATA_PATH_LEN]; + struct my_dir_context *my_ctx = + container_of(ctx, struct my_dir_context, ctx); + char dirpath[DATA_PATH_LEN]; - if (!my_ctx) { - pr_err("Invalid context\n"); - return FILLDIR_ACTOR_STOP; - } - if (my_ctx->stop && *my_ctx->stop) { - pr_info("Stop searching\n"); - return FILLDIR_ACTOR_STOP; - } + if (!my_ctx) { + pr_err("Invalid context\n"); + return FILLDIR_ACTOR_STOP; + } + if (my_ctx->stop && *my_ctx->stop) { + pr_info("Stop searching\n"); + return FILLDIR_ACTOR_STOP; + } - if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen)) - return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".." + if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen)) + return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".." - if (d_type == DT_DIR && namelen >= 8 && !strncmp(name, "vmdl", 4) && - !strncmp(name + namelen - 4, ".tmp", 4)) { - pr_info("Skipping directory: %.*s\n", namelen, name); - return FILLDIR_ACTOR_CONTINUE; // Skip staging package - } - + if (d_type == DT_DIR && namelen >= 8 && !strncmp(name, "vmdl", 4) && + !strncmp(name + namelen - 4, ".tmp", 4)) { + pr_info("Skipping directory: %.*s\n", namelen, name); + return FILLDIR_ACTOR_CONTINUE; // Skip staging package + } + + if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir, + namelen, name) >= DATA_PATH_LEN) { + pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen, + name); + return FILLDIR_ACTOR_CONTINUE; + } - if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir, - namelen, name) >= DATA_PATH_LEN) { - pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen, - name); - return FILLDIR_ACTOR_CONTINUE; - } + if (d_type == DT_DIR && my_ctx->depth > 0 && + (my_ctx->stop && !*my_ctx->stop)) { + struct data_path *data = kmalloc(sizeof(struct data_path), GFP_ATOMIC); - if (d_type == DT_DIR && my_ctx->depth > 0 && - (my_ctx->stop && !*my_ctx->stop)) { - struct data_path *data = kmalloc(sizeof(struct data_path), GFP_ATOMIC); + if (!data) { + pr_err("Failed to allocate memory for %s\n", dirpath); + return FILLDIR_ACTOR_CONTINUE; + } - if (!data) { - pr_err("Failed to allocate memory for %s\n", dirpath); - return FILLDIR_ACTOR_CONTINUE; - } + strscpy(data->dirpath, dirpath, DATA_PATH_LEN); + data->depth = my_ctx->depth - 1; + list_add_tail(&data->list, my_ctx->data_path_list); + } else { + if ((namelen == 8) && (strncmp(name, "base.apk", namelen) == 0)) { + struct apk_path_hash *pos, *n; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + unsigned int hash = full_name_hash(dirpath, strlen(dirpath)); +#else + unsigned int hash = full_name_hash(NULL, dirpath, strlen(dirpath)); +#endif + list_for_each_entry(pos, &apk_path_hash_list, list) { + if (hash == pos->hash) { + pos->exists = true; + return FILLDIR_ACTOR_CONTINUE; + } + } - strscpy(data->dirpath, dirpath, DATA_PATH_LEN); - data->depth = my_ctx->depth - 1; - list_add_tail(&data->list, my_ctx->data_path_list); - } else { - if ((namelen == 8) && (strncmp(name, "base.apk", namelen) == 0)) { - struct apk_path_hash *pos, *n; - unsigned int hash = full_name_hash(NULL, dirpath, strlen(dirpath)); - list_for_each_entry(pos, &apk_path_hash_list, list) { - if (hash == pos->hash) { - pos->exists = true; - return FILLDIR_ACTOR_CONTINUE; - } - } + int signature_index = -1; + bool is_multi_manager = is_dynamic_manager_apk( + dirpath, &signature_index); - int signature_index = -1; - bool is_multi_manager = is_dynamic_manager_apk( - dirpath, &signature_index); + pr_info("Found new base.apk at path: %s, is_multi_manager: %d, signature_index: %d\n", + dirpath, is_multi_manager, signature_index); + + // Check for dynamic sign or multi-manager signatures + if (is_multi_manager && (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2)) { + crown_manager(dirpath, my_ctx->private_data, signature_index); + + struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC); + if (apk_data) { + apk_data->hash = hash; + apk_data->exists = true; + list_add_tail(&apk_data->list, &apk_path_hash_list); + } + } else if (is_manager_apk(dirpath)) { + crown_manager(dirpath, my_ctx->private_data, 0); + *my_ctx->stop = 1; - pr_info("Found new base.apk at path: %s, is_multi_manager: %d, signature_index: %d\n", - dirpath, is_multi_manager, signature_index); - - // Check for dynamic sign or multi-manager signatures - if (is_multi_manager && (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2)) { - crown_manager(dirpath, my_ctx->private_data, signature_index); - - struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC); - if (apk_data) { - apk_data->hash = hash; - apk_data->exists = true; - list_add_tail(&apk_data->list, &apk_path_hash_list); - } + // Manager found, clear APK cache list + list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) { + list_del(&pos->list); + kfree(pos); + } + } else { + struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC); + if (apk_data) { + apk_data->hash = hash; + apk_data->exists = true; + list_add_tail(&apk_data->list, &apk_path_hash_list); + } + } + } + } - } else if (is_manager_apk(dirpath)) { - crown_manager(dirpath, my_ctx->private_data, 0); - *my_ctx->stop = 1; - - // Manager found, clear APK cache list - list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) { - list_del(&pos->list); - kfree(pos); - } - } else { - struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC); - if (apk_data) { - apk_data->hash = hash; - apk_data->exists = true; - list_add_tail(&apk_data->list, &apk_path_hash_list); - } - } - } - } - - return FILLDIR_ACTOR_CONTINUE; + return FILLDIR_ACTOR_CONTINUE; } void search_manager(const char *path, int depth, struct list_head *uid_data) { - int i, stop = 0; - struct list_head data_path_list; - INIT_LIST_HEAD(&data_path_list); - unsigned long data_app_magic = 0; - - // Initialize APK cache list - struct apk_path_hash *pos, *n; - list_for_each_entry(pos, &apk_path_hash_list, list) { - pos->exists = false; - } + int i, stop = 0; + struct list_head data_path_list; + INIT_LIST_HEAD(&data_path_list); + unsigned long data_app_magic = 0; + + // Initialize APK cache list + struct apk_path_hash *pos, *n; + list_for_each_entry(pos, &apk_path_hash_list, list) { + pos->exists = false; + } - // First depth - struct data_path data; - strscpy(data.dirpath, path, DATA_PATH_LEN); - data.depth = depth; - list_add_tail(&data.list, &data_path_list); + // First depth + struct data_path data; + strscpy(data.dirpath, path, DATA_PATH_LEN); + data.depth = depth; + list_add_tail(&data.list, &data_path_list); - for (i = depth; i >= 0; i--) { - struct data_path *pos, *n; + for (i = depth; i >= 0; i--) { + struct data_path *pos, *n; - list_for_each_entry_safe(pos, n, &data_path_list, list) { - struct my_dir_context ctx = { .ctx.actor = my_actor, - .data_path_list = &data_path_list, - .parent_dir = pos->dirpath, - .private_data = uid_data, - .depth = pos->depth, - .stop = &stop }; - struct file *file; + list_for_each_entry_safe(pos, n, &data_path_list, list) { + struct my_dir_context ctx = { .ctx.actor = my_actor, + .data_path_list = &data_path_list, + .parent_dir = pos->dirpath, + .private_data = uid_data, + .depth = pos->depth, + .stop = &stop }; + struct file *file; - if (!stop) { - file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0); - if (IS_ERR(file)) { - pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file)); - goto skip_iterate; - } - - // grab magic on first folder, which is /data/app - if (!data_app_magic) { - if (file->f_inode->i_sb->s_magic) { - data_app_magic = file->f_inode->i_sb->s_magic; - pr_info("%s: dir: %s got magic! 0x%lx\n", __func__, pos->dirpath, data_app_magic); - } else { - filp_close(file, NULL); - goto skip_iterate; - } - } - - if (file->f_inode->i_sb->s_magic != data_app_magic) { - pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n", __func__, pos->dirpath, - file->f_inode->i_sb->s_magic, data_app_magic); - filp_close(file, NULL); - goto skip_iterate; - } + if (!stop) { + file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0); + if (IS_ERR(file)) { + pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file)); + goto skip_iterate; + } + + // grab magic on first folder, which is /data/app + if (!data_app_magic) { + if (file->f_inode->i_sb->s_magic) { + data_app_magic = file->f_inode->i_sb->s_magic; + pr_info("%s: dir: %s got magic! 0x%lx\n", __func__, pos->dirpath, data_app_magic); + } else { + filp_close(file, NULL); + goto skip_iterate; + } + } + + if (file->f_inode->i_sb->s_magic != data_app_magic) { + pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n", __func__, pos->dirpath, + file->f_inode->i_sb->s_magic, data_app_magic); + filp_close(file, NULL); + goto skip_iterate; + } - iterate_dir(file, &ctx.ctx); - filp_close(file, NULL); - } + iterate_dir(file, &ctx.ctx); + filp_close(file, NULL); + } skip_iterate: - list_del(&pos->list); - if (pos != &data) - kfree(pos); - } - } + list_del(&pos->list); + if (pos != &data) + kfree(pos); + } + } - // Remove stale cached APK entries - list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) { - if (!pos->exists) { - list_del(&pos->list); - kfree(pos); - } - } + // Remove stale cached APK entries + list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) { + if (!pos->exists) { + list_del(&pos->list); + kfree(pos); + } + } } static bool is_uid_exist(uid_t uid, char *package, void *data) { - struct list_head *list = (struct list_head *)data; - struct uid_data *np; + struct list_head *list = (struct list_head *)data; + struct uid_data *np; - bool exist = false; - list_for_each_entry (np, list, list) { - if (np->uid == uid % 100000 && - strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) { - exist = true; - break; - } - } - return exist; + bool exist = false; + list_for_each_entry (np, list, list) { + if (np->uid == uid % 100000 && + strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) { + exist = true; + break; + } + } + return exist; } -extern bool ksu_uid_scanner_enabled; - -void track_throne() +void track_throne(void) { - struct list_head uid_list; - struct uid_data *np, *n; + struct list_head uid_list; + struct uid_data *np, *n; - // init uid list head - INIT_LIST_HEAD(&uid_list); + // init uid list head + INIT_LIST_HEAD(&uid_list); - if (ksu_uid_scanner_enabled) { - pr_info("Scanning %s directory..\n", KSU_UID_LIST_PATH); - if (uid_from_um_list(&uid_list) == 0) { - pr_info("Loaded UIDs from %s success\n", KSU_UID_LIST_PATH); - } else { - pr_warn("%s read failed, falling back to %s\n", KSU_UID_LIST_PATH, USER_DATA_PATH); - if (scan_user_data_for_uids(&uid_list) < 0) - goto out; - } - } else { - pr_info("User mode scan disabled, scanning %s\n", USER_DATA_PATH); - if (scan_user_data_for_uids(&uid_list) < 0) - goto out; - } + if (ksu_uid_scanner_enabled) { + pr_info("Scanning %s directory..\n", KSU_UID_LIST_PATH); + if (uid_from_um_list(&uid_list) == 0) { + pr_info("Loaded UIDs from %s success\n", KSU_UID_LIST_PATH); + } else { + pr_warn("%s read failed, falling back to %s\n", KSU_UID_LIST_PATH, USER_DATA_PATH); + if (scan_user_data_for_uids(&uid_list) < 0) + goto out; + } + } else { + pr_info("User mode scan disabled, scanning %s\n", USER_DATA_PATH); + if (scan_user_data_for_uids(&uid_list) < 0) + goto out; + } - // check if manager UID exists - bool manager_exist = false; - int current_manager_uid = ksu_get_manager_uid() % 100000; + // check if manager UID exists + bool manager_exist = false; + int current_manager_uid = ksu_get_manager_uid() % 100000; - list_for_each_entry(np, &uid_list, list) { - if (np->uid == current_manager_uid) { - manager_exist = true; - break; - } - } + list_for_each_entry(np, &uid_list, list) { + if (np->uid == current_manager_uid) { + manager_exist = true; + break; + } + } - if (!manager_exist && locked_manager_uid != KSU_INVALID_UID) { - pr_info("Manager APK removed, unlocking previous UID: %d\n", locked_manager_uid); - ksu_invalidate_manager_uid(); - locked_manager_uid = KSU_INVALID_UID; - } + if (!manager_exist && locked_manager_uid != KSU_INVALID_UID) { + pr_info("Manager APK removed, unlocking previous UID: %d\n", locked_manager_uid); + ksu_invalidate_manager_uid(); + locked_manager_uid = KSU_INVALID_UID; + } - // Check if the Dynamic Manager exists (only check locked UIDs) - bool dynamic_manager_exist = false; - if (ksu_is_dynamic_manager_enabled() && locked_dynamic_manager_uid != KSU_INVALID_UID) { - list_for_each_entry(np, &uid_list, list) { - if (np->uid == locked_dynamic_manager_uid) { - dynamic_manager_exist = true; - break; - } - } + // Check if the Dynamic Manager exists (only check locked UIDs) + bool dynamic_manager_exist = false; + if (ksu_is_dynamic_manager_enabled() && locked_dynamic_manager_uid != KSU_INVALID_UID) { + list_for_each_entry(np, &uid_list, list) { + if (np->uid == locked_dynamic_manager_uid) { + dynamic_manager_exist = true; + break; + } + } - if (!dynamic_manager_exist) { - pr_info("Dynamic manager APK removed, unlocking previous UID: %d\n", locked_dynamic_manager_uid); - ksu_remove_manager(locked_dynamic_manager_uid); - locked_dynamic_manager_uid = KSU_INVALID_UID; - } - } + if (!dynamic_manager_exist) { + pr_info("Dynamic manager APK removed, unlocking previous UID: %d\n", locked_dynamic_manager_uid); + ksu_remove_manager(locked_dynamic_manager_uid); + locked_dynamic_manager_uid = KSU_INVALID_UID; + } + } - bool need_search = !manager_exist; - if (ksu_is_dynamic_manager_enabled() && !dynamic_manager_exist) { - need_search = true; - } + bool need_search = !manager_exist; + if (ksu_is_dynamic_manager_enabled() && !dynamic_manager_exist) { + need_search = true; + } - if (need_search) { - pr_info("Searching for manager(s)...\n"); - search_manager("/data/app", 2, &uid_list); - pr_info("Manager search finished\n"); - } - - // then prune the allowlist - ksu_prune_allowlist(is_uid_exist, &uid_list); + if (need_search) { + pr_info("Searching for manager(s)...\n"); + search_manager("/data/app", 2, &uid_list); + pr_info("Manager search finished\n"); + } + + // then prune the allowlist + ksu_prune_allowlist(is_uid_exist, &uid_list); out: - // free uid_list - list_for_each_entry_safe(np, n, &uid_list, list) { - list_del(&np->list); - kfree(np); - } + // free uid_list + list_for_each_entry_safe(np, n, &uid_list, list) { + list_del(&np->list); + kfree(np); + } } -void ksu_throne_tracker_init() +void ksu_throne_tracker_init(void) { - // nothing to do + // nothing to do } -void ksu_throne_tracker_exit() +void ksu_throne_tracker_exit(void) { - // nothing to do -} + // nothing to do +} \ No newline at end of file diff --git a/manager/app/src/main/cpp/jni.c b/manager/app/src/main/cpp/jni.c index a5b0f816..a6f73373 100644 --- a/manager/app/src/main/cpp/jni.c +++ b/manager/app/src/main/cpp/jni.c @@ -6,410 +6,412 @@ #include #include - -NativeBridge(becomeManager, jboolean, jstring pkg) { - const char* cpkg = GetEnvironment()->GetStringUTFChars(env, pkg, JNI_FALSE); - bool result = become_manager(cpkg); - - GetEnvironment()->ReleaseStringUTFChars(env, pkg, cpkg); - return result; -} - NativeBridgeNP(getVersion, jint) { - return get_version(); + uint32_t version = get_version(); + if (version > INT32_MAX) { + LogDebug("Version overflow: %u", version); + } + return (jint)version; } // get VERSION FULL NativeBridgeNP(getFullVersion, jstring) { - char buff[255] = { 0 }; - get_full_version((char *) &buff); - return GetEnvironment()->NewStringUTF(env, buff); + char buff[255] = { 0 }; + get_full_version((char *) &buff); + return GetEnvironment()->NewStringUTF(env, buff); } NativeBridgeNP(getAllowList, jintArray) { - int uids[1024]; - int size = 0; - bool result = get_allow_list(uids, &size); + struct ksu_get_allow_list_cmd cmd = {}; + bool result = get_allow_list(&cmd); - LogDebug("getAllowList: %d, size: %d", result, size); + if (result) { + jsize array_size = (jsize)cmd.count; + if (array_size < 0 || (unsigned int)array_size != cmd.count) { + LogDebug("Invalid array size: %u", cmd.count); + return GetEnvironment()->NewIntArray(env, 0); + } - if (result) { - jintArray array = GetEnvironment()->NewIntArray(env, size); - GetEnvironment()->SetIntArrayRegion(env, array, 0, size, uids); + jintArray array = GetEnvironment()->NewIntArray(env, array_size); + GetEnvironment()->SetIntArrayRegion(env, array, 0, array_size, (const jint *)(cmd.uids)); - return array; - } + return array; + } - return GetEnvironment()->NewIntArray(env, 0); + return GetEnvironment()->NewIntArray(env, 0); } NativeBridgeNP(isSafeMode, jboolean) { - return is_safe_mode(); + return is_safe_mode(); } NativeBridgeNP(isLkmMode, jboolean) { - return is_lkm_mode(); + return is_lkm_mode(); +} + +NativeBridgeNP(isManager, jboolean) { + return is_manager(); } static void fillIntArray(JNIEnv *env, jobject list, int *data, int count) { - jclass cls = GetEnvironment()->GetObjectClass(env, list); - jmethodID add = GetEnvironment()->GetMethodID(env, cls, "add", "(Ljava/lang/Object;)Z"); - jclass integerCls = GetEnvironment()->FindClass(env, "java/lang/Integer"); - jmethodID constructor = GetEnvironment()->GetMethodID(env, integerCls, "", "(I)V"); - for (int i = 0; i < count; ++i) { - jobject integer = GetEnvironment()->NewObject(env, integerCls, constructor, data[i]); - GetEnvironment()->CallBooleanMethod(env, list, add, integer); - } + jclass cls = GetEnvironment()->GetObjectClass(env, list); + jmethodID add = GetEnvironment()->GetMethodID(env, cls, "add", "(Ljava/lang/Object;)Z"); + jclass integerCls = GetEnvironment()->FindClass(env, "java/lang/Integer"); + jmethodID constructor = GetEnvironment()->GetMethodID(env, integerCls, "", "(I)V"); + for (int i = 0; i < count; ++i) { + jobject integer = GetEnvironment()->NewObject(env, integerCls, constructor, data[i]); + GetEnvironment()->CallBooleanMethod(env, list, add, integer); + } } static void addIntToList(JNIEnv *env, jobject list, int ele) { - jclass cls = GetEnvironment()->GetObjectClass(env, list); - jmethodID add = GetEnvironment()->GetMethodID(env, cls, "add", "(Ljava/lang/Object;)Z"); - jclass integerCls = GetEnvironment()->FindClass(env, "java/lang/Integer"); - jmethodID constructor = GetEnvironment()->GetMethodID(env, integerCls, "", "(I)V"); - jobject integer = GetEnvironment()->NewObject(env, integerCls, constructor, ele); - GetEnvironment()->CallBooleanMethod(env, list, add, integer); + jclass cls = GetEnvironment()->GetObjectClass(env, list); + jmethodID add = GetEnvironment()->GetMethodID(env, cls, "add", "(Ljava/lang/Object;)Z"); + jclass integerCls = GetEnvironment()->FindClass(env, "java/lang/Integer"); + jmethodID constructor = GetEnvironment()->GetMethodID(env, integerCls, "", "(I)V"); + jobject integer = GetEnvironment()->NewObject(env, integerCls, constructor, ele); + GetEnvironment()->CallBooleanMethod(env, list, add, integer); } static uint64_t capListToBits(JNIEnv *env, jobject list) { - jclass cls = GetEnvironment()->GetObjectClass(env, list); - jmethodID get = GetEnvironment()->GetMethodID(env, cls, "get", "(I)Ljava/lang/Object;"); - jmethodID size = GetEnvironment()->GetMethodID(env, cls, "size", "()I"); - jint listSize = GetEnvironment()->CallIntMethod(env, list, size); - jclass integerCls = GetEnvironment()->FindClass(env, "java/lang/Integer"); - jmethodID intValue = GetEnvironment()->GetMethodID(env, integerCls, "intValue", "()I"); - uint64_t result = 0; - for (int i = 0; i < listSize; ++i) { - jobject integer = GetEnvironment()->CallObjectMethod(env, list, get, i); - int data = GetEnvironment()->CallIntMethod(env, integer, intValue); + jclass cls = GetEnvironment()->GetObjectClass(env, list); + jmethodID get = GetEnvironment()->GetMethodID(env, cls, "get", "(I)Ljava/lang/Object;"); + jmethodID size = GetEnvironment()->GetMethodID(env, cls, "size", "()I"); + jint listSize = GetEnvironment()->CallIntMethod(env, list, size); + jclass integerCls = GetEnvironment()->FindClass(env, "java/lang/Integer"); + jmethodID intValue = GetEnvironment()->GetMethodID(env, integerCls, "intValue", "()I"); + uint64_t result = 0; + for (int i = 0; i < listSize; ++i) { + jobject integer = GetEnvironment()->CallObjectMethod(env, list, get, i); + int data = GetEnvironment()->CallIntMethod(env, integer, intValue); - if (cap_valid(data)) { - result |= (1ULL << data); - } - } + if (cap_valid(data)) { + result |= (1ULL << data); + } + } - return result; + return result; } static int getListSize(JNIEnv *env, jobject list) { - jclass cls = GetEnvironment()->GetObjectClass(env, list); - jmethodID size = GetEnvironment()->GetMethodID(env, cls, "size", "()I"); - return GetEnvironment()->CallIntMethod(env, list, size); + jclass cls = GetEnvironment()->GetObjectClass(env, list); + jmethodID size = GetEnvironment()->GetMethodID(env, cls, "size", "()I"); + return GetEnvironment()->CallIntMethod(env, list, size); } static void fillArrayWithList(JNIEnv *env, jobject list, int *data, int count) { - jclass cls = GetEnvironment()->GetObjectClass(env, list); - jmethodID get = GetEnvironment()->GetMethodID(env, cls, "get", "(I)Ljava/lang/Object;"); - jclass integerCls = GetEnvironment()->FindClass(env, "java/lang/Integer"); - jmethodID intValue = GetEnvironment()->GetMethodID(env, integerCls, "intValue", "()I"); - for (int i = 0; i < count; ++i) { - jobject integer = GetEnvironment()->CallObjectMethod(env, list, get, i); - data[i] = GetEnvironment()->CallIntMethod(env, integer, intValue); - } + jclass cls = GetEnvironment()->GetObjectClass(env, list); + jmethodID get = GetEnvironment()->GetMethodID(env, cls, "get", "(I)Ljava/lang/Object;"); + jclass integerCls = GetEnvironment()->FindClass(env, "java/lang/Integer"); + jmethodID intValue = GetEnvironment()->GetMethodID(env, integerCls, "intValue", "()I"); + for (int i = 0; i < count; ++i) { + jobject integer = GetEnvironment()->CallObjectMethod(env, list, get, i); + data[i] = GetEnvironment()->CallIntMethod(env, integer, intValue); + } } NativeBridge(getAppProfile, jobject, jstring pkg, jint uid) { - if (GetEnvironment()->GetStringLength(env, pkg) > KSU_MAX_PACKAGE_NAME) { - return NULL; - } + if (GetEnvironment()->GetStringLength(env, pkg) > KSU_MAX_PACKAGE_NAME) { + return NULL; + } - char key[KSU_MAX_PACKAGE_NAME] = { 0 }; - const char* cpkg = GetEnvironment()->GetStringUTFChars(env, pkg, nullptr); - strcpy(key, cpkg); - GetEnvironment()->ReleaseStringUTFChars(env, pkg, cpkg); + char key[KSU_MAX_PACKAGE_NAME] = { 0 }; + const char* cpkg = GetEnvironment()->GetStringUTFChars(env, pkg, nullptr); + strcpy(key, cpkg); + GetEnvironment()->ReleaseStringUTFChars(env, pkg, cpkg); - struct app_profile profile = { 0 }; - profile.version = KSU_APP_PROFILE_VER; + struct app_profile profile = { 0 }; + profile.version = KSU_APP_PROFILE_VER; - strcpy(profile.key, key); - profile.current_uid = uid; + strcpy(profile.key, key); + profile.current_uid = uid; - bool useDefaultProfile = !get_app_profile(key, &profile); + bool useDefaultProfile = get_app_profile(&profile) != 0; - jclass cls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$Profile"); - jmethodID constructor = GetEnvironment()->GetMethodID(env, cls, "", "()V"); - jobject obj = GetEnvironment()->NewObject(env, cls, constructor); - jfieldID keyField = GetEnvironment()->GetFieldID(env, cls, "name", "Ljava/lang/String;"); - jfieldID currentUidField = GetEnvironment()->GetFieldID(env, cls, "currentUid", "I"); - jfieldID allowSuField = GetEnvironment()->GetFieldID(env, cls, "allowSu", "Z"); + jclass cls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$Profile"); + jmethodID constructor = GetEnvironment()->GetMethodID(env, cls, "", "()V"); + jobject obj = GetEnvironment()->NewObject(env, cls, constructor); + jfieldID keyField = GetEnvironment()->GetFieldID(env, cls, "name", "Ljava/lang/String;"); + jfieldID currentUidField = GetEnvironment()->GetFieldID(env, cls, "currentUid", "I"); + jfieldID allowSuField = GetEnvironment()->GetFieldID(env, cls, "allowSu", "Z"); - jfieldID rootUseDefaultField = GetEnvironment()->GetFieldID(env, cls, "rootUseDefault", "Z"); - jfieldID rootTemplateField = GetEnvironment()->GetFieldID(env, cls, "rootTemplate", "Ljava/lang/String;"); + jfieldID rootUseDefaultField = GetEnvironment()->GetFieldID(env, cls, "rootUseDefault", "Z"); + jfieldID rootTemplateField = GetEnvironment()->GetFieldID(env, cls, "rootTemplate", "Ljava/lang/String;"); - jfieldID uidField = GetEnvironment()->GetFieldID(env, cls, "uid", "I"); - jfieldID gidField = GetEnvironment()->GetFieldID(env, cls, "gid", "I"); - jfieldID groupsField = GetEnvironment()->GetFieldID(env, cls, "groups", "Ljava/util/List;"); - jfieldID capabilitiesField = GetEnvironment()->GetFieldID(env, cls, "capabilities", "Ljava/util/List;"); - jfieldID domainField = GetEnvironment()->GetFieldID(env, cls, "context", "Ljava/lang/String;"); - jfieldID namespacesField = GetEnvironment()->GetFieldID(env, cls, "namespace", "I"); + jfieldID uidField = GetEnvironment()->GetFieldID(env, cls, "uid", "I"); + jfieldID gidField = GetEnvironment()->GetFieldID(env, cls, "gid", "I"); + jfieldID groupsField = GetEnvironment()->GetFieldID(env, cls, "groups", "Ljava/util/List;"); + jfieldID capabilitiesField = GetEnvironment()->GetFieldID(env, cls, "capabilities", "Ljava/util/List;"); + jfieldID domainField = GetEnvironment()->GetFieldID(env, cls, "context", "Ljava/lang/String;"); + jfieldID namespacesField = GetEnvironment()->GetFieldID(env, cls, "namespace", "I"); - jfieldID nonRootUseDefaultField = GetEnvironment()->GetFieldID(env, cls, "nonRootUseDefault", "Z"); - jfieldID umountModulesField = GetEnvironment()->GetFieldID(env, cls, "umountModules", "Z"); + jfieldID nonRootUseDefaultField = GetEnvironment()->GetFieldID(env, cls, "nonRootUseDefault", "Z"); + jfieldID umountModulesField = GetEnvironment()->GetFieldID(env, cls, "umountModules", "Z"); - GetEnvironment()->SetObjectField(env, obj, keyField, GetEnvironment()->NewStringUTF(env, profile.key)); - GetEnvironment()->SetIntField(env, obj, currentUidField, profile.current_uid); + GetEnvironment()->SetObjectField(env, obj, keyField, GetEnvironment()->NewStringUTF(env, profile.key)); + GetEnvironment()->SetIntField(env, obj, currentUidField, profile.current_uid); - if (useDefaultProfile) { - // no profile found, so just use default profile: - // don't allow root and use default profile! - LogDebug("use default profile for: %s, %d", key, uid); + if (useDefaultProfile) { + // no profile found, so just use default profile: + // don't allow root and use default profile! + LogDebug("use default profile for: %s, %d", key, uid); - // allow_su = false - // non root use default = true - GetEnvironment()->SetBooleanField(env, obj, allowSuField, false); - GetEnvironment()->SetBooleanField(env, obj, nonRootUseDefaultField, true); + // allow_su = false + // non root use default = true + GetEnvironment()->SetBooleanField(env, obj, allowSuField, false); + GetEnvironment()->SetBooleanField(env, obj, nonRootUseDefaultField, true); - return obj; - } + return obj; + } - bool allowSu = profile.allow_su; + bool allowSu = profile.allow_su; - if (allowSu) { - GetEnvironment()->SetBooleanField(env, obj, rootUseDefaultField, (jboolean) profile.rp_config.use_default); - if (strlen(profile.rp_config.template_name) > 0) { - GetEnvironment()->SetObjectField(env, obj, rootTemplateField, - GetEnvironment()->NewStringUTF(env, profile.rp_config.template_name)); - } + if (allowSu) { + GetEnvironment()->SetBooleanField(env, obj, rootUseDefaultField, (jboolean) profile.rp_config.use_default); + if (strlen(profile.rp_config.template_name) > 0) { + GetEnvironment()->SetObjectField(env, obj, rootTemplateField, + GetEnvironment()->NewStringUTF(env, profile.rp_config.template_name)); + } - GetEnvironment()->SetIntField(env, obj, uidField, profile.rp_config.profile.uid); - GetEnvironment()->SetIntField(env, obj, gidField, profile.rp_config.profile.gid); + GetEnvironment()->SetIntField(env, obj, uidField, profile.rp_config.profile.uid); + GetEnvironment()->SetIntField(env, obj, gidField, profile.rp_config.profile.gid); - jobject groupList = GetEnvironment()->GetObjectField(env, obj, groupsField); - int groupCount = profile.rp_config.profile.groups_count; - if (groupCount > KSU_MAX_GROUPS) { - LogDebug("kernel group count too large: %d???", groupCount); - groupCount = KSU_MAX_GROUPS; - } - fillIntArray(env, groupList, profile.rp_config.profile.groups, groupCount); + jobject groupList = GetEnvironment()->GetObjectField(env, obj, groupsField); + int groupCount = profile.rp_config.profile.groups_count; + if (groupCount > KSU_MAX_GROUPS) { + LogDebug("kernel group count too large: %d???", groupCount); + groupCount = KSU_MAX_GROUPS; + } + fillIntArray(env, groupList, profile.rp_config.profile.groups, groupCount); - jobject capList = GetEnvironment()->GetObjectField(env, obj, capabilitiesField); - for (int i = 0; i <= CAP_LAST_CAP; i++) { - if (profile.rp_config.profile.capabilities.effective & (1ULL << i)) { - addIntToList(env, capList, i); - } - } + jobject capList = GetEnvironment()->GetObjectField(env, obj, capabilitiesField); + for (int i = 0; i <= CAP_LAST_CAP; i++) { + if (profile.rp_config.profile.capabilities.effective & (1ULL << i)) { + addIntToList(env, capList, i); + } + } - GetEnvironment()->SetObjectField(env, obj, domainField, - GetEnvironment()->NewStringUTF(env, profile.rp_config.profile.selinux_domain)); - GetEnvironment()->SetIntField(env, obj, namespacesField, profile.rp_config.profile.namespaces); - GetEnvironment()->SetBooleanField(env, obj, allowSuField, profile.allow_su); - } else { - GetEnvironment()->SetBooleanField(env, obj, nonRootUseDefaultField, profile.nrp_config.use_default); - GetEnvironment()->SetBooleanField(env, obj, umountModulesField, profile.nrp_config.profile.umount_modules); - } + GetEnvironment()->SetObjectField(env, obj, domainField, + GetEnvironment()->NewStringUTF(env, profile.rp_config.profile.selinux_domain)); + GetEnvironment()->SetIntField(env, obj, namespacesField, profile.rp_config.profile.namespaces); + GetEnvironment()->SetBooleanField(env, obj, allowSuField, profile.allow_su); + } else { + GetEnvironment()->SetBooleanField(env, obj, nonRootUseDefaultField, profile.nrp_config.use_default); + GetEnvironment()->SetBooleanField(env, obj, umountModulesField, profile.nrp_config.profile.umount_modules); + } - return obj; + return obj; } NativeBridge(setAppProfile, jboolean, jobject profile) { - jclass cls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$Profile"); + jclass cls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$Profile"); - jfieldID keyField = GetEnvironment()->GetFieldID(env, cls, "name", "Ljava/lang/String;"); - jfieldID currentUidField = GetEnvironment()->GetFieldID(env, cls, "currentUid", "I"); - jfieldID allowSuField = GetEnvironment()->GetFieldID(env, cls, "allowSu", "Z"); + jfieldID keyField = GetEnvironment()->GetFieldID(env, cls, "name", "Ljava/lang/String;"); + jfieldID currentUidField = GetEnvironment()->GetFieldID(env, cls, "currentUid", "I"); + jfieldID allowSuField = GetEnvironment()->GetFieldID(env, cls, "allowSu", "Z"); - jfieldID rootUseDefaultField = GetEnvironment()->GetFieldID(env, cls, "rootUseDefault", "Z"); - jfieldID rootTemplateField = GetEnvironment()->GetFieldID(env, cls, "rootTemplate", "Ljava/lang/String;"); + jfieldID rootUseDefaultField = GetEnvironment()->GetFieldID(env, cls, "rootUseDefault", "Z"); + jfieldID rootTemplateField = GetEnvironment()->GetFieldID(env, cls, "rootTemplate", "Ljava/lang/String;"); - jfieldID uidField = GetEnvironment()->GetFieldID(env, cls, "uid", "I"); - jfieldID gidField = GetEnvironment()->GetFieldID(env, cls, "gid", "I"); - jfieldID groupsField = GetEnvironment()->GetFieldID(env, cls, "groups", "Ljava/util/List;"); - jfieldID capabilitiesField = GetEnvironment()->GetFieldID(env, cls, "capabilities", "Ljava/util/List;"); - jfieldID domainField = GetEnvironment()->GetFieldID(env, cls, "context", "Ljava/lang/String;"); - jfieldID namespacesField = GetEnvironment()->GetFieldID(env, cls, "namespace", "I"); + jfieldID uidField = GetEnvironment()->GetFieldID(env, cls, "uid", "I"); + jfieldID gidField = GetEnvironment()->GetFieldID(env, cls, "gid", "I"); + jfieldID groupsField = GetEnvironment()->GetFieldID(env, cls, "groups", "Ljava/util/List;"); + jfieldID capabilitiesField = GetEnvironment()->GetFieldID(env, cls, "capabilities", "Ljava/util/List;"); + jfieldID domainField = GetEnvironment()->GetFieldID(env, cls, "context", "Ljava/lang/String;"); + jfieldID namespacesField = GetEnvironment()->GetFieldID(env, cls, "namespace", "I"); - jfieldID nonRootUseDefaultField = GetEnvironment()->GetFieldID(env, cls, "nonRootUseDefault", "Z"); - jfieldID umountModulesField = GetEnvironment()->GetFieldID(env, cls, "umountModules", "Z"); + jfieldID nonRootUseDefaultField = GetEnvironment()->GetFieldID(env, cls, "nonRootUseDefault", "Z"); + jfieldID umountModulesField = GetEnvironment()->GetFieldID(env, cls, "umountModules", "Z"); - jobject key = GetEnvironment()->GetObjectField(env, profile, keyField); - if (!key) { - return false; - } - if (GetEnvironment()->GetStringLength(env, (jstring) key) > KSU_MAX_PACKAGE_NAME) { - return false; - } + jobject key = GetEnvironment()->GetObjectField(env, profile, keyField); + if (!key) { + return false; + } + if (GetEnvironment()->GetStringLength(env, (jstring) key) > KSU_MAX_PACKAGE_NAME) { + return false; + } - const char* cpkg = GetEnvironment()->GetStringUTFChars(env, (jstring) key, nullptr); - char p_key[KSU_MAX_PACKAGE_NAME] = { 0 }; - strcpy(p_key, cpkg); - GetEnvironment()->ReleaseStringUTFChars(env, (jstring) key, cpkg); + const char* cpkg = GetEnvironment()->GetStringUTFChars(env, (jstring) key, nullptr); + char p_key[KSU_MAX_PACKAGE_NAME] = { 0 }; + strcpy(p_key, cpkg); + GetEnvironment()->ReleaseStringUTFChars(env, (jstring) key, cpkg); - jint currentUid = GetEnvironment()->GetIntField(env, profile, currentUidField); + jint currentUid = GetEnvironment()->GetIntField(env, profile, currentUidField); - jint uid = GetEnvironment()->GetIntField(env, profile, uidField); - jint gid = GetEnvironment()->GetIntField(env, profile, gidField); - jobject groups = GetEnvironment()->GetObjectField(env, profile, groupsField); - jobject capabilities = GetEnvironment()->GetObjectField(env, profile, capabilitiesField); - jobject domain = GetEnvironment()->GetObjectField(env, profile, domainField); - jboolean allowSu = GetEnvironment()->GetBooleanField(env, profile, allowSuField); - jboolean umountModules = GetEnvironment()->GetBooleanField(env, profile, umountModulesField); + jint uid = GetEnvironment()->GetIntField(env, profile, uidField); + jint gid = GetEnvironment()->GetIntField(env, profile, gidField); + jobject groups = GetEnvironment()->GetObjectField(env, profile, groupsField); + jobject capabilities = GetEnvironment()->GetObjectField(env, profile, capabilitiesField); + jobject domain = GetEnvironment()->GetObjectField(env, profile, domainField); + jboolean allowSu = GetEnvironment()->GetBooleanField(env, profile, allowSuField); + jboolean umountModules = GetEnvironment()->GetBooleanField(env, profile, umountModulesField); - struct app_profile p = { 0 }; - p.version = KSU_APP_PROFILE_VER; + struct app_profile p = { 0 }; + p.version = KSU_APP_PROFILE_VER; - strcpy(p.key, p_key); - p.allow_su = allowSu; - p.current_uid = currentUid; + strcpy(p.key, p_key); + p.allow_su = allowSu; + p.current_uid = currentUid; - if (allowSu) { - p.rp_config.use_default = GetEnvironment()->GetBooleanField(env, profile, rootUseDefaultField); - jobject templateName = GetEnvironment()->GetObjectField(env, profile, rootTemplateField); - if (templateName) { - const char* ctemplateName = GetEnvironment()->GetStringUTFChars(env, (jstring) templateName, nullptr); - strcpy(p.rp_config.template_name, ctemplateName); - GetEnvironment()->ReleaseStringUTFChars(env, (jstring) templateName, ctemplateName); - } + if (allowSu) { + p.rp_config.use_default = GetEnvironment()->GetBooleanField(env, profile, rootUseDefaultField); + jobject templateName = GetEnvironment()->GetObjectField(env, profile, rootTemplateField); + if (templateName) { + const char* ctemplateName = GetEnvironment()->GetStringUTFChars(env, (jstring) templateName, nullptr); + strcpy(p.rp_config.template_name, ctemplateName); + GetEnvironment()->ReleaseStringUTFChars(env, (jstring) templateName, ctemplateName); + } - p.rp_config.profile.uid = uid; - p.rp_config.profile.gid = gid; + p.rp_config.profile.uid = uid; + p.rp_config.profile.gid = gid; - int groups_count = getListSize(env, groups); - if (groups_count > KSU_MAX_GROUPS) { - LogDebug("groups count too large: %d", groups_count); - return false; - } - p.rp_config.profile.groups_count = groups_count; - fillArrayWithList(env, groups, p.rp_config.profile.groups, groups_count); + int groups_count = getListSize(env, groups); + if (groups_count > KSU_MAX_GROUPS) { + LogDebug("groups count too large: %d", groups_count); + return false; + } + p.rp_config.profile.groups_count = groups_count; + fillArrayWithList(env, groups, p.rp_config.profile.groups, groups_count); - p.rp_config.profile.capabilities.effective = capListToBits(env, capabilities); + p.rp_config.profile.capabilities.effective = capListToBits(env, capabilities); - const char* cdomain = GetEnvironment()->GetStringUTFChars(env, (jstring) domain, nullptr); - strcpy(p.rp_config.profile.selinux_domain, cdomain); - GetEnvironment()->ReleaseStringUTFChars(env, (jstring) domain, cdomain); + const char* cdomain = GetEnvironment()->GetStringUTFChars(env, (jstring) domain, nullptr); + strcpy(p.rp_config.profile.selinux_domain, cdomain); + GetEnvironment()->ReleaseStringUTFChars(env, (jstring) domain, cdomain); - p.rp_config.profile.namespaces = GetEnvironment()->GetIntField(env, profile, namespacesField); - } else { - p.nrp_config.use_default = GetEnvironment()->GetBooleanField(env, profile, nonRootUseDefaultField); - p.nrp_config.profile.umount_modules = umountModules; - } + p.rp_config.profile.namespaces = GetEnvironment()->GetIntField(env, profile, namespacesField); + } else { + p.nrp_config.use_default = GetEnvironment()->GetBooleanField(env, profile, nonRootUseDefaultField); + p.nrp_config.profile.umount_modules = umountModules; + } - return set_app_profile(&p); + return set_app_profile(&p); } NativeBridge(uidShouldUmount, jboolean, jint uid) { - return uid_should_umount(uid); + return uid_should_umount(uid); } NativeBridgeNP(isSuEnabled, jboolean) { - return is_su_enabled(); + return is_su_enabled(); } NativeBridge(setSuEnabled, jboolean, jboolean enabled) { - return set_su_enabled(enabled); + return set_su_enabled(enabled); } // Check if KPM is enabled NativeBridgeNP(isKPMEnabled, jboolean) { - return is_KPM_enable(); + return is_KPM_enable(); } // Get HOOK type NativeBridgeNP(getHookType, jstring) { - char hook_type[16]; - get_hook_type(hook_type, sizeof(hook_type)); - return GetEnvironment()->NewStringUTF(env, hook_type); + char hook_type[32] = { 0 }; + get_hook_type((char *) &hook_type); + return GetEnvironment()->NewStringUTF(env, hook_type); } // dynamic manager NativeBridge(setDynamicManager, jboolean, jint size, jstring hash) { - if (!hash) { - LogDebug("setDynamicManager: hash is null"); - return false; - } + if (!hash) { + LogDebug("setDynamicManager: hash is null"); + return false; + } - const char* chash = GetEnvironment()->GetStringUTFChars(env, hash, nullptr); - bool result = set_dynamic_manager((unsigned int)size, chash); - GetEnvironment()->ReleaseStringUTFChars(env, hash, chash); + const char* chash = GetEnvironment()->GetStringUTFChars(env, hash, nullptr); + bool result = set_dynamic_manager((unsigned int)size, chash); + GetEnvironment()->ReleaseStringUTFChars(env, hash, chash); - LogDebug("setDynamicManager: size=0x%x, result=%d", size, result); - return result; + LogDebug("setDynamicManager: size=0x%x, result=%d", size, result); + return result; } NativeBridgeNP(getDynamicManager, jobject) { - struct dynamic_manager_user_config config; - bool result = get_dynamic_manager(&config); + struct dynamic_manager_user_config config; + bool result = get_dynamic_manager(&config); - if (!result) { - LogDebug("getDynamicManager: failed to get dynamic manager config"); - return NULL; - } + if (!result) { + LogDebug("getDynamicManager: failed to get dynamic manager config"); + return NULL; + } - jobject obj = CREATE_JAVA_OBJECT("com/sukisu/ultra/Natives$DynamicManagerConfig"); - jclass cls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$DynamicManagerConfig"); + jobject obj = CREATE_JAVA_OBJECT("com/sukisu/ultra/Natives$DynamicManagerConfig"); + jclass cls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$DynamicManagerConfig"); - SET_INT_FIELD(obj, cls, size, (jint)config.size); - SET_STRING_FIELD(obj, cls, hash, config.hash); + SET_INT_FIELD(obj, cls, size, (jint)config.size); + SET_STRING_FIELD(obj, cls, hash, config.hash); - LogDebug("getDynamicManager: size=0x%x, hash=%.16s...", config.size, config.hash); - return obj; + LogDebug("getDynamicManager: size=0x%x, hash=%.16s...", config.size, config.hash); + return obj; } NativeBridgeNP(clearDynamicManager, jboolean) { - bool result = clear_dynamic_manager(); - LogDebug("clearDynamicManager: result=%d", result); - return result; + bool result = clear_dynamic_manager(); + LogDebug("clearDynamicManager: result=%d", result); + return result; } // Get a list of active managers NativeBridgeNP(getManagersList, jobject) { - struct manager_list_info managerListInfo; - bool result = get_managers_list(&managerListInfo); + struct manager_list_info managerListInfo; + bool result = get_managers_list(&managerListInfo); - if (!result) { - LogDebug("getManagersList: failed to get active managers list"); - return NULL; - } + if (!result) { + LogDebug("getManagersList: failed to get active managers list"); + return NULL; + } - jobject obj = CREATE_JAVA_OBJECT("com/sukisu/ultra/Natives$ManagersList"); - jclass managerListCls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$ManagersList"); + jobject obj = CREATE_JAVA_OBJECT("com/sukisu/ultra/Natives$ManagersList"); + jclass managerListCls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$ManagersList"); - SET_INT_FIELD(obj, managerListCls, count, (jint)managerListInfo.count); + SET_INT_FIELD(obj, managerListCls, count, (jint)managerListInfo.count); - jobject managersList = CREATE_ARRAYLIST(); + jobject managersList = CREATE_ARRAYLIST(); - for (int i = 0; i < managerListInfo.count; i++) { - jobject managerInfo = CREATE_JAVA_OBJECT_WITH_PARAMS( - "com/sukisu/ultra/Natives$ManagerInfo", - "(II)V", - (jint)managerListInfo.managers[i].uid, - (jint)managerListInfo.managers[i].signature_index - ); - ADD_TO_LIST(managersList, managerInfo); - } + for (int i = 0; i < managerListInfo.count; i++) { + jobject managerInfo = CREATE_JAVA_OBJECT_WITH_PARAMS( + "com/sukisu/ultra/Natives$ManagerInfo", + "(II)V", + (jint)managerListInfo.managers[i].uid, + (jint)managerListInfo.managers[i].signature_index + ); + ADD_TO_LIST(managersList, managerInfo); + } - SET_OBJECT_FIELD(obj, managerListCls, managers, managersList); + SET_OBJECT_FIELD(obj, managerListCls, managers, managersList); - LogDebug("getManagersList: count=%d", managerListInfo.count); - return obj; + LogDebug("getManagersList: count=%d", managerListInfo.count); + return obj; } NativeBridge(verifyModuleSignature, jboolean, jstring modulePath) { #if defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM) - if (!modulePath) { - LogDebug("verifyModuleSignature: modulePath is null"); - return false; - } + if (!modulePath) { + LogDebug("verifyModuleSignature: modulePath is null"); + return false; + } - const char* cModulePath = GetEnvironment()->GetStringUTFChars(env, modulePath, nullptr); - bool result = verify_module_signature(cModulePath); - GetEnvironment()->ReleaseStringUTFChars(env, modulePath, cModulePath); + const char* cModulePath = GetEnvironment()->GetStringUTFChars(env, modulePath, nullptr); + bool result = verify_module_signature(cModulePath); + GetEnvironment()->ReleaseStringUTFChars(env, modulePath, cModulePath); - LogDebug("verifyModuleSignature: path=%s, result=%d", cModulePath, result); - return result; + LogDebug("verifyModuleSignature: path=%s, result=%d", cModulePath, result); + return result; #else - LogDebug("verifyModuleSignature: not supported on non-ARM architecture"); - return false; + LogDebug("verifyModuleSignature: not supported on non-ARM architecture"); + return false; #endif } NativeBridgeNP(isUidScannerEnabled, jboolean) { - return is_uid_scanner_enabled(); + return is_uid_scanner_enabled(); } NativeBridge(setUidScannerEnabled, jboolean, jboolean enabled) { - return set_uid_scanner_enabled(enabled); + return set_uid_scanner_enabled(enabled); } NativeBridgeNP(clearUidScannerEnvironment, jboolean) { - return clear_uid_scanner_environment(); + return clear_uid_scanner_environment(); } \ No newline at end of file diff --git a/manager/app/src/main/cpp/ksu.c b/manager/app/src/main/cpp/ksu.c index 245ae290..27c1fe22 100644 --- a/manager/app/src/main/cpp/ksu.c +++ b/manager/app/src/main/cpp/ksu.c @@ -7,6 +7,11 @@ #include #include #include +#include +#include +#include + +#include #include "prelude.h" #include "ksu.h" @@ -21,238 +26,276 @@ extern const char* zako_file_verrcidx2str(uint8_t index); #endif // __aarch64__ || _M_ARM64 || __arm__ || _M_ARM -#define KERNEL_SU_OPTION 0xDEADBEEF +static int fd = -1; -#define CMD_GRANT_ROOT 0 +static inline int scan_driver_fd() { + const char *kName = "[ksu_driver]"; + DIR *fd_dir = opendir("/proc/self/fd"); + if (!fd_dir) { + return -1; + } -#define CMD_BECOME_MANAGER 1 -#define CMD_GET_VERSION 2 -#define CMD_ALLOW_SU 3 -#define CMD_DENY_SU 4 -#define CMD_GET_SU_LIST 5 -#define CMD_GET_DENY_LIST 6 -#define CMD_CHECK_SAFEMODE 9 + int found = -1; + struct dirent *de; + char path[64]; + char target[PATH_MAX]; -#define CMD_GET_APP_PROFILE 10 -#define CMD_SET_APP_PROFILE 11 + while ((de = readdir(fd_dir)) != NULL) { + if (de->d_name[0] == '.') { + continue; + } -#define CMD_IS_UID_GRANTED_ROOT 12 -#define CMD_IS_UID_SHOULD_UMOUNT 13 -#define CMD_IS_SU_ENABLED 14 -#define CMD_ENABLE_SU 15 + char *endptr = nullptr; + long fd_long = strtol(de->d_name, &endptr, 10); + if (!de->d_name[0] || *endptr != '\0' || fd_long < 0 || fd_long > INT_MAX) { + continue; + } -#define CMD_GET_VERSION_FULL 0xC0FFEE1A + snprintf(path, sizeof(path), "/proc/self/fd/%s", de->d_name); + ssize_t n = readlink(path, target, sizeof(target) - 1); + if (n < 0) { + continue; + } + target[n] = '\0'; -#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 + const char *base = strrchr(target, '/'); + base = base ? base + 1 : target; -#define DYNAMIC_MANAGER_OP_SET 0 -#define DYNAMIC_MANAGER_OP_GET 1 -#define DYNAMIC_MANAGER_OP_CLEAR 2 + if (strstr(base, kName)) { + found = (int)fd_long; + break; + } + } -static bool ksuctl(int cmd, void* arg1, void* arg2) { - int32_t result = 0; - int32_t rtn = prctl(KERNEL_SU_OPTION, cmd, arg1, arg2, &result); - - return result == KERNEL_SU_OPTION && rtn == -1; + closedir(fd_dir); + return found; } -bool become_manager(const char* pkg) { - char param[128]; - uid_t uid = getuid(); - uint32_t userId = uid / 100000; - if (userId == 0) { - sprintf(param, "/data/data/%s", pkg); - } else { - snprintf(param, sizeof(param), "/data/user/%d/%s", userId, pkg); - } - - return ksuctl(CMD_BECOME_MANAGER, param, NULL); +static int ksuctl(unsigned long op, void* arg) { + if (fd < 0) { + fd = scan_driver_fd(); + } + return ioctl(fd, op, arg); } -// cache the result to avoid unnecessary syscall -static bool is_lkm; -int get_version() { - int32_t version = -1; - int32_t flags = 0; - ksuctl(CMD_GET_VERSION, &version, &flags); - if (!is_lkm && (flags & 0x1)) { - is_lkm = true; - } - return version; +static struct ksu_get_info_cmd g_version = {0}; + +struct ksu_get_info_cmd get_info() { + if (!g_version.version) { + ksuctl(KSU_IOCTL_GET_INFO, &g_version); + } + return g_version; } -void get_full_version(char* buff) { - ksuctl(CMD_GET_VERSION_FULL, buff, NULL); +uint32_t get_version() { + auto info = get_info(); + return info.version; } -bool get_allow_list(int *uids, int *size) { - return ksuctl(CMD_GET_SU_LIST, uids, size); +bool get_allow_list(struct ksu_get_allow_list_cmd *cmd) { + return ksuctl(KSU_IOCTL_GET_ALLOW_LIST, cmd) == 0; } bool is_safe_mode() { - return ksuctl(CMD_CHECK_SAFEMODE, NULL, NULL); + struct ksu_check_safemode_cmd cmd = {}; + ksuctl(KSU_IOCTL_CHECK_SAFEMODE, &cmd); + return cmd.in_safe_mode; } bool is_lkm_mode() { - // you should call get_version first! - return is_lkm; + auto info = get_info(); + return (info.flags & 0x1) != 0; +} + +bool is_manager() { + auto info = get_info(); + return (info.flags & 0x2) != 0; } bool uid_should_umount(int uid) { - int should; - return ksuctl(CMD_IS_UID_SHOULD_UMOUNT, (void*) ((size_t) uid), &should) && should; + struct ksu_uid_should_umount_cmd cmd = {}; + cmd.uid = uid; + ksuctl(KSU_IOCTL_UID_SHOULD_UMOUNT, &cmd); + return cmd.should_umount; } -bool set_app_profile(const struct app_profile* profile) { - return ksuctl(CMD_SET_APP_PROFILE, (void*) profile, NULL); +bool set_app_profile(const struct app_profile *profile) { + struct ksu_set_app_profile_cmd cmd = {}; + cmd.profile = *profile; + return ksuctl(KSU_IOCTL_SET_APP_PROFILE, &cmd) == 0; } -bool get_app_profile(char* key, struct app_profile* profile) { - return ksuctl(CMD_GET_APP_PROFILE, profile, NULL); +int get_app_profile(struct app_profile *profile) { + struct ksu_get_app_profile_cmd cmd = {.profile = *profile}; + int ret = ksuctl(KSU_IOCTL_GET_APP_PROFILE, &cmd); + *profile = cmd.profile; + return ret; } bool set_su_enabled(bool enabled) { - return ksuctl(CMD_ENABLE_SU, (void*) enabled, NULL); + struct ksu_enable_su_cmd cmd = {.enable = enabled}; + return ksuctl(KSU_IOCTL_ENABLE_SU, &cmd) == 0; } bool is_su_enabled() { - int enabled = true; - // if ksuctl failed, we assume su is enabled, and it cannot be disabled. - ksuctl(CMD_IS_SU_ENABLED, &enabled, NULL); - return enabled; + struct ksu_is_su_enabled_cmd cmd = {}; + return ksuctl(KSU_IOCTL_IS_SU_ENABLED, &cmd) == 0 && cmd.enabled; } -bool is_KPM_enable() { - int enabled = false; - ksuctl(CMD_ENABLE_KPM, &enabled, NULL); - return enabled; +void get_full_version(char* buff) { + struct ksu_get_full_version_cmd cmd = {0}; + if (ksuctl(KSU_IOCTL_GET_FULL_VERSION, &cmd) == 0) { + strncpy(buff, cmd.version_full, KSU_FULL_VERSION_STRING - 1); + buff[KSU_FULL_VERSION_STRING - 1] = '\0'; + } else { + buff[0] = '\0'; + } } -bool get_hook_type(char* hook_type, size_t size) { - if (hook_type == NULL || size == 0) { - return false; - } - - static char cached_hook_type[16] = {0}; - if (cached_hook_type[0] == '\0') { - if (!ksuctl(CMD_HOOK_TYPE, cached_hook_type, NULL)) { - strcpy(cached_hook_type, "Unknown"); - } - } - - strncpy(hook_type, cached_hook_type, size); - hook_type[size - 1] = '\0'; - return true; +bool is_KPM_enable(void) +{ + struct ksu_enable_kpm_cmd cmd = {}; + return ksuctl(KSU_IOCTL_ENABLE_KPM, &cmd) == 0 && cmd.enabled; } -bool set_dynamic_manager(unsigned int size, const char* hash) { - if (hash == NULL) { - return false; - } - - struct dynamic_manager_user_config config; - config.operation = DYNAMIC_MANAGER_OP_SET; - config.size = size; - strncpy(config.hash, hash, sizeof(config.hash) - 1); - config.hash[sizeof(config.hash) - 1] = '\0'; - - return ksuctl(CMD_DYNAMIC_MANAGER, &config, NULL); +void get_hook_type(char *buff) +{ + struct ksu_hook_type_cmd cmd = {0}; + if (ksuctl(KSU_IOCTL_HOOK_TYPE, &cmd) == 0) { + strncpy(buff, cmd.hook_type, 32 - 1); + buff[32 - 1] = '\0'; + } else { + strcpy(buff, "Unknown"); + } } -bool get_dynamic_manager(struct dynamic_manager_user_config* config) { - if (config == NULL) { - return false; - } +bool set_dynamic_manager(unsigned int size, const char *hash) +{ + struct ksu_dynamic_manager_cmd cmd = {0}; + cmd.config.operation = DYNAMIC_MANAGER_OP_SET; + cmd.config.size = size; + strlcpy(cmd.config.hash, hash, sizeof(cmd.config.hash)); - config->operation = DYNAMIC_MANAGER_OP_GET; - return ksuctl(CMD_DYNAMIC_MANAGER, config, NULL); + return ksuctl(KSU_IOCTL_DYNAMIC_MANAGER, &cmd) == 0; } -bool clear_dynamic_manager() { - struct dynamic_manager_user_config config; - config.operation = DYNAMIC_MANAGER_OP_CLEAR; - return ksuctl(CMD_DYNAMIC_MANAGER, &config, NULL); +bool get_dynamic_manager(struct dynamic_manager_user_config *cfg) +{ + if (!cfg) + return false; + + struct ksu_dynamic_manager_cmd cmd = {0}; + cmd.config.operation = DYNAMIC_MANAGER_OP_GET; + + if (ksuctl(KSU_IOCTL_DYNAMIC_MANAGER, &cmd) != 0) + return false; + + *cfg = cmd.config; + return true; } -bool get_managers_list(struct manager_list_info* info) { - if (info == NULL) { - return false; - } - - return ksuctl(CMD_GET_MANAGERS, info, NULL); +bool clear_dynamic_manager(void) +{ + struct ksu_dynamic_manager_cmd cmd = {0}; + cmd.config.operation = DYNAMIC_MANAGER_OP_CLEAR; + return ksuctl(KSU_IOCTL_DYNAMIC_MANAGER, &cmd) == 0; } +bool get_managers_list(struct manager_list_info *info) +{ + if (!info) + return false; + struct ksu_get_managers_cmd cmd = {0}; + if (ksuctl(KSU_IOCTL_GET_MANAGERS, &cmd) != 0) + return false; + + *info = cmd.manager_info; + return true; +} + + bool verify_module_signature(const char* input) { #if defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM) - if (input == NULL) { - LogDebug("verify_module_signature: input path is null"); - return false; - } + if (input == NULL) { + LogDebug("verify_module_signature: input path is null"); + return false; + } - int fd = zako_sys_file_open(input); - if (fd < 0) { - LogDebug("verify_module_signature: failed to open file: %s", input); - return false; - } + int file_fd = zako_sys_file_open(input); + if (file_fd < 0) { + LogDebug("verify_module_signature: failed to open file: %s", input); + return false; + } - uint32_t results = zako_file_verify_esig(fd, 0); + uint32_t results = zako_file_verify_esig(file_fd, 0); - if (results != 0) { - /* If important error occured, verification process should - be considered as failed due to unexpected modification - potentially happened. */ - if ((results & ZAKO_ESV_IMPORTANT_ERROR) != 0) { - LogDebug("verify_module_signature: Verification failed! (important error)"); - } else { - /* This is for manager that doesn't want to do certificate checks */ - LogDebug("verify_module_signature: Verification partially passed"); - } - } else { - LogDebug("verify_module_signature: Verification passed!"); - goto exit; - } + if (results != 0) { + /* If important error occured, verification process should + be considered as failed due to unexpected modification + potentially happened. */ + if ((results & ZAKO_ESV_IMPORTANT_ERROR) != 0) { + LogDebug("verify_module_signature: Verification failed! (important error)"); + } else { + /* This is for manager that doesn't want to do certificate checks */ + LogDebug("verify_module_signature: Verification partially passed"); + } + } else { + LogDebug("verify_module_signature: Verification passed!"); + goto exit; + } - /* Go through all bit fields */ - for (size_t i = 0; i < sizeof(uint32_t) * 8; i++) { - if ((results & (1 << i)) == 0) { - continue; - } + /* Go through all bit fields */ + for (size_t i = 0; i < sizeof(uint32_t) * 8; i++) { + if ((results & (1 << i)) == 0) { + continue; + } - /* Convert error bit field index into human readable string */ - const char* message = zako_file_verrcidx2str((uint8_t)i); - // Error message: message - if (message != NULL) { - LogDebug("verify_module_signature: Error bit %zu: %s", i, message); - } else { - LogDebug("verify_module_signature: Error bit %zu: Unknown error", i); - } - } + /* Convert error bit field index into human readable string */ + const char* message = zako_file_verrcidx2str((uint8_t)i); + // Error message: message + if (message != NULL) { + LogDebug("verify_module_signature: Error bit %zu: %s", i, message); + } else { + LogDebug("verify_module_signature: Error bit %zu: Unknown error", i); + } + } - exit: - close(fd); - LogDebug("verify_module_signature: path=%s, results=0x%x, success=%s", - input, results, (results == 0) ? "true" : "false"); - return results == 0; + exit: + close(file_fd); + LogDebug("verify_module_signature: path=%s, results=0x%x, success=%s", + input, results, (results == 0) ? "true" : "false"); + return results == 0; #else - LogDebug("verify_module_signature: not supported on non-ARM architecture, path=%s", input ? input : "null"); - return false; + LogDebug("verify_module_signature: not supported on non-ARM architecture, path=%s", input ? input : "null"); + return false; #endif } -bool is_uid_scanner_enabled() { - bool status = false; - ksuctl(CMD_ENABLE_UID_SCANNER, (void*)0, &status); - return status; +bool is_uid_scanner_enabled(void) +{ + bool status = false; + + struct ksu_enable_uid_scanner_cmd cmd = { + .operation = UID_SCANNER_OP_GET_STATUS, + .status_ptr = (__u64)(uintptr_t)&status + }; + + return ksuctl(KSU_IOCTL_ENABLE_UID_SCANNER, &cmd) == 0 != 0 && status; } -bool set_uid_scanner_enabled(bool enabled) { - return ksuctl(CMD_ENABLE_UID_SCANNER, (void*)1, (void*)enabled); +bool set_uid_scanner_enabled(bool enabled) +{ + struct ksu_enable_uid_scanner_cmd cmd = { + .operation = UID_SCANNER_OP_TOGGLE, + .enabled = enabled + }; + return ksuctl(KSU_IOCTL_ENABLE_UID_SCANNER, &cmd); } -bool clear_uid_scanner_environment() { - return ksuctl(CMD_ENABLE_UID_SCANNER, (void*)2, NULL); +bool clear_uid_scanner_environment(void) +{ + struct ksu_enable_uid_scanner_cmd cmd = { + .operation = UID_SCANNER_OP_CLEAR_ENV + }; + return ksuctl(KSU_IOCTL_ENABLE_UID_SCANNER, &cmd); } \ No newline at end of file diff --git a/manager/app/src/main/cpp/ksu.h b/manager/app/src/main/cpp/ksu.h index 2ea7fe29..653dae00 100644 --- a/manager/app/src/main/cpp/ksu.h +++ b/manager/app/src/main/cpp/ksu.h @@ -8,14 +8,11 @@ #include "prelude.h" #include #include +#include -bool become_manager(const char *); +#define KSU_FULL_VERSION_STRING 255 -void get_full_version(char* buff); - -int get_version(); - -bool get_allow_list(int *uids, int *size); +uint32_t get_version(); bool uid_should_umount(int uid); @@ -23,6 +20,10 @@ bool is_safe_mode(); bool is_lkm_mode(); +bool is_manager(); + +void get_full_version(char* buff); + #define KSU_APP_PROFILE_VER 2 #define KSU_MAX_PACKAGE_NAME 256 // NGROUPS_MAX for Linux is 65535 generally, but we only supports 32 groups. @@ -33,72 +34,76 @@ bool is_lkm_mode(); #define DYNAMIC_MANAGER_OP_GET 1 #define DYNAMIC_MANAGER_OP_CLEAR 2 +#define UID_SCANNER_OP_GET_STATUS 0 +#define UID_SCANNER_OP_TOGGLE 1 +#define UID_SCANNER_OP_CLEAR_ENV 2 + struct dynamic_manager_user_config { - unsigned int operation; - unsigned int size; - char hash[65]; + unsigned int operation; + unsigned int size; + char hash[65]; }; struct root_profile { - int32_t uid; - int32_t gid; + int32_t uid; + int32_t gid; - int32_t groups_count; - int32_t groups[KSU_MAX_GROUPS]; + int32_t groups_count; + int32_t groups[KSU_MAX_GROUPS]; - // kernel_cap_t is u32[2] for capabilities v3 - struct { - uint64_t effective; - uint64_t permitted; - uint64_t inheritable; - } capabilities; + // kernel_cap_t is u32[2] for capabilities v3 + struct { + uint64_t effective; + uint64_t permitted; + uint64_t inheritable; + } capabilities; - char selinux_domain[KSU_SELINUX_DOMAIN]; + char selinux_domain[KSU_SELINUX_DOMAIN]; - int32_t namespaces; + int32_t namespaces; }; struct non_root_profile { - bool umount_modules; + bool umount_modules; }; struct app_profile { - // It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this. - uint32_t version; + // It may be utilized for backward compatibility, although we have never explicitly made any promises regarding this. + uint32_t version; - // this is usually the package of the app, but can be other value for special apps - char key[KSU_MAX_PACKAGE_NAME]; - int32_t current_uid; - bool allow_su; + // this is usually the package of the app, but can be other value for special apps + char key[KSU_MAX_PACKAGE_NAME]; + int32_t current_uid; + bool allow_su; - union { - struct { - bool use_default; - char template_name[KSU_MAX_PACKAGE_NAME]; + union { + struct { + bool use_default; + char template_name[KSU_MAX_PACKAGE_NAME]; - struct root_profile profile; - } rp_config; + struct root_profile profile; + } rp_config; - struct { - bool use_default; + struct { + bool use_default; - struct non_root_profile profile; - } nrp_config; - }; + struct non_root_profile profile; + } nrp_config; + }; }; struct manager_list_info { - int count; - struct { - uid_t uid; - int signature_index; - } managers[2]; + int count; + struct { + uid_t uid; + int signature_index; + } managers[2]; }; bool set_app_profile(const struct app_profile* profile); -bool get_app_profile(char* key, struct app_profile* profile); +int get_app_profile(struct app_profile* profile); bool set_su_enabled(bool enabled); @@ -106,8 +111,7 @@ bool is_su_enabled(); bool is_KPM_enable(); -bool get_hook_type(char* hook_type, size_t size); - +void get_hook_type(char* hook_type); bool set_dynamic_manager(unsigned int size, const char* hash); @@ -125,4 +129,119 @@ bool set_uid_scanner_enabled(bool enabled); bool clear_uid_scanner_environment(); +struct ksu_become_daemon_cmd { + __u8 token[65]; // Input: daemon token (null-terminated) +}; + +struct ksu_get_info_cmd { + __u32 version; // Output: KERNEL_SU_VERSION + __u32 flags; // Output: flags (bit 0: MODULE mode) +}; + +struct ksu_report_event_cmd { + __u32 event; // Input: EVENT_POST_FS_DATA, EVENT_BOOT_COMPLETED, etc. +}; + +struct ksu_set_sepolicy_cmd { + __u64 cmd; // Input: sepolicy command + __aligned_u64 arg; // Input: sepolicy argument pointer +}; + +struct ksu_check_safemode_cmd { + __u8 in_safe_mode; // Output: true if in safe mode, false otherwise +}; + +struct ksu_get_allow_list_cmd { + __u32 uids[128]; // Output: array of allowed/denied UIDs + __u32 count; // Output: number of UIDs in array + __u8 allow; // Input: true for allow list, false for deny list +}; + +struct ksu_uid_granted_root_cmd { + __u32 uid; // Input: target UID to check + __u8 granted; // Output: true if granted, false otherwise +}; + +struct ksu_uid_should_umount_cmd { + __u32 uid; // Input: target UID to check + __u8 should_umount; // Output: true if should umount, false otherwise +}; + +struct ksu_get_manager_uid_cmd { + __u32 uid; // Output: manager UID +}; + +struct ksu_set_manager_uid_cmd { + __u32 uid; // Input: new manager UID +}; + +struct ksu_get_app_profile_cmd { + struct app_profile profile; // Input/Output: app profile structure +}; + +struct ksu_set_app_profile_cmd { + struct app_profile profile; // Input: app profile structure +}; + +struct ksu_is_su_enabled_cmd { + __u8 enabled; // Output: true if su compat enabled +}; + +struct ksu_enable_su_cmd { + __u8 enable; // Input: true to enable, false to disable +}; + +// Other command structures +struct ksu_get_full_version_cmd { + char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string +}; + +struct ksu_hook_type_cmd { + char hook_type[32]; // Output: hook type string +}; + +struct ksu_enable_kpm_cmd { + __u8 enabled; // Output: true if KPM is enabled +}; + +struct ksu_dynamic_manager_cmd { + struct dynamic_manager_user_config config; // Input/Output: dynamic manager config +}; + +struct ksu_get_managers_cmd { + struct manager_list_info manager_info; // Output: manager list information +}; + +struct ksu_enable_uid_scanner_cmd { + __u32 operation; // Input: operation type (UID_SCANNER_OP_GET_STATUS, UID_SCANNER_OP_TOGGLE, UID_SCANNER_OP_CLEAR_ENV) + __u32 enabled; // Input: enable or disable (for UID_SCANNER_OP_TOGGLE) + __u64 status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS) +}; + +// IOCTL command definitions +#define KSU_IOCTL_GRANT_ROOT _IO('K', 1) +#define KSU_IOCTL_GET_INFO _IOR('K', 2, struct ksu_get_info_cmd) +#define KSU_IOCTL_REPORT_EVENT _IOW('K', 3, struct ksu_report_event_cmd) +#define KSU_IOCTL_SET_SEPOLICY _IOWR('K', 4, struct ksu_set_sepolicy_cmd) +#define KSU_IOCTL_CHECK_SAFEMODE _IOR('K', 5, struct ksu_check_safemode_cmd) +#define KSU_IOCTL_GET_ALLOW_LIST _IOWR('K', 6, struct ksu_get_allow_list_cmd) +#define KSU_IOCTL_GET_DENY_LIST _IOWR('K', 7, struct ksu_get_allow_list_cmd) +#define KSU_IOCTL_UID_GRANTED_ROOT _IOWR('K', 8, struct ksu_uid_granted_root_cmd) +#define KSU_IOCTL_UID_SHOULD_UMOUNT _IOWR('K', 9, struct ksu_uid_should_umount_cmd) +#define KSU_IOCTL_GET_MANAGER_UID _IOR('K', 10, struct ksu_get_manager_uid_cmd) +#define KSU_IOCTL_GET_APP_PROFILE _IOWR('K', 11, struct ksu_get_app_profile_cmd) +#define KSU_IOCTL_SET_APP_PROFILE _IOW('K', 12, struct ksu_set_app_profile_cmd) +#define KSU_IOCTL_IS_SU_ENABLED _IOR('K', 13, struct ksu_is_su_enabled_cmd) +#define KSU_IOCTL_ENABLE_SU _IOW('K', 14, struct ksu_enable_su_cmd) + +// Other IOCTL command definitions +#define KSU_IOCTL_GET_FULL_VERSION _IOR('K', 100, struct ksu_get_full_version_cmd) +#define KSU_IOCTL_HOOK_TYPE _IOR('K', 101, struct ksu_hook_type_cmd) +#define KSU_IOCTL_ENABLE_KPM _IOR('K', 102, struct ksu_enable_kpm_cmd) +#define KSU_IOCTL_DYNAMIC_MANAGER _IOWR('K', 103, struct ksu_dynamic_manager_cmd) +#define KSU_IOCTL_GET_MANAGERS _IOWR('K', 104, struct ksu_get_managers_cmd) +#define KSU_IOCTL_ENABLE_UID_SCANNER _IOWR('K', 105, struct ksu_enable_uid_scanner_cmd) + +bool get_allow_list(struct ksu_get_allow_list_cmd*); + #endif //KERNELSU_KSU_H \ No newline at end of file diff --git a/manager/app/src/main/cpp/prelude.h b/manager/app/src/main/cpp/prelude.h index 780d8414..18e19faa 100644 --- a/manager/app/src/main/cpp/prelude.h +++ b/manager/app/src/main/cpp/prelude.h @@ -14,51 +14,51 @@ // Macros to simplify field setup #define SET_BOOLEAN_FIELD(obj, cls, fieldName, value) do { \ - jfieldID field = GetEnvironment()->GetFieldID(env, cls, #fieldName, "Z"); \ - GetEnvironment()->SetBooleanField(env, obj, field, value); \ + jfieldID field = GetEnvironment()->GetFieldID(env, cls, #fieldName, "Z"); \ + GetEnvironment()->SetBooleanField(env, obj, field, value); \ } while(0) #define SET_INT_FIELD(obj, cls, fieldName, value) do { \ - jfieldID field = GetEnvironment()->GetFieldID(env, cls, #fieldName, "I"); \ - GetEnvironment()->SetIntField(env, obj, field, value); \ + jfieldID field = GetEnvironment()->GetFieldID(env, cls, #fieldName, "I"); \ + GetEnvironment()->SetIntField(env, obj, field, value); \ } while(0) #define SET_STRING_FIELD(obj, cls, fieldName, value) do { \ - jfieldID field = GetEnvironment()->GetFieldID(env, cls, #fieldName, "Ljava/lang/String;"); \ - GetEnvironment()->SetObjectField(env, obj, field, GetEnvironment()->NewStringUTF(env, value)); \ + jfieldID field = GetEnvironment()->GetFieldID(env, cls, #fieldName, "Ljava/lang/String;"); \ + GetEnvironment()->SetObjectField(env, obj, field, GetEnvironment()->NewStringUTF(env, value)); \ } while(0) #define SET_OBJECT_FIELD(obj, cls, fieldName, value) do { \ - jfieldID field = GetEnvironment()->GetFieldID(env, cls, #fieldName, "Ljava/util/List;"); \ - GetEnvironment()->SetObjectField(env, obj, field, value); \ + jfieldID field = GetEnvironment()->GetFieldID(env, cls, #fieldName, "Ljava/util/List;"); \ + GetEnvironment()->SetObjectField(env, obj, field, value); \ } while(0) // Macros for creating Java objects #define CREATE_JAVA_OBJECT(className) ({ \ - jclass cls = GetEnvironment()->FindClass(env, className); \ - jmethodID constructor = GetEnvironment()->GetMethodID(env, cls, "", "()V"); \ - GetEnvironment()->NewObject(env, cls, constructor); \ + jclass cls = GetEnvironment()->FindClass(env, className); \ + jmethodID constructor = GetEnvironment()->GetMethodID(env, cls, "", "()V"); \ + GetEnvironment()->NewObject(env, cls, constructor); \ }) // Macros for creating ArrayList #define CREATE_ARRAYLIST() ({ \ - jclass arrayListCls = GetEnvironment()->FindClass(env, "java/util/ArrayList"); \ - jmethodID constructor = GetEnvironment()->GetMethodID(env, arrayListCls, "", "()V"); \ - GetEnvironment()->NewObject(env, arrayListCls, constructor); \ + jclass arrayListCls = GetEnvironment()->FindClass(env, "java/util/ArrayList"); \ + jmethodID constructor = GetEnvironment()->GetMethodID(env, arrayListCls, "", "()V"); \ + GetEnvironment()->NewObject(env, arrayListCls, constructor); \ }) // Macros for adding elements to an ArrayList #define ADD_TO_LIST(list, item) do { \ - jclass cls = GetEnvironment()->GetObjectClass(env, list); \ - jmethodID addMethod = GetEnvironment()->GetMethodID(env, cls, "add", "(Ljava/lang/Object;)Z"); \ - GetEnvironment()->CallBooleanMethod(env, list, addMethod, item); \ + jclass cls = GetEnvironment()->GetObjectClass(env, list); \ + jmethodID addMethod = GetEnvironment()->GetMethodID(env, cls, "add", "(Ljava/lang/Object;)Z"); \ + GetEnvironment()->CallBooleanMethod(env, list, addMethod, item); \ } while(0) // Macros for creating Java objects with parameter constructors #define CREATE_JAVA_OBJECT_WITH_PARAMS(className, signature, ...) ({ \ - jclass cls = GetEnvironment()->FindClass(env, className); \ - jmethodID constructor = GetEnvironment()->GetMethodID(env, cls, "", signature); \ - GetEnvironment()->NewObject(env, cls, constructor, __VA_ARGS__); \ + jclass cls = GetEnvironment()->FindClass(env, className); \ + jmethodID constructor = GetEnvironment()->GetMethodID(env, cls, "", signature); \ + GetEnvironment()->NewObject(env, cls, constructor, __VA_ARGS__); \ }) #ifdef NDEBUG diff --git a/manager/app/src/main/java/com/sukisu/ultra/Natives.kt b/manager/app/src/main/java/com/sukisu/ultra/Natives.kt index 36f37528..f58dc55f 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/Natives.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/Natives.kt @@ -16,17 +16,15 @@ object Natives { // 10946: add capabilities // 10977: change groups_count and groups to avoid overflow write // 11071: Fix the issue of failing to set a custom SELinux type. - const val MINIMAL_SUPPORTED_KERNEL = 11071 - const val MINIMAL_SUPPORTED_KERNEL_FULL = "v3.1.8" - - // 11640: Support query working mode, LKM or GKI - // when MINIMAL_SUPPORTED_KERNEL > 11640, we can remove this constant. - const val MINIMAL_SUPPORTED_KERNEL_LKM = 11648 + // 12143: breaking: new supercall impl + const val MINIMAL_SUPPORTED_KERNEL = 12143 // 12040: Support disable sucompat mode const val MINIMAL_SUPPORTED_SU_COMPAT = 12040 const val KERNEL_SU_DOMAIN = "u:r:su:s0" + const val MINIMAL_SUPPORTED_KERNEL_FULL = "v3.1.8" + const val MINIMAL_SUPPORTED_KPM = 12800 const val MINIMAL_SUPPORTED_DYNAMIC_MANAGER = 13215 @@ -66,8 +64,6 @@ object Natives { System.loadLibrary("kernelsu") } - // become root manager, return true if success. - external fun becomeManager(pkg: String?): Boolean val version: Int external get @@ -81,6 +77,9 @@ object Natives { val isLkmMode: Boolean external get + val isManager: Boolean + external get + external fun uidShouldUmount(uid: Int): Boolean /** @@ -99,6 +98,7 @@ object Natives { */ external fun isSuEnabled(): Boolean external fun setSuEnabled(enabled: Boolean): Boolean + external fun grantRoot(): Boolean external fun isKPMEnabled(): Boolean external fun getHookType(): String diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt index abc61c24..6f0b916f 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/MainActivity.kt @@ -74,7 +74,7 @@ class MainActivity : ComponentActivity() { super.onCreate(savedInstanceState) - val isManager = Natives.becomeManager(packageName) + val isManager = Natives.isManager if (isManager) { install() } diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/activity/component/BottomBar.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/activity/component/BottomBar.kt index 4014ca82..7efffade 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/activity/component/BottomBar.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/activity/component/BottomBar.kt @@ -15,7 +15,6 @@ import com.ramcosta.composedestinations.spec.RouteOrDirection import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator import com.sukisu.ultra.Natives -import com.sukisu.ultra.ksuApp import com.sukisu.ultra.ui.MainActivity import com.sukisu.ultra.ui.activity.util.* import com.sukisu.ultra.ui.activity.util.AppData.getKpmVersionUse @@ -29,7 +28,7 @@ import com.sukisu.ultra.ui.util.* @Composable fun BottomBar(navController: NavHostController) { val navigator = navController.rememberDestinationsNavigator() - val isFullFeatured = AppData.isFullFeatured(ksuApp.packageName) + val isFullFeatured = AppData.isFullFeatured() val kpmVersion = getKpmVersionUse() val cardColor = MaterialTheme.colorScheme.surfaceContainer val activity = LocalContext.current as MainActivity diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/UltraActivityUtils.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/UltraActivityUtils.kt index aec39f15..367e791a 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/UltraActivityUtils.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/activity/util/UltraActivityUtils.kt @@ -175,8 +175,8 @@ object AppData { /** * 检查是否是完整功能模式 */ - fun isFullFeatured(packageName: String): Boolean { - val isManager = Natives.becomeManager(packageName) + fun isFullFeatured(): Boolean { + val isManager = Natives.isManager return isManager && !Natives.requireNewKernel() && rootAvailable() } } diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/component/KsuIsValidCheck.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/component/KsuIsValidCheck.kt index 5adf6087..eb3c5db0 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/component/KsuIsValidCheck.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/component/KsuIsValidCheck.kt @@ -8,7 +8,7 @@ import com.sukisu.ultra.ksuApp fun KsuIsValid( content: @Composable () -> Unit ) { - val isManager = Natives.becomeManager(ksuApp.packageName) + val isManager = Natives.isManager val ksuVersion = if (isManager) Natives.version else null if (ksuVersion != null) { diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Settings.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Settings.kt index a84a2458..6f138b34 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Settings.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/screen/Settings.kt @@ -2,6 +2,7 @@ package com.sukisu.ultra.ui.screen import android.content.Context import android.content.Intent +import android.content.SharedPreferences import android.net.Uri import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult @@ -48,8 +49,9 @@ import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha import com.sukisu.ultra.ui.theme.getCardColors import com.sukisu.ultra.ui.theme.getCardElevation import com.sukisu.ultra.ui.util.* -import com.topjohnwu.superuser.ShellUtils +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.time.LocalDateTime @@ -179,127 +181,7 @@ fun SettingScreen(navigator: DestinationsNavigator) { ) // UID 扫描开关 if (Natives.version >= Natives.MINIMAL_SUPPORTED_UID_SCANNER) { - var uidAutoScanEnabled by rememberSaveable { - mutableStateOf(prefs.getBoolean("uid_auto_scan", false)) - } - - var uidMultiUserScanEnabled by rememberSaveable { - mutableStateOf(prefs.getBoolean("uid_multi_user_scan", false)) - } - - LaunchedEffect(Unit) { - uidAutoScanEnabled = Natives.isUidScannerEnabled() - uidMultiUserScanEnabled = getUidMultiUserScan() - - prefs.edit { - putBoolean("uid_auto_scan", uidAutoScanEnabled) - putBoolean("uid_multi_user_scan", uidMultiUserScanEnabled) - } - } - - // 用户态扫描应用列表开关 - SwitchItem( - icon = Icons.Filled.Scanner, - title = stringResource(R.string.uid_auto_scan_title), - summary = stringResource(R.string.uid_auto_scan_summary), - checked = uidAutoScanEnabled, - onCheckedChange = { enabled -> - scope.launch { - try { - if (setUidAutoScan(enabled)) { - uidAutoScanEnabled = enabled - prefs.edit { putBoolean("uid_auto_scan", enabled) } - - if (!enabled) { - uidMultiUserScanEnabled = false - prefs.edit { putBoolean("uid_multi_user_scan", false) } - } - } else { - snackBarHost.showSnackbar(context.getString(R.string.uid_scanner_setting_failed)) - } - } catch (e: Exception) { - snackBarHost.showSnackbar( - context.getString( - R.string.uid_scanner_setting_error, - e.message ?: "" - ) - ) - } - } - } - ) - - // 多用户应用扫描开关 - 仅在启用用户态扫描时显示 - AnimatedVisibility( - visible = uidAutoScanEnabled, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically() - ) { - SwitchItem( - icon = Icons.Filled.Groups, - title = stringResource(R.string.uid_multi_user_scan_title), - summary = stringResource(R.string.uid_multi_user_scan_summary), - checked = uidMultiUserScanEnabled, - onCheckedChange = { enabled -> - scope.launch { - try { - if (setUidMultiUserScan(enabled)) { - uidMultiUserScanEnabled = enabled - prefs.edit { putBoolean("uid_multi_user_scan", enabled) } - } else { - snackBarHost.showSnackbar(context.getString(R.string.uid_scanner_setting_failed)) - } - } catch (e: Exception) { - snackBarHost.showSnackbar( - context.getString( - R.string.uid_scanner_setting_error, - e.message ?: "" - ) - ) - } - } - } - ) - } - // 清理运行环境 - AnimatedVisibility( - visible = uidAutoScanEnabled, - enter = fadeIn() + expandVertically(), - exit = fadeOut() + shrinkVertically() - ) { - val confirmDialog = rememberConfirmDialog() - val scope = rememberCoroutineScope() - - SettingItem( - icon = Icons.Filled.CleaningServices, - title = stringResource(R.string.clean_runtime_environment), - summary = stringResource(R.string.clean_runtime_environment_summary), - onClick = { - scope.launch { - val result = confirmDialog.awaitConfirm( - title = context.getString(R.string.clean_runtime_environment), - content = context.getString(R.string.clean_runtime_environment_confirm) - ) - if (result == ConfirmResult.Confirmed) { - val cleanResult = cleanRuntimeEnvironment() - if (cleanResult) { - uidAutoScanEnabled = false - prefs.edit { putBoolean("uid_auto_scan", false) } - - uidMultiUserScanEnabled = false - prefs.edit { putBoolean("uid_multi_user_scan", false) } - - Natives.setUidScannerEnabled(false) - - snackBarHost.showSnackbar(context.getString(R.string.clean_runtime_environment_success)) - } else { - snackBarHost.showSnackbar(context.getString(R.string.clean_runtime_environment_failed)) - } - } - } - } - ) - } + UidScannerSection(prefs, snackBarHost, scope, context) } } ) @@ -453,7 +335,7 @@ fun SettingScreen(navigator: DestinationsNavigator) { ) } - val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode + val lkmMode = Natives.isLkmMode if (lkmMode) { UninstallItem(navigator) { loadingDialog.withLoading(it) @@ -481,24 +363,6 @@ fun SettingScreen(navigator: DestinationsNavigator) { } } -fun cleanRuntimeEnvironment(): Boolean { - val shell = getRootShell() - return try { - try { - ShellUtils.fastCmd(shell, "/data/adb/uid_scanner stop") - } catch (_: Exception) { - } - ShellUtils.fastCmdResult(shell, "rm -rf /data/misc/user_uid") - ShellUtils.fastCmdResult(shell, "rm -rf /data/adb/uid_scanner") - ShellUtils.fastCmdResult(shell, "rm -rf /data/adb/ksu/bin/user_uid") - ShellUtils.fastCmdResult(shell, "rm -rf /data/adb/service.d/uid_scanner.sh") - Natives.clearUidScannerEnvironment() - true - } catch (_: Exception) { - false - } -} - @Composable private fun SettingsGroupCard( title: String, @@ -960,4 +824,125 @@ private fun TopBar( windowInsets = WindowInsets.safeDrawing.only(WindowInsetsSides.Top + WindowInsetsSides.Horizontal), scrollBehavior = scrollBehavior ) +} + +@Composable +private fun UidScannerSection( + prefs: SharedPreferences, + snackBarHost: SnackbarHostState, + scope: CoroutineScope, + context: Context +) { + if (Natives.version < Natives.MINIMAL_SUPPORTED_UID_SCANNER) return + + val realAuto = Natives.isUidScannerEnabled() + val realMulti = getUidMultiUserScan() + + var autoOn by remember { mutableStateOf(realAuto) } + var multiOn by remember { mutableStateOf(realMulti) } + + LaunchedEffect(Unit) { + autoOn = realAuto + multiOn = realMulti + prefs.edit { + putBoolean("uid_auto_scan", autoOn) + putBoolean("uid_multi_user_scan", multiOn) + } + } + + SwitchItem( + icon = Icons.Filled.Scanner, + title = stringResource(R.string.uid_auto_scan_title), + summary = stringResource(R.string.uid_auto_scan_summary), + checked = autoOn, + onCheckedChange = { target -> + autoOn = target + if (!target) multiOn = false + + scope.launch(Dispatchers.IO) { + setUidAutoScan(target) + val actual = Natives.isUidScannerEnabled() || readUidScannerFile() + withContext(Dispatchers.Main) { + autoOn = actual + if (!actual) multiOn = false + prefs.edit { + putBoolean("uid_auto_scan", actual) + putBoolean("uid_multi_user_scan", multiOn) + } + if (actual != target) { + snackBarHost.showSnackbar( + context.getString(R.string.uid_scanner_setting_failed) + ) + } + } + } + } + ) + + AnimatedVisibility( + visible = autoOn, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically() + ) { + SwitchItem( + icon = Icons.Filled.Groups, + title = stringResource(R.string.uid_multi_user_scan_title), + summary = stringResource(R.string.uid_multi_user_scan_summary), + checked = multiOn, + onCheckedChange = { target -> + scope.launch(Dispatchers.IO) { + val ok = setUidMultiUserScan(target) + withContext(Dispatchers.Main) { + if (ok) { + multiOn = target + prefs.edit { putBoolean("uid_multi_user_scan", target) } + } else { + snackBarHost.showSnackbar( + context.getString(R.string.uid_scanner_setting_failed) + ) + } + } + } + } + ) + } + + AnimatedVisibility( + visible = autoOn, + enter = fadeIn() + expandVertically(), + exit = fadeOut() + shrinkVertically() + ) { + val confirmDialog = rememberConfirmDialog() + SettingItem( + icon = Icons.Filled.CleaningServices, + title = stringResource(R.string.clean_runtime_environment), + summary = stringResource(R.string.clean_runtime_environment_summary), + onClick = { + scope.launch { + if (confirmDialog.awaitConfirm( + title = context.getString(R.string.clean_runtime_environment), + content = context.getString(R.string.clean_runtime_environment_confirm) + ) == ConfirmResult.Confirmed + ) { + if (cleanRuntimeEnvironment()) { + autoOn = false + multiOn = false + prefs.edit { + putBoolean("uid_auto_scan", false) + putBoolean("uid_multi_user_scan", false) + } + Natives.setUidScannerEnabled(false) + snackBarHost.showSnackbar( + context.getString(R.string.clean_runtime_environment_success) + ) + } else { + snackBarHost.showSnackbar( + context.getString(R.string.clean_runtime_environment_failed) + ) + } + } + } + } + ) + } } \ No newline at end of file diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/util/KsuCli.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/util/KsuCli.kt index 70df5e98..b8e80824 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/util/KsuCli.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/util/KsuCli.kt @@ -21,6 +21,7 @@ import com.sukisu.ultra.Natives import com.sukisu.ultra.ksuApp import org.json.JSONArray import java.io.File +import java.util.concurrent.CountDownLatch /** @@ -34,7 +35,7 @@ private fun getKsuDaemonPath(): String { } object KsuCli { - val SHELL: Shell = createRootShell() + var SHELL: Shell = createRootShell() val GLOBAL_MNT_SHELL: Shell = createRootShell(true) } @@ -578,11 +579,10 @@ fun getUidScannerDaemonPath(): String { return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libuid_scanner.so" } +private const val targetPath = "/data/adb/uid_scanner" fun ensureUidScannerExecutable(): Boolean { val shell = getRootShell() val uidScannerPath = getUidScannerDaemonPath() - val targetPath = "/data/adb/uid_scanner" - if (!ShellUtils.fastCmdResult(shell, "test -f $targetPath")) { val copyResult = ShellUtils.fastCmdResult(shell, "cp $uidScannerPath $targetPath") if (!copyResult) { @@ -593,7 +593,6 @@ fun ensureUidScannerExecutable(): Boolean { val result = ShellUtils.fastCmdResult(shell, "chmod 755 $targetPath") return result } -private const val targetPath = "/data/adb/uid_scanner" fun setUidAutoScan(enabled: Boolean): Boolean { val shell = getRootShell() @@ -634,3 +633,30 @@ fun getUidMultiUserScan(): Boolean { false } } + +fun cleanRuntimeEnvironment(): Boolean { + val shell = getRootShell() + return try { + try { + ShellUtils.fastCmd(shell, "/data/adb/uid_scanner stop") + } catch (_: Exception) { + } + ShellUtils.fastCmdResult(shell, "rm -rf /data/misc/user_uid") + ShellUtils.fastCmdResult(shell, "rm -rf /data/adb/uid_scanner") + ShellUtils.fastCmdResult(shell, "rm -rf /data/adb/ksu/bin/user_uid") + ShellUtils.fastCmdResult(shell, "rm -rf /data/adb/service.d/uid_scanner.sh") + Natives.clearUidScannerEnvironment() + true + } catch (_: Exception) { + false + } +} + +fun readUidScannerFile(): Boolean { + val shell = getRootShell() + return try { + ShellUtils.fastCmd(shell, "cat /data/adb/ksu/.uid_scanner").trim() == "1" + } catch (_: Exception) { + false + } +} diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt index 38ed74a3..f807aec8 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/viewmodel/HomeViewModel.kt @@ -121,18 +121,12 @@ class HomeViewModel : ViewModel() { try { val kernelVersion = getKernelVersion() val isManager = try { - Natives.becomeManager(ksuApp.packageName ?: "com.sukisu.ultra") + Natives.isManager } catch (_: Exception) { false } - val ksuVersion = if (isManager) { - try { - Natives.version - } catch (_: Exception) { - null - } - } else null + val ksuVersion = if (isManager) Natives.version else null val fullVersion = try { Natives.getFullVersion() @@ -163,13 +157,7 @@ class HomeViewModel : ViewModel() { } val lkmMode = ksuVersion?.let { - try { - if (it >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && kernelVersion.isGKI()) { - Natives.isLkmMode - } else null - } catch (_: Exception) { - null - } + if (kernelVersion.isGKI()) Natives.isLkmMode else null } val isRootAvailable = try { @@ -346,7 +334,7 @@ class HomeViewModel : ViewModel() { try { // 检查KSU状态是否发生变化 val currentKsuVersion = try { - if (Natives.becomeManager(ksuApp.packageName ?: "com.sukisu.ultra")) { + if (Natives.isManager) { Natives.version } else null } catch (_: Exception) { diff --git a/manager/app/src/main/java/com/sukisu/ultra/ui/webui/KsuLibSuProvider.kt b/manager/app/src/main/java/com/sukisu/ultra/ui/webui/KsuLibSuProvider.kt index a0f003a8..dad41e34 100644 --- a/manager/app/src/main/java/com/sukisu/ultra/ui/webui/KsuLibSuProvider.kt +++ b/manager/app/src/main/java/com/sukisu/ultra/ui/webui/KsuLibSuProvider.kt @@ -17,7 +17,7 @@ class KsuLibSuProvider : IProvider { override fun isAvailable() = true - override suspend fun isAuthorized() = Natives.becomeManager(ksuApp.packageName) + override suspend fun isAuthorized() = Natives.isManager private val serviceIntent get() = PlatformIntent( diff --git a/userspace/ksud/src/ksucalls.rs b/userspace/ksud/src/ksucalls.rs index 596df545..eae40020 100644 --- a/userspace/ksud/src/ksucalls.rs +++ b/userspace/ksud/src/ksucalls.rs @@ -1,10 +1,133 @@ -const EVENT_POST_FS_DATA: u64 = 1; -const EVENT_BOOT_COMPLETED: u64 = 2; -const EVENT_MODULE_MOUNTED: u64 = 3; +use std::fs; +#[cfg(any(target_os = "linux", target_os = "android"))] +use std::os::fd::RawFd; +use std::sync::OnceLock; + +// Event constants +const EVENT_POST_FS_DATA: u32 = 1; +const EVENT_BOOT_COMPLETED: u32 = 2; +const EVENT_MODULE_MOUNTED: u32 = 3; + +const KSU_IOCTL_GRANT_ROOT: u32 = 0x4B01; // _IO('K', 1) +const KSU_IOCTL_GET_INFO: u32 = 0x80084b02; // _IOR('K', 2, struct ksu_get_info_cmd) +const KSU_IOCTL_REPORT_EVENT: u32 = 0x40044b03; // _IOW('K', 3, struct ksu_report_event_cmd) +const KSU_IOCTL_SET_SEPOLICY: u32 = 0xc0104b04; // _IOWR('K', 4, struct ksu_set_sepolicy_cmd) +const KSU_IOCTL_CHECK_SAFEMODE: u32 = 0x80014b05; // _IOR('K', 5, struct ksu_check_safemode_cmd) + +#[repr(C)] +#[derive(Clone, Copy, Default)] +struct GetInfoCmd { + version: u32, + flags: u32, +} + +#[repr(C)] +struct ReportEventCmd { + event: u32, +} + +#[repr(C)] +#[derive(Clone, Copy, Default)] +pub struct SetSepolicyCmd { + pub cmd: u64, + pub arg: u64, +} + +#[repr(C)] +#[derive(Clone, Copy, Default)] +struct CheckSafemodeCmd { + in_safe_mode: u8, +} + +// Global driver fd cache +#[cfg(any(target_os = "linux", target_os = "android"))] +static DRIVER_FD: OnceLock = OnceLock::new(); +#[cfg(any(target_os = "linux", target_os = "android"))] +static INFO_CACHE: OnceLock = OnceLock::new(); + +const KSU_INSTALL_MAGIC1: u32 = 0xDEADBEEF; +const KSU_INSTALL_MAGIC2: u32 = 0xCAFEBABE; + +#[cfg(any(target_os = "linux", target_os = "android"))] +fn scan_driver_fd() -> Option { + let fd_dir = fs::read_dir("/proc/self/fd").ok()?; + + for entry in fd_dir.flatten() { + if let Ok(fd_num) = entry.file_name().to_string_lossy().parse::() { + let link_path = format!("/proc/self/fd/{}", fd_num); + if let Ok(target) = fs::read_link(&link_path) { + let target_str = target.to_string_lossy(); + if target_str.contains("[ksu_driver]") { + return Some(fd_num); + } + } + } + } + + None +} + +// Get cached driver fd +#[cfg(any(target_os = "linux", target_os = "android"))] +fn init_driver_fd() -> Option { + let fd = scan_driver_fd(); + if fd.is_none() { + let mut fd = -1; + unsafe { + libc::syscall( + libc::SYS_reboot, + KSU_INSTALL_MAGIC1, + KSU_INSTALL_MAGIC2, + 0, + &mut fd, + ); + }; + if fd >= 0 { Some(fd) } else { None } + } else { + fd + } +} + +// ioctl wrapper using libc +#[cfg(any(target_os = "linux", target_os = "android"))] +fn ksuctl(request: u32, arg: *mut T) -> std::io::Result { + use std::io; + + let fd = *DRIVER_FD.get_or_init(|| init_driver_fd().unwrap_or(-1)); + unsafe { + #[cfg(not(target_env = "gnu"))] + let ret = libc::ioctl(fd as libc::c_int, request as i32, arg); + #[cfg(target_env = "gnu")] + let ret = libc::ioctl(fd as libc::c_int, request as u64, arg); + if ret < 0 { + Err(io::Error::last_os_error()) + } else { + Ok(ret) + } + } +} + +#[cfg(not(any(target_os = "linux", target_os = "android")))] +fn ksuctl(_request: u32, _arg: *mut T) -> std::io::Result { + Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) +} + +// API implementations +#[cfg(any(target_os = "linux", target_os = "android"))] +fn get_info() -> GetInfoCmd { + *INFO_CACHE.get_or_init(|| { + let mut cmd = GetInfoCmd { + version: 0, + flags: 0, + }; + let _ = ksuctl(KSU_IOCTL_GET_INFO, &mut cmd as *mut _); + cmd + }) +} #[cfg(any(target_os = "linux", target_os = "android"))] pub fn get_version() -> i32 { - rustix::process::ksu_get_version() + get_info().version as i32 } #[cfg(not(any(target_os = "linux", target_os = "android")))] @@ -13,22 +136,24 @@ pub fn get_version() -> i32 { } #[cfg(any(target_os = "linux", target_os = "android"))] -fn report_event(event: u64) { - rustix::process::ksu_report_event(event) +pub fn grant_root() -> std::io::Result<()> { + ksuctl(KSU_IOCTL_GRANT_ROOT, std::ptr::null_mut::())?; + Ok(()) } #[cfg(not(any(target_os = "linux", target_os = "android")))] -fn report_event(_event: u64) {} +pub fn grant_root() -> std::io::Result<()> { + Err(std::io::Error::from_raw_os_error(libc::ENOSYS)) +} #[cfg(any(target_os = "linux", target_os = "android"))] -pub fn check_kernel_safemode() -> bool { - rustix::process::ksu_check_kernel_safemode() +fn report_event(event: u32) { + let mut cmd = ReportEventCmd { event }; + let _ = ksuctl(KSU_IOCTL_REPORT_EVENT, &mut cmd as *mut _); } #[cfg(not(any(target_os = "linux", target_os = "android")))] -pub fn check_kernel_safemode() -> bool { - false -} +fn report_event(_event: u32) {} pub fn report_post_fs_data() { report_event(EVENT_POST_FS_DATA); @@ -41,3 +166,21 @@ pub fn report_boot_complete() { pub fn report_module_mounted() { report_event(EVENT_MODULE_MOUNTED); } + +#[cfg(any(target_os = "linux", target_os = "android"))] +pub fn check_kernel_safemode() -> bool { + let mut cmd = CheckSafemodeCmd { in_safe_mode: 0 }; + let _ = ksuctl(KSU_IOCTL_CHECK_SAFEMODE, &mut cmd as *mut _); + cmd.in_safe_mode != 0 +} + +#[cfg(not(any(target_os = "linux", target_os = "android")))] +pub fn check_kernel_safemode() -> bool { + false +} + +pub fn set_sepolicy(cmd: &SetSepolicyCmd) -> std::io::Result<()> { + let mut ioctl_cmd = *cmd; + ksuctl(KSU_IOCTL_SET_SEPOLICY, &mut ioctl_cmd as *mut _)?; + Ok(()) +} \ No newline at end of file diff --git a/userspace/ksud/src/sepolicy.rs b/userspace/ksud/src/sepolicy.rs index 897e9d9a..a9daf8e4 100644 --- a/userspace/ksud/src/sepolicy.rs +++ b/userspace/ksud/src/sepolicy.rs @@ -20,7 +20,7 @@ fn parse_single_word(input: &str) -> IResult<&str, &str> { take_while1(is_sepolicy_char).parse(input) } -fn parse_bracket_objs<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> { +fn parse_bracket_objs(input: &str) -> IResult<&str, SeObject<'_>> { let (input, (_, words, _)) = ( tag("{"), take_while_m_n(1, 100, |c: char| is_sepolicy_char(c) || c.is_whitespace()), @@ -30,12 +30,12 @@ fn parse_bracket_objs<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> { Ok((input, words.split_whitespace().collect())) } -fn parse_single_obj<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> { +fn parse_single_obj(input: &str) -> IResult<&str, SeObject<'_>> { let (input, word) = take_while1(is_sepolicy_char).parse(input)?; Ok((input, vec![word])) } -fn parse_star<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> { +fn parse_star(input: &str) -> IResult<&str, SeObject<'_>> { let (input, _) = tag("*").parse(input)?; Ok((input, vec!["*"])) } @@ -43,12 +43,12 @@ fn parse_star<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> { // 1. a single sepolicy word // 2. { obj1 obj2 obj3 ...} // 3. * -fn parse_seobj<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> { +fn parse_seobj(input: &str) -> IResult<&str, SeObject<'_>> { let (input, strs) = alt((parse_single_obj, parse_bracket_objs, parse_star)).parse(input)?; Ok((input, strs)) } -fn parse_seobj_no_star<'a>(input: &'a str) -> IResult<&'a str, SeObject<'a>> { +fn parse_seobj_no_star(input: &str) -> IResult<&str, SeObject<'_>> { let (input, strs) = alt((parse_single_obj, parse_bracket_objs)).parse(input)?; Ok((input, strs)) } @@ -697,7 +697,12 @@ fn apply_one_rule<'a>(statement: &'a PolicyStatement<'a>, strict: bool) -> Resul let policies: Vec = statement.try_into()?; for policy in policies { - if !rustix::process::ksu_set_policy(&FfiPolicy::from(policy)) { + let ffi_policy = FfiPolicy::from(policy); + let cmd = crate::ksucalls::SetSepolicyCmd { + cmd: 0, + arg: &ffi_policy as *const _ as u64, + }; + if crate::ksucalls::set_sepolicy(&cmd).is_err() { log::warn!("apply rule: {statement:?} failed."); if strict { return Err(anyhow::anyhow!("apply rule {:?} failed.", statement)); diff --git a/userspace/ksud/src/su.rs b/userspace/ksud/src/su.rs index 9cd3afbb..44f37b99 100644 --- a/userspace/ksud/src/su.rs +++ b/userspace/ksud/src/su.rs @@ -17,7 +17,7 @@ use crate::{ #[cfg(any(target_os = "linux", target_os = "android"))] pub fn grant_root(global_mnt: bool) -> Result<()> { - rustix::process::ksu_grant_root()?; + crate::ksucalls::grant_root()?; let mut command = Command::new("sh"); let command = unsafe {