diff --git a/kernel/apk_sign.c b/kernel/apk_sign.c index fda32ac7e9ea..a1a27b69b084 100644 --- a/kernel/apk_sign.c +++ b/kernel/apk_sign.c @@ -188,7 +188,7 @@ static __always_inline bool check_v2_signature(char *path, struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0); if (IS_ERR(fp)) { pr_err("open %s error.\n", path); - return PTR_ERR(fp); + return false; } // disable inotify for this file @@ -239,7 +239,6 @@ static __always_inline bool check_v2_signature(char *path, } ksu_kernel_read_compat(fp, &id, 0x4, &pos); // id offset = 4; - pr_info("id: 0x%08x\n", id); if (id == 0x7109871au) { v2_signing_blocks++; v2_signing_valid = @@ -251,6 +250,8 @@ static __always_inline bool check_v2_signature(char *path, } else if (id == 0x1b93ad61u) { // http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#74 v3_1_signing_exist = true; + } else { + pr_info("Unknown id: 0x%08x\n", id); } pos += (size8 - offset); } diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 5bbac8dcc1a0..ec9ca1358de3 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -230,75 +230,12 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, #endif if (arg2 == CMD_BECOME_MANAGER) { - // quick check if (is_manager()) { if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { pr_err("become_manager: prctl reply error\n"); } return 0; } - if (ksu_is_manager_uid_valid()) { -#ifdef CONFIG_KSU_DEBUG - pr_info("manager already exist: %d\n", - ksu_get_manager_uid()); -#endif - return 0; - } - - // someone wants to be root manager, just check it! - // arg3 should be `/data/user//` - char param[128]; - if (ksu_strncpy_from_user_nofault(param, arg3, sizeof(param)) == - -EFAULT) { -#ifdef CONFIG_KSU_DEBUG - pr_err("become_manager: copy param err\n"); -#endif - goto block; - } - - // for user 0, it is /data/data - // for user 999, it is /data/user/999 - const char *prefix; - char prefixTmp[64]; - int userId = current_uid().val / 100000; - if (userId == 0) { - prefix = "/data/data"; - } else { - snprintf(prefixTmp, sizeof(prefixTmp), "/data/user/%d", - userId); - prefix = prefixTmp; - } - - if (startswith(param, (char *)prefix) != 0) { - pr_info("become_manager: invalid param: %s\n", param); - goto block; - } - - // stat the param, app must have permission to do this - // otherwise it may fake the path! - struct path path; - if (kern_path(param, LOOKUP_DIRECTORY, &path)) { - pr_err("become_manager: kern_path err\n"); - goto block; - } - uid_t inode_uid = path.dentry->d_inode->i_uid.val; - path_put(&path); - if (inode_uid != current_uid().val) { - pr_err("become_manager: path uid != current uid\n"); - goto block; - } - char *pkg = param + strlen(prefix); - pr_info("become_manager: param pkg: %s\n", pkg); - - bool success = become_manager(pkg); - if (success) { - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("become_manager: prctl reply error\n"); - } - return 0; - } - block: - last_failed_uid = current_uid().val; return 0; } diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index eb72f202ae39..2ae960afe93f 100644 --- a/kernel/selinux/rules.c +++ b/kernel/selinux/rules.c @@ -69,6 +69,11 @@ void apply_kernelsu_rules() // we need to save allowlist in /data/adb/ksu ksu_allow(db, "kernel", "adb_data_file", "dir", ALL); ksu_allow(db, "kernel", "adb_data_file", "file", ALL); + // we need to search /data/app + ksu_allow(db, "kernel", "apk_data_file", "file", "open"); + ksu_allow(db, "kernel", "apk_data_file", "dir", "open"); + ksu_allow(db, "kernel", "apk_data_file", "dir", "read"); + ksu_allow(db, "kernel", "apk_data_file", "dir", "search"); // we may need to do mount on shell ksu_allow(db, "kernel", "shell_data_file", "file", ALL); // we need to read /data/system/packages.list diff --git a/kernel/uid_observer.c b/kernel/uid_observer.c index cd216e1733b7..d912ce2db3a4 100644 --- a/kernel/uid_observer.c +++ b/kernel/uid_observer.c @@ -23,6 +23,155 @@ struct uid_data { char package[KSU_MAX_PACKAGE_NAME]; }; +static int get_pkg_from_apk_path(char *pkg, const char *path) +{ + int len = strlen(path); + if (len >= KSU_MAX_PACKAGE_NAME || len < 1) + return -1; + + const char *last_slash = NULL; + const char *second_last_slash = NULL; + + for (int i = len - 1; i >= 0; i--) { + if (path[i] == '/') { + if (!last_slash) { + last_slash = &path[i]; + } else { + second_last_slash = &path[i]; + break; + } + } + } + + if (!last_slash || !second_last_slash) + return -1; + + const char *last_hyphen = strchr(second_last_slash, '-'); + if (!last_hyphen || last_hyphen > last_slash) + return -1; + + int pkg_len = last_hyphen - second_last_slash - 1; + if (pkg_len >= KSU_MAX_PACKAGE_NAME || pkg_len <= 0) + return -1; + + // Copying the package name + strncpy(pkg, second_last_slash + 1, pkg_len); + pkg[pkg_len] = '\0'; + + return 0; +} + +static void crown_manager(const char *apk, struct list_head *uid_data) +{ + char pkg[KSU_MAX_PACKAGE_NAME]; + if (get_pkg_from_apk_path(pkg, apk) < 0) { + pr_err("Failed to get package name from apk path: %s\n", apk); + return; + } + + pr_info("manager pkg: %s\n", pkg); + + struct list_head *list = (struct list_head *)uid_data; + struct uid_data *np; + + list_for_each_entry (np, list, list) { + if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) { + pr_info("Crowning manager: %s(uid=%d)\n", pkg, np->uid); + ksu_set_manager_uid(np->uid); + break; + } + } +} + +struct my_dir_context { + struct dir_context ctx; + char *parent_dir; + void *private_data; + int depth; + int *stop; +}; + +int my_actor(struct dir_context *ctx, const char *name, int namelen, loff_t off, + u64 ino, unsigned int d_type) +{ + struct my_dir_context *my_ctx = + container_of(ctx, struct my_dir_context, ctx); + struct file *file; + char *dirpath; + + if (!my_ctx) { + pr_err("Invalid context\n"); + return -EINVAL; + } + if (my_ctx->stop && *my_ctx->stop) { + return 1; + } + + if (!strncmp(name, "..", namelen) || !strncmp(name, ".", namelen)) + return 0; // Skip "." and ".." + + dirpath = kmalloc(PATH_MAX, GFP_KERNEL); + if (!dirpath) { + return -ENOMEM; // Failed to obtain directory path + } + snprintf(dirpath, PATH_MAX, "%s/%.*s", my_ctx->parent_dir, namelen, + name); + + if (d_type == DT_DIR && my_ctx->depth > 0 && + (my_ctx->stop && !*my_ctx->stop)) { + struct my_dir_context sub_ctx = { .ctx.actor = my_actor, + .parent_dir = dirpath, + .private_data = + my_ctx->private_data, + .depth = my_ctx->depth - 1, + .stop = my_ctx->stop }; + file = ksu_filp_open_compat(dirpath, O_RDONLY, 0); + if (IS_ERR(file)) { + pr_err("Failed to open directory: %s, err: %d\n", + dirpath, PTR_ERR(file)); + kfree(dirpath); + return PTR_ERR(file); + } + + iterate_dir(file, &sub_ctx.ctx); + filp_close(file, NULL); + } else { + if ((strlen(name) == strlen("base.apk")) && + (strncmp(name, "base.apk", strlen("base.apk")) == 0)) { + bool is_manager = is_manager_apk(dirpath); + pr_info("Found base.apk at path: %s, is_manager: %d\n", + dirpath, is_manager); + if (is_manager) { + crown_manager(dirpath, my_ctx->private_data); + *my_ctx->stop = 1; + } + } + kfree(dirpath); + } + + return 0; +} + +void search_manager(const char *path, int depth, struct list_head *uid_data) +{ + struct file *file; + int stop = 0; + struct my_dir_context ctx = { .ctx.actor = my_actor, + .parent_dir = (char *)path, + .private_data = uid_data, + .depth = depth, + .stop = &stop }; + + file = ksu_filp_open_compat(path, O_RDONLY, 0); + if (IS_ERR(file)) { + pr_err("Failed to open directory: %s\n", path); + return; + } + + iterate_dir(file, &ctx.ctx); + filp_close(file, NULL); +} + static bool is_uid_exist(uid_t uid, char *package, void *data) { struct list_head *list = (struct list_head *)data; @@ -111,9 +260,13 @@ static void do_update_uid(struct work_struct *work) } } - if (!manager_exist && ksu_is_manager_uid_valid()) { - pr_info("manager is uninstalled, invalidate it!\n"); - ksu_invalidate_manager_uid(); + if (!manager_exist) { + if (ksu_is_manager_uid_valid()) { + pr_info("manager is uninstalled, invalidate it!\n"); + ksu_invalidate_manager_uid(); + } + pr_info("Searching manager...\n"); + search_manager("/data/app", 2, &uid_list); } // then prune the allowlist