kernel: fmt

This commit is contained in:
ShirkNeko
2025-11-18 21:39:31 +08:00
parent aa51ef5c24
commit 118fcf507a
47 changed files with 4241 additions and 4241 deletions

View File

@@ -56,8 +56,8 @@ ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:' CommentPragmas: '^ IWYU pragma:'
#CompactNamespaces: false # Unknown to clang-format-4.0 #CompactNamespaces: false # Unknown to clang-format-4.0
ConstructorInitializerAllOnOneLineOrOnePerLine: false ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 4 ConstructorInitializerIndentWidth: 8
ContinuationIndentWidth: 4 ContinuationIndentWidth: 8
Cpp11BracedListStyle: false Cpp11BracedListStyle: false
DerivePointerAlignment: false DerivePointerAlignment: false
DisableFormat: false DisableFormat: false
@@ -501,7 +501,7 @@ IncludeCategories:
IncludeIsMainRegex: '(Test)?$' IncludeIsMainRegex: '(Test)?$'
IndentCaseLabels: false IndentCaseLabels: false
#IndentPPDirectives: None # Unknown to clang-format-5.0 #IndentPPDirectives: None # Unknown to clang-format-5.0
IndentWidth: 4 IndentWidth: 8
IndentWrappedFunctionNames: false IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave JavaScriptQuotes: Leave
JavaScriptWrapImports: true JavaScriptWrapImports: true
@@ -511,7 +511,7 @@ MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1 MaxEmptyLinesToKeep: 1
NamespaceIndentation: None NamespaceIndentation: None
#ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0 #ObjCBinPackProtocolList: Auto # Unknown to clang-format-5.0
ObjCBlockIndentWidth: 4 ObjCBlockIndentWidth: 8
ObjCSpaceAfterProperty: true ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: true ObjCSpaceBeforeProtocolList: true
@@ -543,6 +543,6 @@ SpacesInCStyleCastParentheses: false
SpacesInParentheses: false SpacesInParentheses: false
SpacesInSquareBrackets: false SpacesInSquareBrackets: false
Standard: Cpp03 Standard: Cpp03
TabWidth: 4 TabWidth: 8
UseTab: Never UseTab: Always
... ...

View File

@@ -1,51 +1,51 @@
menu "KernelSU" menu "KernelSU"
config KSU config KSU
tristate "KernelSU function support" tristate "KernelSU function support"
default y default y
help help
Enable kernel-level root privileges on Android System. Enable kernel-level root privileges on Android System.
To compile as a module, choose M here: the To compile as a module, choose M here: the
module will be called kernelsu. module will be called kernelsu.
config KSU_DEBUG config KSU_DEBUG
bool "KernelSU debug mode" bool "KernelSU debug mode"
depends on KSU depends on KSU
default n default n
help help
Enable KernelSU debug mode. Enable KernelSU debug mode.
config KSU_MULTI_MANAGER_SUPPORT config KSU_MULTI_MANAGER_SUPPORT
bool "Multi KernelSU manager support" bool "Multi KernelSU manager support"
depends on KSU depends on KSU
default n default n
help help
Enable multi KernelSU manager support Enable multi KernelSU manager support
config KSU_ALLOWLIST_WORKAROUND config KSU_ALLOWLIST_WORKAROUND
bool "KernelSU Session Keyring Init workaround" bool "KernelSU Session Keyring Init workaround"
depends on KSU depends on KSU
default n default n
help help
Enable session keyring init workaround for problematic devices. Enable session keyring init workaround for problematic devices.
Useful for situations where the SU allowlist is not kept after a reboot Useful for situations where the SU allowlist is not kept after a reboot
config KPM config KPM
bool "Enable SukiSU KPM" bool "Enable SukiSU KPM"
depends on KSU && 64BIT depends on KSU && 64BIT
select KALLSYMS select KALLSYMS
select KALLSYMS_ALL select KALLSYMS_ALL
default n default n
help help
Enabling this option will activate the KPM feature of SukiSU. Enabling this option will activate the KPM feature of SukiSU.
This option is suitable for scenarios where you need to force KPM to be enabled. This option is suitable for scenarios where you need to force KPM to be enabled.
but it may affect system stability. but it may affect system stability.
config KSU_MANUAL_HOOK config KSU_MANUAL_HOOK
bool "Hook KernelSU manually" bool "Hook KernelSU manually"
depends on KSU != m depends on KSU != m
help help
If enabled, Hook required KernelSU syscalls with manually-patched function. If enabled, Hook required KernelSU syscalls with manually-patched function.
menu "KernelSU - SUSFS" menu "KernelSU - SUSFS"

View File

@@ -336,4 +336,4 @@ This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. Public License instead of this License.

View File

@@ -83,7 +83,7 @@ static void init_default_profiles(void)
default_root_profile.groups_count = 1; default_root_profile.groups_count = 1;
default_root_profile.groups[0] = 0; default_root_profile.groups[0] = 0;
memcpy(&default_root_profile.capabilities.effective, &full_cap, memcpy(&default_root_profile.capabilities.effective, &full_cap,
sizeof(default_root_profile.capabilities.effective)); sizeof(default_root_profile.capabilities.effective));
default_root_profile.namespaces = 0; default_root_profile.namespaces = 0;
strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN); strcpy(default_root_profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
@@ -127,7 +127,7 @@ static void ksu_grant_root_to_shell(void)
}; };
strcpy(profile.key, "com.android.shell"); strcpy(profile.key, "com.android.shell");
strcpy(profile.rp_config.profile.selinux_domain, strcpy(profile.rp_config.profile.selinux_domain,
KSU_DEFAULT_SELINUX_DOMAIN); KSU_DEFAULT_SELINUX_DOMAIN);
ksu_set_app_profile(&profile, false); ksu_set_app_profile(&profile, false);
} }
#endif #endif
@@ -199,7 +199,7 @@ bool ksu_set_app_profile(struct app_profile *profile, bool persist)
p = list_entry(pos, struct perm_data, list); p = list_entry(pos, struct perm_data, list);
// both uid and package must match, otherwise it will break multiple package with different user id // both uid and package must match, otherwise it will break multiple package with different user id
if (profile->current_uid == p->profile.current_uid && if (profile->current_uid == p->profile.current_uid &&
!strcmp(profile->key, p->profile.key)) { !strcmp(profile->key, p->profile.key)) {
// found it, just override it all! // found it, just override it all!
memcpy(&p->profile, profile, sizeof(*profile)); memcpy(&p->profile, profile, sizeof(*profile));
result = true; result = true;
@@ -238,9 +238,9 @@ out:
} else { } else {
if (profile->allow_su) { if (profile->allow_su) {
/* /*
* 1024 apps with uid higher than BITMAP_UID_MAX * 1024 apps with uid higher than BITMAP_UID_MAX
* registered to request superuser? * registered to request superuser?
*/ */
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) { if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
pr_err("too many apps registered\n"); pr_err("too many apps registered\n");
WARN_ON(1); WARN_ON(1);
@@ -258,13 +258,13 @@ out:
if (unlikely(!strcmp(profile->key, "$"))) { if (unlikely(!strcmp(profile->key, "$"))) {
// set default non root profile // set default non root profile
memcpy(&default_non_root_profile, &profile->nrp_config.profile, memcpy(&default_non_root_profile, &profile->nrp_config.profile,
sizeof(default_non_root_profile)); sizeof(default_non_root_profile));
} }
if (unlikely(!strcmp(profile->key, "#"))) { if (unlikely(!strcmp(profile->key, "#"))) {
// set default root profile // set default root profile
memcpy(&default_root_profile, &profile->rp_config.profile, memcpy(&default_root_profile, &profile->rp_config.profile,
sizeof(default_root_profile)); sizeof(default_root_profile));
} }
if (persist) { if (persist) {
@@ -286,7 +286,7 @@ bool __ksu_is_allow_uid(uid_t uid)
} }
if (likely(ksu_is_manager_uid_valid()) && if (likely(ksu_is_manager_uid_valid()) &&
unlikely(ksu_get_manager_uid() == uid)) { unlikely(ksu_get_manager_uid() == uid)) {
// manager is always allowed! // manager is always allowed!
return true; return true;
} }
@@ -317,7 +317,7 @@ bool ksu_uid_should_umount(uid_t uid)
{ {
struct app_profile profile = { .current_uid = uid }; struct app_profile profile = { .current_uid = uid };
if (likely(ksu_is_manager_uid_valid()) && if (likely(ksu_is_manager_uid_valid()) &&
unlikely(ksu_get_manager_uid() == uid)) { unlikely(ksu_get_manager_uid() == uid)) {
// we should not umount on manager! // we should not umount on manager!
return false; return false;
} }
@@ -387,19 +387,19 @@ static void do_persistent_allow_list(struct callback_head *_cb)
KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644); KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
pr_err("save_allow_list create file failed: %ld\n", pr_err("save_allow_list create file failed: %ld\n",
PTR_ERR(fp)); PTR_ERR(fp));
goto unlock; goto unlock;
} }
// store magic and version // store magic and version
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) !=
sizeof(magic)) { sizeof(magic)) {
pr_err("save_allow_list write magic failed.\n"); pr_err("save_allow_list write magic failed.\n");
goto close_file; goto close_file;
} }
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) !=
sizeof(version)) { sizeof(version)) {
pr_err("save_allow_list write version failed.\n"); pr_err("save_allow_list write version failed.\n");
goto close_file; goto close_file;
} }
@@ -466,14 +466,14 @@ void ksu_load_allow_list(void)
// verify magic // verify magic
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) !=
sizeof(magic) || sizeof(magic) ||
magic != FILE_MAGIC) { magic != FILE_MAGIC) {
pr_err("allowlist file invalid: %d!\n", magic); pr_err("allowlist file invalid: %d!\n", magic);
goto exit; goto exit;
} }
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) !=
sizeof(version)) { sizeof(version)) {
pr_err("allowlist read version: %d failed\n", version); pr_err("allowlist read version: %d failed\n", version);
goto exit; goto exit;
} }
@@ -484,7 +484,7 @@ void ksu_load_allow_list(void)
struct app_profile profile; struct app_profile profile;
ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile), ret = ksu_kernel_read_compat(fp, &profile, sizeof(profile),
&off); &off);
if (ret <= 0) { if (ret <= 0) {
pr_info("load_allow_list read err: %zd\n", ret); pr_info("load_allow_list read err: %zd\n", ret);
@@ -572,77 +572,77 @@ void ksu_allowlist_exit(void)
#ifdef CONFIG_KSU_MANUAL_SU #ifdef CONFIG_KSU_MANUAL_SU
bool ksu_temp_grant_root_once(uid_t uid) bool ksu_temp_grant_root_once(uid_t uid)
{ {
struct app_profile profile = { struct app_profile profile = {
.version = KSU_APP_PROFILE_VER, .version = KSU_APP_PROFILE_VER,
.allow_su = true, .allow_su = true,
.current_uid = uid, .current_uid = uid,
}; };
const char *default_key = "com.temp.once"; const char *default_key = "com.temp.once";
struct perm_data *p = NULL; struct perm_data *p = NULL;
struct list_head *pos = NULL; struct list_head *pos = NULL;
bool found = false; bool found = false;
list_for_each (pos, &allow_list) { list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list); p = list_entry(pos, struct perm_data, list);
if (p->profile.current_uid == uid) { if (p->profile.current_uid == uid) {
strcpy(profile.key, p->profile.key); strcpy(profile.key, p->profile.key);
found = true; found = true;
break; break;
} }
} }
if (!found) { if (!found) {
strcpy(profile.key, default_key); strcpy(profile.key, default_key);
} }
profile.rp_config.profile.uid = default_root_profile.uid; profile.rp_config.profile.uid = default_root_profile.uid;
profile.rp_config.profile.gid = default_root_profile.gid; profile.rp_config.profile.gid = default_root_profile.gid;
profile.rp_config.profile.groups_count = default_root_profile.groups_count; profile.rp_config.profile.groups_count = default_root_profile.groups_count;
memcpy(profile.rp_config.profile.groups, default_root_profile.groups, sizeof(default_root_profile.groups)); memcpy(profile.rp_config.profile.groups, default_root_profile.groups, sizeof(default_root_profile.groups));
memcpy(&profile.rp_config.profile.capabilities, &default_root_profile.capabilities, sizeof(default_root_profile.capabilities)); memcpy(&profile.rp_config.profile.capabilities, &default_root_profile.capabilities, sizeof(default_root_profile.capabilities));
profile.rp_config.profile.namespaces = default_root_profile.namespaces; profile.rp_config.profile.namespaces = default_root_profile.namespaces;
strcpy(profile.rp_config.profile.selinux_domain, default_root_profile.selinux_domain); strcpy(profile.rp_config.profile.selinux_domain, default_root_profile.selinux_domain);
bool ok = ksu_set_app_profile(&profile, false); bool ok = ksu_set_app_profile(&profile, false);
if (ok) if (ok)
pr_info("pending_root: UID=%d granted and persisted\n", uid); pr_info("pending_root: UID=%d granted and persisted\n", uid);
return ok; return ok;
} }
void ksu_temp_revoke_root_once(uid_t uid) void ksu_temp_revoke_root_once(uid_t uid)
{ {
struct app_profile profile = { struct app_profile profile = {
.version = KSU_APP_PROFILE_VER, .version = KSU_APP_PROFILE_VER,
.allow_su = false, .allow_su = false,
.current_uid = uid, .current_uid = uid,
}; };
const char *default_key = "com.temp.once"; const char *default_key = "com.temp.once";
struct perm_data *p = NULL; struct perm_data *p = NULL;
struct list_head *pos = NULL; struct list_head *pos = NULL;
bool found = false; bool found = false;
list_for_each (pos, &allow_list) { list_for_each (pos, &allow_list) {
p = list_entry(pos, struct perm_data, list); p = list_entry(pos, struct perm_data, list);
if (p->profile.current_uid == uid) { if (p->profile.current_uid == uid) {
strcpy(profile.key, p->profile.key); strcpy(profile.key, p->profile.key);
found = true; found = true;
break; break;
} }
} }
if (!found) { if (!found) {
strcpy(profile.key, default_key); strcpy(profile.key, default_key);
} }
profile.nrp_config.profile.umount_modules = default_non_root_profile.umount_modules; profile.nrp_config.profile.umount_modules = default_non_root_profile.umount_modules;
strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN); strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN);
ksu_set_app_profile(&profile, false); ksu_set_app_profile(&profile, false);
persistent_allow_list(); persistent_allow_list();
pr_info("pending_root: UID=%d removed and persist updated\n", uid); pr_info("pending_root: UID=%d removed and persist updated\n", uid);
} }
#endif #endif

View File

@@ -23,7 +23,7 @@ bool __ksu_is_allow_uid(uid_t uid);
// Check if the uid is in allow list, or current is ksu domain root // Check if the uid is in allow list, or current is ksu domain root
bool __ksu_is_allow_uid_for_current(uid_t uid); bool __ksu_is_allow_uid_for_current(uid_t uid);
#define ksu_is_allow_uid_for_current(uid) \ #define ksu_is_allow_uid_for_current(uid) \
unlikely(__ksu_is_allow_uid_for_current(uid)) unlikely(__ksu_is_allow_uid_for_current(uid))
bool ksu_get_allow_list(int *array, int *length, bool allow); bool ksu_get_allow_list(int *array, int *length, bool allow);

View File

@@ -21,68 +21,68 @@
#include "kernel_compat.h" #include "kernel_compat.h"
struct sdesc { struct sdesc {
struct shash_desc shash; struct shash_desc shash;
char ctx[]; char ctx[];
}; };
static apk_sign_key_t apk_sign_keys[] = { static apk_sign_key_t apk_sign_keys[] = {
{EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // SukiSU {EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // SukiSU
#ifdef CONFIG_KSU_MULTI_MANAGER_SUPPORT #ifdef CONFIG_KSU_MULTI_MANAGER_SUPPORT
{EXPECTED_SIZE_WEISHU, EXPECTED_HASH_WEISHU}, // Official {EXPECTED_SIZE_WEISHU, EXPECTED_HASH_WEISHU}, // Official
{EXPECTED_SIZE_5EC1CFF, EXPECTED_HASH_5EC1CFF}, // 5ec1cff/KernelSU {EXPECTED_SIZE_5EC1CFF, EXPECTED_HASH_5EC1CFF}, // 5ec1cff/KernelSU
{EXPECTED_SIZE_RSUNTK, EXPECTED_HASH_RSUNTK}, // rsuntk/KernelSU {EXPECTED_SIZE_RSUNTK, EXPECTED_HASH_RSUNTK}, // rsuntk/KernelSU
{EXPECTED_SIZE_NEKO, EXPECTED_HASH_NEKO}, // Neko/KernelSU {EXPECTED_SIZE_NEKO, EXPECTED_HASH_NEKO}, // Neko/KernelSU
#ifdef EXPECTED_SIZE #ifdef EXPECTED_SIZE
{EXPECTED_SIZE, EXPECTED_HASH}, // Custom {EXPECTED_SIZE, EXPECTED_HASH}, // Custom
#endif #endif
#endif #endif
}; };
static struct sdesc *init_sdesc(struct crypto_shash *alg) static struct sdesc *init_sdesc(struct crypto_shash *alg)
{ {
struct sdesc *sdesc; struct sdesc *sdesc;
int size; int size;
size = sizeof(struct shash_desc) + crypto_shash_descsize(alg); size = sizeof(struct shash_desc) + crypto_shash_descsize(alg);
sdesc = kzalloc(size, GFP_KERNEL); sdesc = kzalloc(size, GFP_KERNEL);
if (!sdesc) if (!sdesc)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
sdesc->shash.tfm = alg; sdesc->shash.tfm = alg;
return sdesc; return sdesc;
} }
static int calc_hash(struct crypto_shash *alg, const unsigned char *data, static int calc_hash(struct crypto_shash *alg, const unsigned char *data,
unsigned int datalen, unsigned char *digest) unsigned int datalen, unsigned char *digest)
{ {
struct sdesc *sdesc; struct sdesc *sdesc;
int ret; int ret;
sdesc = init_sdesc(alg); sdesc = init_sdesc(alg);
if (IS_ERR(sdesc)) { if (IS_ERR(sdesc)) {
pr_info("can't alloc sdesc\n"); pr_info("can't alloc sdesc\n");
return PTR_ERR(sdesc); return PTR_ERR(sdesc);
} }
ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest); ret = crypto_shash_digest(&sdesc->shash, data, datalen, digest);
kfree(sdesc); kfree(sdesc);
return ret; return ret;
} }
static int ksu_sha256(const unsigned char *data, unsigned int datalen, static int ksu_sha256(const unsigned char *data, unsigned int datalen,
unsigned char *digest) unsigned char *digest)
{ {
struct crypto_shash *alg; struct crypto_shash *alg;
char *hash_alg_name = "sha256"; char *hash_alg_name = "sha256";
int ret; int ret;
alg = crypto_alloc_shash(hash_alg_name, 0, 0); alg = crypto_alloc_shash(hash_alg_name, 0, 0);
if (IS_ERR(alg)) { if (IS_ERR(alg)) {
pr_info("can't alloc alg %s\n", hash_alg_name); pr_info("can't alloc alg %s\n", hash_alg_name);
return PTR_ERR(alg); return PTR_ERR(alg);
} }
ret = calc_hash(alg, data, datalen, digest); ret = calc_hash(alg, data, datalen, digest);
crypto_free_shash(alg); crypto_free_shash(alg);
return ret; return ret;
} }
@@ -90,307 +90,307 @@ static struct dynamic_sign_key dynamic_sign = DYNAMIC_SIGN_DEFAULT_CONFIG;
static bool check_dynamic_sign(struct file *fp, u32 size4, loff_t *pos, int *matched_index) static bool check_dynamic_sign(struct file *fp, u32 size4, loff_t *pos, int *matched_index)
{ {
struct dynamic_sign_key current_dynamic_key = dynamic_sign; struct dynamic_sign_key current_dynamic_key = dynamic_sign;
if (ksu_get_dynamic_manager_config(&current_dynamic_key.size, &current_dynamic_key.hash)) { if (ksu_get_dynamic_manager_config(&current_dynamic_key.size, &current_dynamic_key.hash)) {
pr_debug("Using dynamic manager config: size=0x%x, hash=%.16s...\n", pr_debug("Using dynamic manager config: size=0x%x, hash=%.16s...\n",
current_dynamic_key.size, current_dynamic_key.hash); current_dynamic_key.size, current_dynamic_key.hash);
} }
if (size4 != current_dynamic_key.size) { if (size4 != current_dynamic_key.size) {
return false; return false;
} }
#define CERT_MAX_LENGTH 1024 #define CERT_MAX_LENGTH 1024
char cert[CERT_MAX_LENGTH]; char cert[CERT_MAX_LENGTH];
if (size4 > CERT_MAX_LENGTH) { if (size4 > CERT_MAX_LENGTH) {
pr_info("cert length overlimit\n"); pr_info("cert length overlimit\n");
return false; return false;
} }
ksu_kernel_read_compat(fp, cert, size4, pos); ksu_kernel_read_compat(fp, cert, size4, pos);
unsigned char digest[SHA256_DIGEST_SIZE]; unsigned char digest[SHA256_DIGEST_SIZE];
if (ksu_sha256(cert, size4, digest) < 0) { if (ksu_sha256(cert, size4, digest) < 0) {
pr_info("sha256 error\n"); pr_info("sha256 error\n");
return false; return false;
} }
char hash_str[SHA256_DIGEST_SIZE * 2 + 1]; char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
hash_str[SHA256_DIGEST_SIZE * 2] = '\0'; hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE); bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
pr_info("sha256: %s, expected: %s, index: dynamic\n", hash_str, current_dynamic_key.hash); pr_info("sha256: %s, expected: %s, index: dynamic\n", hash_str, current_dynamic_key.hash);
if (strcmp(current_dynamic_key.hash, hash_str) == 0) { if (strcmp(current_dynamic_key.hash, hash_str) == 0) {
if (matched_index) { if (matched_index) {
*matched_index = DYNAMIC_SIGN_INDEX; *matched_index = DYNAMIC_SIGN_INDEX;
} }
return true; return true;
} }
return false; return false;
} }
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, int *matched_index) static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, int *matched_index)
{ {
int i; int i;
apk_sign_key_t sign_key; apk_sign_key_t sign_key;
bool signature_valid = false; bool signature_valid = false;
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
*offset += 0x4 * 3; *offset += 0x4 * 3;
ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length ksu_kernel_read_compat(fp, size4, 0x4, pos); // digests-sequence length
*pos += *size4; *pos += *size4;
*offset += 0x4 + *size4; *offset += 0x4 + *size4;
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificates length
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
*offset += 0x4 * 2; *offset += 0x4 * 2;
if (ksu_is_dynamic_manager_enabled()) { if (ksu_is_dynamic_manager_enabled()) {
loff_t temp_pos = *pos; loff_t temp_pos = *pos;
if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) { if (check_dynamic_sign(fp, *size4, &temp_pos, matched_index)) {
*pos = temp_pos; *pos = temp_pos;
*offset += *size4; *offset += *size4;
return true; return true;
} }
} }
for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) { for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
sign_key = apk_sign_keys[i]; sign_key = apk_sign_keys[i];
if (*size4 != sign_key.size) if (*size4 != sign_key.size)
continue; continue;
*offset += *size4; *offset += *size4;
#define CERT_MAX_LENGTH 1024 #define CERT_MAX_LENGTH 1024
char cert[CERT_MAX_LENGTH]; char cert[CERT_MAX_LENGTH];
if (*size4 > CERT_MAX_LENGTH) { if (*size4 > CERT_MAX_LENGTH) {
pr_info("cert length overlimit\n"); pr_info("cert length overlimit\n");
return false; return false;
} }
ksu_kernel_read_compat(fp, cert, *size4, pos); ksu_kernel_read_compat(fp, cert, *size4, pos);
unsigned char digest[SHA256_DIGEST_SIZE]; unsigned char digest[SHA256_DIGEST_SIZE];
if (ksu_sha256(cert, *size4, digest) < 0 ) { if (ksu_sha256(cert, *size4, digest) < 0 ) {
pr_info("sha256 error\n"); pr_info("sha256 error\n");
return false; return false;
} }
char hash_str[SHA256_DIGEST_SIZE * 2 + 1]; char hash_str[SHA256_DIGEST_SIZE * 2 + 1];
hash_str[SHA256_DIGEST_SIZE * 2] = '\0'; hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE); bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
pr_info("sha256: %s, expected: %s, index: %d\n", hash_str, sign_key.sha256, i); pr_info("sha256: %s, expected: %s, index: %d\n", hash_str, sign_key.sha256, i);
if (strcmp(sign_key.sha256, hash_str) == 0) { if (strcmp(sign_key.sha256, hash_str) == 0) {
signature_valid = true; signature_valid = true;
if (matched_index) { if (matched_index) {
*matched_index = i; *matched_index = i;
} }
break; break;
} }
} }
return signature_valid; return signature_valid;
} }
struct zip_entry_header { struct zip_entry_header {
uint32_t signature; uint32_t signature;
uint16_t version; uint16_t version;
uint16_t flags; uint16_t flags;
uint16_t compression; uint16_t compression;
uint16_t mod_time; uint16_t mod_time;
uint16_t mod_date; uint16_t mod_date;
uint32_t crc32; uint32_t crc32;
uint32_t compressed_size; uint32_t compressed_size;
uint32_t uncompressed_size; uint32_t uncompressed_size;
uint16_t file_name_length; uint16_t file_name_length;
uint16_t extra_field_length; uint16_t extra_field_length;
} __attribute__((packed)); } __attribute__((packed));
// This is a necessary but not sufficient condition, but it is enough for us // This is a necessary but not sufficient condition, but it is enough for us
static bool has_v1_signature_file(struct file *fp) static bool has_v1_signature_file(struct file *fp)
{ {
struct zip_entry_header header; struct zip_entry_header header;
const char MANIFEST[] = "META-INF/MANIFEST.MF"; const char MANIFEST[] = "META-INF/MANIFEST.MF";
loff_t pos = 0; loff_t pos = 0;
while (ksu_kernel_read_compat(fp, &header, while (ksu_kernel_read_compat(fp, &header,
sizeof(struct zip_entry_header), &pos) == sizeof(struct zip_entry_header), &pos) ==
sizeof(struct zip_entry_header)) { sizeof(struct zip_entry_header)) {
if (header.signature != 0x04034b50) { if (header.signature != 0x04034b50) {
// ZIP magic: 'PK' // ZIP magic: 'PK'
return false; return false;
} }
// Read the entry file name // Read the entry file name
if (header.file_name_length == sizeof(MANIFEST) - 1) { if (header.file_name_length == sizeof(MANIFEST) - 1) {
char fileName[sizeof(MANIFEST)]; char fileName[sizeof(MANIFEST)];
ksu_kernel_read_compat(fp, fileName, ksu_kernel_read_compat(fp, fileName,
header.file_name_length, &pos); header.file_name_length, &pos);
fileName[header.file_name_length] = '\0'; fileName[header.file_name_length] = '\0';
// Check if the entry matches META-INF/MANIFEST.MF // Check if the entry matches META-INF/MANIFEST.MF
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) == if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) ==
0) { 0) {
return true; return true;
} }
} else { } else {
// Skip the entry file name // Skip the entry file name
pos += header.file_name_length; pos += header.file_name_length;
} }
// Skip to the next entry // Skip to the next entry
pos += header.extra_field_length + header.compressed_size; pos += header.extra_field_length + header.compressed_size;
} }
return false; return false;
} }
static __always_inline bool check_v2_signature(char *path, bool check_multi_manager, int *signature_index) static __always_inline bool check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
{ {
unsigned char buffer[0x11] = { 0 }; unsigned char buffer[0x11] = { 0 };
u32 size4; u32 size4;
u64 size8, size_of_block; u64 size8, size_of_block;
loff_t pos; loff_t pos;
bool v2_signing_valid = false; bool v2_signing_valid = false;
int v2_signing_blocks = 0; int v2_signing_blocks = 0;
bool v3_signing_exist = false; bool v3_signing_exist = false;
bool v3_1_signing_exist = false; bool v3_1_signing_exist = false;
int matched_index = -1; int matched_index = -1;
int i; int i;
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0); struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
pr_err("open %s error.\n", path); pr_err("open %s error.\n", path);
return false; return false;
} }
// If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip // If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) { if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
filp_close(fp, 0); filp_close(fp, 0);
return 0; return 0;
} }
// disable inotify for this file // disable inotify for this file
fp->f_mode |= FMODE_NONOTIFY; fp->f_mode |= FMODE_NONOTIFY;
// https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD) // https://en.wikipedia.org/wiki/Zip_(file_format)#End_of_central_directory_record_(EOCD)
for (i = 0;; ++i) { for (i = 0;; ++i) {
unsigned short n; unsigned short n;
pos = generic_file_llseek(fp, -i - 2, SEEK_END); pos = generic_file_llseek(fp, -i - 2, SEEK_END);
ksu_kernel_read_compat(fp, &n, 2, &pos); ksu_kernel_read_compat(fp, &n, 2, &pos);
if (n == i) { if (n == i) {
pos -= 22; pos -= 22;
ksu_kernel_read_compat(fp, &size4, 4, &pos); ksu_kernel_read_compat(fp, &size4, 4, &pos);
if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) { if ((size4 ^ 0xcafebabeu) == 0xccfbf1eeu) {
break; break;
} }
} }
if (i == 0xffff) { if (i == 0xffff) {
pr_info("error: cannot find eocd\n"); pr_info("error: cannot find eocd\n");
goto clean; goto clean;
} }
} }
pos += 12; pos += 12;
// offset // offset
ksu_kernel_read_compat(fp, &size4, 0x4, &pos); ksu_kernel_read_compat(fp, &size4, 0x4, &pos);
pos = size4 - 0x18; pos = size4 - 0x18;
ksu_kernel_read_compat(fp, &size8, 0x8, &pos); ksu_kernel_read_compat(fp, &size8, 0x8, &pos);
ksu_kernel_read_compat(fp, buffer, 0x10, &pos); ksu_kernel_read_compat(fp, buffer, 0x10, &pos);
if (strcmp((char *)buffer, "APK Sig Block 42")) { if (strcmp((char *)buffer, "APK Sig Block 42")) {
goto clean; goto clean;
} }
pos = size4 - (size8 + 0x8); pos = size4 - (size8 + 0x8);
ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos); ksu_kernel_read_compat(fp, &size_of_block, 0x8, &pos);
if (size_of_block != size8) { if (size_of_block != size8) {
goto clean; goto clean;
} }
int loop_count = 0; int loop_count = 0;
while (loop_count++ < 10) { while (loop_count++ < 10) {
uint32_t id; uint32_t id;
uint32_t offset; uint32_t offset;
ksu_kernel_read_compat(fp, &size8, 0x8, ksu_kernel_read_compat(fp, &size8, 0x8,
&pos); // sequence length &pos); // sequence length
if (size8 == size_of_block) { if (size8 == size_of_block) {
break; break;
} }
ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id
offset = 4; offset = 4;
if (id == 0x7109871au) { if (id == 0x7109871au) {
v2_signing_blocks++; v2_signing_blocks++;
bool result = check_block(fp, &size4, &pos, &offset, &matched_index); bool result = check_block(fp, &size4, &pos, &offset, &matched_index);
if (result) { if (result) {
v2_signing_valid = true; v2_signing_valid = true;
} }
} else if (id == 0xf05368c0u) { } else if (id == 0xf05368c0u) {
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73 // http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
v3_signing_exist = true; v3_signing_exist = true;
} else if (id == 0x1b93ad61u) { } else if (id == 0x1b93ad61u) {
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74 // http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74
v3_1_signing_exist = true; v3_1_signing_exist = true;
} else { } else {
#ifdef CONFIG_KSU_DEBUG #ifdef CONFIG_KSU_DEBUG
pr_info("Unknown id: 0x%08x\n", id); pr_info("Unknown id: 0x%08x\n", id);
#endif #endif
} }
pos += (size8 - offset); pos += (size8 - offset);
} }
if (v2_signing_blocks != 1) { if (v2_signing_blocks != 1) {
#ifdef CONFIG_KSU_DEBUG #ifdef CONFIG_KSU_DEBUG
pr_err("Unexpected v2 signature count: %d\n", pr_err("Unexpected v2 signature count: %d\n",
v2_signing_blocks); v2_signing_blocks);
#endif #endif
v2_signing_valid = false; v2_signing_valid = false;
} }
if (v2_signing_valid) { if (v2_signing_valid) {
int has_v1_signing = has_v1_signature_file(fp); int has_v1_signing = has_v1_signature_file(fp);
if (has_v1_signing) { if (has_v1_signing) {
pr_err("Unexpected v1 signature scheme found!\n"); pr_err("Unexpected v1 signature scheme found!\n");
filp_close(fp, 0); filp_close(fp, 0);
return false; return false;
} }
} }
clean: clean:
filp_close(fp, 0); filp_close(fp, 0);
if (v3_signing_exist || v3_1_signing_exist) { if (v3_signing_exist || v3_1_signing_exist) {
#ifdef CONFIG_KSU_DEBUG #ifdef CONFIG_KSU_DEBUG
pr_err("Unexpected v3 signature scheme found!\n"); pr_err("Unexpected v3 signature scheme found!\n");
#endif #endif
return false; return false;
} }
if (v2_signing_valid) { if (v2_signing_valid) {
if (signature_index) { if (signature_index) {
*signature_index = matched_index; *signature_index = matched_index;
} }
if (check_multi_manager) { if (check_multi_manager) {
// 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign // 0: ShirkNeko/SukiSU, DYNAMIC_SIGN_INDEX : Dynamic Sign
if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) { if (matched_index == 0 || matched_index == DYNAMIC_SIGN_INDEX) {
pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index); pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index);
return true; return true;
} }
return false; return false;
} else { } else {
// Common manager check: any valid signature will do // Common manager check: any valid signature will do
return true; return true;
} }
} }
return false; return false;
} }
#ifdef CONFIG_KSU_DEBUG #ifdef CONFIG_KSU_DEBUG
@@ -401,28 +401,28 @@ int ksu_debug_manager_uid = -1;
static int set_expected_size(const char *val, const struct kernel_param *kp) static int set_expected_size(const char *val, const struct kernel_param *kp)
{ {
int rv = param_set_uint(val, kp); int rv = param_set_uint(val, kp);
ksu_set_manager_uid(ksu_debug_manager_uid); ksu_set_manager_uid(ksu_debug_manager_uid);
pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid); pr_info("ksu_manager_uid set to %d\n", ksu_debug_manager_uid);
return rv; return rv;
} }
static struct kernel_param_ops expected_size_ops = { static struct kernel_param_ops expected_size_ops = {
.set = set_expected_size, .set = set_expected_size,
.get = param_get_uint, .get = param_get_uint,
}; };
module_param_cb(ksu_debug_manager_uid, &expected_size_ops, module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
&ksu_debug_manager_uid, S_IRUSR | S_IWUSR); &ksu_debug_manager_uid, S_IRUSR | S_IWUSR);
#endif #endif
bool is_manager_apk(char *path) bool is_manager_apk(char *path)
{ {
return check_v2_signature(path, false, NULL); return check_v2_signature(path, false, NULL);
} }
bool is_dynamic_manager_apk(char *path, int *signature_index) bool is_dynamic_manager_apk(char *path, int *signature_index)
{ {
return check_v2_signature(path, true, signature_index); return check_v2_signature(path, true, signature_index);
} }

View File

@@ -29,9 +29,9 @@
#include "sulog.h" #include "sulog.h"
#if LINUX_VERSION_CODE >= KERNEL_VERSION (6, 7, 0) #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 #else
static struct group_info root_groups = { .usage = ATOMIC_INIT(2) }; static struct group_info root_groups = { .usage = ATOMIC_INIT(2) };
#endif #endif
static void setup_groups(struct root_profile *profile, struct cred *cred) static void setup_groups(struct root_profile *profile, struct cred *cred)
@@ -86,7 +86,7 @@ void disable_seccomp(struct task_struct *tsk)
assert_spin_locked(&tsk->sighand->siglock); assert_spin_locked(&tsk->sighand->siglock);
// disable seccomp // disable seccomp
#if defined(CONFIG_GENERIC_ENTRY) && \ #if defined(CONFIG_GENERIC_ENTRY) && \
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
clear_syscall_work(SECCOMP); clear_syscall_work(SECCOMP);
#else #else
@@ -101,13 +101,13 @@ void disable_seccomp(struct task_struct *tsk)
atomic_set(&tsk->seccomp.filter_count, 0); atomic_set(&tsk->seccomp.filter_count, 0);
#endif #endif
// some old kernel backport seccomp_filter_release.. // some old kernel backport seccomp_filter_release..
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) && \ #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) && \
defined(KSU_OPTIONAL_SECCOMP_FILTER_RELEASE) defined(KSU_OPTIONAL_SECCOMP_FILTER_RELEASE)
seccomp_filter_release(tsk); seccomp_filter_release(tsk);
#else #else
// never, ever call seccomp_filter_release on 6.10+ (no effect) // never, ever call seccomp_filter_release on 6.10+ (no effect)
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) && \ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) && \
LINUX_VERSION_CODE < KERNEL_VERSION(6, 10, 0)) LINUX_VERSION_CODE < KERNEL_VERSION(6, 10, 0))
seccomp_filter_release(tsk); seccomp_filter_release(tsk);
#else #else
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0) #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 9, 0)
@@ -151,7 +151,7 @@ void escape_with_root_profile(void)
cred->securebits = 0; cred->securebits = 0;
BUILD_BUG_ON(sizeof(profile->capabilities.effective) != BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
sizeof(kernel_cap_t)); sizeof(kernel_cap_t));
// setup capabilities // setup capabilities
// we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process // we need CAP_DAC_READ_SEARCH becuase `/data/adb/ksud` is not accessible for non root process
@@ -159,11 +159,11 @@ void escape_with_root_profile(void)
u64 cap_for_ksud = u64 cap_for_ksud =
profile->capabilities.effective | CAP_DAC_READ_SEARCH; profile->capabilities.effective | CAP_DAC_READ_SEARCH;
memcpy(&cred->cap_effective, &cap_for_ksud, memcpy(&cred->cap_effective, &cap_for_ksud,
sizeof(cred->cap_effective)); sizeof(cred->cap_effective));
memcpy(&cred->cap_permitted, &profile->capabilities.effective, memcpy(&cred->cap_permitted, &profile->capabilities.effective,
sizeof(cred->cap_permitted)); sizeof(cred->cap_permitted));
memcpy(&cred->cap_bset, &profile->capabilities.effective, memcpy(&cred->cap_bset, &profile->capabilities.effective,
sizeof(cred->cap_bset)); sizeof(cred->cap_bset));
setup_groups(profile, cred); setup_groups(profile, cred);
@@ -177,7 +177,7 @@ void escape_with_root_profile(void)
setup_selinux(profile->selinux_domain); setup_selinux(profile->selinux_domain);
#if __SULOG_GATE #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");
#endif #endif
#ifndef CONFIG_KSU_SUSFS #ifndef CONFIG_KSU_SUSFS
@@ -193,149 +193,149 @@ void escape_with_root_profile(void)
#include "ksud.h" #include "ksud.h"
#ifndef DEVPTS_SUPER_MAGIC #ifndef DEVPTS_SUPER_MAGIC
#define DEVPTS_SUPER_MAGIC 0x1cd1 #define DEVPTS_SUPER_MAGIC 0x1cd1
#endif #endif
static int __manual_su_handle_devpts(struct inode *inode) static int __manual_su_handle_devpts(struct inode *inode)
{ {
if (!current->mm) { if (!current->mm) {
return 0; return 0;
} }
uid_t uid = current_uid().val; uid_t uid = current_uid().val;
if (uid % 100000 < 10000) { if (uid % 100000 < 10000) {
// not untrusted_app, ignore it // not untrusted_app, ignore it
return 0; return 0;
} }
if (likely(!ksu_is_allow_uid_for_current(uid))) if (likely(!ksu_is_allow_uid_for_current(uid)))
return 0; return 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || defined(KSU_OPTIONAL_SELINUX_INODE) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || defined(KSU_OPTIONAL_SELINUX_INODE)
struct inode_security_struct *sec = selinux_inode(inode); struct inode_security_struct *sec = selinux_inode(inode);
#else #else
struct inode_security_struct *sec = struct inode_security_struct *sec =
(struct inode_security_struct *)inode->i_security; (struct inode_security_struct *)inode->i_security;
#endif #endif
if (ksu_file_sid && sec) if (ksu_file_sid && sec)
sec->sid = ksu_file_sid; sec->sid = ksu_file_sid;
return 0; return 0;
} }
static void disable_seccomp_for_task(struct task_struct *tsk) static void disable_seccomp_for_task(struct task_struct *tsk)
{ {
assert_spin_locked(&tsk->sighand->siglock); assert_spin_locked(&tsk->sighand->siglock);
#ifdef CONFIG_SECCOMP #ifdef CONFIG_SECCOMP
if (tsk->seccomp.mode == SECCOMP_MODE_DISABLED && !tsk->seccomp.filter) if (tsk->seccomp.mode == SECCOMP_MODE_DISABLED && !tsk->seccomp.filter)
return; return;
#endif #endif
clear_tsk_thread_flag(tsk, TIF_SECCOMP); clear_tsk_thread_flag(tsk, TIF_SECCOMP);
#ifdef CONFIG_SECCOMP #ifdef CONFIG_SECCOMP
tsk->seccomp.mode = SECCOMP_MODE_DISABLED; tsk->seccomp.mode = SECCOMP_MODE_DISABLED;
if (tsk->seccomp.filter) { if (tsk->seccomp.filter) {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
seccomp_filter_release(tsk); seccomp_filter_release(tsk);
#else #else
put_seccomp_filter(tsk); put_seccomp_filter(tsk);
tsk->seccomp.filter = NULL; tsk->seccomp.filter = NULL;
#endif #endif
} }
#endif #endif
} }
void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid) void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid)
{ {
struct cred *newcreds; struct cred *newcreds;
struct task_struct *target_task; 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 // Find target task by PID
rcu_read_lock(); rcu_read_lock();
target_task = pid_task(find_vpid(target_pid), PIDTYPE_PID); target_task = pid_task(find_vpid(target_pid), PIDTYPE_PID);
if (!target_task) { if (!target_task) {
rcu_read_unlock(); rcu_read_unlock();
pr_err("cmd_su: target task not found for PID: %d\n", target_pid); pr_err("cmd_su: target task not found for PID: %d\n", target_pid);
#if __SULOG_GATE #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 #endif
return; return;
} }
get_task_struct(target_task); get_task_struct(target_task);
rcu_read_unlock(); rcu_read_unlock();
if (task_uid(target_task).val == 0) { if (task_uid(target_task).val == 0) {
pr_warn("cmd_su: target task is already root, PID: %d\n", target_pid); pr_warn("cmd_su: target task is already root, PID: %d\n", target_pid);
put_task_struct(target_task); put_task_struct(target_task);
return; return;
} }
newcreds = prepare_kernel_cred(target_task); newcreds = prepare_kernel_cred(target_task);
if (newcreds == NULL) { if (newcreds == NULL) {
pr_err("cmd_su: failed to allocate new cred for PID: %d\n", target_pid); pr_err("cmd_su: failed to allocate new cred for PID: %d\n", target_pid);
#if __SULOG_GATE #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 #endif
put_task_struct(target_task); put_task_struct(target_task);
return; 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->uid.val = profile->uid;
newcreds->suid.val = profile->uid; newcreds->suid.val = profile->uid;
newcreds->euid.val = profile->uid; newcreds->euid.val = profile->uid;
newcreds->fsuid.val = profile->uid; newcreds->fsuid.val = profile->uid;
newcreds->gid.val = profile->gid; newcreds->gid.val = profile->gid;
newcreds->fsgid.val = profile->gid; newcreds->fsgid.val = profile->gid;
newcreds->sgid.val = profile->gid; newcreds->sgid.val = profile->gid;
newcreds->egid.val = profile->gid; newcreds->egid.val = profile->gid;
newcreds->securebits = 0; newcreds->securebits = 0;
u64 cap_for_cmd_su = profile->capabilities.effective | CAP_DAC_READ_SEARCH | CAP_SETUID | CAP_SETGID; 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_effective, &cap_for_cmd_su, sizeof(newcreds->cap_effective));
memcpy(&newcreds->cap_permitted, &profile->capabilities.effective, sizeof(newcreds->cap_permitted)); memcpy(&newcreds->cap_permitted, &profile->capabilities.effective, sizeof(newcreds->cap_permitted));
memcpy(&newcreds->cap_bset, &profile->capabilities.effective, sizeof(newcreds->cap_bset)); memcpy(&newcreds->cap_bset, &profile->capabilities.effective, sizeof(newcreds->cap_bset));
setup_groups(profile, newcreds); setup_groups(profile, newcreds);
task_lock(target_task); 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->real_cred, newcreds);
rcu_assign_pointer(target_task->cred, get_cred(newcreds)); rcu_assign_pointer(target_task->cred, get_cred(newcreds));
task_unlock(target_task); task_unlock(target_task);
if (target_task->sighand) { if (target_task->sighand) {
spin_lock_irqsave(&target_task->sighand->siglock, flags); spin_lock_irqsave(&target_task->sighand->siglock, flags);
disable_seccomp_for_task(target_task); disable_seccomp_for_task(target_task);
spin_unlock_irqrestore(&target_task->sighand->siglock, flags); spin_unlock_irqrestore(&target_task->sighand->siglock, flags);
} }
setup_selinux(profile->selinux_domain); setup_selinux(profile->selinux_domain);
put_cred(old_creds); put_cred(old_creds);
wake_up_process(target_task); wake_up_process(target_task);
if (target_task->signal->tty) { if (target_task->signal->tty) {
struct inode *inode = target_task->signal->tty->driver_data; struct inode *inode = target_task->signal->tty->driver_data;
if (inode && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) { if (inode && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) {
__manual_su_handle_devpts(inode); __manual_su_handle_devpts(inode);
} }
} }
put_task_struct(target_task); put_task_struct(target_task);
#if __SULOG_GATE #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 #endif
#ifndef CONFIG_KSU_SUSFS #ifndef CONFIG_KSU_SUSFS
struct task_struct *p = current; struct task_struct *p = current;
struct task_struct *t; struct task_struct *t;
for_each_thread (p, t) { for_each_thread (p, t) {
ksu_set_task_tracepoint_flag(t); ksu_set_task_tracepoint_flag(t);
} }
#endif #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 #endif

View File

@@ -24,9 +24,9 @@
// Dynamic sign configuration // Dynamic sign configuration
static struct dynamic_manager_config dynamic_manager = { static struct dynamic_manager_config dynamic_manager = {
.size = 0x300, .size = 0x300,
.hash = "0000000000000000000000000000000000000000000000000000000000000000", .hash = "0000000000000000000000000000000000000000000000000000000000000000",
.is_set = 0 .is_set = 0
}; };
// Multi-manager state // Multi-manager state
@@ -41,465 +41,465 @@ static struct work_struct clear_dynamic_manager_work;
bool ksu_is_dynamic_manager_enabled(void) bool ksu_is_dynamic_manager_enabled(void)
{ {
unsigned long flags; unsigned long flags;
bool enabled; bool enabled;
spin_lock_irqsave(&dynamic_manager_lock, flags); spin_lock_irqsave(&dynamic_manager_lock, flags);
enabled = dynamic_manager.is_set; enabled = dynamic_manager.is_set;
spin_unlock_irqrestore(&dynamic_manager_lock, flags); spin_unlock_irqrestore(&dynamic_manager_lock, flags);
return enabled; return enabled;
} }
void ksu_add_manager(uid_t uid, int signature_index) void ksu_add_manager(uid_t uid, int signature_index)
{ {
unsigned long flags; unsigned long flags;
int i; int i;
if (!ksu_is_dynamic_manager_enabled()) { if (!ksu_is_dynamic_manager_enabled()) {
pr_info("Dynamic sign not enabled, skipping multi-manager add\n"); pr_info("Dynamic sign not enabled, skipping multi-manager add\n");
return; return;
} }
spin_lock_irqsave(&managers_lock, flags); spin_lock_irqsave(&managers_lock, flags);
// Check if manager already exists and update // Check if manager already exists and update
for (i = 0; i < MAX_MANAGERS; i++) { for (i = 0; i < MAX_MANAGERS; i++) {
if (active_managers[i].is_active && active_managers[i].uid == uid) { if (active_managers[i].is_active && active_managers[i].uid == uid) {
active_managers[i].signature_index = signature_index; active_managers[i].signature_index = signature_index;
spin_unlock_irqrestore(&managers_lock, flags); spin_unlock_irqrestore(&managers_lock, flags);
pr_info("Updated manager uid=%d, signature_index=%d\n", uid, signature_index); pr_info("Updated manager uid=%d, signature_index=%d\n", uid, signature_index);
return; return;
} }
} }
// Find free slot for new manager // Find free slot for new manager
for (i = 0; i < MAX_MANAGERS; i++) { for (i = 0; i < MAX_MANAGERS; i++) {
if (!active_managers[i].is_active) { if (!active_managers[i].is_active) {
active_managers[i].uid = uid; active_managers[i].uid = uid;
active_managers[i].signature_index = signature_index; active_managers[i].signature_index = signature_index;
active_managers[i].is_active = true; active_managers[i].is_active = true;
spin_unlock_irqrestore(&managers_lock, flags); spin_unlock_irqrestore(&managers_lock, flags);
pr_info("Added manager uid=%d, signature_index=%d\n", uid, signature_index); pr_info("Added manager uid=%d, signature_index=%d\n", uid, signature_index);
return; return;
} }
} }
spin_unlock_irqrestore(&managers_lock, flags); spin_unlock_irqrestore(&managers_lock, flags);
pr_warn("Failed to add manager, no free slots\n"); pr_warn("Failed to add manager, no free slots\n");
} }
void ksu_remove_manager(uid_t uid) void ksu_remove_manager(uid_t uid)
{ {
unsigned long flags; unsigned long flags;
int i; int i;
if (!ksu_is_dynamic_manager_enabled()) { if (!ksu_is_dynamic_manager_enabled()) {
return; return;
} }
spin_lock_irqsave(&managers_lock, flags); spin_lock_irqsave(&managers_lock, flags);
for (i = 0; i < MAX_MANAGERS; i++) { for (i = 0; i < MAX_MANAGERS; i++) {
if (active_managers[i].is_active && active_managers[i].uid == uid) { if (active_managers[i].is_active && active_managers[i].uid == uid) {
active_managers[i].is_active = false; active_managers[i].is_active = false;
pr_info("Removed manager uid=%d\n", uid); pr_info("Removed manager uid=%d\n", uid);
break; break;
} }
} }
spin_unlock_irqrestore(&managers_lock, flags); spin_unlock_irqrestore(&managers_lock, flags);
} }
bool ksu_is_any_manager(uid_t uid) bool ksu_is_any_manager(uid_t uid)
{ {
unsigned long flags; unsigned long flags;
bool is_manager = false; bool is_manager = false;
int i; int i;
if (!ksu_is_dynamic_manager_enabled()) { if (!ksu_is_dynamic_manager_enabled()) {
return false; return false;
} }
spin_lock_irqsave(&managers_lock, flags); spin_lock_irqsave(&managers_lock, flags);
for (i = 0; i < MAX_MANAGERS; i++) { for (i = 0; i < MAX_MANAGERS; i++) {
if (active_managers[i].is_active && active_managers[i].uid == uid) { if (active_managers[i].is_active && active_managers[i].uid == uid) {
is_manager = true; is_manager = true;
break; break;
} }
} }
spin_unlock_irqrestore(&managers_lock, flags); spin_unlock_irqrestore(&managers_lock, flags);
return is_manager; return is_manager;
} }
int ksu_get_manager_signature_index(uid_t uid) int ksu_get_manager_signature_index(uid_t uid)
{ {
unsigned long flags; unsigned long flags;
int signature_index = -1; int signature_index = -1;
int i; int i;
// Check traditional manager first // Check traditional manager first
if (ksu_manager_uid != KSU_INVALID_UID && uid == ksu_manager_uid) { if (ksu_manager_uid != KSU_INVALID_UID && uid == ksu_manager_uid) {
return DYNAMIC_SIGN_INDEX; return DYNAMIC_SIGN_INDEX;
} }
if (!ksu_is_dynamic_manager_enabled()) { if (!ksu_is_dynamic_manager_enabled()) {
return -1; return -1;
} }
spin_lock_irqsave(&managers_lock, flags); spin_lock_irqsave(&managers_lock, flags);
for (i = 0; i < MAX_MANAGERS; i++) { for (i = 0; i < MAX_MANAGERS; i++) {
if (active_managers[i].is_active && active_managers[i].uid == uid) { if (active_managers[i].is_active && active_managers[i].uid == uid) {
signature_index = active_managers[i].signature_index; signature_index = active_managers[i].signature_index;
break; break;
} }
} }
spin_unlock_irqrestore(&managers_lock, flags); spin_unlock_irqrestore(&managers_lock, flags);
return signature_index; return signature_index;
} }
static void clear_dynamic_manager(void) static void clear_dynamic_manager(void)
{ {
unsigned long flags; unsigned long flags;
int i; int i;
spin_lock_irqsave(&managers_lock, flags); spin_lock_irqsave(&managers_lock, flags);
for (i = 0; i < MAX_MANAGERS; i++) { for (i = 0; i < MAX_MANAGERS; i++) {
if (active_managers[i].is_active) { if (active_managers[i].is_active) {
pr_info("Clearing dynamic manager uid=%d (signature_index=%d) for rescan\n", pr_info("Clearing dynamic manager uid=%d (signature_index=%d) for rescan\n",
active_managers[i].uid, active_managers[i].signature_index); active_managers[i].uid, active_managers[i].signature_index);
active_managers[i].is_active = false; active_managers[i].is_active = false;
} }
} }
spin_unlock_irqrestore(&managers_lock, flags); spin_unlock_irqrestore(&managers_lock, flags);
} }
int ksu_get_active_managers(struct manager_list_info *info) int ksu_get_active_managers(struct manager_list_info *info)
{ {
unsigned long flags; unsigned long flags;
int i, count = 0; int i, count = 0;
if (!info) { if (!info) {
return -EINVAL; return -EINVAL;
} }
// Add traditional manager first // Add traditional manager first
if (ksu_manager_uid != KSU_INVALID_UID && count < 2) { if (ksu_manager_uid != KSU_INVALID_UID && count < 2) {
info->managers[count].uid = ksu_manager_uid; info->managers[count].uid = ksu_manager_uid;
info->managers[count].signature_index = 0; info->managers[count].signature_index = 0;
count++; count++;
} }
// Add dynamic managers // Add dynamic managers
if (ksu_is_dynamic_manager_enabled()) { if (ksu_is_dynamic_manager_enabled()) {
spin_lock_irqsave(&managers_lock, flags); spin_lock_irqsave(&managers_lock, flags);
for (i = 0; i < MAX_MANAGERS && count < 2; i++) { for (i = 0; i < MAX_MANAGERS && count < 2; i++) {
if (active_managers[i].is_active) { if (active_managers[i].is_active) {
info->managers[count].uid = active_managers[i].uid; info->managers[count].uid = active_managers[i].uid;
info->managers[count].signature_index = active_managers[i].signature_index; info->managers[count].signature_index = active_managers[i].signature_index;
count++; count++;
} }
} }
spin_unlock_irqrestore(&managers_lock, flags); spin_unlock_irqrestore(&managers_lock, flags);
} }
info->count = count; info->count = count;
return 0; return 0;
} }
static void do_save_dynamic_manager(struct work_struct *work) static void do_save_dynamic_manager(struct work_struct *work)
{ {
u32 magic = DYNAMIC_MANAGER_FILE_MAGIC; u32 magic = DYNAMIC_MANAGER_FILE_MAGIC;
u32 version = DYNAMIC_MANAGER_FILE_VERSION; u32 version = DYNAMIC_MANAGER_FILE_VERSION;
struct dynamic_manager_config config_to_save; struct dynamic_manager_config config_to_save;
loff_t off = 0; loff_t off = 0;
unsigned long flags; unsigned long flags;
struct file *fp; struct file *fp;
spin_lock_irqsave(&dynamic_manager_lock, flags); spin_lock_irqsave(&dynamic_manager_lock, flags);
config_to_save = dynamic_manager; config_to_save = dynamic_manager;
spin_unlock_irqrestore(&dynamic_manager_lock, flags); spin_unlock_irqrestore(&dynamic_manager_lock, flags);
if (!config_to_save.is_set) { if (!config_to_save.is_set) {
pr_info("Dynamic sign config not set, skipping save\n"); pr_info("Dynamic sign config not set, skipping save\n");
return; return;
} }
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644); fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
pr_err("save_dynamic_manager create file failed: %ld\n", PTR_ERR(fp)); pr_err("save_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
return; return;
} }
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) { if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
pr_err("save_dynamic_manager write magic failed.\n"); pr_err("save_dynamic_manager write magic failed.\n");
goto exit; goto exit;
} }
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != sizeof(version)) { if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
pr_err("save_dynamic_manager write version failed.\n"); pr_err("save_dynamic_manager write version failed.\n");
goto exit; goto exit;
} }
if (ksu_kernel_write_compat(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) { if (ksu_kernel_write_compat(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) {
pr_err("save_dynamic_manager write config failed.\n"); pr_err("save_dynamic_manager write config failed.\n");
goto exit; goto exit;
} }
pr_info("Dynamic sign config saved successfully\n"); pr_info("Dynamic sign config saved successfully\n");
exit: exit:
filp_close(fp, 0); filp_close(fp, 0);
} }
static void do_load_dynamic_manager(struct work_struct *work) static void do_load_dynamic_manager(struct work_struct *work)
{ {
loff_t off = 0; loff_t off = 0;
ssize_t ret = 0; ssize_t ret = 0;
struct file *fp = NULL; struct file *fp = NULL;
u32 magic; u32 magic;
u32 version; u32 version;
struct dynamic_manager_config loaded_config; struct dynamic_manager_config loaded_config;
unsigned long flags; unsigned long flags;
int i; int i;
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_RDONLY, 0); fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_RDONLY, 0);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
if (PTR_ERR(fp) == -ENOENT) { if (PTR_ERR(fp) == -ENOENT) {
pr_info("No saved dynamic manager config found\n"); pr_info("No saved dynamic manager config found\n");
} else { } else {
pr_err("load_dynamic_manager open file failed: %ld\n", PTR_ERR(fp)); pr_err("load_dynamic_manager open file failed: %ld\n", PTR_ERR(fp));
} }
return; return;
} }
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) || if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
magic != DYNAMIC_MANAGER_FILE_MAGIC) { magic != DYNAMIC_MANAGER_FILE_MAGIC) {
pr_err("dynamic manager file invalid magic: %x!\n", magic); pr_err("dynamic manager file invalid magic: %x!\n", magic);
goto exit; goto exit;
} }
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != sizeof(version)) { if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
pr_err("dynamic manager read version failed\n"); pr_err("dynamic manager read version failed\n");
goto exit; goto exit;
} }
pr_info("dynamic manager file version: %d\n", version); pr_info("dynamic manager file version: %d\n", version);
ret = ksu_kernel_read_compat(fp, &loaded_config, sizeof(loaded_config), &off); ret = ksu_kernel_read_compat(fp, &loaded_config, sizeof(loaded_config), &off);
if (ret <= 0) { if (ret <= 0) {
pr_info("load_dynamic_manager read err: %zd\n", ret); pr_info("load_dynamic_manager read err: %zd\n", ret);
goto exit; goto exit;
} }
if (ret != sizeof(loaded_config)) { if (ret != sizeof(loaded_config)) {
pr_err("load_dynamic_manager read incomplete config: %zd/%zu\n", ret, sizeof(loaded_config)); pr_err("load_dynamic_manager read incomplete config: %zd/%zu\n", ret, sizeof(loaded_config));
goto exit; goto exit;
} }
if (loaded_config.size < 0x100 || loaded_config.size > 0x1000) { if (loaded_config.size < 0x100 || loaded_config.size > 0x1000) {
pr_err("Invalid saved config size: 0x%x\n", loaded_config.size); pr_err("Invalid saved config size: 0x%x\n", loaded_config.size);
goto exit; goto exit;
} }
if (strlen(loaded_config.hash) != 64) { if (strlen(loaded_config.hash) != 64) {
pr_err("Invalid saved config hash length: %zu\n", strlen(loaded_config.hash)); pr_err("Invalid saved config hash length: %zu\n", strlen(loaded_config.hash));
goto exit; goto exit;
} }
// Validate hash format // Validate hash format
for (i = 0; i < 64; i++) { for (i = 0; i < 64; i++) {
char c = loaded_config.hash[i]; char c = loaded_config.hash[i];
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) { if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
pr_err("Invalid saved config hash character at position %d: %c\n", i, c); pr_err("Invalid saved config hash character at position %d: %c\n", i, c);
goto exit; goto exit;
} }
} }
spin_lock_irqsave(&dynamic_manager_lock, flags); spin_lock_irqsave(&dynamic_manager_lock, flags);
dynamic_manager = loaded_config; dynamic_manager = loaded_config;
spin_unlock_irqrestore(&dynamic_manager_lock, flags); spin_unlock_irqrestore(&dynamic_manager_lock, flags);
pr_info("Dynamic sign config loaded: size=0x%x, hash=%.16s...\n", pr_info("Dynamic sign config loaded: size=0x%x, hash=%.16s...\n",
loaded_config.size, loaded_config.hash); loaded_config.size, loaded_config.hash);
exit: exit:
filp_close(fp, 0); filp_close(fp, 0);
} }
static bool persistent_dynamic_manager(void) static bool persistent_dynamic_manager(void)
{ {
return ksu_queue_work(&save_dynamic_manager_work); return ksu_queue_work(&save_dynamic_manager_work);
} }
static void do_clear_dynamic_manager(struct work_struct *work) static void do_clear_dynamic_manager(struct work_struct *work)
{ {
loff_t off = 0; loff_t off = 0;
struct file *fp; struct file *fp;
char zero_buffer[512]; char zero_buffer[512];
memset(zero_buffer, 0, sizeof(zero_buffer)); memset(zero_buffer, 0, sizeof(zero_buffer));
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644); fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
pr_err("clear_dynamic_manager create file failed: %ld\n", PTR_ERR(fp)); pr_err("clear_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
return; return;
} }
// Write null bytes to overwrite the file content // Write null bytes to overwrite the file content
if (ksu_kernel_write_compat(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) { if (ksu_kernel_write_compat(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) {
pr_err("clear_dynamic_manager write null bytes failed.\n"); pr_err("clear_dynamic_manager write null bytes failed.\n");
} else { } else {
pr_info("Dynamic sign config file cleared successfully\n"); pr_info("Dynamic sign config file cleared successfully\n");
} }
filp_close(fp, 0); filp_close(fp, 0);
} }
static bool clear_dynamic_manager_file(void) static bool clear_dynamic_manager_file(void)
{ {
return ksu_queue_work(&clear_dynamic_manager_work); return ksu_queue_work(&clear_dynamic_manager_work);
} }
int ksu_handle_dynamic_manager(struct dynamic_manager_user_config *config) int ksu_handle_dynamic_manager(struct dynamic_manager_user_config *config)
{ {
unsigned long flags; unsigned long flags;
int ret = 0; int ret = 0;
int i; int i;
if (!config) { if (!config) {
return -EINVAL; return -EINVAL;
} }
switch (config->operation) { switch (config->operation) {
case DYNAMIC_MANAGER_OP_SET: case DYNAMIC_MANAGER_OP_SET:
if (config->size < 0x100 || config->size > 0x1000) { if (config->size < 0x100 || config->size > 0x1000) {
pr_err("invalid size: 0x%x\n", config->size); pr_err("invalid size: 0x%x\n", config->size);
return -EINVAL; return -EINVAL;
} }
if (strlen(config->hash) != 64) { if (strlen(config->hash) != 64) {
pr_err("invalid hash length: %zu\n", strlen(config->hash)); pr_err("invalid hash length: %zu\n", strlen(config->hash));
return -EINVAL; return -EINVAL;
} }
// Validate hash format // Validate hash format
for (i = 0; i < 64; i++) { for (i = 0; i < 64; i++) {
char c = config->hash[i]; char c = config->hash[i];
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) { if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
pr_err("invalid hash character at position %d: %c\n", i, c); pr_err("invalid hash character at position %d: %c\n", i, c);
return -EINVAL; return -EINVAL;
} }
} }
spin_lock_irqsave(&dynamic_manager_lock, flags); spin_lock_irqsave(&dynamic_manager_lock, flags);
dynamic_manager.size = config->size; dynamic_manager.size = config->size;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
strscpy(dynamic_manager.hash, config->hash, sizeof(dynamic_manager.hash)); strscpy(dynamic_manager.hash, config->hash, sizeof(dynamic_manager.hash));
#else #else
strlcpy(dynamic_manager.hash, config->hash, sizeof(dynamic_manager.hash)); strlcpy(dynamic_manager.hash, config->hash, sizeof(dynamic_manager.hash));
#endif #endif
dynamic_manager.is_set = 1; dynamic_manager.is_set = 1;
spin_unlock_irqrestore(&dynamic_manager_lock, flags); spin_unlock_irqrestore(&dynamic_manager_lock, flags);
persistent_dynamic_manager(); persistent_dynamic_manager();
pr_info("dynamic manager updated: size=0x%x, hash=%.16s... (multi-manager enabled)\n", pr_info("dynamic manager updated: size=0x%x, hash=%.16s... (multi-manager enabled)\n",
config->size, config->hash); config->size, config->hash);
break; break;
case DYNAMIC_MANAGER_OP_GET: case DYNAMIC_MANAGER_OP_GET:
spin_lock_irqsave(&dynamic_manager_lock, flags); spin_lock_irqsave(&dynamic_manager_lock, flags);
if (dynamic_manager.is_set) { if (dynamic_manager.is_set) {
config->size = dynamic_manager.size; config->size = dynamic_manager.size;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
strscpy(config->hash, dynamic_manager.hash, sizeof(config->hash)); strscpy(config->hash, dynamic_manager.hash, sizeof(config->hash));
#else #else
strlcpy(config->hash, dynamic_manager.hash, sizeof(config->hash)); strlcpy(config->hash, dynamic_manager.hash, sizeof(config->hash));
#endif #endif
ret = 0; ret = 0;
} else { } else {
ret = -ENODATA; ret = -ENODATA;
} }
spin_unlock_irqrestore(&dynamic_manager_lock, flags); spin_unlock_irqrestore(&dynamic_manager_lock, flags);
break; break;
case DYNAMIC_MANAGER_OP_CLEAR: case DYNAMIC_MANAGER_OP_CLEAR:
spin_lock_irqsave(&dynamic_manager_lock, flags); spin_lock_irqsave(&dynamic_manager_lock, flags);
dynamic_manager.size = 0x300; dynamic_manager.size = 0x300;
strcpy(dynamic_manager.hash, "0000000000000000000000000000000000000000000000000000000000000000"); strcpy(dynamic_manager.hash, "0000000000000000000000000000000000000000000000000000000000000000");
dynamic_manager.is_set = 0; dynamic_manager.is_set = 0;
spin_unlock_irqrestore(&dynamic_manager_lock, flags); spin_unlock_irqrestore(&dynamic_manager_lock, flags);
// Clear only dynamic managers, preserve default manager // Clear only dynamic managers, preserve default manager
clear_dynamic_manager(); clear_dynamic_manager();
// Clear file using the same method as save // Clear file using the same method as save
clear_dynamic_manager_file(); clear_dynamic_manager_file();
pr_info("Dynamic sign config cleared (multi-manager disabled)\n"); pr_info("Dynamic sign config cleared (multi-manager disabled)\n");
break; break;
default: default:
pr_err("Invalid dynamic manager operation: %d\n", config->operation); pr_err("Invalid dynamic manager operation: %d\n", config->operation);
return -EINVAL; return -EINVAL;
} }
return ret; return ret;
} }
bool ksu_load_dynamic_manager(void) bool ksu_load_dynamic_manager(void)
{ {
return ksu_queue_work(&load_dynamic_manager_work); return ksu_queue_work(&load_dynamic_manager_work);
} }
void ksu_dynamic_manager_init(void) void ksu_dynamic_manager_init(void)
{ {
int i; int i;
INIT_WORK(&save_dynamic_manager_work, do_save_dynamic_manager); INIT_WORK(&save_dynamic_manager_work, do_save_dynamic_manager);
INIT_WORK(&load_dynamic_manager_work, do_load_dynamic_manager); INIT_WORK(&load_dynamic_manager_work, do_load_dynamic_manager);
INIT_WORK(&clear_dynamic_manager_work, do_clear_dynamic_manager); INIT_WORK(&clear_dynamic_manager_work, do_clear_dynamic_manager);
// Initialize manager slots // Initialize manager slots
for (i = 0; i < MAX_MANAGERS; i++) { for (i = 0; i < MAX_MANAGERS; i++) {
active_managers[i].is_active = false; active_managers[i].is_active = false;
} }
ksu_load_dynamic_manager(); ksu_load_dynamic_manager();
pr_info("Dynamic sign initialized with conditional multi-manager support\n"); pr_info("Dynamic sign initialized with conditional multi-manager support\n");
} }
void ksu_dynamic_manager_exit(void) void ksu_dynamic_manager_exit(void)
{ {
clear_dynamic_manager(); clear_dynamic_manager();
// Save current config before exit // Save current config before exit
do_save_dynamic_manager(NULL); do_save_dynamic_manager(NULL);
pr_info("Dynamic sign exited with persistent storage\n"); pr_info("Dynamic sign exited with persistent storage\n");
} }
// Get dynamic manager configuration for signature verification // Get dynamic manager configuration for signature verification
bool ksu_get_dynamic_manager_config(unsigned int *size, const char **hash) bool ksu_get_dynamic_manager_config(unsigned int *size, const char **hash)
{ {
unsigned long flags; unsigned long flags;
bool valid = false; bool valid = false;
spin_lock_irqsave(&dynamic_manager_lock, flags); spin_lock_irqsave(&dynamic_manager_lock, flags);
if (dynamic_manager.is_set) { if (dynamic_manager.is_set) {
if (size) *size = dynamic_manager.size; if (size) *size = dynamic_manager.size;
if (hash) *hash = dynamic_manager.hash; if (hash) *hash = dynamic_manager.hash;
valid = true; valid = true;
} }
spin_unlock_irqrestore(&dynamic_manager_lock, flags); spin_unlock_irqrestore(&dynamic_manager_lock, flags);
return valid; return valid;
} }

View File

@@ -10,25 +10,25 @@
#define DYNAMIC_SIGN_INDEX 100 #define DYNAMIC_SIGN_INDEX 100
struct dynamic_sign_key { struct dynamic_sign_key {
unsigned int size; unsigned int size;
const char *hash; const char *hash;
}; };
#define DYNAMIC_SIGN_DEFAULT_CONFIG { \ #define DYNAMIC_SIGN_DEFAULT_CONFIG { \
.size = 0x300, \ .size = 0x300, \
.hash = "0000000000000000000000000000000000000000000000000000000000000000" \ .hash = "0000000000000000000000000000000000000000000000000000000000000000" \
} }
struct dynamic_manager_config { struct dynamic_manager_config {
unsigned int size; unsigned int size;
char hash[65]; char hash[65];
int is_set; int is_set;
}; };
struct manager_info { struct manager_info {
uid_t uid; uid_t uid;
int signature_index; int signature_index;
bool is_active; bool is_active;
}; };
// Dynamic sign operations // Dynamic sign operations

View File

@@ -21,7 +21,7 @@ int ksu_register_feature_handler(const struct ksu_feature_handler *handler)
if (!handler->get_handler && !handler->set_handler) { if (!handler->get_handler && !handler->set_handler) {
pr_err("feature: no handler provided for feature %u\n", pr_err("feature: no handler provided for feature %u\n",
handler->feature_id); handler->feature_id);
return -EINVAL; return -EINVAL;
} }
@@ -104,7 +104,7 @@ int ksu_get_feature(u32 feature_id, u64 *value, bool *supported)
ret = handler->get_handler(value); ret = handler->get_handler(value);
if (ret) { if (ret) {
pr_err("feature: get_handler for %u failed: %d\n", feature_id, pr_err("feature: get_handler for %u failed: %d\n", feature_id,
ret); ret);
} }
out: out:
@@ -141,7 +141,7 @@ int ksu_set_feature(u32 feature_id, u64 value)
ret = handler->set_handler(value); ret = handler->set_handler(value);
if (ret) { if (ret) {
pr_err("feature: set_handler for %u failed: %d\n", feature_id, pr_err("feature: set_handler for %u failed: %d\n", feature_id,
ret); ret);
} }
out: out:

View File

@@ -320,7 +320,7 @@ static int ksu_wrapper_fadvise(struct file *fp, loff_t off1, loff_t off2, int fl
static int ksu_wrapper_release(struct inode *inode, struct file *filp) { static int ksu_wrapper_release(struct inode *inode, struct file *filp) {
ksu_delete_file_wrapper(filp->private_data); ksu_delete_file_wrapper(filp->private_data);
return 0; return 0;
} }
struct ksu_file_wrapper* ksu_create_file_wrapper(struct file* fp) { struct ksu_file_wrapper* ksu_create_file_wrapper(struct file* fp) {

View File

@@ -9,7 +9,7 @@
#include "klog.h" // IWYU pragma: keep #include "klog.h" // IWYU pragma: keep
#include "kernel_compat.h" #include "kernel_compat.h"
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
#include <linux/key.h> #include <linux/key.h>
#include <linux/errno.h> #include <linux/errno.h>
@@ -40,10 +40,10 @@ static int install_session_keyring(struct key *keyring)
struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode) struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
{ {
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
if (init_session_keyring != NULL && !current_cred()->session_keyring && if (init_session_keyring != NULL && !current_cred()->session_keyring &&
(current->flags & PF_WQ_WORKER)) { (current->flags & PF_WQ_WORKER)) {
pr_info("installing init session keyring for older kernel\n"); pr_info("installing init session keyring for older kernel\n");
install_session_keyring(init_session_keyring); install_session_keyring(init_session_keyring);
} }
@@ -52,9 +52,9 @@ struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
} }
ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
loff_t *pos) loff_t *pos)
{ {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \
defined(KSU_OPTIONAL_KERNEL_READ) defined(KSU_OPTIONAL_KERNEL_READ)
return kernel_read(p, buf, count, pos); return kernel_read(p, buf, count, pos);
#else #else
@@ -70,7 +70,7 @@ ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
loff_t *pos) loff_t *pos)
{ {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) || \
defined(KSU_OPTIONAL_KERNEL_WRITE) defined(KSU_OPTIONAL_KERNEL_WRITE)
return kernel_write(p, buf, count, pos); return kernel_write(p, buf, count, pos);
#else #else
@@ -83,7 +83,7 @@ ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count,
#endif #endif
} }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || \ #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) || \
defined(KSU_OPTIONAL_STRNCPY) defined(KSU_OPTIONAL_STRNCPY)
long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
long count) long count)

View File

@@ -13,9 +13,9 @@
* Huawei Hisi Kernel EBITMAP Enable or Disable Flag , * Huawei Hisi Kernel EBITMAP Enable or Disable Flag ,
* From ss/ebitmap.h * From ss/ebitmap.h
*/ */
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0)) && \
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \ (LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)) || \
(LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \ (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0)) && \
(LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)) (LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0))
#ifdef HISI_SELINUX_EBITMAP_RO #ifdef HISI_SELINUX_EBITMAP_RO
#define CONFIG_IS_HW_HISI #define CONFIG_IS_HW_HISI
@@ -36,11 +36,11 @@ extern long ksu_strncpy_from_user_nofault(char *dst,
extern struct file *ksu_filp_open_compat(const char *filename, int flags, extern struct file *ksu_filp_open_compat(const char *filename, int flags,
umode_t mode); umode_t mode);
extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count,
loff_t *pos); loff_t *pos);
extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
size_t count, loff_t *pos); size_t count, loff_t *pos);
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
extern struct key *init_session_keyring; extern struct key *init_session_keyring;
#endif #endif

View File

@@ -35,23 +35,23 @@ bool ksu_kernel_umount_enabled = true;
static int kernel_umount_feature_get(u64 *value) static int kernel_umount_feature_get(u64 *value)
{ {
*value = ksu_kernel_umount_enabled ? 1 : 0; *value = ksu_kernel_umount_enabled ? 1 : 0;
return 0; return 0;
} }
static int kernel_umount_feature_set(u64 value) static int kernel_umount_feature_set(u64 value)
{ {
bool enable = value != 0; bool enable = value != 0;
ksu_kernel_umount_enabled = enable; ksu_kernel_umount_enabled = enable;
pr_info("kernel_umount: set to %d\n", enable); pr_info("kernel_umount: set to %d\n", enable);
return 0; return 0;
} }
static const struct ksu_feature_handler kernel_umount_handler = { static const struct ksu_feature_handler kernel_umount_handler = {
.feature_id = KSU_FEATURE_KERNEL_UMOUNT, .feature_id = KSU_FEATURE_KERNEL_UMOUNT,
.name = "kernel_umount", .name = "kernel_umount",
.get_handler = kernel_umount_feature_get, .get_handler = kernel_umount_feature_get,
.set_handler = kernel_umount_feature_set, .set_handler = kernel_umount_feature_set,
}; };
#ifdef CONFIG_KSU_SUSFS #ifdef CONFIG_KSU_SUSFS
@@ -102,130 +102,130 @@ static void try_umount(const char *mnt, int flags)
void try_umount(const char *mnt, int flags) void try_umount(const char *mnt, int flags)
#endif // #ifndef CONFIG_KSU_SUSFS_TRY_UMOUNT #endif // #ifndef CONFIG_KSU_SUSFS_TRY_UMOUNT
{ {
struct path path; struct path path;
int err = kern_path(mnt, 0, &path); int err = kern_path(mnt, 0, &path);
if (err) { if (err) {
return; return;
} }
if (path.dentry != path.mnt->mnt_root) { if (path.dentry != path.mnt->mnt_root) {
// it is not root mountpoint, maybe umounted by others already. // it is not root mountpoint, maybe umounted by others already.
path_put(&path); path_put(&path);
return; return;
} }
#if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG) #if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG)
if (susfs_is_log_enabled) { if (susfs_is_log_enabled) {
pr_info("susfs: umounting '%s'\n", mnt); pr_info("susfs: umounting '%s'\n", mnt);
} }
#endif // #if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG) #endif // #if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG)
ksu_umount_mnt(&path, flags); ksu_umount_mnt(&path, flags);
} }
#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
void susfs_try_umount_all(void) { void susfs_try_umount_all(void) {
susfs_try_umount(); susfs_try_umount();
} }
#endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT #endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
#if !defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) #if !defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT)
struct umount_tw { struct umount_tw {
struct callback_head cb; struct callback_head cb;
const struct cred *old_cred; const struct cred *old_cred;
}; };
static void umount_tw_func(struct callback_head *cb) static void umount_tw_func(struct callback_head *cb)
{ {
struct umount_tw *tw = container_of(cb, struct umount_tw, cb); struct umount_tw *tw = container_of(cb, struct umount_tw, cb);
const struct cred *saved = NULL; const struct cred *saved = NULL;
if (tw->old_cred) { if (tw->old_cred) {
saved = override_creds(tw->old_cred); saved = override_creds(tw->old_cred);
} }
struct mount_entry *entry; struct mount_entry *entry;
down_read(&mount_list_lock); down_read(&mount_list_lock);
list_for_each_entry(entry, &mount_list, list) { list_for_each_entry(entry, &mount_list, list) {
pr_info("%s: unmounting: %s flags 0x%x\n", __func__, entry->umountable, entry->flags); pr_info("%s: unmounting: %s flags 0x%x\n", __func__, entry->umountable, entry->flags);
try_umount(entry->umountable, entry->flags); try_umount(entry->umountable, entry->flags);
} }
up_read(&mount_list_lock); up_read(&mount_list_lock);
if (saved) if (saved)
revert_creds(saved); revert_creds(saved);
if (tw->old_cred) if (tw->old_cred)
put_cred(tw->old_cred); put_cred(tw->old_cred);
kfree(tw); kfree(tw);
} }
int ksu_handle_umount(uid_t old_uid, uid_t new_uid) int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
{ {
struct umount_tw *tw; struct umount_tw *tw;
#if defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) #if defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT)
// this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it! // this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it!
if (!ksu_module_mounted) { if (!ksu_module_mounted) {
return 0; return 0;
} }
if (!ksu_kernel_umount_enabled) { if (!ksu_kernel_umount_enabled) {
return 0; return 0;
} }
// FIXME: isolated process which directly forks from zygote is not handled // FIXME: isolated process which directly forks from zygote is not handled
if (!is_appuid(new_uid)) { if (!is_appuid(new_uid)) {
return 0; return 0;
} }
if (!ksu_uid_should_umount(new_uid)) { if (!ksu_uid_should_umount(new_uid)) {
return 0; return 0;
} }
// check old process's selinux context, if it is not zygote, ignore it! // 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 // 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! // when we umount for such process, that is a disaster!
bool is_zygote_child = is_zygote(get_current_cred()); bool is_zygote_child = is_zygote(get_current_cred());
if (!is_zygote_child) { if (!is_zygote_child) {
pr_info("handle umount ignore non zygote child: %d\n", current->pid); pr_info("handle umount ignore non zygote child: %d\n", current->pid);
return 0; return 0;
} }
#if __SULOG_GATE #if __SULOG_GATE
ksu_sulog_report_syscall(new_uid, NULL, "setuid", NULL); ksu_sulog_report_syscall(new_uid, NULL, "setuid", NULL);
#endif #endif
#endif // #if defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) #endif // #if defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT)
// umount the target mnt // umount the target mnt
pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid); pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid);
tw = kzalloc(sizeof(*tw), GFP_ATOMIC); tw = kzalloc(sizeof(*tw), GFP_ATOMIC);
if (!tw) if (!tw)
return 0; return 0;
tw->old_cred = get_current_cred(); tw->old_cred = get_current_cred();
tw->cb.func = umount_tw_func; tw->cb.func = umount_tw_func;
int err = task_work_add(current, &tw->cb, TWA_RESUME); int err = task_work_add(current, &tw->cb, TWA_RESUME);
if (err) { if (err) {
if (tw->old_cred) { if (tw->old_cred) {
put_cred(tw->old_cred); put_cred(tw->old_cred);
} }
kfree(tw); kfree(tw);
pr_warn("unmount add task_work failed\n"); pr_warn("unmount add task_work failed\n");
} }
return 0; return 0;
} }
#endif // #if !defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) #endif // #if !defined(CONFIG_KSU_SUSFS) || !defined(CONFIG_KSU_SUSFS_TRY_UMOUNT)
void ksu_kernel_umount_init(void) void ksu_kernel_umount_init(void)
{ {
if (ksu_register_feature_handler(&kernel_umount_handler)) { if (ksu_register_feature_handler(&kernel_umount_handler)) {
pr_err("Failed to register kernel_umount feature handler\n"); pr_err("Failed to register kernel_umount feature handler\n");
} }
} }
void ksu_kernel_umount_exit(void) void ksu_kernel_umount_exit(void)
{ {
ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT); ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT);
} }

View File

@@ -15,9 +15,9 @@ int ksu_handle_umount(uid_t old_uid, uid_t new_uid);
// for the umount list // for the umount list
struct mount_entry { struct mount_entry {
char *umountable; char *umountable;
unsigned int flags; unsigned int flags;
struct list_head list; struct list_head list;
}; };
extern struct list_head mount_list; extern struct list_head mount_list;
extern struct rw_semaphore mount_list_lock; extern struct rw_semaphore mount_list_lock;

View File

@@ -31,70 +31,70 @@
static int sukisu_is_su_allow_uid(uid_t uid) static int sukisu_is_su_allow_uid(uid_t uid)
{ {
return ksu_is_allow_uid_for_current(uid) ? 1 : 0; return ksu_is_allow_uid_for_current(uid) ? 1 : 0;
} }
static int sukisu_get_ap_mod_exclude(uid_t uid) static int sukisu_get_ap_mod_exclude(uid_t uid)
{ {
return 0; /* Not supported */ return 0; /* Not supported */
} }
static int sukisu_is_uid_should_umount(uid_t uid) static int sukisu_is_uid_should_umount(uid_t uid)
{ {
return ksu_uid_should_umount(uid) ? 1 : 0; return ksu_uid_should_umount(uid) ? 1 : 0;
} }
static int sukisu_is_current_uid_manager(void) static int sukisu_is_current_uid_manager(void)
{ {
return is_manager(); return is_manager();
} }
static uid_t sukisu_get_manager_uid(void) static uid_t sukisu_get_manager_uid(void)
{ {
return ksu_manager_uid; return ksu_manager_uid;
} }
static void sukisu_set_manager_uid(uid_t uid, int force) static void sukisu_set_manager_uid(uid_t uid, int force)
{ {
if (force || ksu_manager_uid == -1) if (force || ksu_manager_uid == -1)
ksu_manager_uid = uid; ksu_manager_uid = uid;
} }
struct CompactAddressSymbol { struct CompactAddressSymbol {
const char *symbol_name; const char *symbol_name;
void *addr; void *addr;
}; };
unsigned long sukisu_compact_find_symbol(const char *name); unsigned long sukisu_compact_find_symbol(const char *name);
static struct CompactAddressSymbol address_symbol[] = { static struct CompactAddressSymbol address_symbol[] = {
{ "kallsyms_lookup_name", &kallsyms_lookup_name }, { "kallsyms_lookup_name", &kallsyms_lookup_name },
{ "compact_find_symbol", &sukisu_compact_find_symbol }, { "compact_find_symbol", &sukisu_compact_find_symbol },
{ "is_run_in_sukisu_ultra", (void *)1 }, { "is_run_in_sukisu_ultra", (void *)1 },
{ "is_su_allow_uid", &sukisu_is_su_allow_uid }, { "is_su_allow_uid", &sukisu_is_su_allow_uid },
{ "get_ap_mod_exclude", &sukisu_get_ap_mod_exclude }, { "get_ap_mod_exclude", &sukisu_get_ap_mod_exclude },
{ "is_uid_should_umount", &sukisu_is_uid_should_umount }, { "is_uid_should_umount", &sukisu_is_uid_should_umount },
{ "is_current_uid_manager", &sukisu_is_current_uid_manager }, { "is_current_uid_manager", &sukisu_is_current_uid_manager },
{ "get_manager_uid", &sukisu_get_manager_uid }, { "get_manager_uid", &sukisu_get_manager_uid },
{ "sukisu_set_manager_uid", &sukisu_set_manager_uid } { "sukisu_set_manager_uid", &sukisu_set_manager_uid }
}; };
unsigned long sukisu_compact_find_symbol(const char* name) unsigned long sukisu_compact_find_symbol(const char* name)
{ {
int i; int i;
unsigned long addr; unsigned long addr;
for (i = 0; i < (sizeof(address_symbol) / sizeof(struct CompactAddressSymbol)); i++) { for (i = 0; i < (sizeof(address_symbol) / sizeof(struct CompactAddressSymbol)); i++) {
struct CompactAddressSymbol *symbol = &address_symbol[i]; struct CompactAddressSymbol *symbol = &address_symbol[i];
if (strcmp(name, symbol->symbol_name) == 0) if (strcmp(name, symbol->symbol_name) == 0)
return (unsigned long)symbol->addr; return (unsigned long)symbol->addr;
} }
addr = kallsyms_lookup_name(name); addr = kallsyms_lookup_name(name);
if (addr) if (addr)
return addr; return addr;
return 0; return 0;
} }
EXPORT_SYMBOL(sukisu_compact_find_symbol); EXPORT_SYMBOL(sukisu_compact_find_symbol);

View File

@@ -44,240 +44,240 @@
#ifndef NO_OPTIMIZE #ifndef NO_OPTIMIZE
#if defined(__GNUC__) && !defined(__clang__) #if defined(__GNUC__) && !defined(__clang__)
#define NO_OPTIMIZE __attribute__((optimize("O0"))) #define NO_OPTIMIZE __attribute__((optimize("O0")))
#elif defined(__clang__) #elif defined(__clang__)
#define NO_OPTIMIZE __attribute__((optnone)) #define NO_OPTIMIZE __attribute__((optnone))
#else #else
#define NO_OPTIMIZE #define NO_OPTIMIZE
#endif #endif
#endif #endif
noinline NO_OPTIMIZE void sukisu_kpm_load_module_path(const char *path, noinline NO_OPTIMIZE void sukisu_kpm_load_module_path(const char *path,
const char *args, void *ptr, int *result) const char *args, void *ptr, int *result)
{ {
pr_info("kpm: Stub function called (sukisu_kpm_load_module_path). " pr_info("kpm: Stub function called (sukisu_kpm_load_module_path). "
"path=%s args=%s ptr=%p\n", path, args, ptr); "path=%s args=%s ptr=%p\n", path, args, ptr);
__asm__ volatile("nop"); __asm__ volatile("nop");
} }
EXPORT_SYMBOL(sukisu_kpm_load_module_path); EXPORT_SYMBOL(sukisu_kpm_load_module_path);
noinline NO_OPTIMIZE void sukisu_kpm_unload_module(const char *name, noinline NO_OPTIMIZE void sukisu_kpm_unload_module(const char *name,
void *ptr, int *result) void *ptr, int *result)
{ {
pr_info("kpm: Stub function called (sukisu_kpm_unload_module). " pr_info("kpm: Stub function called (sukisu_kpm_unload_module). "
"name=%s ptr=%p\n", name, ptr); "name=%s ptr=%p\n", name, ptr);
__asm__ volatile("nop"); __asm__ volatile("nop");
} }
EXPORT_SYMBOL(sukisu_kpm_unload_module); EXPORT_SYMBOL(sukisu_kpm_unload_module);
noinline NO_OPTIMIZE void sukisu_kpm_num(int *result) noinline NO_OPTIMIZE void sukisu_kpm_num(int *result)
{ {
pr_info("kpm: Stub function called (sukisu_kpm_num).\n"); pr_info("kpm: Stub function called (sukisu_kpm_num).\n");
__asm__ volatile("nop"); __asm__ volatile("nop");
} }
EXPORT_SYMBOL(sukisu_kpm_num); EXPORT_SYMBOL(sukisu_kpm_num);
noinline NO_OPTIMIZE void sukisu_kpm_info(const char *name, char *buf, int bufferSize, noinline NO_OPTIMIZE void sukisu_kpm_info(const char *name, char *buf, int bufferSize,
int *size) int *size)
{ {
pr_info("kpm: Stub function called (sukisu_kpm_info). " pr_info("kpm: Stub function called (sukisu_kpm_info). "
"name=%s buffer=%p\n", name, buf); "name=%s buffer=%p\n", name, buf);
__asm__ volatile("nop"); __asm__ volatile("nop");
} }
EXPORT_SYMBOL(sukisu_kpm_info); EXPORT_SYMBOL(sukisu_kpm_info);
noinline NO_OPTIMIZE void sukisu_kpm_list(void *out, int bufferSize, noinline NO_OPTIMIZE void sukisu_kpm_list(void *out, int bufferSize,
int *result) int *result)
{ {
pr_info("kpm: Stub function called (sukisu_kpm_list). " pr_info("kpm: Stub function called (sukisu_kpm_list). "
"buffer=%p size=%d\n", out, bufferSize); "buffer=%p size=%d\n", out, bufferSize);
} }
EXPORT_SYMBOL(sukisu_kpm_list); EXPORT_SYMBOL(sukisu_kpm_list);
noinline NO_OPTIMIZE void sukisu_kpm_control(const char *name, const char *args, long arg_len, noinline NO_OPTIMIZE void sukisu_kpm_control(const char *name, const char *args, long arg_len,
int *result) int *result)
{ {
pr_info("kpm: Stub function called (sukisu_kpm_control). " pr_info("kpm: Stub function called (sukisu_kpm_control). "
"name=%p args=%p arg_len=%ld\n", name, args, arg_len); "name=%p args=%p arg_len=%ld\n", name, args, arg_len);
__asm__ volatile("nop"); __asm__ volatile("nop");
} }
EXPORT_SYMBOL(sukisu_kpm_control); EXPORT_SYMBOL(sukisu_kpm_control);
noinline NO_OPTIMIZE void sukisu_kpm_version(char *buf, int bufferSize) noinline NO_OPTIMIZE void sukisu_kpm_version(char *buf, int bufferSize)
{ {
pr_info("kpm: Stub function called (sukisu_kpm_version). " pr_info("kpm: Stub function called (sukisu_kpm_version). "
"buffer=%p\n", buf); "buffer=%p\n", buf);
} }
EXPORT_SYMBOL(sukisu_kpm_version); EXPORT_SYMBOL(sukisu_kpm_version);
noinline int sukisu_handle_kpm(unsigned long control_code, unsigned long arg1, unsigned long arg2, noinline int sukisu_handle_kpm(unsigned long control_code, unsigned long arg1, unsigned long arg2,
unsigned long result_code) unsigned long result_code)
{ {
int res = -1; int res = -1;
if (control_code == SUKISU_KPM_LOAD) { if (control_code == SUKISU_KPM_LOAD) {
char kernel_load_path[256]; char kernel_load_path[256];
char kernel_args_buffer[256]; char kernel_args_buffer[256];
if (arg1 == 0) { if (arg1 == 0) {
res = -EINVAL; res = -EINVAL;
goto exit; goto exit;
} }
if (!ksu_access_ok(arg1, sizeof(kernel_load_path))) { if (!ksu_access_ok(arg1, sizeof(kernel_load_path))) {
goto invalid_arg; goto invalid_arg;
} }
strncpy_from_user((char *)&kernel_load_path, (const char *)arg1, sizeof(kernel_load_path)); strncpy_from_user((char *)&kernel_load_path, (const char *)arg1, sizeof(kernel_load_path));
if (arg2 != 0) { if (arg2 != 0) {
if (!ksu_access_ok(arg2, sizeof(kernel_args_buffer))) { if (!ksu_access_ok(arg2, sizeof(kernel_args_buffer))) {
goto invalid_arg; goto invalid_arg;
} }
strncpy_from_user((char *)&kernel_args_buffer, (const char *)arg2, sizeof(kernel_args_buffer)); strncpy_from_user((char *)&kernel_args_buffer, (const char *)arg2, sizeof(kernel_args_buffer));
} }
sukisu_kpm_load_module_path((const char *)&kernel_load_path, sukisu_kpm_load_module_path((const char *)&kernel_load_path,
(const char *)&kernel_args_buffer, NULL, &res); (const char *)&kernel_args_buffer, NULL, &res);
} else if (control_code == SUKISU_KPM_UNLOAD) { } else if (control_code == SUKISU_KPM_UNLOAD) {
char kernel_name_buffer[256]; char kernel_name_buffer[256];
if (arg1 == 0) { if (arg1 == 0) {
res = -EINVAL; res = -EINVAL;
goto exit; goto exit;
} }
if (!ksu_access_ok(arg1, sizeof(kernel_name_buffer))) { if (!ksu_access_ok(arg1, sizeof(kernel_name_buffer))) {
goto invalid_arg; goto invalid_arg;
} }
strncpy_from_user((char *)&kernel_name_buffer, (const char *)arg1, sizeof(kernel_name_buffer)); strncpy_from_user((char *)&kernel_name_buffer, (const char *)arg1, sizeof(kernel_name_buffer));
sukisu_kpm_unload_module((const char *)&kernel_name_buffer, NULL, &res); sukisu_kpm_unload_module((const char *)&kernel_name_buffer, NULL, &res);
} else if (control_code == SUKISU_KPM_NUM) { } else if (control_code == SUKISU_KPM_NUM) {
sukisu_kpm_num(&res); sukisu_kpm_num(&res);
} else if (control_code == SUKISU_KPM_INFO) { } else if (control_code == SUKISU_KPM_INFO) {
char kernel_name_buffer[256]; char kernel_name_buffer[256];
char buf[256]; char buf[256];
int size; int size;
if (arg1 == 0 || arg2 == 0) { if (arg1 == 0 || arg2 == 0) {
res = -EINVAL; res = -EINVAL;
goto exit; goto exit;
} }
if (!ksu_access_ok(arg1, sizeof(kernel_name_buffer))) { if (!ksu_access_ok(arg1, sizeof(kernel_name_buffer))) {
goto invalid_arg; goto invalid_arg;
} }
strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg1, sizeof(kernel_name_buffer)); strncpy_from_user((char *)&kernel_name_buffer, (const char __user *)arg1, sizeof(kernel_name_buffer));
sukisu_kpm_info((const char *)&kernel_name_buffer, (char *)&buf, sizeof(buf), &size); sukisu_kpm_info((const char *)&kernel_name_buffer, (char *)&buf, sizeof(buf), &size);
if (!ksu_access_ok(arg2, size)) { if (!ksu_access_ok(arg2, size)) {
goto invalid_arg; goto invalid_arg;
} }
res = copy_to_user(arg2, &buf, size); res = copy_to_user(arg2, &buf, size);
} else if (control_code == SUKISU_KPM_LIST) { } else if (control_code == SUKISU_KPM_LIST) {
char buf[1024]; char buf[1024];
int len = (int) arg2; int len = (int) arg2;
if (len <= 0) { if (len <= 0) {
res = -EINVAL; res = -EINVAL;
goto exit; goto exit;
} }
if (!ksu_access_ok(arg2, len)) { if (!ksu_access_ok(arg2, len)) {
goto invalid_arg; goto invalid_arg;
} }
sukisu_kpm_list((char *)&buf, sizeof(buf), &res); sukisu_kpm_list((char *)&buf, sizeof(buf), &res);
if (res > len) { if (res > len) {
res = -ENOBUFS; res = -ENOBUFS;
goto exit; goto exit;
} }
if (copy_to_user(arg1, &buf, len) != 0) if (copy_to_user(arg1, &buf, len) != 0)
pr_info("kpm: Copy to user failed."); pr_info("kpm: Copy to user failed.");
} else if (control_code == SUKISU_KPM_CONTROL) { } else if (control_code == SUKISU_KPM_CONTROL) {
char kpm_name[KPM_NAME_LEN] = { 0 }; char kpm_name[KPM_NAME_LEN] = { 0 };
char kpm_args[KPM_ARGS_LEN] = { 0 }; char kpm_args[KPM_ARGS_LEN] = { 0 };
if (!ksu_access_ok(arg1, sizeof(kpm_name))) { if (!ksu_access_ok(arg1, sizeof(kpm_name))) {
goto invalid_arg; goto invalid_arg;
} }
if (!ksu_access_ok(arg2, sizeof(kpm_args))) { if (!ksu_access_ok(arg2, sizeof(kpm_args))) {
goto invalid_arg; goto invalid_arg;
} }
long name_len = strncpy_from_user((char *)&kpm_name, (const char __user *)arg1, sizeof(kpm_name)); long name_len = strncpy_from_user((char *)&kpm_name, (const char __user *)arg1, sizeof(kpm_name));
if (name_len <= 0) { if (name_len <= 0) {
res = -EINVAL; res = -EINVAL;
goto exit; goto exit;
} }
long arg_len = strncpy_from_user((char *)&kpm_args, (const char __user *)arg2, sizeof(kpm_args)); long arg_len = strncpy_from_user((char *)&kpm_args, (const char __user *)arg2, sizeof(kpm_args));
sukisu_kpm_control((const char *)&kpm_name, (const char *)&kpm_args, arg_len, &res); sukisu_kpm_control((const char *)&kpm_name, (const char *)&kpm_args, arg_len, &res);
} else if (control_code == SUKISU_KPM_VERSION) { } else if (control_code == SUKISU_KPM_VERSION) {
char buffer[256] = {0}; char buffer[256] = {0};
sukisu_kpm_version((char*) &buffer, sizeof(buffer)); sukisu_kpm_version((char*) &buffer, sizeof(buffer));
unsigned int outlen = (unsigned int) arg2; unsigned int outlen = (unsigned int) arg2;
int len = strlen(buffer); int len = strlen(buffer);
if (len >= outlen) len = outlen - 1; if (len >= outlen) len = outlen - 1;
res = copy_to_user(arg1, &buffer, len + 1); res = copy_to_user(arg1, &buffer, len + 1);
} }
exit: exit:
if (copy_to_user(result_code, &res, sizeof(res)) != 0) if (copy_to_user(result_code, &res, sizeof(res)) != 0)
pr_info("kpm: Copy to user failed."); pr_info("kpm: Copy to user failed.");
return 0; return 0;
invalid_arg: invalid_arg:
pr_err("kpm: invalid pointer detected! arg1: %px arg2: %px\n", (void *)arg1, (void *)arg2); pr_err("kpm: invalid pointer detected! arg1: %px arg2: %px\n", (void *)arg1, (void *)arg2);
res = -EFAULT; res = -EFAULT;
goto exit; goto exit;
} }
EXPORT_SYMBOL(sukisu_handle_kpm); EXPORT_SYMBOL(sukisu_handle_kpm);
int sukisu_is_kpm_control_code(unsigned long control_code) { int sukisu_is_kpm_control_code(unsigned long control_code) {
return (control_code >= CMD_KPM_CONTROL && return (control_code >= CMD_KPM_CONTROL &&
control_code <= CMD_KPM_CONTROL_MAX) ? 1 : 0; control_code <= CMD_KPM_CONTROL_MAX) ? 1 : 0;
} }
int do_kpm(void __user *arg) int do_kpm(void __user *arg)
{ {
struct ksu_kpm_cmd cmd; struct ksu_kpm_cmd cmd;
if (copy_from_user(&cmd, arg, sizeof(cmd))) { if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("kpm: copy_from_user failed\n"); pr_err("kpm: copy_from_user failed\n");
return -EFAULT; return -EFAULT;
} }
if (!ksu_access_ok(cmd.control_code, sizeof(int))) { if (!ksu_access_ok(cmd.control_code, sizeof(int))) {
pr_err("kpm: invalid control_code pointer %px\n", (void *)cmd.control_code); pr_err("kpm: invalid control_code pointer %px\n", (void *)cmd.control_code);
return -EFAULT; return -EFAULT;
} }
if (!ksu_access_ok(cmd.result_code, sizeof(int))) { if (!ksu_access_ok(cmd.result_code, sizeof(int))) {
pr_err("kpm: invalid result_code pointer %px\n", (void *)cmd.result_code); pr_err("kpm: invalid result_code pointer %px\n", (void *)cmd.result_code);
return -EFAULT; return -EFAULT;
} }
return sukisu_handle_kpm(cmd.control_code, cmd.arg1, cmd.arg2, cmd.result_code); return sukisu_handle_kpm(cmd.control_code, cmd.arg1, cmd.arg2, cmd.result_code);
} }

View File

@@ -5,10 +5,10 @@
#include <linux/ioctl.h> #include <linux/ioctl.h>
struct ksu_kpm_cmd { struct ksu_kpm_cmd {
__aligned_u64 __user control_code; __aligned_u64 __user control_code;
__aligned_u64 __user arg1; __aligned_u64 __user arg1;
__aligned_u64 __user arg2; __aligned_u64 __user arg2;
__aligned_u64 __user result_code; __aligned_u64 __user result_code;
}; };
int sukisu_handle_kpm(unsigned long control_code, unsigned long arg1, unsigned long arg2, unsigned long result_code); int sukisu_handle_kpm(unsigned long control_code, unsigned long arg1, unsigned long arg2, unsigned long result_code);

View File

@@ -36,154 +36,154 @@
#include "compact.h" #include "compact.h"
struct DynamicStructMember { struct DynamicStructMember {
const char *name; const char *name;
size_t size; size_t size;
size_t offset; size_t offset;
}; };
struct DynamicStructInfo { struct DynamicStructInfo {
const char *name; const char *name;
size_t count; size_t count;
size_t total_size; size_t total_size;
struct DynamicStructMember *members; struct DynamicStructMember *members;
}; };
#define DYNAMIC_STRUCT_BEGIN(struct_name) \ #define DYNAMIC_STRUCT_BEGIN(struct_name) \
static struct DynamicStructMember struct_name##_members[] = { static struct DynamicStructMember struct_name##_members[] = {
#define DEFINE_MEMBER(struct_name, member) \ #define DEFINE_MEMBER(struct_name, member) \
{ \ { \
.name = #member, \ .name = #member, \
.size = sizeof(((struct struct_name *)0)->member), \ .size = sizeof(((struct struct_name *)0)->member), \
.offset = offsetof(struct struct_name, member) \ .offset = offsetof(struct struct_name, member) \
}, },
#define DYNAMIC_STRUCT_END(struct_name) \ #define DYNAMIC_STRUCT_END(struct_name) \
}; \ }; \
static struct DynamicStructInfo struct_name##_info = { \ static struct DynamicStructInfo struct_name##_info = { \
.name = #struct_name, \ .name = #struct_name, \
.count = sizeof(struct_name##_members) / sizeof(struct DynamicStructMember), \ .count = sizeof(struct_name##_members) / sizeof(struct DynamicStructMember), \
.total_size = sizeof(struct struct_name), \ .total_size = sizeof(struct struct_name), \
.members = struct_name##_members \ .members = struct_name##_members \
}; };
DYNAMIC_STRUCT_BEGIN(mount) DYNAMIC_STRUCT_BEGIN(mount)
DEFINE_MEMBER(mount, mnt_parent) DEFINE_MEMBER(mount, mnt_parent)
DEFINE_MEMBER(mount, mnt) DEFINE_MEMBER(mount, mnt)
DEFINE_MEMBER(mount, mnt_id) DEFINE_MEMBER(mount, mnt_id)
DEFINE_MEMBER(mount, mnt_group_id) DEFINE_MEMBER(mount, mnt_group_id)
DEFINE_MEMBER(mount, mnt_expiry_mark) DEFINE_MEMBER(mount, mnt_expiry_mark)
DEFINE_MEMBER(mount, mnt_master) DEFINE_MEMBER(mount, mnt_master)
DEFINE_MEMBER(mount, mnt_devname) DEFINE_MEMBER(mount, mnt_devname)
DYNAMIC_STRUCT_END(mount) DYNAMIC_STRUCT_END(mount)
DYNAMIC_STRUCT_BEGIN(vfsmount) DYNAMIC_STRUCT_BEGIN(vfsmount)
DEFINE_MEMBER(vfsmount, mnt_root) DEFINE_MEMBER(vfsmount, mnt_root)
DEFINE_MEMBER(vfsmount, mnt_sb) DEFINE_MEMBER(vfsmount, mnt_sb)
DEFINE_MEMBER(vfsmount, mnt_flags) DEFINE_MEMBER(vfsmount, mnt_flags)
DYNAMIC_STRUCT_END(vfsmount) DYNAMIC_STRUCT_END(vfsmount)
DYNAMIC_STRUCT_BEGIN(mnt_namespace) DYNAMIC_STRUCT_BEGIN(mnt_namespace)
DEFINE_MEMBER(mnt_namespace, ns) DEFINE_MEMBER(mnt_namespace, ns)
DEFINE_MEMBER(mnt_namespace, root) DEFINE_MEMBER(mnt_namespace, root)
DEFINE_MEMBER(mnt_namespace, seq) DEFINE_MEMBER(mnt_namespace, seq)
DEFINE_MEMBER(mnt_namespace, mounts) DEFINE_MEMBER(mnt_namespace, mounts)
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)
DEFINE_MEMBER(mnt_namespace, count) DEFINE_MEMBER(mnt_namespace, count)
#endif #endif
DYNAMIC_STRUCT_END(mnt_namespace) DYNAMIC_STRUCT_END(mnt_namespace)
#ifdef CONFIG_KPROBES #ifdef CONFIG_KPROBES
DYNAMIC_STRUCT_BEGIN(kprobe) DYNAMIC_STRUCT_BEGIN(kprobe)
DEFINE_MEMBER(kprobe, addr) DEFINE_MEMBER(kprobe, addr)
DEFINE_MEMBER(kprobe, symbol_name) DEFINE_MEMBER(kprobe, symbol_name)
DEFINE_MEMBER(kprobe, offset) DEFINE_MEMBER(kprobe, offset)
DEFINE_MEMBER(kprobe, pre_handler) DEFINE_MEMBER(kprobe, pre_handler)
DEFINE_MEMBER(kprobe, post_handler) DEFINE_MEMBER(kprobe, post_handler)
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0) #if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)
DEFINE_MEMBER(kprobe, fault_handler) DEFINE_MEMBER(kprobe, fault_handler)
#endif #endif
DEFINE_MEMBER(kprobe, flags) DEFINE_MEMBER(kprobe, flags)
DYNAMIC_STRUCT_END(kprobe) DYNAMIC_STRUCT_END(kprobe)
#endif #endif
DYNAMIC_STRUCT_BEGIN(vm_area_struct) DYNAMIC_STRUCT_BEGIN(vm_area_struct)
DEFINE_MEMBER(vm_area_struct,vm_start) DEFINE_MEMBER(vm_area_struct,vm_start)
DEFINE_MEMBER(vm_area_struct,vm_end) DEFINE_MEMBER(vm_area_struct,vm_end)
DEFINE_MEMBER(vm_area_struct,vm_flags) DEFINE_MEMBER(vm_area_struct,vm_flags)
DEFINE_MEMBER(vm_area_struct,anon_vma) DEFINE_MEMBER(vm_area_struct,anon_vma)
DEFINE_MEMBER(vm_area_struct,vm_pgoff) DEFINE_MEMBER(vm_area_struct,vm_pgoff)
DEFINE_MEMBER(vm_area_struct,vm_file) DEFINE_MEMBER(vm_area_struct,vm_file)
DEFINE_MEMBER(vm_area_struct,vm_private_data) DEFINE_MEMBER(vm_area_struct,vm_private_data)
#ifdef CONFIG_ANON_VMA_NAME #ifdef CONFIG_ANON_VMA_NAME
DEFINE_MEMBER(vm_area_struct, anon_name) DEFINE_MEMBER(vm_area_struct, anon_name)
#endif #endif
DEFINE_MEMBER(vm_area_struct, vm_ops) DEFINE_MEMBER(vm_area_struct, vm_ops)
DYNAMIC_STRUCT_END(vm_area_struct) DYNAMIC_STRUCT_END(vm_area_struct)
DYNAMIC_STRUCT_BEGIN(vm_operations_struct) DYNAMIC_STRUCT_BEGIN(vm_operations_struct)
DEFINE_MEMBER(vm_operations_struct, open) DEFINE_MEMBER(vm_operations_struct, open)
DEFINE_MEMBER(vm_operations_struct, close) DEFINE_MEMBER(vm_operations_struct, close)
DEFINE_MEMBER(vm_operations_struct, name) DEFINE_MEMBER(vm_operations_struct, name)
DEFINE_MEMBER(vm_operations_struct, access) DEFINE_MEMBER(vm_operations_struct, access)
DYNAMIC_STRUCT_END(vm_operations_struct) DYNAMIC_STRUCT_END(vm_operations_struct)
DYNAMIC_STRUCT_BEGIN(netlink_kernel_cfg) DYNAMIC_STRUCT_BEGIN(netlink_kernel_cfg)
DEFINE_MEMBER(netlink_kernel_cfg, groups) DEFINE_MEMBER(netlink_kernel_cfg, groups)
DEFINE_MEMBER(netlink_kernel_cfg, flags) DEFINE_MEMBER(netlink_kernel_cfg, flags)
DEFINE_MEMBER(netlink_kernel_cfg, input) DEFINE_MEMBER(netlink_kernel_cfg, input)
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0) #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 11, 0)
DEFINE_MEMBER(netlink_kernel_cfg, cb_mutex) DEFINE_MEMBER(netlink_kernel_cfg, cb_mutex)
#endif #endif
DEFINE_MEMBER(netlink_kernel_cfg, bind) DEFINE_MEMBER(netlink_kernel_cfg, bind)
DEFINE_MEMBER(netlink_kernel_cfg, unbind) DEFINE_MEMBER(netlink_kernel_cfg, unbind)
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
DEFINE_MEMBER(netlink_kernel_cfg, compare) DEFINE_MEMBER(netlink_kernel_cfg, compare)
#endif #endif
DYNAMIC_STRUCT_END(netlink_kernel_cfg) DYNAMIC_STRUCT_END(netlink_kernel_cfg)
DYNAMIC_STRUCT_BEGIN(task_struct) DYNAMIC_STRUCT_BEGIN(task_struct)
DEFINE_MEMBER(task_struct, pid) DEFINE_MEMBER(task_struct, pid)
DEFINE_MEMBER(task_struct, tgid) DEFINE_MEMBER(task_struct, tgid)
DEFINE_MEMBER(task_struct, cred) DEFINE_MEMBER(task_struct, cred)
DEFINE_MEMBER(task_struct, real_cred) DEFINE_MEMBER(task_struct, real_cred)
DEFINE_MEMBER(task_struct, comm) DEFINE_MEMBER(task_struct, comm)
DEFINE_MEMBER(task_struct, parent) DEFINE_MEMBER(task_struct, parent)
DEFINE_MEMBER(task_struct, group_leader) DEFINE_MEMBER(task_struct, group_leader)
DEFINE_MEMBER(task_struct, mm) DEFINE_MEMBER(task_struct, mm)
DEFINE_MEMBER(task_struct, active_mm) DEFINE_MEMBER(task_struct, active_mm)
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0) #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 19, 0)
DEFINE_MEMBER(task_struct, pids[PIDTYPE_PID].pid) DEFINE_MEMBER(task_struct, pids[PIDTYPE_PID].pid)
#else #else
DEFINE_MEMBER(task_struct, thread_pid) DEFINE_MEMBER(task_struct, thread_pid)
#endif #endif
DEFINE_MEMBER(task_struct, files) DEFINE_MEMBER(task_struct, files)
DEFINE_MEMBER(task_struct, seccomp) DEFINE_MEMBER(task_struct, seccomp)
#ifdef CONFIG_THREAD_INFO_IN_TASK #ifdef CONFIG_THREAD_INFO_IN_TASK
DEFINE_MEMBER(task_struct, thread_info) DEFINE_MEMBER(task_struct, thread_info)
#endif #endif
#ifdef CONFIG_CGROUPS #ifdef CONFIG_CGROUPS
DEFINE_MEMBER(task_struct, cgroups) DEFINE_MEMBER(task_struct, cgroups)
#endif #endif
#ifdef CONFIG_SECURITY #ifdef CONFIG_SECURITY
DEFINE_MEMBER(task_struct, security) DEFINE_MEMBER(task_struct, security)
#endif #endif
DEFINE_MEMBER(task_struct, thread) DEFINE_MEMBER(task_struct, thread)
DYNAMIC_STRUCT_END(task_struct) DYNAMIC_STRUCT_END(task_struct)
#define STRUCT_INFO(name) &(name##_info) #define STRUCT_INFO(name) &(name##_info)
static struct DynamicStructInfo *dynamic_struct_infos[] = { static struct DynamicStructInfo *dynamic_struct_infos[] = {
STRUCT_INFO(mount), STRUCT_INFO(mount),
STRUCT_INFO(vfsmount), STRUCT_INFO(vfsmount),
STRUCT_INFO(mnt_namespace), STRUCT_INFO(mnt_namespace),
#ifdef CONFIG_KPROBES #ifdef CONFIG_KPROBES
STRUCT_INFO(kprobe), STRUCT_INFO(kprobe),
#endif #endif
STRUCT_INFO(vm_area_struct), STRUCT_INFO(vm_area_struct),
STRUCT_INFO(vm_operations_struct), STRUCT_INFO(vm_operations_struct),
STRUCT_INFO(netlink_kernel_cfg), STRUCT_INFO(netlink_kernel_cfg),
STRUCT_INFO(task_struct) STRUCT_INFO(task_struct)
}; };
/* /*
@@ -192,21 +192,21 @@ static struct DynamicStructInfo *dynamic_struct_infos[] = {
*/ */
int sukisu_super_find_struct(const char *struct_name, size_t *out_size, int *out_members) int sukisu_super_find_struct(const char *struct_name, size_t *out_size, int *out_members)
{ {
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) { for (size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
struct DynamicStructInfo *info = dynamic_struct_infos[i]; struct DynamicStructInfo *info = dynamic_struct_infos[i];
if (strcmp(struct_name, info->name) == 0) { if (strcmp(struct_name, info->name) == 0) {
if (out_size) if (out_size)
*out_size = info->total_size; *out_size = info->total_size;
if (out_members) if (out_members)
*out_members = info->count; *out_members = info->count;
return 0; return 0;
} }
} }
return -1; return -1;
} }
EXPORT_SYMBOL(sukisu_super_find_struct); EXPORT_SYMBOL(sukisu_super_find_struct);
@@ -217,34 +217,34 @@ EXPORT_SYMBOL(sukisu_super_find_struct);
* return -2 if member not defined * return -2 if member not defined
*/ */
int sukisu_super_access(const char *struct_name, const char *member_name, size_t *out_offset, int sukisu_super_access(const char *struct_name, const char *member_name, size_t *out_offset,
size_t *out_size) size_t *out_size)
{ {
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) { for (size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
struct DynamicStructInfo *info = dynamic_struct_infos[i]; struct DynamicStructInfo *info = dynamic_struct_infos[i];
if (strcmp(struct_name, info->name) == 0) { if (strcmp(struct_name, info->name) == 0) {
for (size_t i1 = 0; i1 < info->count; i1++) { for (size_t i1 = 0; i1 < info->count; i1++) {
if (strcmp(info->members[i1].name, member_name) == 0) { if (strcmp(info->members[i1].name, member_name) == 0) {
if (out_offset) if (out_offset)
*out_offset = info->members[i].offset; *out_offset = info->members[i].offset;
if (out_size) if (out_size)
*out_size = info->members[i].size; *out_size = info->members[i].size;
return 0; return 0;
} }
} }
return -2; return -2;
} }
} }
return -1; return -1;
} }
EXPORT_SYMBOL(sukisu_super_access); EXPORT_SYMBOL(sukisu_super_access);
#define DYNAMIC_CONTAINER_OF(offset, member_ptr) ({ \ #define DYNAMIC_CONTAINER_OF(offset, member_ptr) ({ \
(offset != (size_t)-1) ? (void*)((char*)(member_ptr) - offset) : NULL; \ (offset != (size_t)-1) ? (void*)((char*)(member_ptr) - offset) : NULL; \
}) })
/* /*
@@ -254,27 +254,27 @@ EXPORT_SYMBOL(sukisu_super_access);
* return -2 if target member not defined * return -2 if target member not defined
*/ */
int sukisu_super_container_of(const char *struct_name, const char *member_name, void *ptr, int sukisu_super_container_of(const char *struct_name, const char *member_name, void *ptr,
void **out_ptr) void **out_ptr)
{ {
if (ptr == NULL) if (ptr == NULL)
return -3; return -3;
for (size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) { for (size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
struct DynamicStructInfo *info = dynamic_struct_infos[i]; struct DynamicStructInfo *info = dynamic_struct_infos[i];
if (strcmp(struct_name, info->name) == 0) { if (strcmp(struct_name, info->name) == 0) {
for (size_t i1 = 0; i1 < info->count; i1++) { for (size_t i1 = 0; i1 < info->count; i1++) {
if (strcmp(info->members[i1].name, member_name) == 0) { if (strcmp(info->members[i1].name, member_name) == 0) {
*out_ptr = (void *)DYNAMIC_CONTAINER_OF(info->members[i1].offset, ptr); *out_ptr = (void *)DYNAMIC_CONTAINER_OF(info->members[i1].offset, ptr);
return 0; return 0;
} }
} }
return -2; return -2;
} }
} }
return -1; return -1;
} }
EXPORT_SYMBOL(sukisu_super_container_of); EXPORT_SYMBOL(sukisu_super_container_of);

View File

@@ -8,8 +8,8 @@
extern int sukisu_super_find_struct(const char *struct_name, size_t *out_size, int *out_members); extern int sukisu_super_find_struct(const char *struct_name, size_t *out_size, int *out_members);
extern int sukisu_super_access(const char *struct_name, const char *member_name, size_t *out_offset, extern int sukisu_super_access(const char *struct_name, const char *member_name, size_t *out_offset,
size_t *out_size); size_t *out_size);
extern int sukisu_super_container_of(const char *struct_name, const char *member_name, void *ptr, extern int sukisu_super_container_of(const char *struct_name, const char *member_name, void *ptr,
void **out_ptr); void **out_ptr);
#endif #endif

View File

@@ -39,11 +39,11 @@ void sukisu_custom_config_init(void)
void sukisu_custom_config_exit(void) void sukisu_custom_config_exit(void)
{ {
ksu_uid_exit(); ksu_uid_exit();
ksu_throne_comm_exit(); ksu_throne_comm_exit();
ksu_dynamic_manager_exit(); ksu_dynamic_manager_exit();
#if __SULOG_GATE #if __SULOG_GATE
ksu_sulog_exit(); ksu_sulog_exit();
#endif #endif
} }
@@ -53,69 +53,69 @@ int __init kernelsu_init(void)
UTS_RELEASE, UTS_MACHINE, KSU_VERSION); UTS_RELEASE, UTS_MACHINE, KSU_VERSION);
#ifdef CONFIG_KSU_DEBUG #ifdef CONFIG_KSU_DEBUG
pr_alert("*************************************************************"); pr_alert("*************************************************************");
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
pr_alert("** **"); pr_alert("** **");
pr_alert("** You are running KernelSU in DEBUG mode **"); pr_alert("** You are running KernelSU in DEBUG mode **");
pr_alert("** **"); pr_alert("** **");
pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **");
pr_alert("*************************************************************"); pr_alert("*************************************************************");
#endif #endif
ksu_feature_init(); ksu_feature_init();
ksu_lsm_hook_init(); ksu_lsm_hook_init();
ksu_supercalls_init(); ksu_supercalls_init();
sukisu_custom_config_init(); sukisu_custom_config_init();
ksu_syscall_hook_manager_init(); ksu_syscall_hook_manager_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_SUSFS #ifdef CONFIG_KSU_SUSFS
susfs_init(); susfs_init();
#endif #endif
#if defined(CONFIG_KPROBES) && !defined(CONFIG_KSU_SUSFS) #if defined(CONFIG_KPROBES) && !defined(CONFIG_KSU_SUSFS)
ksu_ksud_init(); ksu_ksud_init();
#endif #endif
#ifdef MODULE #ifdef MODULE
#ifndef CONFIG_KSU_DEBUG #ifndef CONFIG_KSU_DEBUG
kobject_del(&THIS_MODULE->mkobj.kobj); kobject_del(&THIS_MODULE->mkobj.kobj);
#endif #endif
#endif #endif
return 0; return 0;
} }
extern void ksu_observer_exit(void); extern void ksu_observer_exit(void);
void kernelsu_exit(void) void kernelsu_exit(void)
{ {
ksu_allowlist_exit(); ksu_allowlist_exit();
ksu_observer_exit(); ksu_observer_exit();
ksu_throne_tracker_exit(); ksu_throne_tracker_exit();
destroy_workqueue(ksu_workqueue); destroy_workqueue(ksu_workqueue);
#if defined(CONFIG_KPROBES) && !defined(CONFIG_KSU_SUSFS) #if defined(CONFIG_KPROBES) && !defined(CONFIG_KSU_SUSFS)
ksu_ksud_exit(); ksu_ksud_exit();
#endif #endif
ksu_syscall_hook_manager_exit(); ksu_syscall_hook_manager_exit();
sukisu_custom_config_exit(); sukisu_custom_config_exit();
ksu_supercalls_exit(); ksu_supercalls_exit();
ksu_feature_exit(); ksu_feature_exit();
} }
module_init(kernelsu_init); module_init(kernelsu_init);

View File

@@ -28,17 +28,17 @@ extern bool ksu_uid_scanner_enabled;
#define UID_SCANNER_OP_CLEAR_ENV 2 #define UID_SCANNER_OP_CLEAR_ENV 2
struct dynamic_manager_user_config { struct dynamic_manager_user_config {
unsigned int operation; unsigned int operation;
unsigned int size; unsigned int size;
char hash[65]; char hash[65];
}; };
struct manager_list_info { struct manager_list_info {
int count; int count;
struct { struct {
uid_t uid; uid_t uid;
int signature_index; int signature_index;
} managers[2]; } managers[2];
}; };
bool ksu_queue_work(struct work_struct *work); bool ksu_queue_work(struct work_struct *work);
@@ -48,16 +48,16 @@ void ksu_lsm_hook_init(void);
#if 0 #if 0
static inline int startswith(char *s, char *prefix) static inline int startswith(char *s, char *prefix)
{ {
return strncmp(s, prefix, strlen(prefix)); return strncmp(s, prefix, strlen(prefix));
} }
static inline int endswith(const char *s, const char *t) static inline int endswith(const char *s, const char *t)
{ {
size_t slen = strlen(s); size_t slen = strlen(s);
size_t tlen = strlen(t); size_t tlen = strlen(t);
if (tlen > slen) if (tlen > slen)
return 1; return 1;
return strcmp(s + slen - tlen, t); return strcmp(s + slen - tlen, t);
} }
#endif #endif

View File

@@ -40,27 +40,27 @@ bool ksu_module_mounted __read_mostly = false;
bool ksu_boot_completed __read_mostly = false; bool ksu_boot_completed __read_mostly = false;
static const char KERNEL_SU_RC[] = static const char KERNEL_SU_RC[] =
"\n" "\n"
"on post-fs-data\n" "on post-fs-data\n"
" start logd\n" " start logd\n"
// We should wait for the post-fs-data finish // We should wait for the post-fs-data finish
" exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n" " exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n"
"\n" "\n"
"on nonencrypted\n" "on nonencrypted\n"
" exec u:r:su:s0 root -- " KSUD_PATH " services\n" " exec u:r:su:s0 root -- " KSUD_PATH " services\n"
"\n" "\n"
"on property:vold.decrypt=trigger_restart_framework\n" "on property:vold.decrypt=trigger_restart_framework\n"
" exec u:r:su:s0 root -- " KSUD_PATH " services\n" " exec u:r:su:s0 root -- " KSUD_PATH " services\n"
"\n" "\n"
"on property:sys.boot_completed=1\n" "on property:sys.boot_completed=1\n"
" exec u:r:su:s0 root -- " KSUD_PATH " boot-completed\n" " exec u:r:su:s0 root -- " KSUD_PATH " boot-completed\n"
"\n" "\n"
"\n"; "\n";
static void stop_vfs_read_hook(void); static void stop_vfs_read_hook(void);
static void stop_execve_hook(void); static void stop_execve_hook(void);
@@ -83,22 +83,22 @@ static bool is_boot_phase = true;
void on_post_fs_data(void) void on_post_fs_data(void)
{ {
static bool done = false; static bool done = false;
if (done) { if (done) {
pr_info("on_post_fs_data already done\n"); pr_info("on_post_fs_data already done\n");
return; return;
} }
done = true; done = true;
pr_info("on_post_fs_data!\n"); pr_info("on_post_fs_data!\n");
ksu_load_allow_list(); ksu_load_allow_list();
ksu_observer_init(); ksu_observer_init();
// sanity check, this may influence the performance // sanity check, this may influence the performance
stop_input_hook(); stop_input_hook();
// End of boot state // End of boot state
is_boot_phase = false; is_boot_phase = false;
ksu_file_sid = ksu_get_ksu_file_sid(); ksu_file_sid = ksu_get_ksu_file_sid();
pr_info("ksu_file sid: %d\n", ksu_file_sid); pr_info("ksu_file sid: %d\n", ksu_file_sid);
} }
@@ -106,73 +106,73 @@ extern void ext4_unregister_sysfs(struct super_block *sb);
int nuke_ext4_sysfs(const char* mnt) int nuke_ext4_sysfs(const char* mnt)
{ {
#ifdef CONFIG_EXT4_FS #ifdef CONFIG_EXT4_FS
struct path path; struct path path;
int err = kern_path(mnt, 0, &path); int err = kern_path(mnt, 0, &path);
if (err) { if (err) {
pr_err("nuke path err: %d\n", err); pr_err("nuke path err: %d\n", err);
return err; return err;
} }
struct super_block *sb = path.dentry->d_inode->i_sb; struct super_block *sb = path.dentry->d_inode->i_sb;
const char *name = sb->s_type->name; const char *name = sb->s_type->name;
if (strcmp(name, "ext4") != 0) { if (strcmp(name, "ext4") != 0) {
pr_info("nuke but module aren't mounted\n"); pr_info("nuke but module aren't mounted\n");
path_put(&path); path_put(&path);
return -EINVAL; return -EINVAL;
} }
ext4_unregister_sysfs(sb); ext4_unregister_sysfs(sb);
path_put(&path); path_put(&path);
return 0; return 0;
#endif #endif
} }
void on_module_mounted(void){ void on_module_mounted(void){
pr_info("on_module_mounted!\n"); pr_info("on_module_mounted!\n");
ksu_module_mounted = true; ksu_module_mounted = true;
} }
void on_boot_completed(void){ void on_boot_completed(void){
ksu_boot_completed = true; ksu_boot_completed = true;
pr_info("on_boot_completed!\n"); pr_info("on_boot_completed!\n");
track_throne(true); track_throne(true);
} }
#ifndef CONFIG_KSU_SUSFS #ifndef CONFIG_KSU_SUSFS
#define MAX_ARG_STRINGS 0x7FFFFFFF #define MAX_ARG_STRINGS 0x7FFFFFFF
struct user_arg_ptr { struct user_arg_ptr {
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
bool is_compat; bool is_compat;
#endif #endif
union { union {
const char __user *const __user *native; const char __user *const __user *native;
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
const compat_uptr_t __user *compat; const compat_uptr_t __user *compat;
#endif #endif
} ptr; } ptr;
}; };
#endif // #ifndef CONFIG_KSU_SUSFS #endif // #ifndef CONFIG_KSU_SUSFS
static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr) static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
{ {
const char __user *native; const char __user *native;
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
if (unlikely(argv.is_compat)) { if (unlikely(argv.is_compat)) {
compat_uptr_t compat; compat_uptr_t compat;
if (get_user(compat, argv.ptr.compat + nr)) if (get_user(compat, argv.ptr.compat + nr))
return ERR_PTR(-EFAULT); return ERR_PTR(-EFAULT);
return compat_ptr(compat); return compat_ptr(compat);
} }
#endif #endif
if (get_user(native, argv.ptr.native + nr)) if (get_user(native, argv.ptr.native + nr))
return ERR_PTR(-EFAULT); return ERR_PTR(-EFAULT);
return native; return native;
} }
/* /*
@@ -186,160 +186,160 @@ static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
static int __maybe_unused count(struct user_arg_ptr argv, int max) static int __maybe_unused count(struct user_arg_ptr argv, int max)
{ {
int i = 0; int i = 0;
if (argv.ptr.native != NULL) { if (argv.ptr.native != NULL) {
for (;;) { for (;;) {
const char __user *p = get_user_arg_ptr(argv, i); const char __user *p = get_user_arg_ptr(argv, i);
if (!p) if (!p)
break; break;
if (IS_ERR(p)) if (IS_ERR(p))
return -EFAULT; return -EFAULT;
if (i >= max) if (i >= max)
return -E2BIG; return -E2BIG;
++i; ++i;
if (fatal_signal_pending(current)) if (fatal_signal_pending(current))
return -ERESTARTNOHAND; return -ERESTARTNOHAND;
} }
} }
return i; return i;
} }
static void on_post_fs_data_cbfun(struct callback_head *cb) static void on_post_fs_data_cbfun(struct callback_head *cb)
{ {
on_post_fs_data(); on_post_fs_data();
} }
static struct callback_head on_post_fs_data_cb = { .func = static struct callback_head on_post_fs_data_cb = { .func =
on_post_fs_data_cbfun }; on_post_fs_data_cbfun };
// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version // IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
struct user_arg_ptr *argv, struct user_arg_ptr *argv,
struct user_arg_ptr *envp, int *flags) struct user_arg_ptr *envp, int *flags)
{ {
#ifndef KSU_KPROBES_HOOK #ifndef KSU_KPROBES_HOOK
if (!ksu_execveat_hook) { if (!ksu_execveat_hook) {
return 0; return 0;
} }
#endif #endif
struct filename *filename; struct filename *filename;
static const char app_process[] = "/system/bin/app_process"; static const char app_process[] = "/system/bin/app_process";
static bool first_app_process = true; static bool first_app_process = true;
/* This applies to versions Android 10+ */ /* This applies to versions Android 10+ */
static const char system_bin_init[] = "/system/bin/init"; static const char system_bin_init[] = "/system/bin/init";
/* This applies to versions between Android 6 ~ 9 */ /* This applies to versions between Android 6 ~ 9 */
static const char old_system_init[] = "/init"; static const char old_system_init[] = "/init";
static bool init_second_stage_executed = false; static bool init_second_stage_executed = false;
if (!filename_ptr) if (!filename_ptr)
return 0; return 0;
filename = *filename_ptr; filename = *filename_ptr;
if (IS_ERR(filename)) { if (IS_ERR(filename)) {
return 0; return 0;
} }
if (unlikely(!memcmp(filename->name, system_bin_init, if (unlikely(!memcmp(filename->name, system_bin_init,
sizeof(system_bin_init) - 1) && sizeof(system_bin_init) - 1) &&
argv)) { argv)) {
// /system/bin/init executed // /system/bin/init executed
int argc = count(*argv, MAX_ARG_STRINGS); int argc = count(*argv, MAX_ARG_STRINGS);
pr_info("/system/bin/init argc: %d\n", argc); pr_info("/system/bin/init argc: %d\n", argc);
if (argc > 1 && !init_second_stage_executed) { if (argc > 1 && !init_second_stage_executed) {
const char __user *p = get_user_arg_ptr(*argv, 1); const char __user *p = get_user_arg_ptr(*argv, 1);
if (p && !IS_ERR(p)) { if (p && !IS_ERR(p)) {
char first_arg[16]; char first_arg[16];
ksu_strncpy_from_user_nofault(first_arg, p, sizeof(first_arg)); ksu_strncpy_from_user_nofault(first_arg, p, sizeof(first_arg));
pr_info("/system/bin/init first arg: %s\n", first_arg); pr_info("/system/bin/init first arg: %s\n", first_arg);
if (!strcmp(first_arg, "second_stage")) { if (!strcmp(first_arg, "second_stage")) {
pr_info("/system/bin/init second_stage executed\n"); pr_info("/system/bin/init second_stage executed\n");
apply_kernelsu_rules(); apply_kernelsu_rules();
init_second_stage_executed = true; init_second_stage_executed = true;
} }
} else { } else {
pr_err("/system/bin/init parse args err!\n"); pr_err("/system/bin/init parse args err!\n");
} }
} }
} else if (unlikely(!memcmp(filename->name, old_system_init, } else if (unlikely(!memcmp(filename->name, old_system_init,
sizeof(old_system_init) - 1) && sizeof(old_system_init) - 1) &&
argv)) { argv)) {
// /init executed // /init executed
int argc = count(*argv, MAX_ARG_STRINGS); int argc = count(*argv, MAX_ARG_STRINGS);
pr_info("/init argc: %d\n", argc); pr_info("/init argc: %d\n", argc);
if (argc > 1 && !init_second_stage_executed) { if (argc > 1 && !init_second_stage_executed) {
/* This applies to versions between Android 6 ~ 7 */ /* This applies to versions between Android 6 ~ 7 */
const char __user *p = get_user_arg_ptr(*argv, 1); const char __user *p = get_user_arg_ptr(*argv, 1);
if (p && !IS_ERR(p)) { if (p && !IS_ERR(p)) {
char first_arg[16]; char first_arg[16];
ksu_strncpy_from_user_nofault(first_arg, p, sizeof(first_arg)); ksu_strncpy_from_user_nofault(first_arg, p, sizeof(first_arg));
pr_info("/init first arg: %s\n", first_arg); pr_info("/init first arg: %s\n", first_arg);
if (!strcmp(first_arg, "--second-stage")) { if (!strcmp(first_arg, "--second-stage")) {
pr_info("/init second_stage executed\n"); pr_info("/init second_stage executed\n");
apply_kernelsu_rules(); apply_kernelsu_rules();
init_second_stage_executed = true; init_second_stage_executed = true;
} }
} else { } else {
pr_err("/init parse args err!\n"); pr_err("/init parse args err!\n");
} }
} else if (argc == 1 && !init_second_stage_executed && envp) { } else if (argc == 1 && !init_second_stage_executed && envp) {
/* This applies to versions between Android 8 ~ 9 */ /* This applies to versions between Android 8 ~ 9 */
int envc = count(*envp, MAX_ARG_STRINGS); int envc = count(*envp, MAX_ARG_STRINGS);
if (envc > 0) { if (envc > 0) {
int n; int n;
for (n = 1; n <= envc; n++) { for (n = 1; n <= envc; n++) {
const char __user *p = get_user_arg_ptr(*envp, n); const char __user *p = get_user_arg_ptr(*envp, n);
if (!p || IS_ERR(p)) { if (!p || IS_ERR(p)) {
continue; continue;
} }
char env[256]; char env[256];
// Reading environment variable strings from user space // Reading environment variable strings from user space
if (ksu_strncpy_from_user_nofault(env, p, sizeof(env)) < 0) if (ksu_strncpy_from_user_nofault(env, p, sizeof(env)) < 0)
continue; continue;
// Parsing environment variable names and values // Parsing environment variable names and values
char *env_name = env; char *env_name = env;
char *env_value = strchr(env, '='); char *env_value = strchr(env, '=');
if (env_value == NULL) if (env_value == NULL)
continue; continue;
// Replace equal sign with string terminator // Replace equal sign with string terminator
*env_value = '\0'; *env_value = '\0';
env_value++; env_value++;
// Check if the environment variable name and value are matching // Check if the environment variable name and value are matching
if (!strcmp(env_name, "INIT_SECOND_STAGE") && if (!strcmp(env_name, "INIT_SECOND_STAGE") &&
(!strcmp(env_value, "1") || (!strcmp(env_value, "1") ||
!strcmp(env_value, "true"))) { !strcmp(env_value, "true"))) {
pr_info("/init second_stage executed\n"); pr_info("/init second_stage executed\n");
apply_kernelsu_rules(); apply_kernelsu_rules();
init_second_stage_executed = true; init_second_stage_executed = true;
} }
} }
} }
} }
} }
if (unlikely(first_app_process && !memcmp(filename->name, app_process, if (unlikely(first_app_process && !memcmp(filename->name, app_process,
sizeof(app_process) - 1))) { sizeof(app_process) - 1))) {
first_app_process = false; first_app_process = false;
pr_info("exec app_process, /data prepared, second_stage: %d\n", pr_info("exec app_process, /data prepared, second_stage: %d\n",
init_second_stage_executed); init_second_stage_executed);
struct task_struct *init_task; struct task_struct *init_task;
rcu_read_lock(); rcu_read_lock();
init_task = rcu_dereference(current->real_parent); init_task = rcu_dereference(current->real_parent);
if (init_task) { if (init_task) {
task_work_add(init_task, &on_post_fs_data_cb, TWA_RESUME); task_work_add(init_task, &on_post_fs_data_cb, TWA_RESUME);
} }
rcu_read_unlock(); rcu_read_unlock();
stop_execve_hook(); stop_execve_hook();
} }
return 0; return 0;
} }
static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *); static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *);
@@ -348,295 +348,295 @@ static struct file_operations fops_proxy;
static ssize_t read_count_append = 0; static ssize_t read_count_append = 0;
static ssize_t read_proxy(struct file *file, char __user *buf, size_t count, 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; bool first_read = file->f_pos == 0;
ssize_t ret = orig_read(file, buf, count, pos); ssize_t ret = orig_read(file, buf, count, pos);
if (first_read) { if (first_read) {
pr_info("read_proxy append %ld + %ld\n", ret, read_count_append); pr_info("read_proxy append %ld + %ld\n", ret, read_count_append);
ret += read_count_append; ret += read_count_append;
} }
return ret; return ret;
} }
static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to) static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to)
{ {
bool first_read = iocb->ki_pos == 0; bool first_read = iocb->ki_pos == 0;
ssize_t ret = orig_read_iter(iocb, to); ssize_t ret = orig_read_iter(iocb, to);
if (first_read) { if (first_read) {
pr_info("read_iter_proxy append %ld + %ld\n", ret, read_count_append); pr_info("read_iter_proxy append %ld + %ld\n", ret, read_count_append);
ret += read_count_append; ret += read_count_append;
} }
return ret; return ret;
} }
static int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, static int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
size_t *count_ptr, loff_t **pos) size_t *count_ptr, loff_t **pos)
{ {
#ifndef KSU_KPROBES_HOOK #ifndef KSU_KPROBES_HOOK
if (!ksu_vfs_read_hook) { if (!ksu_vfs_read_hook) {
return 0; return 0;
} }
#endif #endif
struct file *file; struct file *file;
char __user *buf; char __user *buf;
size_t count; size_t count;
if (strcmp(current->comm, "init")) { if (strcmp(current->comm, "init")) {
// we are only interest in `init` process // we are only interest in `init` process
return 0; return 0;
} }
file = *file_ptr; file = *file_ptr;
if (IS_ERR(file)) { if (IS_ERR(file)) {
return 0; return 0;
} }
if (!d_is_reg(file->f_path.dentry)) { if (!d_is_reg(file->f_path.dentry)) {
return 0; return 0;
} }
const char *short_name = file->f_path.dentry->d_name.name; const char *short_name = file->f_path.dentry->d_name.name;
if (strcmp(short_name, "atrace.rc")) { if (strcmp(short_name, "atrace.rc")) {
// we are only interest `atrace.rc` file name file // we are only interest `atrace.rc` file name file
return 0; return 0;
} }
char path[256]; char path[256];
char *dpath = d_path(&file->f_path, path, sizeof(path)); char *dpath = d_path(&file->f_path, path, sizeof(path));
if (IS_ERR(dpath)) { if (IS_ERR(dpath)) {
return 0; return 0;
} }
if (strcmp(dpath, "/system/etc/init/atrace.rc")) { if (strcmp(dpath, "/system/etc/init/atrace.rc")) {
return 0; return 0;
} }
// we only process the first read // we only process the first read
static bool rc_inserted = false; static bool rc_inserted = false;
if (rc_inserted) { if (rc_inserted) {
// we don't need this kprobe, unregister it! // we don't need this kprobe, unregister it!
stop_vfs_read_hook(); stop_vfs_read_hook();
return 0; return 0;
} }
rc_inserted = true; rc_inserted = true;
// now we can sure that the init process is reading // now we can sure that the init process is reading
// `/system/etc/init/atrace.rc` // `/system/etc/init/atrace.rc`
buf = *buf_ptr; buf = *buf_ptr;
count = *count_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, pr_info("vfs_read: %s, comm: %s, count: %zu, rc_count: %zu\n", dpath,
current->comm, count, rc_count); current->comm, count, rc_count);
if (count < rc_count) { if (count < rc_count) {
pr_err("count: %zu < rc_count: %zu\n", count, rc_count); pr_err("count: %zu < rc_count: %zu\n", count, rc_count);
return 0; return 0;
} }
size_t ret = copy_to_user(buf, KERNEL_SU_RC, rc_count); size_t ret = copy_to_user(buf, KERNEL_SU_RC, rc_count);
if (ret) { if (ret) {
pr_err("copy ksud.rc failed: %zu\n", ret); pr_err("copy ksud.rc failed: %zu\n", ret);
return 0; return 0;
} }
// we've succeed to insert ksud.rc, now we need to proxy the read and modify the result! // 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. // 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. // We just replace the whole file_operations with a proxy one.
memcpy(&fops_proxy, file->f_op, sizeof(struct file_operations)); memcpy(&fops_proxy, file->f_op, sizeof(struct file_operations));
orig_read = file->f_op->read; orig_read = file->f_op->read;
if (orig_read) { if (orig_read) {
fops_proxy.read = read_proxy; fops_proxy.read = read_proxy;
} }
orig_read_iter = file->f_op->read_iter; orig_read_iter = file->f_op->read_iter;
if (orig_read_iter) { if (orig_read_iter) {
fops_proxy.read_iter = read_iter_proxy; fops_proxy.read_iter = read_iter_proxy;
} }
// replace the file_operations // replace the file_operations
file->f_op = &fops_proxy; file->f_op = &fops_proxy;
read_count_append = rc_count; read_count_append = rc_count;
*buf_ptr = buf + rc_count; *buf_ptr = buf + rc_count;
*count_ptr = count - rc_count; *count_ptr = count - rc_count;
return 0; return 0;
} }
int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, 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); struct file *file = fget(fd);
if (!file) { if (!file) {
return 0; return 0;
} }
int result = ksu_handle_vfs_read(&file, buf_ptr, count_ptr, NULL); int result = ksu_handle_vfs_read(&file, buf_ptr, count_ptr, NULL);
fput(file); fput(file);
return result; return result;
} }
static unsigned int volumedown_pressed_count = 0; static unsigned int volumedown_pressed_count = 0;
static bool is_volumedown_enough(unsigned int count) 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 ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
int *value) int *value)
{ {
#ifndef KSU_KPROBES_HOOK #ifndef KSU_KPROBES_HOOK
if (!ksu_input_hook) { if (!ksu_input_hook) {
return 0; return 0;
} }
#endif #endif
if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) { if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) {
int val = *value; int val = *value;
pr_info("KEY_VOLUMEDOWN val: %d\n", val); pr_info("KEY_VOLUMEDOWN val: %d\n", val);
if (val && is_boot_phase) { if (val && is_boot_phase) {
// key pressed, count it // key pressed, count it
volumedown_pressed_count += 1; volumedown_pressed_count += 1;
if (is_volumedown_enough(volumedown_pressed_count)) { if (is_volumedown_enough(volumedown_pressed_count)) {
stop_input_hook(); stop_input_hook();
} }
} }
} }
return 0; return 0;
} }
bool ksu_is_safe_mode() bool ksu_is_safe_mode()
{ {
static bool safe_mode = false; static bool safe_mode = false;
if (safe_mode) { if (safe_mode) {
// don't need to check again, userspace may call multiple times // don't need to check again, userspace may call multiple times
return true; return true;
} }
// stop hook first! // stop hook first!
stop_input_hook(); stop_input_hook();
pr_info("volumedown_pressed_count: %d\n", volumedown_pressed_count); pr_info("volumedown_pressed_count: %d\n", volumedown_pressed_count);
if (is_volumedown_enough(volumedown_pressed_count)) { if (is_volumedown_enough(volumedown_pressed_count)) {
// pressed over 3 times // pressed over 3 times
pr_info("KEY_VOLUMEDOWN pressed max times, safe mode detected!\n"); pr_info("KEY_VOLUMEDOWN pressed max times, safe mode detected!\n");
safe_mode = true; safe_mode = true;
return true; return true;
} }
return false; return false;
} }
#if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS) #if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS)
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
{ {
struct pt_regs *real_regs = PT_REAL_REGS(regs); struct pt_regs *real_regs = PT_REAL_REGS(regs);
const char __user **filename_user = const char __user **filename_user =
(const char **)&PT_REGS_PARM1(real_regs); (const char **)&PT_REGS_PARM1(real_regs);
const char __user *const __user *__argv = const char __user *const __user *__argv =
(const char __user *const __user *)PT_REGS_PARM2(real_regs); (const char __user *const __user *)PT_REGS_PARM2(real_regs);
struct user_arg_ptr argv = { .ptr.native = __argv }; struct user_arg_ptr argv = { .ptr.native = __argv };
struct filename filename_in, *filename_p; struct filename filename_in, *filename_p;
char path[32]; char path[32];
if (!filename_user) if (!filename_user)
return 0; return 0;
memset(path, 0, sizeof(path)); memset(path, 0, sizeof(path));
ksu_strncpy_from_user_nofault(path, *filename_user, 32); ksu_strncpy_from_user_nofault(path, *filename_user, 32);
filename_in.name = path; filename_in.name = path;
filename_p = &filename_in; filename_p = &filename_in;
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, NULL); return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, NULL);
} }
static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs) static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
{ {
struct pt_regs *real_regs = PT_REAL_REGS(regs); struct pt_regs *real_regs = PT_REAL_REGS(regs);
unsigned int fd = PT_REGS_PARM1(real_regs); unsigned int fd = PT_REGS_PARM1(real_regs);
char __user **buf_ptr = (char __user **)&PT_REGS_PARM2(real_regs); char __user **buf_ptr = (char __user **)&PT_REGS_PARM2(real_regs);
size_t count_ptr = (size_t *)&PT_REGS_PARM3(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, 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 *type = (unsigned int *)&PT_REGS_PARM2(regs);
unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs); unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs);
int *value = (int *)&PT_REGS_CCALL_PARM4(regs); int *value = (int *)&PT_REGS_CCALL_PARM4(regs);
return ksu_handle_input_handle_event(type, code, value); return ksu_handle_input_handle_event(type, code, value);
} }
static struct kprobe execve_kp = { static struct kprobe execve_kp = {
.symbol_name = SYS_EXECVE_SYMBOL, .symbol_name = SYS_EXECVE_SYMBOL,
.pre_handler = sys_execve_handler_pre, .pre_handler = sys_execve_handler_pre,
}; };
static struct kprobe vfs_read_kp = { static struct kprobe vfs_read_kp = {
.symbol_name = SYS_READ_SYMBOL, .symbol_name = SYS_READ_SYMBOL,
.pre_handler = sys_read_handler_pre, .pre_handler = sys_read_handler_pre,
}; };
static struct kprobe input_event_kp = { static struct kprobe input_event_kp = {
.symbol_name = "input_event", .symbol_name = "input_event",
.pre_handler = input_handle_event_handler_pre, .pre_handler = input_handle_event_handler_pre,
}; };
static void do_stop_vfs_read_hook(struct work_struct *work) 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) 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) static void do_stop_input_hook(struct work_struct *work)
{ {
unregister_kprobe(&input_event_kp); unregister_kprobe(&input_event_kp);
} }
#endif #endif
static void stop_vfs_read_hook(void) static void stop_vfs_read_hook(void)
{ {
#if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS) #if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS)
bool ret = schedule_work(&stop_vfs_read_work); bool ret = schedule_work(&stop_vfs_read_work);
pr_info("unregister vfs_read kprobe: %d!\n", ret); pr_info("unregister vfs_read kprobe: %d!\n", ret);
#else #else
ksu_vfs_read_hook = false; ksu_vfs_read_hook = false;
pr_info("stop vfs_read_hook\n"); pr_info("stop vfs_read_hook\n");
#endif #endif
} }
static void stop_execve_hook(void) static void stop_execve_hook(void)
{ {
#if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS) #if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS)
bool ret = schedule_work(&stop_execve_hook_work); bool ret = schedule_work(&stop_execve_hook_work);
pr_info("unregister execve kprobe: %d!\n", ret); pr_info("unregister execve kprobe: %d!\n", ret);
#else #else
ksu_execveat_hook = false; ksu_execveat_hook = false;
pr_info("stop execve_hook\n"); pr_info("stop execve_hook\n");
#endif #endif
} }
static void stop_input_hook(void) static void stop_input_hook(void)
{ {
static bool input_hook_stopped = false; static bool input_hook_stopped = false;
if (input_hook_stopped) { if (input_hook_stopped) {
return; return;
} }
input_hook_stopped = true; input_hook_stopped = true;
#if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS) #if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS)
bool ret = schedule_work(&stop_input_hook_work); bool ret = schedule_work(&stop_input_hook_work);
pr_info("unregister input kprobe: %d!\n", ret); pr_info("unregister input kprobe: %d!\n", ret);
#else #else
ksu_input_hook = false; ksu_input_hook = false;
pr_info("stop input_hook\n"); pr_info("stop input_hook\n");
#endif #endif
} }
@@ -644,31 +644,31 @@ static void stop_input_hook(void)
void ksu_ksud_init(void) void ksu_ksud_init(void)
{ {
#if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS) #if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS)
int ret; int ret;
ret = register_kprobe(&execve_kp); ret = register_kprobe(&execve_kp);
pr_info("ksud: execve_kp: %d\n", ret); pr_info("ksud: execve_kp: %d\n", ret);
ret = register_kprobe(&vfs_read_kp); ret = register_kprobe(&vfs_read_kp);
pr_info("ksud: vfs_read_kp: %d\n", ret); pr_info("ksud: vfs_read_kp: %d\n", ret);
ret = register_kprobe(&input_event_kp); ret = register_kprobe(&input_event_kp);
pr_info("ksud: input_event_kp: %d\n", ret); pr_info("ksud: input_event_kp: %d\n", ret);
INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_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_execve_hook_work, do_stop_execve_hook);
INIT_WORK(&stop_input_hook_work, do_stop_input_hook); INIT_WORK(&stop_input_hook_work, do_stop_input_hook);
#endif #endif
} }
void ksu_ksud_exit(void) void ksu_ksud_exit(void)
{ {
#if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS) #if defined(KSU_KPROBES_HOOK) && !defined(CONFIG_KSU_SUSFS)
unregister_kprobe(&execve_kp); unregister_kprobe(&execve_kp);
// this should be done before unregister vfs_read_kp // this should be done before unregister vfs_read_kp
// unregister_kprobe(&vfs_read_kp); // unregister_kprobe(&vfs_read_kp);
unregister_kprobe(&input_event_kp); unregister_kprobe(&input_event_kp);
#endif #endif
is_boot_phase = false; is_boot_phase = false;
volumedown_pressed_count = 0; volumedown_pressed_count = 0;
} }

View File

@@ -24,19 +24,19 @@ extern bool ksu_boot_completed;
#define MAX_ARG_STRINGS 0x7FFFFFFF #define MAX_ARG_STRINGS 0x7FFFFFFF
struct user_arg_ptr { struct user_arg_ptr {
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
bool is_compat; bool is_compat;
#endif #endif
union { union {
const char __user *const __user *native; const char __user *const __user *native;
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
const compat_uptr_t __user *compat; const compat_uptr_t __user *compat;
#endif #endif
} ptr; } ptr;
}; };
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
struct user_arg_ptr *argv, struct user_arg_ptr *argv,
struct user_arg_ptr *envp, int *flags); struct user_arg_ptr *envp, int *flags);
#endif // #ifdef CONFIG_KSU_SUSFS #endif // #ifdef CONFIG_KSU_SUSFS
#endif #endif

View File

@@ -16,59 +16,59 @@
#include "manual_su.h" #include "manual_su.h"
static int ksu_task_alloc(struct task_struct *task, static int ksu_task_alloc(struct task_struct *task,
unsigned long clone_flags) unsigned long clone_flags)
{ {
ksu_try_escalate_for_uid(task_uid(task).val); ksu_try_escalate_for_uid(task_uid(task).val);
return 0; return 0;
} }
#endif #endif
// kernel 4.4 and 4.9 // kernel 4.4 and 4.9
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
defined(CONFIG_IS_HW_HISI) || \ defined(CONFIG_IS_HW_HISI) || \
defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred, static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
unsigned perm) unsigned perm)
{ {
if (init_session_keyring != NULL) { if (init_session_keyring != NULL) {
return 0; return 0;
} }
if (strcmp(current->comm, "init")) { if (strcmp(current->comm, "init")) {
// we are only interested in `init` process // we are only interested in `init` process
return 0; return 0;
} }
init_session_keyring = cred->session_keyring; init_session_keyring = cred->session_keyring;
pr_info("kernel_compat: got init_session_keyring\n"); pr_info("kernel_compat: got init_session_keyring\n");
return 0; return 0;
} }
#endif #endif
static struct security_hook_list ksu_hooks[] = { static struct security_hook_list ksu_hooks[] = {
#if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0) && defined(CONFIG_KSU_MANUAL_SU) #if LINUX_VERSION_CODE > KERNEL_VERSION(4, 10, 0) && defined(CONFIG_KSU_MANUAL_SU)
LSM_HOOK_INIT(task_alloc, ksu_task_alloc), LSM_HOOK_INIT(task_alloc, ksu_task_alloc),
#endif #endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
LSM_HOOK_INIT(key_permission, ksu_key_permission) LSM_HOOK_INIT(key_permission, ksu_key_permission)
#endif #endif
}; };
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
const struct lsm_id ksu_lsmid = { const struct lsm_id ksu_lsmid = {
.name = "ksu", .name = "ksu",
.id = 912, .id = 912,
}; };
#endif #endif
void __init ksu_lsm_hook_init(void) void __init ksu_lsm_hook_init(void)
{ {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 8, 0)
// https://elixir.bootlin.com/linux/v6.8/source/include/linux/lsm_hooks.h#L120 // 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); security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), &ksu_lsmid);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) #elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu"); security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu");
#else #else
// https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892 // https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892
security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks)); security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks));
#endif #endif
} }

View File

@@ -15,43 +15,43 @@ extern int ksu_get_manager_signature_index(uid_t uid);
static inline bool ksu_is_manager_uid_valid(void) static inline bool ksu_is_manager_uid_valid(void)
{ {
return ksu_manager_uid != KSU_INVALID_UID; return ksu_manager_uid != KSU_INVALID_UID;
} }
#ifndef CONFIG_KSU_SUSFS #ifndef CONFIG_KSU_SUSFS
static inline bool is_manager(void) static inline bool is_manager(void)
{ {
return unlikely(ksu_is_any_manager(current_uid().val) || return unlikely(ksu_is_any_manager(current_uid().val) ||
(ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val)); (ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val));
} }
#else #else
static inline bool is_manager() static inline bool is_manager()
{ {
return unlikely((ksu_manager_uid == current_uid().val % 100000) || return unlikely((ksu_manager_uid == current_uid().val % 100000) ||
(ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val % 100000)); (ksu_manager_uid != KSU_INVALID_UID && ksu_manager_uid == current_uid().val % 100000));
} }
#endif #endif
static inline uid_t ksu_get_manager_uid(void) static inline uid_t ksu_get_manager_uid(void)
{ {
return ksu_manager_uid; return ksu_manager_uid;
} }
#ifndef CONFIG_KSU_SUSFS #ifndef CONFIG_KSU_SUSFS
static inline void ksu_set_manager_uid(uid_t uid) static inline void ksu_set_manager_uid(uid_t uid)
{ {
ksu_manager_uid = uid; ksu_manager_uid = uid;
} }
#else #else
static inline void ksu_set_manager_uid(uid_t uid) static inline void ksu_set_manager_uid(uid_t uid)
{ {
ksu_manager_uid = uid % 100000; ksu_manager_uid = uid % 100000;
} }
#endif #endif
static inline void ksu_invalidate_manager_uid(void) static inline void ksu_invalidate_manager_uid(void)
{ {
ksu_manager_uid = KSU_INVALID_UID; ksu_manager_uid = KSU_INVALID_UID;
} }
int ksu_observer_init(void); int ksu_observer_init(void);

View File

@@ -10,8 +10,8 @@
#define EXPECTED_HASH_5EC1CFF "7e0c6d7278a3bb8e364e0fcba95afaf3666cf5ff3c245a3b63c8833bd0445cc4" #define EXPECTED_HASH_5EC1CFF "7e0c6d7278a3bb8e364e0fcba95afaf3666cf5ff3c245a3b63c8833bd0445cc4"
// rsuntk/KernelSU // rsuntk/KernelSU
#define EXPECTED_SIZE_RSUNTK 0x396 #define EXPECTED_SIZE_RSUNTK 0x396
#define EXPECTED_HASH_RSUNTK "f415f4ed9435427e1fdf7f1fccd4dbc07b3d6b8751e4dbcec6f19671f427870b" #define EXPECTED_HASH_RSUNTK "f415f4ed9435427e1fdf7f1fccd4dbc07b3d6b8751e4dbcec6f19671f427870b"
// ShirkNeko/KernelSU // ShirkNeko/KernelSU
#define EXPECTED_SIZE_SHIRKNEKO 0x35c #define EXPECTED_SIZE_SHIRKNEKO 0x35c
@@ -26,8 +26,8 @@
#define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000" #define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000"
typedef struct { typedef struct {
unsigned size; unsigned size;
const char *sha256; const char *sha256;
} apk_sign_key_t; } apk_sign_key_t;
#endif /* MANAGER_SIGN_H */ #endif /* MANAGER_SIGN_H */

View File

@@ -29,330 +29,330 @@ static DEFINE_SPINLOCK(token_lock);
static char* get_token_from_envp(void) static char* get_token_from_envp(void)
{ {
struct mm_struct *mm; struct mm_struct *mm;
char *envp_start, *envp_end; char *envp_start, *envp_end;
char *env_ptr, *token = NULL; char *env_ptr, *token = NULL;
unsigned long env_len; unsigned long env_len;
char *env_copy = NULL; char *env_copy = NULL;
if (!current->mm) if (!current->mm)
return NULL; return NULL;
mm = current->mm; mm = current->mm;
down_read(&mm->mmap_lock); down_read(&mm->mmap_lock);
envp_start = (char *)mm->env_start; envp_start = (char *)mm->env_start;
envp_end = (char *)mm->env_end; envp_end = (char *)mm->env_end;
env_len = envp_end - envp_start; env_len = envp_end - envp_start;
if (env_len <= 0 || env_len > PAGE_SIZE * 32) { if (env_len <= 0 || env_len > PAGE_SIZE * 32) {
up_read(&mm->mmap_lock); up_read(&mm->mmap_lock);
return NULL; return NULL;
} }
env_copy = kzalloc(env_len + 1, GFP_KERNEL); env_copy = kzalloc(env_len + 1, GFP_KERNEL);
if (!env_copy) { if (!env_copy) {
up_read(&mm->mmap_lock); up_read(&mm->mmap_lock);
return NULL; return NULL;
} }
if (copy_from_user(env_copy, envp_start, env_len)) { if (copy_from_user(env_copy, envp_start, env_len)) {
kfree(env_copy); kfree(env_copy);
up_read(&mm->mmap_lock); up_read(&mm->mmap_lock);
return NULL; return NULL;
} }
up_read(&mm->mmap_lock); up_read(&mm->mmap_lock);
env_copy[env_len] = '\0'; env_copy[env_len] = '\0';
env_ptr = env_copy; env_ptr = env_copy;
while (env_ptr < env_copy + env_len) { while (env_ptr < env_copy + env_len) {
if (strncmp(env_ptr, KSU_TOKEN_ENV_NAME "=", strlen(KSU_TOKEN_ENV_NAME) + 1) == 0) { if (strncmp(env_ptr, KSU_TOKEN_ENV_NAME "=", strlen(KSU_TOKEN_ENV_NAME) + 1) == 0) {
char *token_start = env_ptr + strlen(KSU_TOKEN_ENV_NAME) + 1; char *token_start = env_ptr + strlen(KSU_TOKEN_ENV_NAME) + 1;
char *token_end = strchr(token_start, '\0'); char *token_end = strchr(token_start, '\0');
if (token_end && (token_end - token_start) == KSU_TOKEN_LENGTH) { if (token_end && (token_end - token_start) == KSU_TOKEN_LENGTH) {
token = kzalloc(KSU_TOKEN_LENGTH + 1, GFP_KERNEL); token = kzalloc(KSU_TOKEN_LENGTH + 1, GFP_KERNEL);
if (token) { if (token) {
memcpy(token, token_start, KSU_TOKEN_LENGTH); memcpy(token, token_start, KSU_TOKEN_LENGTH);
token[KSU_TOKEN_LENGTH] = '\0'; token[KSU_TOKEN_LENGTH] = '\0';
pr_info("manual_su: found auth token in environment\n"); pr_info("manual_su: found auth token in environment\n");
} }
} }
break; break;
} }
env_ptr += strlen(env_ptr) + 1; env_ptr += strlen(env_ptr) + 1;
} }
kfree(env_copy); kfree(env_copy);
return token; return token;
} }
static char* ksu_generate_auth_token(void) static char* ksu_generate_auth_token(void)
{ {
static char token_buffer[KSU_TOKEN_LENGTH + 1]; static char token_buffer[KSU_TOKEN_LENGTH + 1];
unsigned long flags; unsigned long flags;
int i; int i;
ksu_cleanup_expired_tokens(); ksu_cleanup_expired_tokens();
spin_lock_irqsave(&token_lock, flags); spin_lock_irqsave(&token_lock, flags);
if (token_count >= MAX_TOKENS) { if (token_count >= MAX_TOKENS) {
for (i = 0; i < MAX_TOKENS - 1; i++) { for (i = 0; i < MAX_TOKENS - 1; i++) {
auth_tokens[i] = auth_tokens[i + 1]; auth_tokens[i] = auth_tokens[i + 1];
} }
token_count = MAX_TOKENS - 1; token_count = MAX_TOKENS - 1;
} }
for (i = 0; i < KSU_TOKEN_LENGTH; i++) { for (i = 0; i < KSU_TOKEN_LENGTH; i++) {
u8 rand_byte; u8 rand_byte;
get_random_bytes(&rand_byte, 1); get_random_bytes(&rand_byte, 1);
int char_type = rand_byte % 3; int char_type = rand_byte % 3;
if (char_type == 0) { if (char_type == 0) {
token_buffer[i] = 'A' + (rand_byte % 26); token_buffer[i] = 'A' + (rand_byte % 26);
} else if (char_type == 1) { } else if (char_type == 1) {
token_buffer[i] = 'a' + (rand_byte % 26); token_buffer[i] = 'a' + (rand_byte % 26);
} else { } else {
token_buffer[i] = '0' + (rand_byte % 10); token_buffer[i] = '0' + (rand_byte % 10);
} }
} }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
strscpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1); strscpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
#else #else
strlcpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1); strlcpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1);
#endif #endif
auth_tokens[token_count].expire_time = jiffies + KSU_TOKEN_EXPIRE_TIME * HZ; auth_tokens[token_count].expire_time = jiffies + KSU_TOKEN_EXPIRE_TIME * HZ;
auth_tokens[token_count].used = false; auth_tokens[token_count].used = false;
token_count++; token_count++;
spin_unlock_irqrestore(&token_lock, flags); spin_unlock_irqrestore(&token_lock, flags);
pr_info("manual_su: generated new auth token (expires in %d seconds)\n", KSU_TOKEN_EXPIRE_TIME); pr_info("manual_su: generated new auth token (expires in %d seconds)\n", KSU_TOKEN_EXPIRE_TIME);
return token_buffer; return token_buffer;
} }
static bool ksu_verify_auth_token(const char *token) static bool ksu_verify_auth_token(const char *token)
{ {
unsigned long flags; unsigned long flags;
bool valid = false; bool valid = false;
int i; int i;
if (!token || strlen(token) != KSU_TOKEN_LENGTH) { if (!token || strlen(token) != KSU_TOKEN_LENGTH) {
return false; return false;
} }
spin_lock_irqsave(&token_lock, flags); spin_lock_irqsave(&token_lock, flags);
for (i = 0; i < token_count; i++) { for (i = 0; i < token_count; i++) {
if (!auth_tokens[i].used && if (!auth_tokens[i].used &&
time_before(jiffies, auth_tokens[i].expire_time) && time_before(jiffies, auth_tokens[i].expire_time) &&
strcmp(auth_tokens[i].token, token) == 0) { strcmp(auth_tokens[i].token, token) == 0) {
auth_tokens[i].used = true; auth_tokens[i].used = true;
valid = true; valid = true;
pr_info("manual_su: auth token verified successfully\n"); pr_info("manual_su: auth token verified successfully\n");
break; break;
} }
} }
spin_unlock_irqrestore(&token_lock, flags); spin_unlock_irqrestore(&token_lock, flags);
if (!valid) { if (!valid) {
pr_warn("manual_su: invalid or expired auth token\n"); pr_warn("manual_su: invalid or expired auth token\n");
} }
return valid; return valid;
} }
static void ksu_cleanup_expired_tokens(void) static void ksu_cleanup_expired_tokens(void)
{ {
unsigned long flags; unsigned long flags;
int i, j; int i, j;
spin_lock_irqsave(&token_lock, flags); spin_lock_irqsave(&token_lock, flags);
for (i = 0; i < token_count; ) { for (i = 0; i < token_count; ) {
if (time_after(jiffies, auth_tokens[i].expire_time) || auth_tokens[i].used) { if (time_after(jiffies, auth_tokens[i].expire_time) || auth_tokens[i].used) {
for (j = i; j < token_count - 1; j++) { for (j = i; j < token_count - 1; j++) {
auth_tokens[j] = auth_tokens[j + 1]; auth_tokens[j] = auth_tokens[j + 1];
} }
token_count--; token_count--;
pr_debug("manual_su: cleaned up expired/used token\n"); pr_debug("manual_su: cleaned up expired/used token\n");
} else { } else {
i++; i++;
} }
} }
spin_unlock_irqrestore(&token_lock, flags); spin_unlock_irqrestore(&token_lock, flags);
} }
static int handle_token_generation(struct manual_su_request *request) static int handle_token_generation(struct manual_su_request *request)
{ {
if (current_uid().val > 2000) { if (current_uid().val > 2000) {
pr_warn("manual_su: token generation denied for app UID %d\n", current_uid().val); pr_warn("manual_su: token generation denied for app UID %d\n", current_uid().val);
return -EPERM; return -EPERM;
} }
char *new_token = ksu_generate_auth_token(); char *new_token = ksu_generate_auth_token();
if (!new_token) { if (!new_token) {
pr_err("manual_su: failed to generate token\n"); pr_err("manual_su: failed to generate token\n");
return -ENOMEM; return -ENOMEM;
} }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
strscpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1); strscpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
#else #else
strlcpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1); strlcpy(request->token_buffer, new_token, KSU_TOKEN_LENGTH + 1);
#endif #endif
pr_info("manual_su: auth token generated successfully\n"); pr_info("manual_su: auth token generated successfully\n");
return 0; return 0;
} }
static int handle_escalation_request(struct manual_su_request *request) static int handle_escalation_request(struct manual_su_request *request)
{ {
uid_t target_uid = request->target_uid; uid_t target_uid = request->target_uid;
pid_t target_pid = request->target_pid; pid_t target_pid = request->target_pid;
struct task_struct *tsk; struct task_struct *tsk;
rcu_read_lock(); rcu_read_lock();
tsk = pid_task(find_vpid(target_pid), PIDTYPE_PID); tsk = pid_task(find_vpid(target_pid), PIDTYPE_PID);
if (!tsk || ksu_task_is_dead(tsk)) { if (!tsk || ksu_task_is_dead(tsk)) {
rcu_read_unlock(); rcu_read_unlock();
pr_err("cmd_su: PID %d is invalid or dead\n", target_pid); pr_err("cmd_su: PID %d is invalid or dead\n", target_pid);
return -ESRCH; return -ESRCH;
} }
rcu_read_unlock(); rcu_read_unlock();
if (current_uid().val == 0 || is_manager() || ksu_is_allow_uid_for_current(current_uid().val)) if (current_uid().val == 0 || is_manager() || ksu_is_allow_uid_for_current(current_uid().val))
goto allowed; goto allowed;
char *env_token = get_token_from_envp(); char *env_token = get_token_from_envp();
if (!env_token) { if (!env_token) {
pr_warn("manual_su: no auth token found in environment\n"); pr_warn("manual_su: no auth token found in environment\n");
return -EACCES; return -EACCES;
} }
bool token_valid = ksu_verify_auth_token(env_token); bool token_valid = ksu_verify_auth_token(env_token);
kfree(env_token); kfree(env_token);
if (!token_valid) { if (!token_valid) {
pr_warn("manual_su: token verification failed\n"); pr_warn("manual_su: token verification failed\n");
return -EACCES; return -EACCES;
} }
allowed: allowed:
current_verified = true; current_verified = true;
escape_to_root_for_cmd_su(target_uid, target_pid); escape_to_root_for_cmd_su(target_uid, target_pid);
return 0; return 0;
} }
static int handle_add_pending_request(struct manual_su_request *request) static int handle_add_pending_request(struct manual_su_request *request)
{ {
uid_t target_uid = request->target_uid; uid_t target_uid = request->target_uid;
if (!is_current_verified()) { if (!is_current_verified()) {
pr_warn("manual_su: add_pending denied, not verified\n"); pr_warn("manual_su: add_pending denied, not verified\n");
return -EPERM; return -EPERM;
} }
add_pending_root(target_uid); add_pending_root(target_uid);
current_verified = false; current_verified = false;
pr_info("manual_su: pending root added for UID %d\n", target_uid); pr_info("manual_su: pending root added for UID %d\n", target_uid);
return 0; return 0;
} }
int ksu_handle_manual_su_request(int option, struct manual_su_request *request) int ksu_handle_manual_su_request(int option, struct manual_su_request *request)
{ {
if (!request) { if (!request) {
pr_err("manual_su: invalid request pointer\n"); pr_err("manual_su: invalid request pointer\n");
return -EINVAL; return -EINVAL;
} }
switch (option) { switch (option) {
case MANUAL_SU_OP_GENERATE_TOKEN: case MANUAL_SU_OP_GENERATE_TOKEN:
pr_info("manual_su: handling token generation request\n"); pr_info("manual_su: handling token generation request\n");
return handle_token_generation(request); return handle_token_generation(request);
case MANUAL_SU_OP_ESCALATE: case MANUAL_SU_OP_ESCALATE:
pr_info("manual_su: handling escalation request for UID %d, PID %d\n", pr_info("manual_su: handling escalation request for UID %d, PID %d\n",
request->target_uid, request->target_pid); request->target_uid, request->target_pid);
return handle_escalation_request(request); return handle_escalation_request(request);
case MANUAL_SU_OP_ADD_PENDING: case MANUAL_SU_OP_ADD_PENDING:
pr_info("manual_su: handling add pending request for UID %d\n", request->target_uid); pr_info("manual_su: handling add pending request for UID %d\n", request->target_uid);
return handle_add_pending_request(request); return handle_add_pending_request(request);
default: default:
pr_err("manual_su: unknown option %d\n", option); pr_err("manual_su: unknown option %d\n", option);
return -EINVAL; return -EINVAL;
} }
} }
static bool is_current_verified(void) static bool is_current_verified(void)
{ {
return current_verified; return current_verified;
} }
bool is_pending_root(uid_t uid) bool is_pending_root(uid_t uid)
{ {
for (int i = 0; i < pending_cnt; i++) { for (int i = 0; i < pending_cnt; i++) {
if (pending_uids[i].uid == uid) { if (pending_uids[i].uid == uid) {
pending_uids[i].use_count++; pending_uids[i].use_count++;
pending_uids[i].remove_calls++; pending_uids[i].remove_calls++;
return true; return true;
} }
} }
return false; return false;
} }
void remove_pending_root(uid_t uid) void remove_pending_root(uid_t uid)
{ {
for (int i = 0; i < pending_cnt; i++) { for (int i = 0; i < pending_cnt; i++) {
if (pending_uids[i].uid == uid) { if (pending_uids[i].uid == uid) {
pending_uids[i].remove_calls++; pending_uids[i].remove_calls++;
if (pending_uids[i].remove_calls >= REMOVE_DELAY_CALLS) { if (pending_uids[i].remove_calls >= REMOVE_DELAY_CALLS) {
pending_uids[i] = pending_uids[--pending_cnt]; pending_uids[i] = pending_uids[--pending_cnt];
pr_info("pending_root: removed UID %d after %d calls\n", uid, REMOVE_DELAY_CALLS); pr_info("pending_root: removed UID %d after %d calls\n", uid, REMOVE_DELAY_CALLS);
ksu_temp_revoke_root_once(uid); ksu_temp_revoke_root_once(uid);
} else { } else {
pr_info("pending_root: UID %d remove_call=%d (<%d)\n", pr_info("pending_root: UID %d remove_call=%d (<%d)\n",
uid, pending_uids[i].remove_calls, REMOVE_DELAY_CALLS); uid, pending_uids[i].remove_calls, REMOVE_DELAY_CALLS);
} }
return; return;
} }
} }
} }
static void add_pending_root(uid_t uid) static void add_pending_root(uid_t uid)
{ {
if (pending_cnt >= MAX_PENDING) { if (pending_cnt >= MAX_PENDING) {
pr_warn("pending_root: cache full\n"); pr_warn("pending_root: cache full\n");
return; return;
} }
for (int i = 0; i < pending_cnt; i++) { for (int i = 0; i < pending_cnt; i++) {
if (pending_uids[i].uid == uid) { if (pending_uids[i].uid == uid) {
pending_uids[i].use_count = 0; pending_uids[i].use_count = 0;
pending_uids[i].remove_calls = 0; pending_uids[i].remove_calls = 0;
return; return;
} }
} }
pending_uids[pending_cnt++] = (struct pending_uid){uid, 0}; pending_uids[pending_cnt++] = (struct pending_uid){uid, 0};
ksu_temp_grant_root_once(uid); ksu_temp_grant_root_once(uid);
pr_info("pending_root: cached UID %d\n", uid); pr_info("pending_root: cached UID %d\n", uid);
} }
void ksu_try_escalate_for_uid(uid_t uid) void ksu_try_escalate_for_uid(uid_t uid)
{ {
if (!is_pending_root(uid)) if (!is_pending_root(uid))
return; return;
pr_info("pending_root: UID=%d temporarily allowed\n", uid); pr_info("pending_root: UID=%d temporarily allowed\n", uid);
remove_pending_root(uid); remove_pending_root(uid);
} }

View File

@@ -25,21 +25,21 @@
#define MANUAL_SU_OP_ADD_PENDING 2 #define MANUAL_SU_OP_ADD_PENDING 2
struct pending_uid { struct pending_uid {
uid_t uid; uid_t uid;
int use_count; int use_count;
int remove_calls; int remove_calls;
}; };
struct manual_su_request { struct manual_su_request {
uid_t target_uid; uid_t target_uid;
pid_t target_pid; pid_t target_pid;
char token_buffer[KSU_TOKEN_LENGTH + 1]; char token_buffer[KSU_TOKEN_LENGTH + 1];
}; };
struct ksu_token_entry { struct ksu_token_entry {
char token[KSU_TOKEN_LENGTH + 1]; char token[KSU_TOKEN_LENGTH + 1];
unsigned long expire_time; unsigned long expire_time;
bool used; bool used;
}; };
int ksu_handle_manual_su_request(int option, struct manual_su_request *request); int ksu_handle_manual_su_request(int option, struct manual_su_request *request);

View File

@@ -15,11 +15,11 @@
#define MASK_SYSTEM (FS_CREATE | FS_MOVE | FS_EVENT_ON_CHILD) #define MASK_SYSTEM (FS_CREATE | FS_MOVE | FS_EVENT_ON_CHILD)
struct watch_dir { struct watch_dir {
const char *path; const char *path;
u32 mask; u32 mask;
struct path kpath; struct path kpath;
struct inode *inode; struct inode *inode;
struct fsnotify_mark *mark; struct fsnotify_mark *mark;
}; };
static struct fsnotify_group *g; static struct fsnotify_group *g;
@@ -27,132 +27,132 @@ static struct fsnotify_group *g;
#include "pkg_observer_defs.h" // KSU_DECL_FSNOTIFY_OPS #include "pkg_observer_defs.h" // KSU_DECL_FSNOTIFY_OPS
static KSU_DECL_FSNOTIFY_OPS(ksu_handle_generic_event) static KSU_DECL_FSNOTIFY_OPS(ksu_handle_generic_event)
{ {
if (!file_name || (mask & FS_ISDIR)) if (!file_name || (mask & FS_ISDIR))
return 0; return 0;
if (ksu_fname_len(file_name) == 13 && if (ksu_fname_len(file_name) == 13 &&
!memcmp(ksu_fname_arg(file_name), "packages.list", 13)) { !memcmp(ksu_fname_arg(file_name), "packages.list", 13)) {
pr_info("packages.list detected: %d\n", mask); pr_info("packages.list detected: %d\n", mask);
if (ksu_uid_scanner_enabled) { if (ksu_uid_scanner_enabled) {
ksu_request_userspace_scan(); ksu_request_userspace_scan();
} }
track_throne(false); track_throne(false);
} }
return 0; return 0;
} }
static const struct fsnotify_ops ksu_ops = { static const struct fsnotify_ops ksu_ops = {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
.handle_inode_event = ksu_handle_generic_event, .handle_inode_event = ksu_handle_generic_event,
#else #else
.handle_event = ksu_handle_generic_event, .handle_event = ksu_handle_generic_event,
#endif #endif
}; };
static void __maybe_unused m_free(struct fsnotify_mark *m) static void __maybe_unused m_free(struct fsnotify_mark *m)
{ {
if (m) { if (m) {
kfree(m); kfree(m);
} }
} }
static int add_mark_on_inode(struct inode *inode, u32 mask, static int add_mark_on_inode(struct inode *inode, u32 mask,
struct fsnotify_mark **out) struct fsnotify_mark **out)
{ {
struct fsnotify_mark *m; struct fsnotify_mark *m;
int ret; int ret;
m = kzalloc(sizeof(*m), GFP_KERNEL); m = kzalloc(sizeof(*m), GFP_KERNEL);
if (!m) if (!m)
return -ENOMEM; return -ENOMEM;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0)
fsnotify_init_mark(m, g); fsnotify_init_mark(m, g);
m->mask = mask; m->mask = mask;
ret = fsnotify_add_inode_mark(m, inode, 0); ret = fsnotify_add_inode_mark(m, inode, 0);
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) #elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
fsnotify_init_mark(m, g); fsnotify_init_mark(m, g);
m->mask = mask; m->mask = mask;
ret = fsnotify_add_mark(m, inode, NULL, 0); ret = fsnotify_add_mark(m, inode, NULL, 0);
#else #else
fsnotify_init_mark(m, g, m_free); fsnotify_init_mark(m, g, m_free);
m->mask = mask; m->mask = mask;
ret = fsnotify_add_mark(m, g, inode, NULL, 0); ret = fsnotify_add_mark(m, g, inode, NULL, 0);
#endif #endif
if (ret < 0) { if (ret < 0) {
fsnotify_put_mark(m); fsnotify_put_mark(m);
return ret; return ret;
} }
*out = m; *out = m;
return 0; return 0;
} }
static int watch_one_dir(struct watch_dir *wd) static int watch_one_dir(struct watch_dir *wd)
{ {
int ret = kern_path(wd->path, LOOKUP_FOLLOW, &wd->kpath); int ret = kern_path(wd->path, LOOKUP_FOLLOW, &wd->kpath);
if (ret) { if (ret) {
pr_info("path not ready: %s (%d)\n", wd->path, ret); pr_info("path not ready: %s (%d)\n", wd->path, ret);
return ret; return ret;
} }
wd->inode = d_inode(wd->kpath.dentry); wd->inode = d_inode(wd->kpath.dentry);
ihold(wd->inode); ihold(wd->inode);
ret = add_mark_on_inode(wd->inode, wd->mask, &wd->mark); ret = add_mark_on_inode(wd->inode, wd->mask, &wd->mark);
if (ret) { if (ret) {
pr_err("Add mark failed for %s (%d)\n", wd->path, ret); pr_err("Add mark failed for %s (%d)\n", wd->path, ret);
path_put(&wd->kpath); path_put(&wd->kpath);
iput(wd->inode); iput(wd->inode);
wd->inode = NULL; wd->inode = NULL;
return ret; return ret;
} }
pr_info("watching %s\n", wd->path); pr_info("watching %s\n", wd->path);
return 0; return 0;
} }
static void unwatch_one_dir(struct watch_dir *wd) static void unwatch_one_dir(struct watch_dir *wd)
{ {
if (wd->mark) { if (wd->mark) {
fsnotify_destroy_mark(wd->mark, g); fsnotify_destroy_mark(wd->mark, g);
fsnotify_put_mark(wd->mark); fsnotify_put_mark(wd->mark);
wd->mark = NULL; wd->mark = NULL;
} }
if (wd->inode) { if (wd->inode) {
iput(wd->inode); iput(wd->inode);
wd->inode = NULL; wd->inode = NULL;
} }
if (wd->kpath.dentry) { if (wd->kpath.dentry) {
path_put(&wd->kpath); path_put(&wd->kpath);
memset(&wd->kpath, 0, sizeof(wd->kpath)); memset(&wd->kpath, 0, sizeof(wd->kpath));
} }
} }
static struct watch_dir g_watch = { static struct watch_dir g_watch = {
.path = "/data/system", .path = "/data/system",
.mask = MASK_SYSTEM .mask = MASK_SYSTEM
}; };
int ksu_observer_init(void) int ksu_observer_init(void)
{ {
int ret = 0; int ret = 0;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
g = fsnotify_alloc_group(&ksu_ops, 0); g = fsnotify_alloc_group(&ksu_ops, 0);
#else #else
g = fsnotify_alloc_group(&ksu_ops); g = fsnotify_alloc_group(&ksu_ops);
#endif #endif
if (IS_ERR(g)) if (IS_ERR(g))
return PTR_ERR(g); return PTR_ERR(g);
ret = watch_one_dir(&g_watch); ret = watch_one_dir(&g_watch);
pr_info("%s done.\n", __func__); pr_info("%s done.\n", __func__);
return 0; return 0;
} }
void ksu_observer_exit(void) void ksu_observer_exit(void)
{ {
unwatch_one_dir(&g_watch); unwatch_one_dir(&g_watch);
fsnotify_put_group(g); fsnotify_put_group(g);
pr_info("%s: done.\n", __func__); pr_info("%s: done.\n", __func__);
} }

View File

@@ -138,14 +138,14 @@ void apply_kernelsu_rules(void)
// Allow system server kill su process // Allow system server kill su process
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid"); ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill"); ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
#ifdef CONFIG_KSU_SUSFS #ifdef CONFIG_KSU_SUSFS
// Allow umount in zygote process without installing zygisk // Allow umount in zygote process without installing zygisk
ksu_allow(db, "zygote", "labeledfs", "filesystem", "unmount"); ksu_allow(db, "zygote", "labeledfs", "filesystem", "unmount");
susfs_set_priv_app_sid(); susfs_set_priv_app_sid();
susfs_set_init_sid(); susfs_set_init_sid();
susfs_set_ksu_sid(); susfs_set_ksu_sid();
susfs_set_zygote_sid(); susfs_set_zygote_sid();
#endif #endif
mutex_unlock(&ksu_rules); mutex_unlock(&ksu_rules);
} }
@@ -175,7 +175,7 @@ struct sepol_data {
}; };
static int get_object(char *buf, char __user *user_object, size_t buf_sz, static int get_object(char *buf, char __user *user_object, size_t buf_sz,
char **object) char **object)
{ {
if (!user_object) { if (!user_object) {
*object = ALL; *object = ALL;
@@ -190,7 +190,7 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz,
return 0; return 0;
} }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \ #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \
!defined(KSU_COMPAT_USE_SELINUX_STATE) !defined(KSU_COMPAT_USE_SELINUX_STATE)
extern int avc_ss_reset(u32 seqno); extern int avc_ss_reset(u32 seqno);
#else #else
@@ -199,7 +199,7 @@ extern int avc_ss_reset(struct selinux_avc *avc, u32 seqno);
// reset avc cache table, otherwise the new rules will not take effect if already denied // reset avc cache table, otherwise the new rules will not take effect if already denied
static void reset_avc_cache(void) static void reset_avc_cache(void)
{ {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \ #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \
!defined(KSU_COMPAT_USE_SELINUX_STATE) !defined(KSU_COMPAT_USE_SELINUX_STATE)
avc_ss_reset(0); avc_ss_reset(0);
selnl_notify_policyload(0); selnl_notify_policyload(0);
@@ -248,25 +248,25 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
char *s, *t, *c, *p; char *s, *t, *c, *p;
if (get_object(src_buf, (void __user *)data.sepol1, if (get_object(src_buf, (void __user *)data.sepol1,
sizeof(src_buf), &s) < 0) { sizeof(src_buf), &s) < 0) {
pr_err("sepol: copy src failed.\n"); pr_err("sepol: copy src failed.\n");
goto exit; goto exit;
} }
if (get_object(tgt_buf, (void __user *)data.sepol2, if (get_object(tgt_buf, (void __user *)data.sepol2,
sizeof(tgt_buf), &t) < 0) { sizeof(tgt_buf), &t) < 0) {
pr_err("sepol: copy tgt failed.\n"); pr_err("sepol: copy tgt failed.\n");
goto exit; goto exit;
} }
if (get_object(cls_buf, (void __user *)data.sepol3, if (get_object(cls_buf, (void __user *)data.sepol3,
sizeof(cls_buf), &c) < 0) { sizeof(cls_buf), &c) < 0) {
pr_err("sepol: copy cls failed.\n"); pr_err("sepol: copy cls failed.\n");
goto exit; goto exit;
} }
if (get_object(perm_buf, (void __user *)data.sepol4, if (get_object(perm_buf, (void __user *)data.sepol4,
sizeof(perm_buf), &p) < 0) { sizeof(perm_buf), &p) < 0) {
pr_err("sepol: copy perm failed.\n"); pr_err("sepol: copy perm failed.\n");
goto exit; goto exit;
} }
@@ -298,27 +298,27 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
char *s, *t, *c; char *s, *t, *c;
if (get_object(src_buf, (void __user *)data.sepol1, if (get_object(src_buf, (void __user *)data.sepol1,
sizeof(src_buf), &s) < 0) { sizeof(src_buf), &s) < 0) {
pr_err("sepol: copy src failed.\n"); pr_err("sepol: copy src failed.\n");
goto exit; goto exit;
} }
if (get_object(tgt_buf, (void __user *)data.sepol2, if (get_object(tgt_buf, (void __user *)data.sepol2,
sizeof(tgt_buf), &t) < 0) { sizeof(tgt_buf), &t) < 0) {
pr_err("sepol: copy tgt failed.\n"); pr_err("sepol: copy tgt failed.\n");
goto exit; goto exit;
} }
if (get_object(cls_buf, (void __user *)data.sepol3, if (get_object(cls_buf, (void __user *)data.sepol3,
sizeof(cls_buf), &c) < 0) { sizeof(cls_buf), &c) < 0) {
pr_err("sepol: copy cls failed.\n"); pr_err("sepol: copy cls failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(operation, (void __user *)data.sepol4, if (strncpy_from_user(operation, (void __user *)data.sepol4,
sizeof(operation)) < 0) { sizeof(operation)) < 0) {
pr_err("sepol: copy operation failed.\n"); pr_err("sepol: copy operation failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(perm_set, (void __user *)data.sepol5, if (strncpy_from_user(perm_set, (void __user *)data.sepol5,
sizeof(perm_set)) < 0) { sizeof(perm_set)) < 0) {
pr_err("sepol: copy perm_set failed.\n"); pr_err("sepol: copy perm_set failed.\n");
goto exit; goto exit;
} }
@@ -340,7 +340,7 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
char src[MAX_SEPOL_LEN]; char src[MAX_SEPOL_LEN];
if (strncpy_from_user(src, (void __user *)data.sepol1, if (strncpy_from_user(src, (void __user *)data.sepol1,
sizeof(src)) < 0) { sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n"); pr_err("sepol: copy src failed.\n");
goto exit; goto exit;
} }
@@ -363,12 +363,12 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
char attr[MAX_SEPOL_LEN]; char attr[MAX_SEPOL_LEN];
if (strncpy_from_user(type, (void __user *)data.sepol1, if (strncpy_from_user(type, (void __user *)data.sepol1,
sizeof(type)) < 0) { sizeof(type)) < 0) {
pr_err("sepol: copy type failed.\n"); pr_err("sepol: copy type failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(attr, (void __user *)data.sepol2, if (strncpy_from_user(attr, (void __user *)data.sepol2,
sizeof(attr)) < 0) { sizeof(attr)) < 0) {
pr_err("sepol: copy attr failed.\n"); pr_err("sepol: copy attr failed.\n");
goto exit; goto exit;
} }
@@ -390,7 +390,7 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
char attr[MAX_SEPOL_LEN]; char attr[MAX_SEPOL_LEN];
if (strncpy_from_user(attr, (void __user *)data.sepol1, if (strncpy_from_user(attr, (void __user *)data.sepol1,
sizeof(attr)) < 0) { sizeof(attr)) < 0) {
pr_err("sepol: copy attr failed.\n"); pr_err("sepol: copy attr failed.\n");
goto exit; goto exit;
} }
@@ -409,22 +409,22 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
char object[MAX_SEPOL_LEN]; char object[MAX_SEPOL_LEN];
if (strncpy_from_user(src, (void __user *)data.sepol1, if (strncpy_from_user(src, (void __user *)data.sepol1,
sizeof(src)) < 0) { sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n"); pr_err("sepol: copy src failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(tgt, (void __user *)data.sepol2, if (strncpy_from_user(tgt, (void __user *)data.sepol2,
sizeof(tgt)) < 0) { sizeof(tgt)) < 0) {
pr_err("sepol: copy tgt failed.\n"); pr_err("sepol: copy tgt failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(cls, (void __user *)data.sepol3, if (strncpy_from_user(cls, (void __user *)data.sepol3,
sizeof(cls)) < 0) { sizeof(cls)) < 0) {
pr_err("sepol: copy cls failed.\n"); pr_err("sepol: copy cls failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(default_type, (void __user *)data.sepol4, if (strncpy_from_user(default_type, (void __user *)data.sepol4,
sizeof(default_type)) < 0) { sizeof(default_type)) < 0) {
pr_err("sepol: copy default_type failed.\n"); pr_err("sepol: copy default_type failed.\n");
goto exit; goto exit;
} }
@@ -433,8 +433,8 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
real_object = NULL; real_object = NULL;
} else { } else {
if (strncpy_from_user(object, if (strncpy_from_user(object,
(void __user *)data.sepol5, (void __user *)data.sepol5,
sizeof(object)) < 0) { sizeof(object)) < 0) {
pr_err("sepol: copy object failed.\n"); pr_err("sepol: copy object failed.\n");
goto exit; goto exit;
} }
@@ -454,22 +454,22 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
char default_type[MAX_SEPOL_LEN]; char default_type[MAX_SEPOL_LEN];
if (strncpy_from_user(src, (void __user *)data.sepol1, if (strncpy_from_user(src, (void __user *)data.sepol1,
sizeof(src)) < 0) { sizeof(src)) < 0) {
pr_err("sepol: copy src failed.\n"); pr_err("sepol: copy src failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(tgt, (void __user *)data.sepol2, if (strncpy_from_user(tgt, (void __user *)data.sepol2,
sizeof(tgt)) < 0) { sizeof(tgt)) < 0) {
pr_err("sepol: copy tgt failed.\n"); pr_err("sepol: copy tgt failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(cls, (void __user *)data.sepol3, if (strncpy_from_user(cls, (void __user *)data.sepol3,
sizeof(cls)) < 0) { sizeof(cls)) < 0) {
pr_err("sepol: copy cls failed.\n"); pr_err("sepol: copy cls failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(default_type, (void __user *)data.sepol4, if (strncpy_from_user(default_type, (void __user *)data.sepol4,
sizeof(default_type)) < 0) { sizeof(default_type)) < 0) {
pr_err("sepol: copy default_type failed.\n"); pr_err("sepol: copy default_type failed.\n");
goto exit; goto exit;
} }
@@ -492,17 +492,17 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
char path[MAX_SEPOL_LEN]; char path[MAX_SEPOL_LEN];
char context[MAX_SEPOL_LEN]; char context[MAX_SEPOL_LEN];
if (strncpy_from_user(name, (void __user *)data.sepol1, if (strncpy_from_user(name, (void __user *)data.sepol1,
sizeof(name)) < 0) { sizeof(name)) < 0) {
pr_err("sepol: copy name failed.\n"); pr_err("sepol: copy name failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(path, (void __user *)data.sepol2, if (strncpy_from_user(path, (void __user *)data.sepol2,
sizeof(path)) < 0) { sizeof(path)) < 0) {
pr_err("sepol: copy path failed.\n"); pr_err("sepol: copy path failed.\n");
goto exit; goto exit;
} }
if (strncpy_from_user(context, (void __user *)data.sepol3, if (strncpy_from_user(context, (void __user *)data.sepol3,
sizeof(context)) < 0) { sizeof(context)) < 0) {
pr_err("sepol: copy context failed.\n"); pr_err("sepol: copy context failed.\n");
goto exit; goto exit;
} }

View File

@@ -82,7 +82,7 @@ bool getenforce(void)
return __is_selinux_enforcing(); return __is_selinux_enforcing();
} }
#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \ #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) && \
!defined(KSU_COMPAT_HAS_CURRENT_SID) !defined(KSU_COMPAT_HAS_CURRENT_SID)
/* /*
* get the subjective security ID of the current task * get the subjective security ID of the current task
@@ -197,81 +197,81 @@ u32 susfs_priv_app_sid = 0;
static inline void susfs_set_sid(const char *secctx_name, u32 *out_sid) static inline void susfs_set_sid(const char *secctx_name, u32 *out_sid)
{ {
int err; int err;
if (!secctx_name || !out_sid) { if (!secctx_name || !out_sid) {
pr_err("secctx_name || out_sid is NULL\n"); pr_err("secctx_name || out_sid is NULL\n");
return; return;
} }
err = security_secctx_to_secid(secctx_name, strlen(secctx_name), err = security_secctx_to_secid(secctx_name, strlen(secctx_name),
out_sid); out_sid);
if (err) { if (err) {
pr_err("failed setting sid for '%s', err: %d\n", secctx_name, err); pr_err("failed setting sid for '%s', err: %d\n", secctx_name, err);
return; return;
} }
pr_info("sid '%u' is set for secctx_name '%s'\n", *out_sid, secctx_name); pr_info("sid '%u' is set for secctx_name '%s'\n", *out_sid, secctx_name);
} }
bool susfs_is_sid_equal(void *sec, u32 sid2) { bool susfs_is_sid_equal(void *sec, u32 sid2) {
struct task_security_struct *tsec = (struct task_security_struct *)sec; struct task_security_struct *tsec = (struct task_security_struct *)sec;
if (!tsec) { if (!tsec) {
return false; return false;
} }
return tsec->sid == sid2; return tsec->sid == sid2;
} }
u32 susfs_get_sid_from_name(const char *secctx_name) u32 susfs_get_sid_from_name(const char *secctx_name)
{ {
u32 out_sid = 0; u32 out_sid = 0;
int err; int err;
if (!secctx_name) { if (!secctx_name) {
pr_err("secctx_name is NULL\n"); pr_err("secctx_name is NULL\n");
return 0; return 0;
} }
err = security_secctx_to_secid(secctx_name, strlen(secctx_name), err = security_secctx_to_secid(secctx_name, strlen(secctx_name),
&out_sid); &out_sid);
if (err) { if (err) {
pr_err("failed getting sid from secctx_name: %s, err: %d\n", secctx_name, err); pr_err("failed getting sid from secctx_name: %s, err: %d\n", secctx_name, err);
return 0; return 0;
} }
return out_sid; return out_sid;
} }
u32 susfs_get_current_sid(void) { u32 susfs_get_current_sid(void) {
return current_sid(); return current_sid();
} }
void susfs_set_zygote_sid(void) void susfs_set_zygote_sid(void)
{ {
susfs_set_sid(KERNEL_ZYGOTE_DOMAIN, &susfs_zygote_sid); susfs_set_sid(KERNEL_ZYGOTE_DOMAIN, &susfs_zygote_sid);
} }
bool susfs_is_current_zygote_domain(void) { bool susfs_is_current_zygote_domain(void) {
return unlikely(current_sid() == susfs_zygote_sid); return unlikely(current_sid() == susfs_zygote_sid);
} }
void susfs_set_ksu_sid(void) void susfs_set_ksu_sid(void)
{ {
susfs_set_sid(KERNEL_SU_DOMAIN, &susfs_ksu_sid); susfs_set_sid(KERNEL_SU_DOMAIN, &susfs_ksu_sid);
} }
bool susfs_is_current_ksu_domain(void) { bool susfs_is_current_ksu_domain(void) {
return unlikely(current_sid() == susfs_ksu_sid); return unlikely(current_sid() == susfs_ksu_sid);
} }
void susfs_set_init_sid(void) void susfs_set_init_sid(void)
{ {
susfs_set_sid(KERNEL_INIT_DOMAIN, &susfs_init_sid); susfs_set_sid(KERNEL_INIT_DOMAIN, &susfs_init_sid);
} }
bool susfs_is_current_init_domain(void) { bool susfs_is_current_init_domain(void) {
return unlikely(current_sid() == susfs_init_sid); return unlikely(current_sid() == susfs_init_sid);
} }
void susfs_set_priv_app_sid(void) void susfs_set_priv_app_sid(void)
{ {
susfs_set_sid(KERNEL_PRIV_APP_DOMAIN, &susfs_priv_app_sid); susfs_set_sid(KERNEL_PRIV_APP_DOMAIN, &susfs_priv_app_sid);
} }
#endif #endif

View File

@@ -5,7 +5,7 @@
#include "linux/version.h" #include "linux/version.h"
#include "linux/cred.h" #include "linux/cred.h"
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || \ #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || \
defined(KSU_COMPAT_HAS_SELINUX_STATE) defined(KSU_COMPAT_HAS_SELINUX_STATE)
#define KSU_COMPAT_USE_SELINUX_STATE #define KSU_COMPAT_USE_SELINUX_STATE
#endif #endif

View File

@@ -19,16 +19,16 @@ static struct avtab_node *get_avtab_node(struct policydb *db,
struct avtab_extended_perms *xperms); struct avtab_extended_perms *xperms);
static bool add_rule(struct policydb *db, const char *s, const char *t, 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, static void add_rule_raw(struct policydb *db, struct type_datum *src,
struct type_datum *tgt, struct class_datum *cls, struct type_datum *tgt, struct class_datum *cls,
struct perm_datum *perm, int effect, bool invert); struct perm_datum *perm, int effect, bool invert);
static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src, static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
struct type_datum *tgt, struct class_datum *cls, struct type_datum *tgt, struct class_datum *cls,
uint16_t low, uint16_t high, int effect, uint16_t low, uint16_t high, int effect,
bool invert); bool invert);
static bool add_xperm_rule(struct policydb *db, const char *s, const char *t, static bool add_xperm_rule(struct policydb *db, const char *s, const char *t,
const char *c, const char *range, int effect, const char *c, const char *range, int effect,
bool invert); bool invert);
@@ -37,8 +37,8 @@ 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, static bool add_filename_trans(struct policydb *db, const char *s,
const char *t, const char *c, const char *d, const char *t, const char *c, const char *d,
const char *o); const char *o);
static bool add_genfscon(struct policydb *db, const char *fs_name, static bool add_genfscon(struct policydb *db, const char *fs_name,
const char *path, const char *context); const char *path, const char *context);
@@ -52,7 +52,7 @@ 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, static bool add_typeattribute(struct policydb *db, const char *type,
const char *attr); const char *attr);
////////////////////////////////////////////////////// //////////////////////////////////////////////////////
// Implementation // Implementation
@@ -62,18 +62,18 @@ static bool add_typeattribute(struct policydb *db, const char *type,
// rules // rules
#define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert) #define strip_av(effect, invert) ((effect == AVTAB_AUDITDENY) == !invert)
#define ksu_hash_for_each(node_ptr, n_slot, cur) \ #define ksu_hash_for_each(node_ptr, n_slot, cur) \
int i; \ int i; \
for (i = 0; i < n_slot; ++i) \ for (i = 0; i < n_slot; ++i) \
for (cur = node_ptr[i]; cur; cur = cur->next) for (cur = node_ptr[i]; cur; cur = cur->next)
// htable is a struct instead of pointer above 5.8.0: // 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 // https://elixir.bootlin.com/linux/v5.8-rc1/source/security/selinux/ss/symtab.h
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
#define ksu_hashtab_for_each(htab, cur) \ #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 #else
#define ksu_hashtab_for_each(htab, cur) \ #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 #endif
@@ -84,7 +84,7 @@ static bool add_typeattribute(struct policydb *db, const char *type,
#define symtab_insert(s, name, datum) hashtab_insert((s)->table, name, datum) #define symtab_insert(s, name, datum) hashtab_insert((s)->table, name, datum)
#endif #endif
#define avtab_for_each(avtab, cur) \ #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, static struct avtab_node *get_avtab_node(struct policydb *db,
@@ -99,8 +99,8 @@ static struct avtab_node *get_avtab_node(struct policydb *db,
node = avtab_search_node(&db->te_avtab, key); node = avtab_search_node(&db->te_avtab, key);
while (node) { while (node) {
if ((node->datum.u.xperms->specified == if ((node->datum.u.xperms->specified ==
xperms->specified) && xperms->specified) &&
(node->datum.u.xperms->driver == xperms->driver)) { (node->datum.u.xperms->driver == xperms->driver)) {
match = true; match = true;
break; break;
} }
@@ -115,9 +115,9 @@ static struct avtab_node *get_avtab_node(struct policydb *db,
if (!node) { if (!node) {
struct avtab_datum avdatum = {}; struct avtab_datum avdatum = {};
/* /*
* AUDITDENY, aka DONTAUDIT, are &= assigned, versus |= for * AUDITDENY, aka DONTAUDIT, are &= assigned, versus |= for
* others. Initialize the data accordingly. * others. Initialize the data accordingly.
*/ */
if (key->specified & AVTAB_XPERMS) { if (key->specified & AVTAB_XPERMS) {
avdatum.u.xperms = xperms; avdatum.u.xperms = xperms;
} else { } else {
@@ -133,7 +133,7 @@ static struct avtab_node *get_avtab_node(struct policydb *db,
grow_size += sizeof(u8); grow_size += sizeof(u8);
grow_size += sizeof(u8); grow_size += sizeof(u8);
grow_size += sizeof(u32) * grow_size += sizeof(u32) *
ARRAY_SIZE(avdatum.u.xperms->perms.p); ARRAY_SIZE(avdatum.u.xperms->perms.p);
} }
db->len += grow_size; db->len += grow_size;
} }
@@ -142,7 +142,7 @@ static struct avtab_node *get_avtab_node(struct policydb *db,
} }
static bool add_rule(struct policydb *db, const char *s, const char *t, 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 type_datum *src = NULL, *tgt = NULL;
struct class_datum *cls = NULL; struct class_datum *cls = NULL;
@@ -202,8 +202,8 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
ksu_hashtab_for_each(db->p_types.table, node) ksu_hashtab_for_each(db->p_types.table, node)
{ {
add_rule_raw(db, add_rule_raw(db,
(struct type_datum *)node->datum, (struct type_datum *)node->datum,
tgt, cls, perm, effect, invert); tgt, cls, perm, effect, invert);
}; };
} else { } else {
ksu_hashtab_for_each(db->p_types.table, node) ksu_hashtab_for_each(db->p_types.table, node)
@@ -212,7 +212,7 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
(struct type_datum *)(node->datum); (struct type_datum *)(node->datum);
if (type->attribute) { if (type->attribute) {
add_rule_raw(db, type, tgt, cls, perm, add_rule_raw(db, type, tgt, cls, perm,
effect, invert); effect, invert);
} }
}; };
} }
@@ -222,8 +222,8 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
ksu_hashtab_for_each(db->p_types.table, node) ksu_hashtab_for_each(db->p_types.table, node)
{ {
add_rule_raw(db, src, add_rule_raw(db, src,
(struct type_datum *)node->datum, (struct type_datum *)node->datum,
cls, perm, effect, invert); cls, perm, effect, invert);
}; };
} else { } else {
ksu_hashtab_for_each(db->p_types.table, node) ksu_hashtab_for_each(db->p_types.table, node)
@@ -232,7 +232,7 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
(struct type_datum *)(node->datum); (struct type_datum *)(node->datum);
if (type->attribute) { if (type->attribute) {
add_rule_raw(db, src, type, cls, perm, add_rule_raw(db, src, type, cls, perm,
effect, invert); effect, invert);
} }
}; };
} }
@@ -241,8 +241,8 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
ksu_hashtab_for_each(db->p_classes.table, node) ksu_hashtab_for_each(db->p_classes.table, node)
{ {
add_rule_raw(db, src, tgt, add_rule_raw(db, src, tgt,
(struct class_datum *)node->datum, perm, (struct class_datum *)node->datum, perm,
effect, invert); effect, invert);
} }
} else { } else {
struct avtab_key key; struct avtab_key key;
@@ -275,9 +275,9 @@ static void add_rule_raw(struct policydb *db, struct type_datum *src,
#define xperm_clear(x, p) (p[x >> 5] &= ~(1 << (x & 0x1f))) #define xperm_clear(x, p) (p[x >> 5] &= ~(1 << (x & 0x1f)))
static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src, static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
struct type_datum *tgt, struct class_datum *cls, struct type_datum *tgt, struct class_datum *cls,
uint16_t low, uint16_t high, int effect, uint16_t low, uint16_t high, int effect,
bool invert) bool invert)
{ {
if (src == NULL) { if (src == NULL) {
struct hashtab_node *node; struct hashtab_node *node;
@@ -331,7 +331,7 @@ static void add_xperm_rule_raw(struct policydb *db, struct type_datum *src,
int i; int i;
if (xperms.specified == AVTAB_XPERMS_IOCTLDRIVER) { if (xperms.specified == AVTAB_XPERMS_IOCTLDRIVER) {
for (i = ioctl_driver(low); i <= ioctl_driver(high); for (i = ioctl_driver(low); i <= ioctl_driver(high);
++i) { ++i) {
if (invert) if (invert)
xperm_clear(i, xperms.perms.p); xperm_clear(i, xperms.perms.p);
else else
@@ -497,8 +497,8 @@ static const struct hashtab_key_params filenametr_key_params = {
#endif #endif
static bool add_filename_trans(struct policydb *db, const char *s, static bool add_filename_trans(struct policydb *db, const char *s,
const char *t, const char *c, const char *d, const char *t, const char *c, const char *d,
const char *o) const char *o)
{ {
struct type_datum *src, *tgt, *def; struct type_datum *src, *tgt, *def;
struct class_datum *cls; struct class_datum *cls;
@@ -553,16 +553,16 @@ static bool add_filename_trans(struct policydb *db, const char *s,
if (trans == NULL) { if (trans == NULL) {
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans), trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
1, GFP_ATOMIC); 1, GFP_ATOMIC);
struct filename_trans_key *new_key = struct filename_trans_key *new_key =
(struct filename_trans_key *)kzalloc(sizeof(*new_key), (struct filename_trans_key *)kzalloc(sizeof(*new_key),
GFP_ATOMIC); GFP_ATOMIC);
*new_key = key; *new_key = key;
new_key->name = kstrdup(key.name, GFP_ATOMIC); new_key->name = kstrdup(key.name, GFP_ATOMIC);
trans->next = last; trans->next = last;
trans->otype = def->value; trans->otype = def->value;
hashtab_insert(&db->filename_trans, new_key, trans, hashtab_insert(&db->filename_trans, new_key, trans,
filenametr_key_params); filenametr_key_params);
} }
db->compat_filename_trans_count++; db->compat_filename_trans_count++;
@@ -579,7 +579,7 @@ static bool add_filename_trans(struct policydb *db, const char *s,
if (trans == NULL) { if (trans == NULL) {
trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans), trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans),
1, GFP_ATOMIC); 1, GFP_ATOMIC);
if (!trans) { if (!trans) {
pr_err("add_filename_trans: Failed to alloc datum\n"); pr_err("add_filename_trans: Failed to alloc datum\n");
return false; return false;
@@ -598,7 +598,7 @@ static bool add_filename_trans(struct policydb *db, const char *s,
} }
return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) == return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) ==
0; 0;
#endif #endif
} }
@@ -635,7 +635,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
u32 value = ++db->p_types.nprim; u32 value = ++db->p_types.nprim;
type = (struct type_datum *)kzalloc(sizeof(struct type_datum), type = (struct type_datum *)kzalloc(sizeof(struct type_datum),
GFP_ATOMIC); GFP_ATOMIC);
if (!type) { if (!type) {
pr_err("add_type: alloc type_datum failed.\n"); pr_err("add_type: alloc type_datum failed.\n");
return false; return false;
@@ -659,8 +659,8 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0)
struct ebitmap *new_type_attr_map_array = struct ebitmap *new_type_attr_map_array =
ksu_realloc(db->type_attr_map_array, ksu_realloc(db->type_attr_map_array,
value * sizeof(struct ebitmap), value * sizeof(struct ebitmap),
(value - 1) * sizeof(struct ebitmap)); (value - 1) * sizeof(struct ebitmap));
if (!new_type_attr_map_array) { if (!new_type_attr_map_array) {
pr_err("add_type: alloc type_attr_map_array failed\n"); pr_err("add_type: alloc type_attr_map_array failed\n");
@@ -669,8 +669,8 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
struct type_datum **new_type_val_to_struct = struct type_datum **new_type_val_to_struct =
ksu_realloc(db->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,
sizeof(*db->type_val_to_struct) * (value - 1)); sizeof(*db->type_val_to_struct) * (value - 1));
if (!new_type_val_to_struct) { if (!new_type_val_to_struct) {
pr_err("add_type: alloc type_val_to_struct failed\n"); pr_err("add_type: alloc type_val_to_struct failed\n");
@@ -679,8 +679,8 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
char **new_val_to_name_types = char **new_val_to_name_types =
ksu_realloc(db->sym_val_to_name[SYM_TYPES], ksu_realloc(db->sym_val_to_name[SYM_TYPES],
sizeof(char *) * value, sizeof(char *) * value,
sizeof(char *) * (value - 1)); sizeof(char *) * (value - 1));
if (!new_val_to_name_types) { if (!new_val_to_name_types) {
pr_err("add_type: alloc val_to_name failed\n"); pr_err("add_type: alloc val_to_name failed\n");
return false; return false;
@@ -809,7 +809,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr)
old_elem = flex_array_get(db->type_attr_map_array, j); old_elem = flex_array_get(db->type_attr_map_array, j);
if (old_elem) if (old_elem)
flex_array_put(new_type_attr_map_array, j, old_elem, flex_array_put(new_type_attr_map_array, j, old_elem,
GFP_ATOMIC | __GFP_ZERO); GFP_ATOMIC | __GFP_ZERO);
} }
for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) { for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) {
@@ -880,7 +880,7 @@ static bool set_type_state(struct policydb *db, const char *type_name,
{ {
type = (struct type_datum *)(node->datum); type = (struct type_datum *)(node->datum);
if (ebitmap_set_bit(&db->permissive_map, type->value, if (ebitmap_set_bit(&db->permissive_map, type->value,
permissive)) permissive))
pr_info("Could not set bit in permissive map\n"); pr_info("Could not set bit in permissive map\n");
}; };
} else { } else {
@@ -891,7 +891,7 @@ static bool set_type_state(struct policydb *db, const char *type_name,
return false; return false;
} }
if (ebitmap_set_bit(&db->permissive_map, type->value, if (ebitmap_set_bit(&db->permissive_map, type->value,
permissive)) { permissive)) {
pr_info("Could not set bit in permissive map\n"); pr_info("Could not set bit in permissive map\n");
return false; return false;
} }
@@ -909,7 +909,7 @@ static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
* HISI_SELINUX_EBITMAP_RO is Huawei's unique features. * HISI_SELINUX_EBITMAP_RO is Huawei's unique features.
*/ */
struct ebitmap *sattr = &db->type_attr_map[type->value - 1], struct ebitmap *sattr = &db->type_attr_map[type->value - 1],
HISI_SELINUX_EBITMAP_RO; HISI_SELINUX_EBITMAP_RO;
#else #else
struct ebitmap *sattr = struct ebitmap *sattr =
flex_array_get(db->type_attr_map_array, type->value - 1); flex_array_get(db->type_attr_map_array, type->value - 1);
@@ -925,8 +925,8 @@ static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
for (n = cls->constraints; n; n = n->next) { for (n = cls->constraints; n; n = n->next) {
for (e = n->expr; e; e = e->next) { for (e = n->expr; e; e = e->next) {
if (e->expr_type == CEXPR_NAMES && if (e->expr_type == CEXPR_NAMES &&
ebitmap_get_bit(&e->type_names->types, ebitmap_get_bit(&e->type_names->types,
attr->value - 1)) { attr->value - 1)) {
ebitmap_set_bit(&e->names, ebitmap_set_bit(&e->names,
type->value - 1, 1); type->value - 1, 1);
} }
@@ -936,7 +936,7 @@ static void add_typeattribute_raw(struct policydb *db, struct type_datum *type,
} }
static bool add_typeattribute(struct policydb *db, const char *type, 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); struct type_datum *type_d = symtab_search(&db->p_types, type);
if (type_d == NULL) { if (type_d == NULL) {
@@ -995,19 +995,19 @@ bool ksu_exists(struct policydb *db, const char *type)
// Access vector rules // Access vector rules
bool ksu_allow(struct policydb *db, const char *src, const char *tgt, bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *perm) const char *cls, const char *perm)
{ {
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, 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, 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);
} }
@@ -1019,24 +1019,24 @@ bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
// Extended permissions access vector rules // Extended permissions access vector rules
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt, bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *range) const char *cls, const char *range)
{ {
return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_ALLOWED, return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_ALLOWED,
false); false);
} }
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt, bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *range) const char *cls, const char *range)
{ {
return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_AUDITALLOW, return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_AUDITALLOW,
false); false);
} }
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt, bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *range) const char *cls, const char *range)
{ {
return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_DONTAUDIT, return add_xperm_rule(db, src, tgt, cls, range, AVTAB_XPERMS_DONTAUDIT,
false); false);
} }
// Type rules // Type rules
@@ -1051,13 +1051,13 @@ bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
} }
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt, bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *def) const char *cls, const char *def)
{ {
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, 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);
} }

View File

@@ -15,32 +15,32 @@ bool ksu_exists(struct policydb *db, const char *type);
// Access vector rules // Access vector rules
bool ksu_allow(struct policydb *db, const char *src, const char *tgt, bool ksu_allow(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *perm); const char *cls, const char *perm);
bool ksu_deny(struct policydb *db, const char *src, const char *tgt, bool ksu_deny(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *perm); const char *cls, const char *perm);
bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt, bool ksu_auditallow(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *perm); const char *cls, const char *perm);
bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt, bool ksu_dontaudit(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *perm); const char *cls, const char *perm);
// Extended permissions access vector rules // Extended permissions access vector rules
bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt, bool ksu_allowxperm(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *range); const char *cls, const char *range);
bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt, bool ksu_auditallowxperm(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *range); const char *cls, const char *range);
bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt, bool ksu_dontauditxperm(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *range); const char *cls, const char *range);
// Type rules // Type rules
bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt, bool ksu_type_transition(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *def, const char *obj); const char *cls, const char *def, const char *obj);
bool ksu_type_change(struct policydb *db, const char *src, const char *tgt, bool ksu_type_change(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *def); const char *cls, const char *def);
bool ksu_type_member(struct policydb *db, const char *src, const char *tgt, bool ksu_type_member(struct policydb *db, const char *src, const char *tgt,
const char *cls, const char *def); const char *cls, const char *def);
// File system labeling // File system labeling
bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path, bool ksu_genfscon(struct policydb *db, const char *fs_name, const char *path,
const char *ctx); const char *ctx);
#endif #endif

View File

@@ -50,20 +50,20 @@
#ifdef CONFIG_KSU_SUSFS #ifdef CONFIG_KSU_SUSFS
static inline bool is_some_system_uid(uid_t uid) static inline bool is_some_system_uid(uid_t uid)
{ {
uid %= 100000; uid %= 100000;
return (uid >= 1000 && uid < 10000); return (uid >= 1000 && uid < 10000);
} }
static inline bool is_zygote_isolated_service_uid(uid_t uid) static inline bool is_zygote_isolated_service_uid(uid_t uid)
{ {
uid %= 100000; uid %= 100000;
return (uid >= 90000 && uid < 100000); return (uid >= 90000 && uid < 100000);
} }
static inline bool is_zygote_normal_app_uid(uid_t uid) static inline bool is_zygote_normal_app_uid(uid_t uid)
{ {
uid %= 100000; uid %= 100000;
return (uid >= 10000 && uid < 19999); return (uid >= 10000 && uid < 19999);
} }
bool susfs_is_umount_for_zygote_system_process_enabled = false; bool susfs_is_umount_for_zygote_system_process_enabled = false;
@@ -148,7 +148,7 @@ int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid)
// disallow appuid decrease to any other uid if it is not allowed to su // disallow appuid decrease to any other uid if it is not allowed to su
if (is_appuid(old_uid)) { if (is_appuid(old_uid)) {
if (euid < current_euid().val && if (euid < current_euid().val &&
!ksu_is_allow_uid_for_current(old_uid)) { !ksu_is_allow_uid_for_current(old_uid)) {
pr_warn("find suspicious EoP: %d %s, from %d to %d\n", pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
current->pid, current->comm, old_uid, current->pid, current->comm, old_uid,
new_uid); new_uid);
@@ -161,7 +161,7 @@ int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid)
// if on private space, see if its possibly the manager // if on private space, see if its possibly the manager
if (new_uid > PER_USER_RANGE && if (new_uid > PER_USER_RANGE &&
new_uid % PER_USER_RANGE == ksu_get_manager_uid()) { new_uid % PER_USER_RANGE == ksu_get_manager_uid()) {
ksu_set_manager_uid(new_uid); ksu_set_manager_uid(new_uid);
} }
@@ -178,7 +178,7 @@ int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid)
if (ksu_is_allow_uid_for_current(new_uid)) { if (ksu_is_allow_uid_for_current(new_uid)) {
if (current->seccomp.mode == SECCOMP_MODE_FILTER && if (current->seccomp.mode == SECCOMP_MODE_FILTER &&
current->seccomp.filter) { current->seccomp.filter) {
spin_lock_irq(&current->sighand->siglock); spin_lock_irq(&current->sighand->siglock);
ksu_seccomp_allow_cache(current->seccomp.filter, ksu_seccomp_allow_cache(current->seccomp.filter,
__NR_reboot); __NR_reboot);
@@ -213,53 +213,53 @@ int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid)
extern bool ksu_kernel_umount_enabled; extern bool ksu_kernel_umount_enabled;
extern bool ksu_module_mounted; extern bool ksu_module_mounted;
int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid){ int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid){
// we rely on the fact that zygote always call setresuid(3) with same uids // we rely on the fact that zygote always call setresuid(3) with same uids
uid_t new_uid = ruid; uid_t new_uid = ruid;
uid_t old_uid = current_uid().val; uid_t old_uid = current_uid().val;
// if old process is root, ignore it. // if old process is root, ignore it.
if (old_uid != 0 && ksu_enhanced_security_enabled) { if (old_uid != 0 && ksu_enhanced_security_enabled) {
// disallow any non-ksu domain escalation from non-root to root! // disallow any non-ksu domain escalation from non-root to root!
// euid is what we care about here as it controls permission // euid is what we care about here as it controls permission
if (unlikely(euid == 0)) { if (unlikely(euid == 0)) {
if (!is_ksu_domain()) { if (!is_ksu_domain()) {
pr_warn("find suspicious EoP: %d %s, from %d to %d\n", pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
current->pid, current->comm, old_uid, new_uid); current->pid, current->comm, old_uid, new_uid);
__force_sig(SIGKILL); __force_sig(SIGKILL);
return 0; return 0;
} }
} }
// disallow appuid decrease to any other uid if it is not allowed to su // disallow appuid decrease to any other uid if it is not allowed to su
if (is_appuid(old_uid)) { if (is_appuid(old_uid)) {
if (euid < current_euid().val && !ksu_is_allow_uid_for_current(old_uid)) { if (euid < current_euid().val && !ksu_is_allow_uid_for_current(old_uid)) {
pr_warn("find suspicious EoP: %d %s, from %d to %d\n", pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
current->pid, current->comm, old_uid, new_uid); current->pid, current->comm, old_uid, new_uid);
__force_sig(SIGKILL); __force_sig(SIGKILL);
return 0; return 0;
} }
} }
return 0; return 0;
} }
// We only interest in process spwaned by zygote // We only interest in process spwaned by zygote
if (!susfs_is_sid_equal(current_cred()->security, susfs_zygote_sid)) { if (!susfs_is_sid_equal(current_cred()->security, susfs_zygote_sid)) {
return 0; return 0;
} }
#if __SULOG_GATE #if __SULOG_GATE
ksu_sulog_report_syscall(new_uid, NULL, "setuid", NULL); ksu_sulog_report_syscall(new_uid, NULL, "setuid", NULL);
#endif #endif
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// Check if spawned process is isolated service first, and force to do umount if so // Check if spawned process is isolated service first, and force to do umount if so
if (is_zygote_isolated_service_uid(new_uid) && susfs_is_umount_for_zygote_iso_service_enabled) { if (is_zygote_isolated_service_uid(new_uid) && susfs_is_umount_for_zygote_iso_service_enabled) {
goto do_umount; goto do_umount;
} }
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT #endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// - Since ksu maanger app uid is excluded in allow_list_arr, so ksu_uid_should_umount(manager_uid) // - Since ksu maanger app uid is excluded in allow_list_arr, so ksu_uid_should_umount(manager_uid)
// will always return true, that's why we need to explicitly check if new_uid belongs to // will always return true, that's why we need to explicitly check if new_uid belongs to
// ksu manager // ksu manager
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
if (ksu_get_manager_uid() == new_uid) { if (ksu_get_manager_uid() == new_uid) {
pr_info("install fd for ksu manager(uid=%d)\n", new_uid); pr_info("install fd for ksu manager(uid=%d)\n", new_uid);
@@ -272,7 +272,7 @@ int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid){
if (ksu_is_allow_uid_for_current(new_uid)) { if (ksu_is_allow_uid_for_current(new_uid)) {
if (current->seccomp.mode == SECCOMP_MODE_FILTER && if (current->seccomp.mode == SECCOMP_MODE_FILTER &&
current->seccomp.filter) { current->seccomp.filter) {
spin_lock_irq(&current->sighand->siglock); spin_lock_irq(&current->sighand->siglock);
ksu_seccomp_allow_cache(current->seccomp.filter, ksu_seccomp_allow_cache(current->seccomp.filter,
__NR_reboot); __NR_reboot);
@@ -297,48 +297,48 @@ int ksu_handle_setresuid(uid_t ruid, uid_t euid, uid_t suid){
} }
#endif #endif
// Check if spawned process is normal user app and needs to be umounted // Check if spawned process is normal user app and needs to be umounted
if (likely(is_zygote_normal_app_uid(new_uid) && ksu_uid_should_umount(new_uid))) { if (likely(is_zygote_normal_app_uid(new_uid) && ksu_uid_should_umount(new_uid))) {
goto do_umount; goto do_umount;
} }
// Lastly, Check if spawned process is some system process and needs to be umounted // Lastly, Check if spawned process is some system process and needs to be umounted
if (unlikely(is_some_system_uid(new_uid) && susfs_is_umount_for_zygote_system_process_enabled)) { if (unlikely(is_some_system_uid(new_uid) && susfs_is_umount_for_zygote_system_process_enabled)) {
goto do_umount; goto do_umount;
} }
return 0; return 0;
do_umount: do_umount:
if (!ksu_kernel_umount_enabled || !ksu_module_mounted) { if (!ksu_kernel_umount_enabled || !ksu_module_mounted) {
goto skip_try_umount; goto skip_try_umount;
} }
#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
pr_info("susfs: running susfs_try_umount_all() for uid: %u\n", new_uid); pr_info("susfs: running susfs_try_umount_all() for uid: %u\n", new_uid);
susfs_try_umount_all(); susfs_try_umount_all();
#else #else
// Handle kernel umount // Handle kernel umount
ksu_handle_umount(old_uid, new_uid); ksu_handle_umount(old_uid, new_uid);
#endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT #endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
skip_try_umount: skip_try_umount:
get_task_struct(current); get_task_struct(current);
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// We can reorder the mnt_id now after all sus mounts are umounted // We can reorder the mnt_id now after all sus mounts are umounted
susfs_reorder_mnt_id(); susfs_reorder_mnt_id();
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT #endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
susfs_set_current_proc_umounted(); susfs_set_current_proc_umounted();
put_task_struct(current); put_task_struct(current);
#ifdef CONFIG_KSU_SUSFS_SUS_PATH #ifdef CONFIG_KSU_SUSFS_SUS_PATH
susfs_run_sus_path_loop(new_uid); susfs_run_sus_path_loop(new_uid);
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_PATH #endif // #ifdef CONFIG_KSU_SUSFS_SUS_PATH
return 0; return 0;
} }
#endif // #ifndef CONFIG_KSU_SUSFS #endif // #ifndef CONFIG_KSU_SUSFS

View File

@@ -4,77 +4,77 @@ set -eu
GKI_ROOT=$(pwd) GKI_ROOT=$(pwd)
display_usage() { display_usage() {
echo "Usage: $0 [--cleanup | <commit-or-tag>]" echo "Usage: $0 [--cleanup | <commit-or-tag>]"
echo " --cleanup: Cleans up previous modifications made by the script." echo " --cleanup: Cleans up previous modifications made by the script."
echo " <commit-or-tag>: Sets up or updates the KernelSU to specified tag or commit." echo " <commit-or-tag>: Sets up or updates the KernelSU to specified tag or commit."
echo " -h, --help: Displays this usage information." echo " -h, --help: Displays this usage information."
echo " (no args): Sets up or updates the KernelSU environment to the latest tagged version." echo " (no args): Sets up or updates the KernelSU environment to the latest tagged version."
} }
initialize_variables() { initialize_variables() {
if test -d "$GKI_ROOT/common/drivers"; then if test -d "$GKI_ROOT/common/drivers"; then
DRIVER_DIR="$GKI_ROOT/common/drivers" DRIVER_DIR="$GKI_ROOT/common/drivers"
elif test -d "$GKI_ROOT/drivers"; then elif test -d "$GKI_ROOT/drivers"; then
DRIVER_DIR="$GKI_ROOT/drivers" DRIVER_DIR="$GKI_ROOT/drivers"
else else
echo '[ERROR] "drivers/" directory not found.' echo '[ERROR] "drivers/" directory not found.'
exit 127 exit 127
fi fi
DRIVER_MAKEFILE=$DRIVER_DIR/Makefile DRIVER_MAKEFILE=$DRIVER_DIR/Makefile
DRIVER_KCONFIG=$DRIVER_DIR/Kconfig DRIVER_KCONFIG=$DRIVER_DIR/Kconfig
} }
# Reverts modifications made by this script # Reverts modifications made by this script
perform_cleanup() { perform_cleanup() {
echo "[+] Cleaning up..." echo "[+] Cleaning up..."
[ -L "$DRIVER_DIR/kernelsu" ] && rm "$DRIVER_DIR/kernelsu" && echo "[-] Symlink removed." [ -L "$DRIVER_DIR/kernelsu" ] && rm "$DRIVER_DIR/kernelsu" && echo "[-] Symlink removed."
grep -q "kernelsu" "$DRIVER_MAKEFILE" && sed -i '/kernelsu/d' "$DRIVER_MAKEFILE" && echo "[-] Makefile reverted." grep -q "kernelsu" "$DRIVER_MAKEFILE" && sed -i '/kernelsu/d' "$DRIVER_MAKEFILE" && echo "[-] Makefile reverted."
grep -q "drivers/kernelsu/Kconfig" "$DRIVER_KCONFIG" && sed -i '/drivers\/kernelsu\/Kconfig/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted." grep -q "drivers/kernelsu/Kconfig" "$DRIVER_KCONFIG" && sed -i '/drivers\/kernelsu\/Kconfig/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted."
if [ -d "$GKI_ROOT/KernelSU" ]; then if [ -d "$GKI_ROOT/KernelSU" ]; then
rm -rf "$GKI_ROOT/KernelSU" && echo "[-] KernelSU directory deleted." rm -rf "$GKI_ROOT/KernelSU" && echo "[-] KernelSU directory deleted."
fi fi
} }
# Sets up or update KernelSU environment # Sets up or update KernelSU environment
setup_kernelsu() { setup_kernelsu() {
echo "[+] Setting up KernelSU..." echo "[+] Setting up KernelSU..."
# Clone the repository and rename it to KernelSU # Clone the repository and rename it to KernelSU
if [ ! -d "$GKI_ROOT/KernelSU" ]; then if [ ! -d "$GKI_ROOT/KernelSU" ]; then
git clone https://github.com/SukiSU-Ultra/SukiSU-Ultra SukiSU-Ultra git clone https://github.com/SukiSU-Ultra/SukiSU-Ultra SukiSU-Ultra
mv SukiSU-Ultra KernelSU mv SukiSU-Ultra KernelSU
echo "[+] Repository cloned and renamed to KernelSU." echo "[+] Repository cloned and renamed to KernelSU."
fi fi
cd "$GKI_ROOT/KernelSU" cd "$GKI_ROOT/KernelSU"
git stash && echo "[-] Stashed current changes." git stash && echo "[-] Stashed current changes."
if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then
git checkout main && echo "[-] Switched to main branch." git checkout main && echo "[-] Switched to main branch."
fi fi
git pull && echo "[+] Repository updated." git pull && echo "[+] Repository updated."
if [ -z "${1-}" ]; then if [ -z "${1-}" ]; then
git checkout "$(git describe --abbrev=0 --tags)" && echo "[-] Checked out latest tag." git checkout "$(git describe --abbrev=0 --tags)" && echo "[-] Checked out latest tag."
else else
git checkout "$1" && echo "[-] Checked out $1." || echo "[-] Checkout default branch" git checkout "$1" && echo "[-] Checked out $1." || echo "[-] Checkout default branch"
fi fi
cd "$DRIVER_DIR" cd "$DRIVER_DIR"
ln -sf "$(realpath --relative-to="$DRIVER_DIR" "$GKI_ROOT/KernelSU/kernel")" "kernelsu" && echo "[+] Symlink created." ln -sf "$(realpath --relative-to="$DRIVER_DIR" "$GKI_ROOT/KernelSU/kernel")" "kernelsu" && echo "[+] Symlink created."
# Add entries in Makefile and Kconfig if not already existing # Add entries in Makefile and Kconfig if not already existing
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" && echo "[+] Modified Makefile." grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" && echo "[+] Modified Makefile."
grep -q "source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" && echo "[+] Modified Kconfig." grep -q "source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" && echo "[+] Modified Kconfig."
echo '[+] Done.' echo '[+] Done.'
} }
# Process command-line arguments # Process command-line arguments
if [ "$#" -eq 0 ]; then if [ "$#" -eq 0 ]; then
initialize_variables initialize_variables
setup_kernelsu setup_kernelsu
elif [ "$1" = "-h" ] || [ "$1" = "--help" ]; then elif [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
display_usage display_usage
elif [ "$1" = "--cleanup" ]; then elif [ "$1" = "--cleanup" ]; then
initialize_variables initialize_variables
perform_cleanup perform_cleanup
else else
initialize_variables initialize_variables
setup_kernelsu "$@" setup_kernelsu "$@"
fi fi

View File

@@ -37,168 +37,168 @@ bool ksu_su_compat_enabled __read_mostly = true;
static int su_compat_feature_get(u64 *value) static int su_compat_feature_get(u64 *value)
{ {
*value = ksu_su_compat_enabled ? 1 : 0; *value = ksu_su_compat_enabled ? 1 : 0;
return 0; return 0;
} }
static int su_compat_feature_set(u64 value) static int su_compat_feature_set(u64 value)
{ {
bool enable = value != 0; bool enable = value != 0;
ksu_su_compat_enabled = enable; ksu_su_compat_enabled = enable;
pr_info("su_compat: set to %d\n", enable); pr_info("su_compat: set to %d\n", enable);
return 0; return 0;
} }
static const struct ksu_feature_handler su_compat_handler = { static const struct ksu_feature_handler su_compat_handler = {
.feature_id = KSU_FEATURE_SU_COMPAT, .feature_id = KSU_FEATURE_SU_COMPAT,
.name = "su_compat", .name = "su_compat",
.get_handler = su_compat_feature_get, .get_handler = su_compat_feature_get,
.set_handler = su_compat_feature_set, .set_handler = su_compat_feature_set,
}; };
static void __user *userspace_stack_buffer(const void *d, size_t len) static void __user *userspace_stack_buffer(const void *d, size_t len)
{ {
// To avoid having to mmap a page in userspace, just write below the stack // To avoid having to mmap a page in userspace, just write below the stack
// pointer. // pointer.
char __user *p = (void __user *)current_user_stack_pointer() - len; char __user *p = (void __user *)current_user_stack_pointer() - len;
return copy_to_user(p, d, len) ? NULL : p; return copy_to_user(p, d, len) ? NULL : p;
} }
static char __user *sh_user_path(void) 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 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));
} }
#ifndef CONFIG_KSU_SUSFS #ifndef CONFIG_KSU_SUSFS
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
int *__unused_flags) int *__unused_flags)
{ {
const char su[] = SU_PATH; const char su[] = SU_PATH;
#ifdef KSU_MANUAL_HOOK #ifdef KSU_MANUAL_HOOK
if (!ksu_su_compat_enabled) { if (!ksu_su_compat_enabled) {
return 0; return 0;
} }
#endif #endif
if (!ksu_is_allow_uid_for_current(current_uid().val)) { if (!ksu_is_allow_uid_for_current(current_uid().val)) {
return 0; return 0;
} }
char path[sizeof(su) + 1]; char path[sizeof(su) + 1];
memset(path, 0, sizeof(path)); memset(path, 0, sizeof(path));
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path)); ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (unlikely(!memcmp(path, su, sizeof(su)))) { if (unlikely(!memcmp(path, su, sizeof(su)))) {
#if __SULOG_GATE #if __SULOG_GATE
ksu_sulog_report_syscall(current_uid().val, NULL, "faccessat", path); ksu_sulog_report_syscall(current_uid().val, NULL, "faccessat", path);
#endif #endif
pr_info("faccessat su->sh!\n"); pr_info("faccessat su->sh!\n");
*filename_user = sh_user_path(); *filename_user = sh_user_path();
} }
return 0; return 0;
} }
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags) int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
{ {
// const char sh[] = SH_PATH; // const char sh[] = SH_PATH;
const char su[] = SU_PATH; const char su[] = SU_PATH;
#ifdef KSU_MANUAL_HOOK #ifdef KSU_MANUAL_HOOK
if (!ksu_su_compat_enabled) { if (!ksu_su_compat_enabled) {
return 0; return 0;
} }
#endif #endif
if (!ksu_is_allow_uid_for_current(current_uid().val)) { if (!ksu_is_allow_uid_for_current(current_uid().val)) {
return 0; return 0;
} }
if (unlikely(!filename_user)) { if (unlikely(!filename_user)) {
return 0; return 0;
} }
char path[sizeof(su) + 1]; char path[sizeof(su) + 1];
memset(path, 0, sizeof(path)); memset(path, 0, sizeof(path));
// Remove this later!! we use syscall hook, so this will never happen!!!!! // Remove this later!! we use syscall hook, so this will never happen!!!!!
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0
// it becomes a `struct filename *` after 5.18 // it becomes a `struct filename *` after 5.18
// https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216 // https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216
const char sh[] = SH_PATH; const char sh[] = SH_PATH;
struct filename *filename = *((struct filename **)filename_user); struct filename *filename = *((struct filename **)filename_user);
if (IS_ERR(filename)) { if (IS_ERR(filename)) {
return 0; return 0;
} }
if (likely(memcmp(filename->name, su, sizeof(su)))) if (likely(memcmp(filename->name, su, sizeof(su))))
return 0; return 0;
pr_info("vfs_statx su->sh!\n"); pr_info("vfs_statx su->sh!\n");
memcpy((void *)filename->name, sh, sizeof(sh)); memcpy((void *)filename->name, sh, sizeof(sh));
#else #else
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path)); ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (unlikely(!memcmp(path, su, sizeof(su)))) { if (unlikely(!memcmp(path, su, sizeof(su)))) {
#if __SULOG_GATE #if __SULOG_GATE
ksu_sulog_report_syscall(current_uid().val, NULL, "newfstatat", path); ksu_sulog_report_syscall(current_uid().val, NULL, "newfstatat", path);
#endif #endif
pr_info("newfstatat su->sh!\n"); pr_info("newfstatat su->sh!\n");
*filename_user = sh_user_path(); *filename_user = sh_user_path();
} }
#endif #endif
return 0; return 0;
} }
int ksu_handle_execve_sucompat(const char __user **filename_user, int ksu_handle_execve_sucompat(const char __user **filename_user,
void *__never_use_argv, void *__never_use_envp, void *__never_use_argv, void *__never_use_envp,
int *__never_use_flags) int *__never_use_flags)
{ {
const char su[] = SU_PATH; const char su[] = SU_PATH;
char path[sizeof(su) + 1]; char path[sizeof(su) + 1];
#ifdef KSU_MANUAL_HOOK #ifdef KSU_MANUAL_HOOK
if (!ksu_su_compat_enabled){ if (!ksu_su_compat_enabled){
return 0; return 0;
} }
#endif #endif
if (unlikely(!filename_user)) if (unlikely(!filename_user))
return 0; return 0;
memset(path, 0, sizeof(path)); memset(path, 0, sizeof(path));
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path)); ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (likely(memcmp(path, su, sizeof(su)))) if (likely(memcmp(path, su, sizeof(su))))
return 0; return 0;
#if __SULOG_GATE #if __SULOG_GATE
bool is_allowed = ksu_is_allow_uid_for_current(current_uid().val); bool is_allowed = ksu_is_allow_uid_for_current(current_uid().val);
ksu_sulog_report_syscall(current_uid().val, NULL, "execve", path); ksu_sulog_report_syscall(current_uid().val, NULL, "execve", path);
if (!is_allowed) if (!is_allowed)
return 0; return 0;
ksu_sulog_report_su_attempt(current_uid().val, NULL, path, is_allowed); ksu_sulog_report_su_attempt(current_uid().val, NULL, path, is_allowed);
#else #else
if (!ksu_is_allow_uid_for_current(current_uid().val)) { if (!ksu_is_allow_uid_for_current(current_uid().val)) {
return 0; return 0;
} }
#endif #endif
pr_info("sys_execve su found\n"); pr_info("sys_execve su found\n");
*filename_user = ksud_user_path(); *filename_user = ksud_user_path();
escape_with_root_profile(); escape_with_root_profile();
return 0; return 0;
} }
#else #else
static const char sh_path[] = SH_PATH; static const char sh_path[] = SH_PATH;
@@ -207,169 +207,169 @@ static const char ksud_path[] = KSUD_PATH;
// the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code // the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code
int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
void *__never_use_argv, void *__never_use_envp, void *__never_use_argv, void *__never_use_envp,
int *__never_use_flags) int *__never_use_flags)
{ {
struct filename *filename; struct filename *filename;
if (!ksu_su_compat_enabled){ if (!ksu_su_compat_enabled){
return 0; return 0;
} }
if (unlikely(!filename_ptr)) if (unlikely(!filename_ptr))
return 0; return 0;
filename = *filename_ptr; filename = *filename_ptr;
if (IS_ERR(filename)) { if (IS_ERR(filename)) {
return 0; return 0;
} }
if (likely(memcmp(filename->name, su_path, sizeof(su_path)))) if (likely(memcmp(filename->name, su_path, sizeof(su_path))))
return 0; return 0;
#if __SULOG_GATE #if __SULOG_GATE
bool is_allowed = ksu_is_allow_uid_for_current(current_uid().val); bool is_allowed = ksu_is_allow_uid_for_current(current_uid().val);
ksu_sulog_report_syscall(current_uid().val, NULL, "execve", su_path); ksu_sulog_report_syscall(current_uid().val, NULL, "execve", su_path);
ksu_sulog_report_su_attempt(current_uid().val, NULL, su_path, is_allowed); ksu_sulog_report_su_attempt(current_uid().val, NULL, su_path, is_allowed);
#endif #endif
pr_info("do_execveat_common su found\n"); pr_info("do_execveat_common su found\n");
memcpy((void *)filename->name, ksud_path, sizeof(ksud_path)); memcpy((void *)filename->name, ksud_path, sizeof(ksud_path));
escape_with_root_profile(); escape_with_root_profile();
return 0; return 0;
} }
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
void *envp, int *flags) void *envp, int *flags)
{ {
if (ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags)) { if (ksu_handle_execveat_ksud(fd, filename_ptr, argv, envp, flags)) {
return 0; return 0;
} }
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp,
flags); flags);
} }
int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
int *__unused_flags) int *__unused_flags)
{ {
char path[sizeof(su_path) + 1] = {0}; char path[sizeof(su_path) + 1] = {0};
if (!ksu_su_compat_enabled){ if (!ksu_su_compat_enabled){
return 0; return 0;
} }
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_path, sizeof(su_path)))) { if (unlikely(!memcmp(path, su_path, sizeof(su_path)))) {
#if __SULOG_GATE #if __SULOG_GATE
ksu_sulog_report_syscall(current_uid().val, NULL, "faccessat", path); ksu_sulog_report_syscall(current_uid().val, NULL, "faccessat", path);
#endif #endif
pr_info("faccessat su->sh!\n"); pr_info("faccessat su->sh!\n");
*filename_user = sh_user_path(); *filename_user = sh_user_path();
} }
return 0; return 0;
} }
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
int ksu_handle_stat(int *dfd, struct filename **filename, int *flags) { int ksu_handle_stat(int *dfd, struct filename **filename, int *flags) {
if (unlikely(IS_ERR(*filename) || (*filename)->name == NULL)) { if (unlikely(IS_ERR(*filename) || (*filename)->name == NULL)) {
return 0; return 0;
} }
if (likely(memcmp((*filename)->name, su_path, sizeof(su_path)))) { if (likely(memcmp((*filename)->name, su_path, sizeof(su_path)))) {
return 0; return 0;
} }
pr_info("ksu_handle_stat: su->sh!\n"); pr_info("ksu_handle_stat: su->sh!\n");
memcpy((void *)((*filename)->name), sh_path, sizeof(sh_path)); memcpy((void *)((*filename)->name), sh_path, sizeof(sh_path));
return 0; return 0;
} }
#else #else
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags) int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
{ {
if (!ksu_su_compat_enabled){ if (!ksu_su_compat_enabled){
return 0; return 0;
} }
if (unlikely(!filename_user)) { if (unlikely(!filename_user)) {
return 0; return 0;
} }
char path[sizeof(su_path) + 1] = {0}; char path[sizeof(su_path) + 1] = {0};
// Remove this later!! we use syscall hook, so this will never happen!!!!! // Remove this later!! we use syscall hook, so this will never happen!!!!!
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0 #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0
// it becomes a `struct filename *` after 5.18 // it becomes a `struct filename *` after 5.18
// https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216 // https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0) #if LINUX_VERSION_CODE < KERNEL_VERSION(6, 1, 0)
struct filename *filename = *((struct filename **)filename_user); struct filename *filename = *((struct filename **)filename_user);
#endif #endif
if (IS_ERR(filename)) { if (IS_ERR(filename)) {
return 0; return 0;
} }
if (likely(memcmp(filename->name, su_path, sizeof(su_path)))) if (likely(memcmp(filename->name, su_path, sizeof(su_path))))
return 0; return 0;
pr_info("ksu_handle_stat: su->sh!\n"); pr_info("ksu_handle_stat: su->sh!\n");
memcpy((void *)filename->name, sh_path, sizeof(sh_path)); memcpy((void *)filename->name, sh_path, sizeof(sh_path));
#else #else
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path)); ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (unlikely(!memcmp(path, su_path, sizeof(su_path)))) { if (unlikely(!memcmp(path, su_path, sizeof(su_path)))) {
#if __SULOG_GATE #if __SULOG_GATE
ksu_sulog_report_syscall(current_uid().val, NULL, "newfstatat", path); ksu_sulog_report_syscall(current_uid().val, NULL, "newfstatat", path);
#endif #endif
pr_info("ksu_handle_stat: su->sh!\n"); pr_info("ksu_handle_stat: su->sh!\n");
*filename_user = sh_user_path(); *filename_user = sh_user_path();
} }
#endif #endif
return 0; return 0;
} }
#endif // #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) #endif // #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
int ksu_handle_devpts(struct inode *inode) int ksu_handle_devpts(struct inode *inode)
{ {
if (!current->mm) { if (!current->mm) {
return 0; return 0;
} }
if (!ksu_su_compat_enabled){ if (!ksu_su_compat_enabled){
return 0; return 0;
} }
uid_t uid = current_uid().val; uid_t uid = current_uid().val;
if (uid % 100000 < 10000) { if (uid % 100000 < 10000) {
// not untrusted_app, ignore it // not untrusted_app, ignore it
return 0; return 0;
} }
if (!__ksu_is_allow_uid_for_current(uid)) if (!__ksu_is_allow_uid_for_current(uid))
return 0; return 0;
if (ksu_file_sid) { if (ksu_file_sid) {
struct inode_security_struct *sec = selinux_inode(inode); struct inode_security_struct *sec = selinux_inode(inode);
if (sec) { if (sec) {
sec->sid = ksu_file_sid; sec->sid = ksu_file_sid;
} }
} }
return 0; return 0;
} }
#endif // #ifndef CONFIG_KSU_SUSFS #endif // #ifndef CONFIG_KSU_SUSFS
// sucompat: permitted process can execute 'su' to gain root access. // sucompat: permitted process can execute 'su' to gain root access.
void ksu_sucompat_init() void ksu_sucompat_init()
{ {
if (ksu_register_feature_handler(&su_compat_handler)) { if (ksu_register_feature_handler(&su_compat_handler)) {
pr_err("Failed to register su_compat feature handler\n"); pr_err("Failed to register su_compat feature handler\n");
} }
} }
void ksu_sucompat_exit() void ksu_sucompat_exit()
{ {
ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT); ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT);
} }

View File

@@ -16,7 +16,7 @@ int ksu_handle_stat(int *dfd, struct filename **filename, int *flags);
int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags); int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
#endif // #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) && defined(CONFIG_KSU_SUSFS) #endif // #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) && defined(CONFIG_KSU_SUSFS)
int ksu_handle_execve_sucompat(const char __user **filename_user, int ksu_handle_execve_sucompat(const char __user **filename_user,
void *__never_use_argv, void *__never_use_envp, void *__never_use_argv, void *__never_use_envp,
int *__never_use_flags); int *__never_use_flags);
#endif #endif

View File

@@ -30,340 +30,340 @@ static bool sulog_enabled __read_mostly = true;
static int sulog_feature_get(u64 *value) static int sulog_feature_get(u64 *value)
{ {
*value = sulog_enabled ? 1 : 0; *value = sulog_enabled ? 1 : 0;
return 0; return 0;
} }
static int sulog_feature_set(u64 value) static int sulog_feature_set(u64 value)
{ {
bool enable = value != 0; bool enable = value != 0;
sulog_enabled = enable; sulog_enabled = enable;
pr_info("sulog: set to %d\n", enable); pr_info("sulog: set to %d\n", enable);
return 0; return 0;
} }
static const struct ksu_feature_handler sulog_handler = { static const struct ksu_feature_handler sulog_handler = {
.feature_id = KSU_FEATURE_SULOG, .feature_id = KSU_FEATURE_SULOG,
.name = "sulog", .name = "sulog",
.get_handler = sulog_feature_get, .get_handler = sulog_feature_get,
.set_handler = sulog_feature_set, .set_handler = sulog_feature_set,
}; };
static void get_timestamp(char *buf, size_t len) static void get_timestamp(char *buf, size_t len)
{ {
struct timespec64 ts; struct timespec64 ts;
struct tm tm; struct tm tm;
ktime_get_real_ts64(&ts); ktime_get_real_ts64(&ts);
time64_to_tm(ts.tv_sec - sys_tz.tz_minuteswest * 60, 0, &tm); time64_to_tm(ts.tv_sec - sys_tz.tz_minuteswest * 60, 0, &tm);
snprintf(buf, len, "%04ld-%02d-%02d %02d:%02d:%02d", snprintf(buf, len, "%04ld-%02d-%02d %02d:%02d:%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec); tm.tm_hour, tm.tm_min, tm.tm_sec);
} }
static void ksu_get_cmdline(char *full_comm, const char *comm, size_t buf_len) static void ksu_get_cmdline(char *full_comm, const char *comm, size_t buf_len)
{ {
if (!full_comm || buf_len <= 0) if (!full_comm || buf_len <= 0)
return; return;
if (comm && strlen(comm) > 0) { if (comm && strlen(comm) > 0) {
KSU_STRSCPY(full_comm, comm, buf_len); KSU_STRSCPY(full_comm, comm, buf_len);
return; return;
} }
if (in_atomic() || in_interrupt() || irqs_disabled()) { if (in_atomic() || in_interrupt() || irqs_disabled()) {
KSU_STRSCPY(full_comm, current->comm, buf_len); KSU_STRSCPY(full_comm, current->comm, buf_len);
return; return;
} }
if (!current->mm) { if (!current->mm) {
KSU_STRSCPY(full_comm, current->comm, buf_len); KSU_STRSCPY(full_comm, current->comm, buf_len);
return; return;
} }
int n = get_cmdline(current, full_comm, buf_len); int n = get_cmdline(current, full_comm, buf_len);
if (n <= 0) { if (n <= 0) {
KSU_STRSCPY(full_comm, current->comm, buf_len); KSU_STRSCPY(full_comm, current->comm, buf_len);
return; return;
} }
for (int i = 0; i < n && i < buf_len - 1; i++) { for (int i = 0; i < n && i < buf_len - 1; i++) {
if (full_comm[i] == '\0') if (full_comm[i] == '\0')
full_comm[i] = ' '; full_comm[i] = ' ';
} }
full_comm[n < buf_len ? n : buf_len - 1] = '\0'; full_comm[n < buf_len ? n : buf_len - 1] = '\0';
} }
static void sanitize_string(char *str, size_t len) static void sanitize_string(char *str, size_t len)
{ {
if (!str || len == 0) if (!str || len == 0)
return; return;
size_t read_pos = 0, write_pos = 0; size_t read_pos = 0, write_pos = 0;
while (read_pos < len && str[read_pos] != '\0') { while (read_pos < len && str[read_pos] != '\0') {
char c = str[read_pos]; char c = str[read_pos];
if (c == '\n' || c == '\r') { if (c == '\n' || c == '\r') {
read_pos++; read_pos++;
continue; continue;
} }
if (c == ' ' && write_pos > 0 && str[write_pos - 1] == ' ') { if (c == ' ' && write_pos > 0 && str[write_pos - 1] == ' ') {
read_pos++; read_pos++;
continue; continue;
} }
str[write_pos++] = c; str[write_pos++] = c;
read_pos++; read_pos++;
} }
str[write_pos] = '\0'; str[write_pos] = '\0';
} }
static bool dedup_should_print(uid_t uid, u8 type, const char *content, size_t len) static bool dedup_should_print(uid_t uid, u8 type, const char *content, size_t len)
{ {
struct dedup_key key = { struct dedup_key key = {
.crc = dedup_calc_hash(content, len), .crc = dedup_calc_hash(content, len),
.uid = uid, .uid = uid,
.type = type, .type = type,
}; };
u64 now = ktime_get_ns(); u64 now = ktime_get_ns();
u64 delta_ns = DEDUP_SECS * NSEC_PER_SEC; u64 delta_ns = DEDUP_SECS * NSEC_PER_SEC;
u32 idx = key.crc & (SULOG_COMM_LEN - 1); u32 idx = key.crc & (SULOG_COMM_LEN - 1);
spin_lock(&dedup_lock); spin_lock(&dedup_lock);
struct dedup_entry *e = &dedup_tbl[idx]; struct dedup_entry *e = &dedup_tbl[idx];
if (e->key.crc == key.crc && if (e->key.crc == key.crc &&
e->key.uid == key.uid && e->key.uid == key.uid &&
e->key.type == key.type && e->key.type == key.type &&
(now - e->ts_ns) < delta_ns) { (now - e->ts_ns) < delta_ns) {
spin_unlock(&dedup_lock); spin_unlock(&dedup_lock);
return false; return false;
} }
e->key = key; e->key = key;
e->ts_ns = now; e->ts_ns = now;
spin_unlock(&dedup_lock); spin_unlock(&dedup_lock);
return true; return true;
} }
static void sulog_work_handler(struct work_struct *work) static void sulog_work_handler(struct work_struct *work)
{ {
struct file *fp; struct file *fp;
struct sulog_entry *entry, *tmp; struct sulog_entry *entry, *tmp;
LIST_HEAD(local_queue); LIST_HEAD(local_queue);
loff_t pos = 0; loff_t pos = 0;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&dedup_lock, flags); spin_lock_irqsave(&dedup_lock, flags);
list_splice_init(&sulog_queue, &local_queue); list_splice_init(&sulog_queue, &local_queue);
spin_unlock_irqrestore(&dedup_lock, flags); spin_unlock_irqrestore(&dedup_lock, flags);
if (list_empty(&local_queue)) if (list_empty(&local_queue))
return; return;
fp = ksu_filp_open_compat(SULOG_PATH, O_WRONLY | O_CREAT | O_APPEND, 0640); fp = ksu_filp_open_compat(SULOG_PATH, O_WRONLY | O_CREAT | O_APPEND, 0640);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
pr_err("sulog: failed to open log file: %ld\n", PTR_ERR(fp)); pr_err("sulog: failed to open log file: %ld\n", PTR_ERR(fp));
goto cleanup; goto cleanup;
} }
if (fp->f_inode->i_size > SULOG_MAX_SIZE) { if (fp->f_inode->i_size > SULOG_MAX_SIZE) {
if (vfs_truncate(&fp->f_path, 0)) if (vfs_truncate(&fp->f_path, 0))
pr_err("sulog: failed to truncate log file\n"); pr_err("sulog: failed to truncate log file\n");
pos = 0; pos = 0;
} else { } else {
pos = fp->f_inode->i_size; pos = fp->f_inode->i_size;
} }
list_for_each_entry(entry, &local_queue, list) list_for_each_entry(entry, &local_queue, list)
ksu_kernel_write_compat(fp, entry->content, strlen(entry->content), &pos); ksu_kernel_write_compat(fp, entry->content, strlen(entry->content), &pos);
vfs_fsync(fp, 0); vfs_fsync(fp, 0);
filp_close(fp, 0); filp_close(fp, 0);
cleanup: cleanup:
list_for_each_entry_safe(entry, tmp, &local_queue, list) { list_for_each_entry_safe(entry, tmp, &local_queue, list) {
list_del(&entry->list); list_del(&entry->list);
kfree(entry); kfree(entry);
} }
} }
static void sulog_add_entry(char *log_buf, size_t len, uid_t uid, u8 dedup_type) static void sulog_add_entry(char *log_buf, size_t len, uid_t uid, u8 dedup_type)
{ {
struct sulog_entry *entry; struct sulog_entry *entry;
unsigned long flags; unsigned long flags;
if (!sulog_enabled || !log_buf || len == 0) if (!sulog_enabled || !log_buf || len == 0)
return; return;
if (!dedup_should_print(uid, dedup_type, log_buf, len)) if (!dedup_should_print(uid, dedup_type, log_buf, len))
return; return;
entry = kzalloc(sizeof(*entry), GFP_ATOMIC); entry = kzalloc(sizeof(*entry), GFP_ATOMIC);
if (!entry) if (!entry)
return; return;
KSU_STRSCPY(entry->content, log_buf, SULOG_ENTRY_MAX_LEN); KSU_STRSCPY(entry->content, log_buf, SULOG_ENTRY_MAX_LEN);
spin_lock_irqsave(&dedup_lock, flags); spin_lock_irqsave(&dedup_lock, flags);
list_add_tail(&entry->list, &sulog_queue); list_add_tail(&entry->list, &sulog_queue);
spin_unlock_irqrestore(&dedup_lock, flags); spin_unlock_irqrestore(&dedup_lock, flags);
if (sulog_workqueue) if (sulog_workqueue)
queue_work(sulog_workqueue, &sulog_work); queue_work(sulog_workqueue, &sulog_work);
} }
void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method) void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method)
{ {
char log_buf[SULOG_ENTRY_MAX_LEN]; char log_buf[SULOG_ENTRY_MAX_LEN];
char timestamp[32]; char timestamp[32];
char full_comm[SULOG_COMM_LEN]; char full_comm[SULOG_COMM_LEN];
if (!sulog_enabled) if (!sulog_enabled)
return; return;
get_timestamp(timestamp, sizeof(timestamp)); get_timestamp(timestamp, sizeof(timestamp));
ksu_get_cmdline(full_comm, comm, sizeof(full_comm)); ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
sanitize_string(full_comm, sizeof(full_comm)); sanitize_string(full_comm, sizeof(full_comm));
snprintf(log_buf, sizeof(log_buf), snprintf(log_buf, sizeof(log_buf),
"[%s] SU_GRANT: UID=%d COMM=%s METHOD=%s PID=%d\n", "[%s] SU_GRANT: UID=%d COMM=%s METHOD=%s PID=%d\n",
timestamp, uid, full_comm, method ? method : "unknown", current->pid); timestamp, uid, full_comm, method ? method : "unknown", current->pid);
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SU_GRANT); sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SU_GRANT);
} }
void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success) void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success)
{ {
char log_buf[SULOG_ENTRY_MAX_LEN]; char log_buf[SULOG_ENTRY_MAX_LEN];
char timestamp[32]; char timestamp[32];
char full_comm[SULOG_COMM_LEN]; char full_comm[SULOG_COMM_LEN];
if (!sulog_enabled) if (!sulog_enabled)
return; return;
get_timestamp(timestamp, sizeof(timestamp)); get_timestamp(timestamp, sizeof(timestamp));
ksu_get_cmdline(full_comm, comm, sizeof(full_comm)); ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
sanitize_string(full_comm, sizeof(full_comm)); sanitize_string(full_comm, sizeof(full_comm));
snprintf(log_buf, sizeof(log_buf), snprintf(log_buf, sizeof(log_buf),
"[%s] SU_EXEC: UID=%d COMM=%s TARGET=%s RESULT=%s PID=%d\n", "[%s] SU_EXEC: UID=%d COMM=%s TARGET=%s RESULT=%s PID=%d\n",
timestamp, uid, full_comm, target_path ? target_path : "unknown", timestamp, uid, full_comm, target_path ? target_path : "unknown",
success ? "SUCCESS" : "DENIED", current->pid); success ? "SUCCESS" : "DENIED", current->pid);
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SU_ATTEMPT); sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SU_ATTEMPT);
} }
void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed) void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed)
{ {
char log_buf[SULOG_ENTRY_MAX_LEN]; char log_buf[SULOG_ENTRY_MAX_LEN];
char timestamp[32]; char timestamp[32];
char full_comm[SULOG_COMM_LEN]; char full_comm[SULOG_COMM_LEN];
if (!sulog_enabled) if (!sulog_enabled)
return; return;
get_timestamp(timestamp, sizeof(timestamp)); get_timestamp(timestamp, sizeof(timestamp));
ksu_get_cmdline(full_comm, comm, sizeof(full_comm)); ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
sanitize_string(full_comm, sizeof(full_comm)); sanitize_string(full_comm, sizeof(full_comm));
snprintf(log_buf, sizeof(log_buf), snprintf(log_buf, sizeof(log_buf),
"[%s] PERM_CHECK: UID=%d COMM=%s RESULT=%s PID=%d\n", "[%s] PERM_CHECK: UID=%d COMM=%s RESULT=%s PID=%d\n",
timestamp, uid, full_comm, allowed ? "ALLOWED" : "DENIED", current->pid); timestamp, uid, full_comm, allowed ? "ALLOWED" : "DENIED", current->pid);
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_PERM_CHECK); sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_PERM_CHECK);
} }
void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid) void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid)
{ {
char log_buf[SULOG_ENTRY_MAX_LEN]; char log_buf[SULOG_ENTRY_MAX_LEN];
char timestamp[32]; char timestamp[32];
char full_comm[SULOG_COMM_LEN]; char full_comm[SULOG_COMM_LEN];
if (!sulog_enabled) if (!sulog_enabled)
return; return;
get_timestamp(timestamp, sizeof(timestamp)); get_timestamp(timestamp, sizeof(timestamp));
ksu_get_cmdline(full_comm, NULL, sizeof(full_comm)); ksu_get_cmdline(full_comm, NULL, sizeof(full_comm));
sanitize_string(full_comm, sizeof(full_comm)); sanitize_string(full_comm, sizeof(full_comm));
snprintf(log_buf, sizeof(log_buf), snprintf(log_buf, sizeof(log_buf),
"[%s] MANAGER_OP: OP=%s MANAGER_UID=%d TARGET_UID=%d COMM=%s PID=%d\n", "[%s] MANAGER_OP: OP=%s MANAGER_UID=%d TARGET_UID=%d COMM=%s PID=%d\n",
timestamp, operation ? operation : "unknown", manager_uid, target_uid, full_comm, current->pid); timestamp, operation ? operation : "unknown", manager_uid, target_uid, full_comm, current->pid);
sulog_add_entry(log_buf, strlen(log_buf), manager_uid, DEDUP_MANAGER_OP); sulog_add_entry(log_buf, strlen(log_buf), manager_uid, DEDUP_MANAGER_OP);
} }
void ksu_sulog_report_syscall(uid_t uid, const char *comm, const char *syscall, const char *args) void ksu_sulog_report_syscall(uid_t uid, const char *comm, const char *syscall, const char *args)
{ {
char log_buf[SULOG_ENTRY_MAX_LEN]; char log_buf[SULOG_ENTRY_MAX_LEN];
char timestamp[32]; char timestamp[32];
char full_comm[SULOG_COMM_LEN]; char full_comm[SULOG_COMM_LEN];
if (!sulog_enabled) if (!sulog_enabled)
return; return;
get_timestamp(timestamp, sizeof(timestamp)); get_timestamp(timestamp, sizeof(timestamp));
ksu_get_cmdline(full_comm, comm, sizeof(full_comm)); ksu_get_cmdline(full_comm, comm, sizeof(full_comm));
sanitize_string(full_comm, sizeof(full_comm)); sanitize_string(full_comm, sizeof(full_comm));
snprintf(log_buf, sizeof(log_buf), snprintf(log_buf, sizeof(log_buf),
"[%s] SYSCALL: UID=%d COMM=%s SYSCALL=%s ARGS=%s PID=%d\n", "[%s] SYSCALL: UID=%d COMM=%s SYSCALL=%s ARGS=%s PID=%d\n",
timestamp, uid, full_comm, syscall ? syscall : "unknown", timestamp, uid, full_comm, syscall ? syscall : "unknown",
args ? args : "none", current->pid); args ? args : "none", current->pid);
sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SYSCALL); sulog_add_entry(log_buf, strlen(log_buf), uid, DEDUP_SYSCALL);
} }
int ksu_sulog_init(void) int ksu_sulog_init(void)
{ {
if (ksu_register_feature_handler(&sulog_handler)) { if (ksu_register_feature_handler(&sulog_handler)) {
pr_err("Failed to register sulog feature handler\n"); pr_err("Failed to register sulog feature handler\n");
} }
sulog_workqueue = alloc_workqueue("ksu_sulog", WQ_UNBOUND | WQ_HIGHPRI, 1); sulog_workqueue = alloc_workqueue("ksu_sulog", WQ_UNBOUND | WQ_HIGHPRI, 1);
if (!sulog_workqueue) { if (!sulog_workqueue) {
pr_err("sulog: failed to create workqueue\n"); pr_err("sulog: failed to create workqueue\n");
return -ENOMEM; return -ENOMEM;
} }
INIT_WORK(&sulog_work, sulog_work_handler); INIT_WORK(&sulog_work, sulog_work_handler);
pr_info("sulog: initialized successfully\n"); pr_info("sulog: initialized successfully\n");
return 0; return 0;
} }
void ksu_sulog_exit(void) void ksu_sulog_exit(void)
{ {
struct sulog_entry *entry, *tmp; struct sulog_entry *entry, *tmp;
unsigned long flags; unsigned long flags;
ksu_unregister_feature_handler(KSU_FEATURE_SULOG); ksu_unregister_feature_handler(KSU_FEATURE_SULOG);
sulog_enabled = false; sulog_enabled = false;
if (sulog_workqueue) { if (sulog_workqueue) {
flush_workqueue(sulog_workqueue); flush_workqueue(sulog_workqueue);
destroy_workqueue(sulog_workqueue); destroy_workqueue(sulog_workqueue);
sulog_workqueue = NULL; sulog_workqueue = NULL;
} }
spin_lock_irqsave(&dedup_lock, flags); spin_lock_irqsave(&dedup_lock, flags);
list_for_each_entry_safe(entry, tmp, &sulog_queue, list) { list_for_each_entry_safe(entry, tmp, &sulog_queue, list) {
list_del(&entry->list); list_del(&entry->list);
kfree(entry); kfree(entry);
} }
spin_unlock_irqrestore(&dedup_lock, flags); spin_unlock_irqrestore(&dedup_lock, flags);
pr_info("sulog: cleaned up successfully\n"); pr_info("sulog: cleaned up successfully\n");
} }
#endif // __SULOG_GATE #endif // __SULOG_GATE

View File

@@ -5,7 +5,7 @@
#include <linux/version.h> #include <linux/version.h>
#include <linux/crc32.h> // needed for function dedup_calc_hash #include <linux/crc32.h> // needed for function dedup_calc_hash
#define __SULOG_GATE 1 #define __SULOG_GATE 1
#if __SULOG_GATE #if __SULOG_GATE
@@ -20,64 +20,64 @@ extern struct timezone sys_tz;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 10, 0)
static inline size_t strlcpy(char *dest, const char *src, size_t size) static inline size_t strlcpy(char *dest, const char *src, size_t size)
{ {
return strscpy(dest, src, size); return strscpy(dest, src, size);
} }
#endif #endif
#define KSU_STRSCPY(dst, src, size) \ #define KSU_STRSCPY(dst, src, size) \
do { \ do { \
if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) { \ if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)) { \
strscpy(dst, src, size); \ strscpy(dst, src, size); \
} else { \ } else { \
strlcpy(dst, src, size); \ strlcpy(dst, src, size); \
} \ } \
} while (0) } while (0)
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
#include <linux/rtc.h> #include <linux/rtc.h>
static inline void time64_to_tm(time64_t totalsecs, int offset, struct tm *result) static inline void time64_to_tm(time64_t totalsecs, int offset, struct tm *result)
{ {
struct rtc_time rtc_tm; struct rtc_time rtc_tm;
rtc_time64_to_tm(totalsecs, &rtc_tm); rtc_time64_to_tm(totalsecs, &rtc_tm);
result->tm_sec = rtc_tm.tm_sec; result->tm_sec = rtc_tm.tm_sec;
result->tm_min = rtc_tm.tm_min; result->tm_min = rtc_tm.tm_min;
result->tm_hour = rtc_tm.tm_hour; result->tm_hour = rtc_tm.tm_hour;
result->tm_mday = rtc_tm.tm_mday; result->tm_mday = rtc_tm.tm_mday;
result->tm_mon = rtc_tm.tm_mon; result->tm_mon = rtc_tm.tm_mon;
result->tm_year = rtc_tm.tm_year; result->tm_year = rtc_tm.tm_year;
} }
#endif #endif
struct dedup_key { struct dedup_key {
u32 crc; u32 crc;
uid_t uid; uid_t uid;
u8 type; u8 type;
u8 _pad[1]; u8 _pad[1];
}; };
struct dedup_entry { struct dedup_entry {
struct dedup_key key; struct dedup_key key;
u64 ts_ns; u64 ts_ns;
}; };
enum { enum {
DEDUP_SU_GRANT = 0, DEDUP_SU_GRANT = 0,
DEDUP_SU_ATTEMPT, DEDUP_SU_ATTEMPT,
DEDUP_PERM_CHECK, DEDUP_PERM_CHECK,
DEDUP_MANAGER_OP, DEDUP_MANAGER_OP,
DEDUP_SYSCALL, DEDUP_SYSCALL,
}; };
static inline u32 dedup_calc_hash(const char *content, size_t len) static inline u32 dedup_calc_hash(const char *content, size_t len)
{ {
return crc32(0, content, len); return crc32(0, content, len);
} }
struct sulog_entry { struct sulog_entry {
struct list_head list; struct list_head list;
char content[SULOG_ENTRY_MAX_LEN]; char content[SULOG_ENTRY_MAX_LEN];
}; };
void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method); void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method);

File diff suppressed because it is too large Load Diff

View File

@@ -17,76 +17,76 @@
// Command structures for ioctl // Command structures for ioctl
struct ksu_become_daemon_cmd { struct ksu_become_daemon_cmd {
__u8 token[65]; // Input: daemon token (null-terminated) __u8 token[65]; // Input: daemon token (null-terminated)
}; };
struct ksu_get_info_cmd { struct ksu_get_info_cmd {
__u32 version; // Output: KERNEL_SU_VERSION __u32 version; // Output: KERNEL_SU_VERSION
__u32 flags; // Output: flags (bit 0: MODULE mode) __u32 flags; // Output: flags (bit 0: MODULE mode)
__u32 features; // Output: max feature ID supported __u32 features; // Output: max feature ID supported
}; };
struct ksu_report_event_cmd { struct ksu_report_event_cmd {
__u32 event; // Input: EVENT_POST_FS_DATA, EVENT_BOOT_COMPLETED, etc. __u32 event; // Input: EVENT_POST_FS_DATA, EVENT_BOOT_COMPLETED, etc.
}; };
struct ksu_set_sepolicy_cmd { struct ksu_set_sepolicy_cmd {
__u64 cmd; // Input: sepolicy command __u64 cmd; // Input: sepolicy command
__aligned_u64 arg; // Input: sepolicy argument pointer __aligned_u64 arg; // Input: sepolicy argument pointer
}; };
struct ksu_check_safemode_cmd { struct ksu_check_safemode_cmd {
__u8 in_safe_mode; // Output: true if in safe mode, false otherwise __u8 in_safe_mode; // Output: true if in safe mode, false otherwise
}; };
struct ksu_get_allow_list_cmd { struct ksu_get_allow_list_cmd {
__u32 uids[128]; // Output: array of allowed/denied UIDs __u32 uids[128]; // Output: array of allowed/denied UIDs
__u32 count; // Output: number of UIDs in array __u32 count; // Output: number of UIDs in array
__u8 allow; // Input: true for allow list, false for deny list __u8 allow; // Input: true for allow list, false for deny list
}; };
struct ksu_uid_granted_root_cmd { struct ksu_uid_granted_root_cmd {
__u32 uid; // Input: target UID to check __u32 uid; // Input: target UID to check
__u8 granted; // Output: true if granted, false otherwise __u8 granted; // Output: true if granted, false otherwise
}; };
struct ksu_uid_should_umount_cmd { struct ksu_uid_should_umount_cmd {
__u32 uid; // Input: target UID to check __u32 uid; // Input: target UID to check
__u8 should_umount; // Output: true if should umount, false otherwise __u8 should_umount; // Output: true if should umount, false otherwise
}; };
struct ksu_get_manager_uid_cmd { struct ksu_get_manager_uid_cmd {
__u32 uid; // Output: manager UID __u32 uid; // Output: manager UID
}; };
struct ksu_get_app_profile_cmd { struct ksu_get_app_profile_cmd {
struct app_profile profile; // Input/Output: app profile structure struct app_profile profile; // Input/Output: app profile structure
}; };
struct ksu_set_app_profile_cmd { struct ksu_set_app_profile_cmd {
struct app_profile profile; // Input: app profile structure struct app_profile profile; // Input: app profile structure
}; };
struct ksu_get_feature_cmd { struct ksu_get_feature_cmd {
__u32 feature_id; // Input: feature ID (enum ksu_feature_id) __u32 feature_id; // Input: feature ID (enum ksu_feature_id)
__u64 value; // Output: feature value/state __u64 value; // Output: feature value/state
__u8 supported; // Output: true if feature is supported, false otherwise __u8 supported; // Output: true if feature is supported, false otherwise
}; };
struct ksu_set_feature_cmd { struct ksu_set_feature_cmd {
__u32 feature_id; // Input: feature ID (enum ksu_feature_id) __u32 feature_id; // Input: feature ID (enum ksu_feature_id)
__u64 value; // Input: feature value/state to set __u64 value; // Input: feature value/state to set
}; };
struct ksu_get_wrapper_fd_cmd { struct ksu_get_wrapper_fd_cmd {
__u32 fd; // Input: userspace fd __u32 fd; // Input: userspace fd
__u32 flags; // Input: flags of userspace fd __u32 flags; // Input: flags of userspace fd
}; };
struct ksu_manage_mark_cmd { struct ksu_manage_mark_cmd {
__u32 operation; // Input: KSU_MARK_* __u32 operation; // Input: KSU_MARK_*
__s32 pid; // Input: target pid (0 for all processes) __s32 pid; // Input: target pid (0 for all processes)
__u32 result; // Output: for get operation - mark status or reg_count __u32 result; // Output: for get operation - mark status or reg_count
}; };
#define KSU_MARK_GET 1 #define KSU_MARK_GET 1
@@ -95,7 +95,7 @@ struct ksu_manage_mark_cmd {
#define KSU_MARK_REFRESH 4 #define KSU_MARK_REFRESH 4
struct ksu_nuke_ext4_sysfs_cmd { struct ksu_nuke_ext4_sysfs_cmd {
__aligned_u64 arg; // Input: mnt pointer __aligned_u64 arg; // Input: mnt pointer
}; };
struct ksu_add_try_umount_cmd { struct ksu_add_try_umount_cmd {
@@ -111,37 +111,37 @@ struct ksu_add_try_umount_cmd {
// Other command structures // Other command structures
struct ksu_get_full_version_cmd { struct ksu_get_full_version_cmd {
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string
}; };
struct ksu_hook_type_cmd { struct ksu_hook_type_cmd {
char hook_type[32]; // Output: hook type string char hook_type[32]; // Output: hook type string
}; };
struct ksu_enable_kpm_cmd { struct ksu_enable_kpm_cmd {
__u8 enabled; // Output: true if KPM is enabled __u8 enabled; // Output: true if KPM is enabled
}; };
struct ksu_dynamic_manager_cmd { struct ksu_dynamic_manager_cmd {
struct dynamic_manager_user_config config; // Input/Output: dynamic manager config struct dynamic_manager_user_config config; // Input/Output: dynamic manager config
}; };
struct ksu_get_managers_cmd { struct ksu_get_managers_cmd {
struct manager_list_info manager_info; // Output: manager list information struct manager_list_info manager_info; // Output: manager list information
}; };
struct ksu_enable_uid_scanner_cmd { 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 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) __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) void __user *status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS)
}; };
#ifdef CONFIG_KSU_MANUAL_SU #ifdef CONFIG_KSU_MANUAL_SU
struct ksu_manual_su_cmd { struct ksu_manual_su_cmd {
__u32 option; // Input: operation type (MANUAL_SU_OP_GENERATE_TOKEN, MANUAL_SU_OP_ESCALATE, MANUAL_SU_OP_ADD_PENDING) __u32 option; // Input: operation type (MANUAL_SU_OP_GENERATE_TOKEN, MANUAL_SU_OP_ESCALATE, MANUAL_SU_OP_ADD_PENDING)
__u32 target_uid; // Input: target UID __u32 target_uid; // Input: target UID
__u32 target_pid; // Input: target PID __u32 target_pid; // Input: target PID
char token_buffer[33]; // Input/Output: token buffer char token_buffer[33]; // Input/Output: token buffer
}; };
#endif #endif
@@ -181,10 +181,10 @@ typedef bool (*ksu_perm_check_t)(void);
// IOCTL command mapping // IOCTL command mapping
struct ksu_ioctl_cmd_map { struct ksu_ioctl_cmd_map {
unsigned int cmd; unsigned int cmd;
const char *name; const char *name;
ksu_ioctl_handler_t handler; ksu_ioctl_handler_t handler;
ksu_perm_check_t perm_check; // Permission check function ksu_perm_check_t perm_check; // Permission check function
}; };
// Install KSU fd to current process // Install KSU fd to current process

View File

@@ -70,15 +70,15 @@ static void ksu_mark_running_process_locked()
continue; continue;
} }
int uid = task_uid(t).val; int uid = task_uid(t).val;
const struct cred *cred = get_task_cred(t); const struct cred *cred = get_task_cred(t);
bool ksu_root_process = bool ksu_root_process =
uid == 0 && is_task_ksu_domain(cred); uid == 0 && is_task_ksu_domain(cred);
bool is_zygote_process = is_zygote(cred); bool is_zygote_process = is_zygote(cred);
bool is_shell = uid == 2000; bool is_shell = uid == 2000;
// before boot completed, we shall mark init for marking zygote // before boot completed, we shall mark init for marking zygote
bool is_init = t->pid == 1; bool is_init = t->pid == 1;
if (ksu_root_process || is_zygote_process || is_shell || is_init if (ksu_root_process || is_zygote_process || is_shell || is_init
|| ksu_is_allow_uid(uid)) { || ksu_is_allow_uid(uid)) {
ksu_set_task_tracepoint_flag(t); ksu_set_task_tracepoint_flag(t);
pr_info("hook_manager: mark process: pid:%d, uid: %d, comm:%s\n", pr_info("hook_manager: mark process: pid:%d, uid: %d, comm:%s\n",
t->pid, uid, t->comm); t->pid, uid, t->comm);
@@ -87,7 +87,7 @@ static void ksu_mark_running_process_locked()
pr_info("hook_manager: unmark process: pid:%d, uid: %d, comm:%s\n", pr_info("hook_manager: unmark process: pid:%d, uid: %d, comm:%s\n",
t->pid, uid, t->comm); t->pid, uid, t->comm);
} }
put_cred(cred); put_cred(cred);
} }
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
} }
@@ -229,38 +229,38 @@ static struct kretprobe *syscall_unregfunc_rp = NULL;
static inline bool check_syscall_fastpath(int nr) static inline bool check_syscall_fastpath(int nr)
{ {
switch (nr) { switch (nr) {
case __NR_newfstatat: case __NR_newfstatat:
case __NR_faccessat: case __NR_faccessat:
case __NR_execve: case __NR_execve:
case __NR_setresuid: case __NR_setresuid:
case __NR_clone: case __NR_clone:
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0)
case __NR_clone3: case __NR_clone3:
#endif #endif
return true; return true;
default: default:
return false; return false;
} }
} }
// Unmark init's child that are not zygote, adbd or ksud // Unmark init's child that are not zygote, adbd or ksud
int ksu_handle_init_mark_tracker(const char __user **filename_user) int ksu_handle_init_mark_tracker(const char __user **filename_user)
{ {
char path[64]; char path[64];
if (unlikely(!filename_user)) if (unlikely(!filename_user))
return 0; return 0;
memset(path, 0, sizeof(path)); memset(path, 0, sizeof(path));
ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path)); ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path));
if (likely(strstr(path, "/app_process") == NULL && strstr(path, "/adbd") == NULL && strstr(path, "/ksud") == NULL)) { if (likely(strstr(path, "/app_process") == NULL && strstr(path, "/adbd") == NULL && strstr(path, "/ksud") == NULL)) {
pr_info("hook_manager: unmark %d exec %s", current->pid, path); pr_info("hook_manager: unmark %d exec %s", current->pid, path);
ksu_clear_task_tracepoint_flag_if_needed(current); ksu_clear_task_tracepoint_flag_if_needed(current);
} }
return 0; return 0;
} }
#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS #ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS
@@ -304,14 +304,14 @@ static void ksu_sys_enter_handler(void *data, struct pt_regs *regs, long id)
if (current->pid != 1 && is_init(get_current_cred())) { if (current->pid != 1 && is_init(get_current_cred())) {
ksu_handle_init_mark_tracker(filename_user); ksu_handle_init_mark_tracker(filename_user);
} else { } else {
ksu_handle_execve_sucompat(filename_user, NULL, NULL, NULL); ksu_handle_execve_sucompat(filename_user, NULL, NULL, NULL);
} }
return; return;
} }
} }
#endif #endif
// Handle setresuid // Handle setresuid
if (id == __NR_setresuid) { if (id == __NR_setresuid) {
uid_t ruid = (uid_t)PT_REGS_PARM1(regs); uid_t ruid = (uid_t)PT_REGS_PARM1(regs);
uid_t euid = (uid_t)PT_REGS_PARM2(regs); uid_t euid = (uid_t)PT_REGS_PARM2(regs);

View File

@@ -27,18 +27,18 @@ int ksu_set_task_mark(pid_t pid, bool mark);
static inline void ksu_set_task_tracepoint_flag(struct task_struct *t) static inline void ksu_set_task_tracepoint_flag(struct task_struct *t)
{ {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
set_task_syscall_work(t, SYSCALL_TRACEPOINT); set_task_syscall_work(t, SYSCALL_TRACEPOINT);
#else #else
set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT); set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
#endif #endif
} }
static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t) static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t)
{ {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
clear_task_syscall_work(t, SYSCALL_TRACEPOINT); clear_task_syscall_work(t, SYSCALL_TRACEPOINT);
#else #else
clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT); clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT);
#endif #endif
} }

View File

@@ -25,191 +25,191 @@ static bool need_rescan = false;
static void rescan_work_fn(struct work_struct *work) static void rescan_work_fn(struct work_struct *work)
{ {
// Signal userspace through proc interface // Signal userspace through proc interface
need_rescan = true; need_rescan = true;
pr_info("requested userspace uid rescan\n"); pr_info("requested userspace uid rescan\n");
} }
void ksu_request_userspace_scan(void) void ksu_request_userspace_scan(void)
{ {
if (scanner_wq) { if (scanner_wq) {
queue_work(scanner_wq, &scan_work); queue_work(scanner_wq, &scan_work);
} }
} }
void ksu_handle_userspace_update(void) void ksu_handle_userspace_update(void)
{ {
// Called when userspace notifies update complete // Called when userspace notifies update complete
need_rescan = false; need_rescan = false;
pr_info("userspace uid list updated\n"); pr_info("userspace uid list updated\n");
} }
static void do_save_throne_state(struct work_struct *work) static void do_save_throne_state(struct work_struct *work)
{ {
struct file *fp; struct file *fp;
char state_char = ksu_uid_scanner_enabled ? '1' : '0'; char state_char = ksu_uid_scanner_enabled ? '1' : '0';
loff_t off = 0; loff_t off = 0;
fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644); fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
pr_err("save_throne_state create file failed: %ld\n", PTR_ERR(fp)); pr_err("save_throne_state create file failed: %ld\n", PTR_ERR(fp));
return; return;
} }
if (ksu_kernel_write_compat(fp, &state_char, sizeof(state_char), &off) != sizeof(state_char)) { if (ksu_kernel_write_compat(fp, &state_char, sizeof(state_char), &off) != sizeof(state_char)) {
pr_err("save_throne_state write failed\n"); pr_err("save_throne_state write failed\n");
goto exit; goto exit;
} }
pr_info("throne state saved: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled"); pr_info("throne state saved: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled");
exit: exit:
filp_close(fp, 0); filp_close(fp, 0);
} }
void do_load_throne_state(struct work_struct *work) void do_load_throne_state(struct work_struct *work)
{ {
struct file *fp; struct file *fp;
char state_char; char state_char;
loff_t off = 0; loff_t off = 0;
ssize_t ret; ssize_t ret;
fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_RDONLY, 0); fp = ksu_filp_open_compat(UID_SCANNER_STATE_FILE, O_RDONLY, 0);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
pr_info("throne state file not found, using default: disabled\n"); pr_info("throne state file not found, using default: disabled\n");
ksu_uid_scanner_enabled = false; ksu_uid_scanner_enabled = false;
return; return;
} }
ret = ksu_kernel_read_compat(fp, &state_char, sizeof(state_char), &off); ret = ksu_kernel_read_compat(fp, &state_char, sizeof(state_char), &off);
if (ret != sizeof(state_char)) { if (ret != sizeof(state_char)) {
pr_err("load_throne_state read err: %zd\n", ret); pr_err("load_throne_state read err: %zd\n", ret);
ksu_uid_scanner_enabled = false; ksu_uid_scanner_enabled = false;
goto exit; goto exit;
} }
ksu_uid_scanner_enabled = (state_char == '1'); ksu_uid_scanner_enabled = (state_char == '1');
pr_info("throne state loaded: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled"); pr_info("throne state loaded: %s\n", ksu_uid_scanner_enabled ? "enabled" : "disabled");
exit: exit:
filp_close(fp, 0); filp_close(fp, 0);
} }
bool ksu_throne_comm_load_state(void) bool ksu_throne_comm_load_state(void)
{ {
return ksu_queue_work(&ksu_state_load_work); return ksu_queue_work(&ksu_state_load_work);
} }
void ksu_throne_comm_save_state(void) void ksu_throne_comm_save_state(void)
{ {
ksu_queue_work(&ksu_state_save_work); ksu_queue_work(&ksu_state_save_work);
} }
static int uid_scanner_show(struct seq_file *m, void *v) static int uid_scanner_show(struct seq_file *m, void *v)
{ {
if (need_rescan) { if (need_rescan) {
seq_puts(m, "RESCAN\n"); seq_puts(m, "RESCAN\n");
} else { } else {
seq_puts(m, "OK\n"); seq_puts(m, "OK\n");
} }
return 0; return 0;
} }
static int uid_scanner_open(struct inode *inode, struct file *file) static int uid_scanner_open(struct inode *inode, struct file *file)
{ {
return single_open(file, uid_scanner_show, NULL); return single_open(file, uid_scanner_show, NULL);
} }
static ssize_t uid_scanner_write(struct file *file, const char __user *buffer, static ssize_t uid_scanner_write(struct file *file, const char __user *buffer,
size_t count, loff_t *pos) size_t count, loff_t *pos)
{ {
char cmd[16]; char cmd[16];
if (count >= sizeof(cmd)) if (count >= sizeof(cmd))
return -EINVAL; return -EINVAL;
if (copy_from_user(cmd, buffer, count)) if (copy_from_user(cmd, buffer, count))
return -EFAULT; return -EFAULT;
cmd[count] = '\0'; cmd[count] = '\0';
// Remove newline if present // Remove newline if present
if (count > 0 && cmd[count-1] == '\n') if (count > 0 && cmd[count-1] == '\n')
cmd[count-1] = '\0'; cmd[count-1] = '\0';
if (strcmp(cmd, "UPDATED") == 0) { if (strcmp(cmd, "UPDATED") == 0) {
ksu_handle_userspace_update(); ksu_handle_userspace_update();
pr_info("received userspace update notification\n"); pr_info("received userspace update notification\n");
} }
return count; return count;
} }
#ifdef KSU_COMPAT_HAS_PROC_OPS #ifdef KSU_COMPAT_HAS_PROC_OPS
static const struct proc_ops uid_scanner_proc_ops = { static const struct proc_ops uid_scanner_proc_ops = {
.proc_open = uid_scanner_open, .proc_open = uid_scanner_open,
.proc_read = seq_read, .proc_read = seq_read,
.proc_write = uid_scanner_write, .proc_write = uid_scanner_write,
.proc_lseek = seq_lseek, .proc_lseek = seq_lseek,
.proc_release = single_release, .proc_release = single_release,
}; };
#else #else
static const struct file_operations uid_scanner_proc_ops = { static const struct file_operations uid_scanner_proc_ops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = uid_scanner_open, .open = uid_scanner_open,
.read = seq_read, .read = seq_read,
.write = uid_scanner_write, .write = uid_scanner_write,
.llseek = seq_lseek, .llseek = seq_lseek,
.release = single_release, .release = single_release,
}; };
#endif #endif
int ksu_throne_comm_init(void) int ksu_throne_comm_init(void)
{ {
// Create workqueue // Create workqueue
scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1); scanner_wq = alloc_workqueue("ksu_scanner", WQ_UNBOUND, 1);
if (!scanner_wq) { if (!scanner_wq) {
pr_err("failed to create scanner workqueue\n"); pr_err("failed to create scanner workqueue\n");
return -ENOMEM; return -ENOMEM;
} }
INIT_WORK(&scan_work, rescan_work_fn); INIT_WORK(&scan_work, rescan_work_fn);
// Create proc entry // Create proc entry
proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops); proc_entry = proc_create(PROC_UID_SCANNER, 0600, NULL, &uid_scanner_proc_ops);
if (!proc_entry) { if (!proc_entry) {
pr_err("failed to create proc entry\n"); pr_err("failed to create proc entry\n");
destroy_workqueue(scanner_wq); destroy_workqueue(scanner_wq);
return -ENOMEM; return -ENOMEM;
} }
pr_info("throne communication initialized\n"); pr_info("throne communication initialized\n");
return 0; return 0;
} }
void ksu_throne_comm_exit(void) void ksu_throne_comm_exit(void)
{ {
if (proc_entry) { if (proc_entry) {
proc_remove(proc_entry); proc_remove(proc_entry);
proc_entry = NULL; proc_entry = NULL;
} }
if (scanner_wq) { if (scanner_wq) {
destroy_workqueue(scanner_wq); destroy_workqueue(scanner_wq);
scanner_wq = NULL; scanner_wq = NULL;
} }
pr_info("throne communication cleaned up\n"); pr_info("throne communication cleaned up\n");
} }
int ksu_uid_init(void) int ksu_uid_init(void)
{ {
INIT_WORK(&ksu_state_save_work, do_save_throne_state); INIT_WORK(&ksu_state_save_work, do_save_throne_state);
INIT_WORK(&ksu_state_load_work, do_load_throne_state); INIT_WORK(&ksu_state_load_work, do_load_throne_state);
return 0; return 0;
} }
void ksu_uid_exit(void) void ksu_uid_exit(void)
{ {
do_save_throne_state(NULL); do_save_throne_state(NULL);
} }

View File

@@ -25,203 +25,203 @@ static uid_t locked_dynamic_manager_uid = KSU_INVALID_UID;
#define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list" #define SYSTEM_PACKAGES_LIST_PATH "/data/system/packages.list"
struct uid_data { struct uid_data {
struct list_head list; struct list_head list;
u32 uid; u32 uid;
char package[KSU_MAX_PACKAGE_NAME]; char package[KSU_MAX_PACKAGE_NAME];
}; };
// Try read /data/misc/user_uid/uid_list // Try read /data/misc/user_uid/uid_list
static int uid_from_um_list(struct list_head *uid_list) static int uid_from_um_list(struct list_head *uid_list)
{ {
struct file *fp; struct file *fp;
char *buf = NULL; char *buf = NULL;
loff_t size, pos = 0; loff_t size, pos = 0;
ssize_t nr; ssize_t nr;
int cnt = 0; int cnt = 0;
fp = ksu_filp_open_compat(KSU_UID_LIST_PATH, O_RDONLY, 0); fp = ksu_filp_open_compat(KSU_UID_LIST_PATH, O_RDONLY, 0);
if (IS_ERR(fp)) if (IS_ERR(fp))
return -ENOENT; return -ENOENT;
size = fp->f_inode->i_size; size = fp->f_inode->i_size;
if (size <= 0) { if (size <= 0) {
filp_close(fp, NULL); filp_close(fp, NULL);
return -ENODATA; return -ENODATA;
} }
buf = kzalloc(size + 1, GFP_ATOMIC); buf = kzalloc(size + 1, GFP_ATOMIC);
if (!buf) { if (!buf) {
pr_err("uid_list: OOM %lld B\n", size); pr_err("uid_list: OOM %lld B\n", size);
filp_close(fp, NULL); filp_close(fp, NULL);
return -ENOMEM; return -ENOMEM;
} }
nr = ksu_kernel_read_compat(fp, buf, size, &pos); nr = ksu_kernel_read_compat(fp, buf, size, &pos);
filp_close(fp, NULL); filp_close(fp, NULL);
if (nr != size) { if (nr != size) {
pr_err("uid_list: short read %zd/%lld\n", nr, size); pr_err("uid_list: short read %zd/%lld\n", nr, size);
kfree(buf); kfree(buf);
return -EIO; return -EIO;
} }
buf[size] = '\0'; buf[size] = '\0';
for (char *line = buf, *next; line; line = next) { for (char *line = buf, *next; line; line = next) {
next = strchr(line, '\n'); next = strchr(line, '\n');
if (next) *next++ = '\0'; if (next) *next++ = '\0';
while (*line == ' ' || *line == '\t' || *line == '\r') ++line; while (*line == ' ' || *line == '\t' || *line == '\r') ++line;
if (!*line) continue; if (!*line) continue;
char *uid_str = strsep(&line, " \t"); char *uid_str = strsep(&line, " \t");
char *pkg = line; char *pkg = line;
if (!pkg) continue; if (!pkg) continue;
while (*pkg == ' ' || *pkg == '\t') ++pkg; while (*pkg == ' ' || *pkg == '\t') ++pkg;
if (!*pkg) continue; if (!*pkg) continue;
u32 uid; u32 uid;
if (kstrtou32(uid_str, 10, &uid)) { if (kstrtou32(uid_str, 10, &uid)) {
pr_warn_once("uid_list: bad uid <%s>\n", uid_str); pr_warn_once("uid_list: bad uid <%s>\n", uid_str);
continue; continue;
} }
struct uid_data *d = kzalloc(sizeof(*d), GFP_ATOMIC); struct uid_data *d = kzalloc(sizeof(*d), GFP_ATOMIC);
if (unlikely(!d)) { if (unlikely(!d)) {
pr_err("uid_list: OOM uid=%u\n", uid); pr_err("uid_list: OOM uid=%u\n", uid);
continue; continue;
} }
d->uid = uid; d->uid = uid;
strscpy(d->package, pkg, KSU_MAX_PACKAGE_NAME); strscpy(d->package, pkg, KSU_MAX_PACKAGE_NAME);
list_add_tail(&d->list, uid_list); list_add_tail(&d->list, uid_list);
++cnt; ++cnt;
} }
kfree(buf); kfree(buf);
pr_info("uid_list: loaded %d entries\n", cnt); pr_info("uid_list: loaded %d entries\n", cnt);
return cnt > 0 ? 0 : -ENODATA; return cnt > 0 ? 0 : -ENODATA;
} }
static int get_pkg_from_apk_path(char *pkg, const char *path) static int get_pkg_from_apk_path(char *pkg, const char *path)
{ {
int len = strlen(path); int len = strlen(path);
if (len >= KSU_MAX_PACKAGE_NAME || len < 1) if (len >= KSU_MAX_PACKAGE_NAME || len < 1)
return -1; return -1;
const char *last_slash = NULL; const char *last_slash = NULL;
const char *second_last_slash = NULL; const char *second_last_slash = NULL;
int i; int i;
for (i = len - 1; i >= 0; i--) { for (i = len - 1; i >= 0; i--) {
if (path[i] == '/') { if (path[i] == '/') {
if (!last_slash) { if (!last_slash) {
last_slash = &path[i]; last_slash = &path[i];
} else { } else {
second_last_slash = &path[i]; second_last_slash = &path[i];
break; break;
} }
} }
} }
if (!last_slash || !second_last_slash) if (!last_slash || !second_last_slash)
return -1; return -1;
const char *last_hyphen = strchr(second_last_slash, '-'); const char *last_hyphen = strchr(second_last_slash, '-');
if (!last_hyphen || last_hyphen > last_slash) if (!last_hyphen || last_hyphen > last_slash)
return -1; return -1;
int pkg_len = last_hyphen - second_last_slash - 1; int pkg_len = last_hyphen - second_last_slash - 1;
if (pkg_len >= KSU_MAX_PACKAGE_NAME || pkg_len <= 0) if (pkg_len >= KSU_MAX_PACKAGE_NAME || pkg_len <= 0)
return -1; return -1;
// Copying the package name // Copying the package name
strncpy(pkg, second_last_slash + 1, pkg_len); strncpy(pkg, second_last_slash + 1, pkg_len);
pkg[pkg_len] = '\0'; pkg[pkg_len] = '\0';
return 0; return 0;
} }
static void crown_manager(const char *apk, struct list_head *uid_data, int signature_index) static void crown_manager(const char *apk, struct list_head *uid_data, int signature_index)
{ {
char pkg[KSU_MAX_PACKAGE_NAME]; char pkg[KSU_MAX_PACKAGE_NAME];
if (get_pkg_from_apk_path(pkg, apk) < 0) { if (get_pkg_from_apk_path(pkg, apk) < 0) {
pr_err("Failed to get package name from apk path: %s\n", apk); pr_err("Failed to get package name from apk path: %s\n", apk);
return; 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 #ifdef KSU_MANAGER_PACKAGE
// pkg is `/<real package>` // pkg is `/<real package>`
if (strncmp(pkg, KSU_MANAGER_PACKAGE, sizeof(KSU_MANAGER_PACKAGE))) { if (strncmp(pkg, KSU_MANAGER_PACKAGE, sizeof(KSU_MANAGER_PACKAGE))) {
pr_info("manager package is inconsistent with kernel build: %s\n", pr_info("manager package is inconsistent with kernel build: %s\n",
KSU_MANAGER_PACKAGE); KSU_MANAGER_PACKAGE);
return; return;
} }
#endif #endif
struct uid_data *np; struct uid_data *np;
list_for_each_entry(np, uid_data, list) { list_for_each_entry(np, uid_data, list) {
if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) { if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) {
bool is_dynamic = (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2); bool is_dynamic = (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2);
if (is_dynamic) { if (is_dynamic) {
if (locked_dynamic_manager_uid != KSU_INVALID_UID && locked_dynamic_manager_uid != np->uid) { 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); pr_info("Unlocking previous dynamic manager UID: %d\n", locked_dynamic_manager_uid);
ksu_remove_manager(locked_dynamic_manager_uid); ksu_remove_manager(locked_dynamic_manager_uid);
locked_dynamic_manager_uid = KSU_INVALID_UID; locked_dynamic_manager_uid = KSU_INVALID_UID;
} }
} else { } else {
if (locked_manager_uid != KSU_INVALID_UID && locked_manager_uid != np->uid) { if (locked_manager_uid != KSU_INVALID_UID && locked_manager_uid != np->uid) {
pr_info("Unlocking previous manager UID: %d\n", locked_manager_uid); pr_info("Unlocking previous manager UID: %d\n", locked_manager_uid);
ksu_invalidate_manager_uid(); // unlock old one ksu_invalidate_manager_uid(); // unlock old one
locked_manager_uid = KSU_INVALID_UID; locked_manager_uid = KSU_INVALID_UID;
} }
} }
pr_info("Crowning %s manager: %s (uid=%d, signature_index=%d)\n", pr_info("Crowning %s manager: %s (uid=%d, signature_index=%d)\n",
is_dynamic ? "dynamic" : "traditional", pkg, np->uid, signature_index); is_dynamic ? "dynamic" : "traditional", pkg, np->uid, signature_index);
if (is_dynamic) { if (is_dynamic) {
ksu_add_manager(np->uid, signature_index); ksu_add_manager(np->uid, signature_index);
locked_dynamic_manager_uid = np->uid; locked_dynamic_manager_uid = np->uid;
// If there is no traditional manager, set it to the current UID // If there is no traditional manager, set it to the current UID
if (!ksu_is_manager_uid_valid()) { if (!ksu_is_manager_uid_valid()) {
ksu_set_manager_uid(np->uid); ksu_set_manager_uid(np->uid);
locked_manager_uid = np->uid; locked_manager_uid = np->uid;
} }
} else { } else {
ksu_set_manager_uid(np->uid); // throne new UID ksu_set_manager_uid(np->uid); // throne new UID
locked_manager_uid = np->uid; // store locked UID locked_manager_uid = np->uid; // store locked UID
} }
break; break;
} }
} }
} }
#define DATA_PATH_LEN 384 // 384 is enough for /data/app/<package>/base.apk #define DATA_PATH_LEN 384 // 384 is enough for /data/app/<package>/base.apk
struct data_path { struct data_path {
char dirpath[DATA_PATH_LEN]; char dirpath[DATA_PATH_LEN];
int depth; int depth;
struct list_head list; struct list_head list;
}; };
struct apk_path_hash { struct apk_path_hash {
unsigned int hash; unsigned int hash;
bool exists; bool exists;
struct list_head list; struct list_head list;
}; };
static struct list_head apk_path_hash_list = LIST_HEAD_INIT(apk_path_hash_list); static struct list_head apk_path_hash_list = LIST_HEAD_INIT(apk_path_hash_list);
struct my_dir_context { struct my_dir_context {
struct dir_context ctx; struct dir_context ctx;
struct list_head *data_path_list; struct list_head *data_path_list;
char *parent_dir; char *parent_dir;
void *private_data; void *private_data;
int depth; int depth;
int *stop; int *stop;
}; };
// https://docs.kernel.org/filesystems/porting.html // 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. // 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.
@@ -236,334 +236,334 @@ struct my_dir_context {
#endif #endif
FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name, FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
int namelen, loff_t off, u64 ino, int namelen, loff_t off, u64 ino,
unsigned int d_type) unsigned int d_type)
{ {
struct my_dir_context *my_ctx = struct my_dir_context *my_ctx =
container_of(ctx, struct my_dir_context, ctx); container_of(ctx, struct my_dir_context, ctx);
char dirpath[DATA_PATH_LEN]; char dirpath[DATA_PATH_LEN];
if (!my_ctx) { if (!my_ctx) {
pr_err("Invalid context\n"); pr_err("Invalid context\n");
return FILLDIR_ACTOR_STOP; return FILLDIR_ACTOR_STOP;
} }
if (my_ctx->stop && *my_ctx->stop) { if (my_ctx->stop && *my_ctx->stop) {
pr_info("Stop searching\n"); pr_info("Stop searching\n");
return FILLDIR_ACTOR_STOP; return FILLDIR_ACTOR_STOP;
} }
if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen)) if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen))
return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".." return FILLDIR_ACTOR_CONTINUE; // Skip "." and ".."
if (d_type == DT_DIR && namelen >= 8 && !strncmp(name, "vmdl", 4) && if (d_type == DT_DIR && namelen >= 8 && !strncmp(name, "vmdl", 4) &&
!strncmp(name + namelen - 4, ".tmp", 4)) { !strncmp(name + namelen - 4, ".tmp", 4)) {
pr_info("Skipping directory: %.*s\n", namelen, name); pr_info("Skipping directory: %.*s\n", namelen, name);
return FILLDIR_ACTOR_CONTINUE; // Skip staging package return FILLDIR_ACTOR_CONTINUE; // Skip staging package
} }
if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir, if (snprintf(dirpath, DATA_PATH_LEN, "%s/%.*s", my_ctx->parent_dir,
namelen, name) >= DATA_PATH_LEN) { namelen, name) >= DATA_PATH_LEN) {
pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen, pr_err("Path too long: %s/%.*s\n", my_ctx->parent_dir, namelen,
name); name);
return FILLDIR_ACTOR_CONTINUE; return FILLDIR_ACTOR_CONTINUE;
} }
if (d_type == DT_DIR && my_ctx->depth > 0 && if (d_type == DT_DIR && my_ctx->depth > 0 &&
(my_ctx->stop && !*my_ctx->stop)) { (my_ctx->stop && !*my_ctx->stop)) {
struct data_path *data = kzalloc(sizeof(struct data_path), GFP_ATOMIC); struct data_path *data = kzalloc(sizeof(struct data_path), GFP_ATOMIC);
if (!data) { if (!data) {
pr_err("Failed to allocate memory for %s\n", dirpath); pr_err("Failed to allocate memory for %s\n", dirpath);
return FILLDIR_ACTOR_CONTINUE; return FILLDIR_ACTOR_CONTINUE;
} }
strscpy(data->dirpath, dirpath, DATA_PATH_LEN); strscpy(data->dirpath, dirpath, DATA_PATH_LEN);
data->depth = my_ctx->depth - 1; data->depth = my_ctx->depth - 1;
list_add_tail(&data->list, my_ctx->data_path_list); list_add_tail(&data->list, my_ctx->data_path_list);
} else { } else {
if ((namelen == 8) && (strncmp(name, "base.apk", namelen) == 0)) { if ((namelen == 8) && (strncmp(name, "base.apk", namelen) == 0)) {
struct apk_path_hash *pos, *n; struct apk_path_hash *pos, *n;
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0)
unsigned int hash = full_name_hash(dirpath, strlen(dirpath)); unsigned int hash = full_name_hash(dirpath, strlen(dirpath));
#else #else
unsigned int hash = full_name_hash(NULL, dirpath, strlen(dirpath)); unsigned int hash = full_name_hash(NULL, dirpath, strlen(dirpath));
#endif #endif
list_for_each_entry(pos, &apk_path_hash_list, list) { list_for_each_entry(pos, &apk_path_hash_list, list) {
if (hash == pos->hash) { if (hash == pos->hash) {
pos->exists = true; pos->exists = true;
return FILLDIR_ACTOR_CONTINUE; return FILLDIR_ACTOR_CONTINUE;
} }
} }
int signature_index = -1; int signature_index = -1;
bool is_multi_manager = is_dynamic_manager_apk( bool is_multi_manager = is_dynamic_manager_apk(
dirpath, &signature_index); dirpath, &signature_index);
pr_info("Found new base.apk at path: %s, is_multi_manager: %d, signature_index: %d\n", pr_info("Found new base.apk at path: %s, is_multi_manager: %d, signature_index: %d\n",
dirpath, is_multi_manager, signature_index); dirpath, is_multi_manager, signature_index);
// Check for dynamic sign or multi-manager signatures // Check for dynamic sign or multi-manager signatures
if (is_multi_manager && (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2)) { if (is_multi_manager && (signature_index == DYNAMIC_SIGN_INDEX || signature_index >= 2)) {
crown_manager(dirpath, my_ctx->private_data, signature_index); crown_manager(dirpath, my_ctx->private_data, signature_index);
} else if (is_manager_apk(dirpath)) { } else if (is_manager_apk(dirpath)) {
crown_manager(dirpath, my_ctx->private_data, 0); crown_manager(dirpath, my_ctx->private_data, 0);
*my_ctx->stop = 1; *my_ctx->stop = 1;
} }
struct apk_path_hash *apk_data = kzalloc(sizeof(*apk_data), GFP_ATOMIC); struct apk_path_hash *apk_data = kzalloc(sizeof(*apk_data), GFP_ATOMIC);
if (apk_data) { if (apk_data) {
apk_data->hash = hash; apk_data->hash = hash;
apk_data->exists = true; apk_data->exists = true;
list_add_tail(&apk_data->list, &apk_path_hash_list); list_add_tail(&apk_data->list, &apk_path_hash_list);
} }
if (is_manager_apk(dirpath)) { if (is_manager_apk(dirpath)) {
// Manager found, clear APK cache list // Manager found, clear APK cache list
list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) { list_for_each_entry_safe(pos, n, &apk_path_hash_list, list) {
list_del(&pos->list); list_del(&pos->list);
kfree(pos); kfree(pos);
} }
} }
} }
} }
return FILLDIR_ACTOR_CONTINUE; return FILLDIR_ACTOR_CONTINUE;
} }
void search_manager(const char *path, int depth, struct list_head *uid_data) void search_manager(const char *path, int depth, struct list_head *uid_data)
{ {
int i, stop = 0; int i, stop = 0;
struct list_head data_path_list; struct list_head data_path_list;
INIT_LIST_HEAD(&data_path_list); INIT_LIST_HEAD(&data_path_list);
unsigned long data_app_magic = 0; unsigned long data_app_magic = 0;
// Initialize APK cache list // Initialize APK cache list
struct apk_path_hash *pos, *n; struct apk_path_hash *pos, *n;
list_for_each_entry (pos, &apk_path_hash_list, list) { list_for_each_entry (pos, &apk_path_hash_list, list) {
pos->exists = false; pos->exists = false;
} }
// First depth // First depth
struct data_path data; struct data_path data;
strscpy(data.dirpath, path, DATA_PATH_LEN); strscpy(data.dirpath, path, DATA_PATH_LEN);
data.depth = depth; data.depth = depth;
list_add_tail(&data.list, &data_path_list); list_add_tail(&data.list, &data_path_list);
for (i = depth; i >= 0; i--) { for (i = depth; i >= 0; i--) {
struct data_path *pos, *n; struct data_path *pos, *n;
list_for_each_entry_safe (pos, n, &data_path_list, list) { list_for_each_entry_safe (pos, n, &data_path_list, list) {
struct my_dir_context ctx = { .ctx.actor = my_actor, struct my_dir_context ctx = { .ctx.actor = my_actor,
.data_path_list = &data_path_list, .data_path_list = &data_path_list,
.parent_dir = pos->dirpath, .parent_dir = pos->dirpath,
.private_data = uid_data, .private_data = uid_data,
.depth = pos->depth, .depth = pos->depth,
.stop = &stop }; .stop = &stop };
struct file *file; struct file *file;
if (!stop) { if (!stop) {
file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0); file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0);
if (IS_ERR(file)) { if (IS_ERR(file)) {
pr_err("Failed to open directory: %s, err: %ld\n", pr_err("Failed to open directory: %s, err: %ld\n",
pos->dirpath, PTR_ERR(file)); pos->dirpath, PTR_ERR(file));
goto skip_iterate; goto skip_iterate;
} }
// grab magic on first folder, which is /data/app // grab magic on first folder, which is /data/app
if (!data_app_magic) { if (!data_app_magic) {
if (file->f_inode->i_sb->s_magic) { if (file->f_inode->i_sb->s_magic) {
data_app_magic = file->f_inode->i_sb->s_magic; data_app_magic = file->f_inode->i_sb->s_magic;
pr_info("%s: dir: %s got magic! 0x%lx\n", __func__, pr_info("%s: dir: %s got magic! 0x%lx\n", __func__,
pos->dirpath, data_app_magic); pos->dirpath, data_app_magic);
} else { } else {
filp_close(file, NULL); filp_close(file, NULL);
goto skip_iterate; goto skip_iterate;
} }
} }
if (file->f_inode->i_sb->s_magic != data_app_magic) { if (file->f_inode->i_sb->s_magic != data_app_magic) {
pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n", pr_info("%s: skip: %s magic: 0x%lx expected: 0x%lx\n",
__func__, pos->dirpath, __func__, pos->dirpath,
file->f_inode->i_sb->s_magic, data_app_magic); file->f_inode->i_sb->s_magic, data_app_magic);
filp_close(file, NULL); filp_close(file, NULL);
goto skip_iterate; goto skip_iterate;
} }
iterate_dir(file, &ctx.ctx); iterate_dir(file, &ctx.ctx);
filp_close(file, NULL); filp_close(file, NULL);
} }
skip_iterate: skip_iterate:
list_del(&pos->list); list_del(&pos->list);
if (pos != &data) if (pos != &data)
kfree(pos); kfree(pos);
} }
} }
// Remove stale cached APK entries // Remove stale cached APK entries
list_for_each_entry_safe (pos, n, &apk_path_hash_list, list) { list_for_each_entry_safe (pos, n, &apk_path_hash_list, list) {
if (!pos->exists) { if (!pos->exists) {
list_del(&pos->list); list_del(&pos->list);
kfree(pos); kfree(pos);
} }
} }
} }
static bool is_uid_exist(uid_t uid, char *package, void *data) static bool is_uid_exist(uid_t uid, char *package, void *data)
{ {
struct list_head *list = (struct list_head *)data; struct list_head *list = (struct list_head *)data;
struct uid_data *np; struct uid_data *np;
bool exist = false; bool exist = false;
list_for_each_entry (np, list, list) { list_for_each_entry (np, list, list) {
if (np->uid == uid % 100000 && if (np->uid == uid % 100000 &&
strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) { strncmp(np->package, package, KSU_MAX_PACKAGE_NAME) == 0) {
exist = true; exist = true;
break; break;
} }
} }
return exist; return exist;
} }
void track_throne(bool prune_only) void track_throne(bool prune_only)
{ {
struct list_head uid_list; struct list_head uid_list;
struct uid_data *np, *n; struct uid_data *np, *n;
struct file *fp; struct file *fp;
char chr = 0; char chr = 0;
loff_t pos = 0; loff_t pos = 0;
loff_t line_start = 0; loff_t line_start = 0;
char buf[KSU_MAX_PACKAGE_NAME]; char buf[KSU_MAX_PACKAGE_NAME];
static bool manager_exist = false; static bool manager_exist = false;
static bool dynamic_manager_exist = false; static bool dynamic_manager_exist = false;
int current_manager_uid = ksu_get_manager_uid() % 100000; int current_manager_uid = ksu_get_manager_uid() % 100000;
// init uid list head // init uid list head
INIT_LIST_HEAD(&uid_list); INIT_LIST_HEAD(&uid_list);
if (ksu_uid_scanner_enabled) { if (ksu_uid_scanner_enabled) {
pr_info("Scanning %s directory..\n", KSU_UID_LIST_PATH); pr_info("Scanning %s directory..\n", KSU_UID_LIST_PATH);
if (uid_from_um_list(&uid_list) == 0) { if (uid_from_um_list(&uid_list) == 0) {
pr_info("Loaded UIDs from %s success\n", KSU_UID_LIST_PATH); pr_info("Loaded UIDs from %s success\n", KSU_UID_LIST_PATH);
goto uid_ready; goto uid_ready;
} }
pr_warn("%s read failed, fallback to %s\n", pr_warn("%s read failed, fallback to %s\n",
KSU_UID_LIST_PATH, SYSTEM_PACKAGES_LIST_PATH); KSU_UID_LIST_PATH, SYSTEM_PACKAGES_LIST_PATH);
} }
{ {
fp = ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0); fp = ksu_filp_open_compat(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0);
if (IS_ERR(fp)) { if (IS_ERR(fp)) {
pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n", __func__, PTR_ERR(fp)); pr_err("%s: open " SYSTEM_PACKAGES_LIST_PATH " failed: %ld\n", __func__, PTR_ERR(fp));
return; return;
} }
for (;;) { for (;;) {
ssize_t count = ssize_t count =
ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos); ksu_kernel_read_compat(fp, &chr, sizeof(chr), &pos);
if (count != sizeof(chr)) if (count != sizeof(chr))
break; break;
if (chr != '\n') if (chr != '\n')
continue; continue;
count = ksu_kernel_read_compat(fp, buf, sizeof(buf), count = ksu_kernel_read_compat(fp, buf, sizeof(buf),
&line_start); &line_start);
struct uid_data *data = struct uid_data *data =
kzalloc(sizeof(struct uid_data), GFP_ATOMIC); kzalloc(sizeof(struct uid_data), GFP_ATOMIC);
if (!data) { if (!data) {
filp_close(fp, 0); filp_close(fp, 0);
goto out; goto out;
} }
char *tmp = buf; char *tmp = buf;
const char *delim = " "; const char *delim = " ";
char *package = strsep(&tmp, delim); char *package = strsep(&tmp, delim);
char *uid = strsep(&tmp, delim); char *uid = strsep(&tmp, delim);
if (!uid || !package) { if (!uid || !package) {
pr_err("update_uid: package or uid is NULL!\n"); pr_err("update_uid: package or uid is NULL!\n");
break; break;
} }
u32 res; u32 res;
if (kstrtou32(uid, 10, &res)) { if (kstrtou32(uid, 10, &res)) {
pr_err("update_uid: uid parse err\n"); pr_err("update_uid: uid parse err\n");
break; break;
} }
data->uid = res; data->uid = res;
strncpy(data->package, package, KSU_MAX_PACKAGE_NAME); strncpy(data->package, package, KSU_MAX_PACKAGE_NAME);
list_add_tail(&data->list, &uid_list); list_add_tail(&data->list, &uid_list);
// reset line start // reset line start
line_start = pos; line_start = pos;
} }
filp_close(fp, 0); filp_close(fp, 0);
} }
uid_ready: uid_ready:
if (prune_only) if (prune_only)
goto prune; goto prune;
// first, check if manager_uid exist! // first, check if manager_uid exist!
list_for_each_entry(np, &uid_list, list) { list_for_each_entry(np, &uid_list, list) {
if (np->uid == current_manager_uid) { if (np->uid == current_manager_uid) {
manager_exist = true; manager_exist = true;
break; break;
} }
} }
if (!manager_exist && locked_manager_uid != KSU_INVALID_UID) { if (!manager_exist && locked_manager_uid != KSU_INVALID_UID) {
pr_info("Manager APK removed, unlock previous UID: %d\n", pr_info("Manager APK removed, unlock previous UID: %d\n",
locked_manager_uid); locked_manager_uid);
ksu_invalidate_manager_uid(); ksu_invalidate_manager_uid();
locked_manager_uid = KSU_INVALID_UID; locked_manager_uid = KSU_INVALID_UID;
} }
// Check if the Dynamic Manager exists (only check locked UIDs) // Check if the Dynamic Manager exists (only check locked UIDs)
if (ksu_is_dynamic_manager_enabled() && if (ksu_is_dynamic_manager_enabled() &&
locked_dynamic_manager_uid != KSU_INVALID_UID) { locked_dynamic_manager_uid != KSU_INVALID_UID) {
list_for_each_entry(np, &uid_list, list) { list_for_each_entry(np, &uid_list, list) {
if (np->uid == locked_dynamic_manager_uid) { if (np->uid == locked_dynamic_manager_uid) {
dynamic_manager_exist = true; dynamic_manager_exist = true;
break; break;
} }
} }
if (!dynamic_manager_exist) { if (!dynamic_manager_exist) {
pr_info("Dynamic manager APK removed, unlock previous UID: %d\n", pr_info("Dynamic manager APK removed, unlock previous UID: %d\n",
locked_dynamic_manager_uid); locked_dynamic_manager_uid);
ksu_remove_manager(locked_dynamic_manager_uid); ksu_remove_manager(locked_dynamic_manager_uid);
locked_dynamic_manager_uid = KSU_INVALID_UID; locked_dynamic_manager_uid = KSU_INVALID_UID;
} }
} }
bool need_search = !manager_exist; bool need_search = !manager_exist;
if (ksu_is_dynamic_manager_enabled() && !dynamic_manager_exist) if (ksu_is_dynamic_manager_enabled() && !dynamic_manager_exist)
need_search = true; need_search = true;
if (need_search) { if (need_search) {
pr_info("Searching for manager(s)...\n"); pr_info("Searching for manager(s)...\n");
search_manager("/data/app", 2, &uid_list); search_manager("/data/app", 2, &uid_list);
pr_info("Manager search finished\n"); pr_info("Manager search finished\n");
} }
prune: prune:
// then prune the allowlist // then prune the allowlist
ksu_prune_allowlist(is_uid_exist, &uid_list); ksu_prune_allowlist(is_uid_exist, &uid_list);
out: out:
// free uid_list // free uid_list
list_for_each_entry_safe(np, n, &uid_list, list) { list_for_each_entry_safe(np, n, &uid_list, list) {
list_del(&np->list); list_del(&np->list);
kfree(np); kfree(np);
} }
} }
void ksu_throne_tracker_init(void) void ksu_throne_tracker_init(void)
{ {
// nothing to do // nothing to do
} }
void ksu_throne_tracker_exit(void) void ksu_throne_tracker_exit(void)
{ {
// nothing to do // nothing to do
} }