diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 06a5d432..f174f75e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,6 +39,11 @@ jobs: mv kpimg.elf kpimg.elf-android make clean + make kpimg NO_ROOT=1 AUTOLOAD_KPM=1 + mv kpimg kpimg-android-no-root + mv kpimg.elf kpimg.elf-android-no-root + make clean + unset ANDROID make mv kpimg kpimg-linux @@ -116,6 +121,7 @@ jobs: artifacts: | kernel/kpimg-linux kernel/kpimg-android + kernel/kpimg-android-no-root kernel/kpimg-x86 kpms/demo-hello/demo-hello.kpm kpms/demo-inlinehook/demo-inlinehook.kpm diff --git a/doc/en/build.md b/doc/en/build.md index 5dc36200..78c96505 100644 --- a/doc/en/build.md +++ b/doc/en/build.md @@ -10,6 +10,24 @@ export TARGET_COMPILE=aarch64-none-elf- cd kernel export ANDROID=1 # Android version, including support for the 'su' command make + +# Or build without built-in root +make NO_ROOT=1 +# Or Android version without built-in root +make ANDROID=1 NO_ROOT=1 + +# Optional manager flags: +# When NO_ROOT=1, official manager rename hooks and APK scans are disabled by default. +# To enable official manager package monitoring in NO_ROOT builds: +make ANDROID=1 NO_ROOT=1 OFFICIAL_MANAGER=1 +# To disable official manager rename hooks in a standard root build: +make ANDROID=1 NO_MANAGER=1 + +# Autoload KPM modules (for builds without official manager or with NO_ROOT): +# Automatically load KPM modules from /data/adb/kpm or /data/adb/ap/kpm once /data is mounted (post-fs-data, before Zygote): +make ANDROID=1 NO_ROOT=1 AUTOLOAD_KPM=1 +# Or in a standard root build without official manager: +make ANDROID=1 NO_MANAGER=1 AUTOLOAD_KPM=1 ``` ## Build kptools diff --git a/doc/zh-CN/build.md b/doc/zh-CN/build.md index bf60111c..4e514c39 100644 --- a/doc/zh-CN/build.md +++ b/doc/zh-CN/build.md @@ -10,6 +10,24 @@ export TARGET_COMPILE=aarch64-none-elf- cd kernel export ANDROID=1 # Android 版本,包含 su 命令支持 make + +# 或者在不包含内置 root 的情况下编译 +make NO_ROOT=1 +# 或 Android 版本不包含内置 root +make ANDROID=1 NO_ROOT=1 + +# Manager 可选标志: +# 当 NO_ROOT=1 时,官方 Manager 的 rename hook 和 APK 扫描默认处于禁用状态。 +# 在 NO_ROOT 构建中启用官方 Manager 包监控: +make ANDROID=1 NO_ROOT=1 OFFICIAL_MANAGER=1 +# 在标准 root 构建中禁用官方 Manager rename hook: +make ANDROID=1 NO_MANAGER=1 + +# 自动加载 KPM 模块(适用于无官方 Manager 或启用 NO_ROOT 的构建): +# 在 /data 分区挂载后(post-fs-data 阶段,Zygote 启动前)自动从 /data/adb/kpm 或 /data/adb/ap/kpm 加载 KPM 模块: +make ANDROID=1 NO_ROOT=1 AUTOLOAD_KPM=1 +# 或在无官方 Manager 的标准 root 构建中: +make ANDROID=1 NO_MANAGER=1 AUTOLOAD_KPM=1 ``` ## 编译 kptools diff --git a/kernel/Makefile b/kernel/Makefile index 44e7516e..6dd2cada 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -45,6 +45,51 @@ ifdef ANDROID X86_CFLAGS += -DANDROID endif +ENABLE_ROOT ?= 1 +ifeq ($(NO_ROOT),1) + ENABLE_ROOT := 0 +endif +ifeq ($(DISABLE_ROOT),1) + ENABLE_ROOT := 0 +endif + +ifeq ($(ENABLE_ROOT),0) + CFLAGS += -DCONFIG_KP_NO_ROOT + X86_CFLAGS += -DCONFIG_KP_NO_ROOT +endif + +ENABLE_OFFICIAL_MANAGER ?= $(ENABLE_ROOT) +ifeq ($(OFFICIAL_MANAGER),1) + ENABLE_OFFICIAL_MANAGER := $(OFFICIAL_MANAGER) +endif +ifeq ($(NO_MANAGER),1) + ENABLE_OFFICIAL_MANAGER := 0 +endif +ifeq ($(NO_OFFICIAL_MANAGER),1) + ENABLE_OFFICIAL_MANAGER := 0 +endif + +ifeq ($(ENABLE_OFFICIAL_MANAGER),0) + CFLAGS += -DCONFIG_KP_NO_OFFICIAL_MANAGER + X86_CFLAGS += -DCONFIG_KP_NO_OFFICIAL_MANAGER +endif + +ENABLE_AUTOLOAD_KPM ?= 0 +ifeq ($(AUTOLOAD_KPM),1) + ENABLE_AUTOLOAD_KPM := 1 +endif +ifeq ($(CONFIG_KP_AUTOLOAD_KPM),1) + ENABLE_AUTOLOAD_KPM := 1 +endif +ifeq ($(NO_AUTOLOAD_KPM),1) + ENABLE_AUTOLOAD_KPM := 0 +endif + +ifeq ($(ENABLE_AUTOLOAD_KPM),1) + CFLAGS += -DCONFIG_KP_AUTOLOAD_KPM + X86_CFLAGS += -DCONFIG_KP_AUTOLOAD_KPM +endif + INCLUDE := -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include X86_INCLUDE := -I. -Iinclude -Ipatch/include -Ilinux/include -Ilinux/include/uapi/asm-generic -Ilinux/tools/arch/arm64/include @@ -76,6 +121,17 @@ ifdef ANDROID BASE_SRCS += $(wildcard patch/android/*.c) endif +ifeq ($(ENABLE_ROOT),0) + ROOT_SRCS := \ + patch/common/selinux_hide.c \ + patch/common/selinux_sepolicy.c \ + patch/common/sucompat.c \ + patch/common/supercmd.c \ + patch/common/user_event.c + + BASE_SRCS := $(filter-out $(ROOT_SRCS),$(BASE_SRCS)) +endif + SRCS += $(BASE_SRCS) SRCS += $(LINUX_SRCS) diff --git a/kernel/patch/android/userd.c b/kernel/patch/android/userd.c index d2b43b22..a76af698 100644 --- a/kernel/patch/android/userd.c +++ b/kernel/patch/android/userd.c @@ -41,21 +41,23 @@ #include #include #include +#include #include #define REPLACE_RC_FILE "/dev/user_init.rc" -#define ADB_FLODER "/data/adb/" -#define AP_DIR "/data/adb/ap/" +#define ADB_FOLDER "/data/adb" +#define AP_DIR "/data/adb/ap" #define DEV_LOG_DIR "/dev/user_init_log/" -#define AP_BIN_DIR AP_DIR "bin/" -#define AP_LOG_DIR AP_DIR "log/" +#define AP_BIN_DIR AP_DIR "/bin/" +#define AP_LOG_DIR AP_DIR "/log/" #define MAGISK_SCTX "u:r:magisk:s0" #define APD_PATH "/data/adb/apd" #define AP_PACKAGE_CONFIG_PATH "/data/adb/ap/package_config" #define ANDROID_PACKAGES_LIST_PATH "/data/system/packages.list" #define ANDROID_PACKAGES_LIST_TMP_PATH "/data/system/packages.list.tmp" -#define AP_KPM_DIR AP_DIR "kpm/" +#define ADB_KPM_DIR ADB_FOLDER "/kpm" +#define AP_KPM_DIR AP_DIR "/kpm" #define AP_KPM_NAME_LEN 128 #define AP_KPM_MAX_MODULES 256 @@ -72,6 +74,7 @@ extern int android_is_safe_mode; #define TRUSTED_MANAGER_DIGEST_LEN SHA256_BLOCK_SIZE #define TRUSTED_MANAGER_UID_INVALID ((uid_t)-1) +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER struct trusted_manager_entry { const char package[64]; const uint8_t digest[TRUSTED_MANAGER_DIGEST_LEN]; @@ -100,9 +103,9 @@ static const struct trusted_manager_entry trusted_managers[] = { }; static uid_t trusted_manager_uid = TRUSTED_MANAGER_UID_INVALID; -static int global_pkg_pos = 0; - +#endif /* CONFIG_KP_NO_OFFICIAL_MANAGER */ +#ifndef CONFIG_KP_NO_ROOT static const char ORIGIN_RC_FILES[][64] = { "/system/etc/init/hw/init.rc", "/init.rc", @@ -170,7 +173,9 @@ static int expand_rc_template(char *dst, size_t dst_size, const char *template, dst[out] = '\0'; return (int)out; } +#endif /* CONFIG_KP_NO_ROOT */ +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER static const void *kernel_read_file(const char *path, loff_t *len) { set_priv_sel_allow(current, true); @@ -197,6 +202,7 @@ static const void *kernel_read_file(const char *path, loff_t *len) set_priv_sel_allow(current, false); return data; } +#endif static int path_has_suffix(const char *path, const char *suffix) { @@ -209,6 +215,7 @@ static int path_has_suffix(const char *path, const char *suffix) return strcmp(path + path_len - suffix_len, suffix) == 0; } +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER static int is_packages_list_tmp_dentry_path(const char *path) { return path_has_suffix(path, "/system/packages.list.tmp"); @@ -1053,8 +1060,6 @@ static int find_trusted_manager_apk_path(char *apk_path, return rc; } - - static int refresh_trusted_manager_uid_from_packages_list(uid_t *trusted_uid_out, int use_tmp) { uid_t last_uid = TRUSTED_MANAGER_UID_INVALID; @@ -1119,12 +1124,14 @@ static int refresh_trusted_manager_uid_from_packages_list(uid_t *trusted_uid_out *trusted_uid_out = last_uid; return 0; } +#endif /* CONFIG_KP_NO_OFFICIAL_MANAGER */ int refresh_trusted_manager_uid(void) { return refresh_trusted_manager_state(); } +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER static int refresh_trusted_manager_state_from_packages_list(int use_tmp) { uid_t uid = TRUSTED_MANAGER_UID_INVALID; @@ -1141,30 +1148,44 @@ static int refresh_trusted_manager_state_from_packages_list(int use_tmp) return 0; } +#endif int refresh_trusted_manager_state(void) { +#ifdef CONFIG_KP_NO_OFFICIAL_MANAGER + return 0; +#else return refresh_trusted_manager_state_from_packages_list(0); +#endif } KP_EXPORT_SYMBOL(refresh_trusted_manager_uid); int is_trusted_manager_uid_android(uid_t uid) { +#ifdef CONFIG_KP_NO_OFFICIAL_MANAGER + return 0; +#else uid_t trusted_uid = trusted_manager_uid; if (trusted_uid == TRUSTED_MANAGER_UID_INVALID) { return 0; } return uid == trusted_uid; +#endif } KP_EXPORT_SYMBOL(is_trusted_manager_uid_android); uid_t get_trusted_manager_uid(void) { +#ifdef CONFIG_KP_NO_OFFICIAL_MANAGER + return TRUSTED_MANAGER_UID_INVALID; +#else return trusted_manager_uid; +#endif } KP_EXPORT_SYMBOL(get_trusted_manager_uid); +#ifndef CONFIG_KP_NO_ROOT // Simple CSV field parser helper function static char *parse_csv_field(char **line_ptr) { @@ -1208,11 +1229,15 @@ static char *parse_csv_field(char **line_ptr) return start; } +#endif // Load APatch package_config configuration file // Returns: number of entries loaded, or negative error code int load_ap_package_config() { +#ifdef CONFIG_KP_NO_ROOT + return 0; +#else loff_t len = 0; const char *data = kernel_read_file(AP_PACKAGE_CONFIG_PATH, &len); @@ -1400,15 +1425,16 @@ int load_ap_package_config() kvfree(data); log_boot("package_config loaded: %d entries, skipped: %d\n", loaded_count, skipped_count); return loaded_count; +#endif /* CONFIG_KP_NO_ROOT */ } KP_EXPORT_SYMBOL(load_ap_package_config); /* - * Scan /data/adb/ap/kpm without opening children from the readdir callback. + * Scan KPM directory without opening children from the readdir callback. * Some Android kernels hold the directory inode lock while iterate_dir() is * running, so child opens are deliberately deferred until after the scan. - * The expected layout is kpm//.kpm, with an optional - * kpm//disable marker. + * Supports both directory layout (kpm//.kpm) and flat layout + * (kpm/.kpm), along with their respective disable markers. */ struct ap_kpm_scan_ctx { struct dir_context dctx; @@ -1456,7 +1482,20 @@ static int ap_kpm_scan_actor_int(struct dir_context_int *dctx, const char *name, return 0; } -int load_ap_kpm_modules(void) +static bool file_exists_privileged(const char *path) +{ + struct file *f; + set_priv_sel_allow(current, true); + f = filp_open(path, O_RDONLY | O_NOFOLLOW, 0); + set_priv_sel_allow(current, false); + if (f && !IS_ERR(f)) { + filp_close(f, 0); + return true; + } + return false; +} + +static int scan_and_load_kpm_dir(const char *kpm_dir, const char *event) { struct file *dir; char *names; @@ -1464,19 +1503,41 @@ int load_ap_kpm_modules(void) int rc; if (android_is_safe_mode) return 0; - names = vmalloc((size_t)AP_KPM_MAX_MODULES * AP_KPM_NAME_LEN); - if (!names) return -ENOMEM; - memset(names, 0, (size_t)AP_KPM_MAX_MODULES * AP_KPM_NAME_LEN); set_priv_sel_allow(current, true); - dir = filp_open(AP_KPM_DIR, O_RDONLY | O_NOFOLLOW, 0); + dir = filp_open(kpm_dir, O_RDONLY | O_NOFOLLOW | O_DIRECTORY, 0); if (!dir || IS_ERR(dir)) { rc = dir ? PTR_ERR(dir) : -ENOENT; set_priv_sel_allow(current, false); - kvfree(names); - if (rc != -ENOENT) log_boot("open AP KPM directory failed: %d\n", rc); - return rc == -ENOENT ? 0 : rc; + if (rc != -ENOENT && rc != -ENOTDIR) { + log_boot("open KPM directory failed: %s, rc: %d\n", kpm_dir, rc); + } + return (rc == -ENOENT || rc == -ENOTDIR) ? 0 : rc; + } + + // Check directory disable file: disable (e.g. /data/adb/kpm/disable) + // Note: priv_sel_allow is already true here; use direct filp_open to avoid + // file_exists_privileged() clobbering it (that helper resets the flag to false). + char global_disable[AP_KPM_NAME_LEN + 128]; + int gd_len = snprintf(global_disable, sizeof(global_disable), "%sdisable", kpm_dir); + if (gd_len > 0 && gd_len < (int)sizeof(global_disable)) { + struct file *gf = filp_open(global_disable, O_RDONLY | O_NOFOLLOW, 0); + if (gf && !IS_ERR(gf)) { + filp_close(gf, 0); + filp_close(dir, 0); + set_priv_sel_allow(current, false); + log_boot("KPM directory disabled by %s\n", global_disable); + return 0; + } + } + + names = vmalloc((size_t)AP_KPM_MAX_MODULES * AP_KPM_NAME_LEN); + if (!names) { + filp_close(dir, 0); + set_priv_sel_allow(current, false); + return -ENOMEM; } + memset(names, 0, (size_t)AP_KPM_MAX_MODULES * AP_KPM_NAME_LEN); if (kver >= VERSION(6, 1, 0)) { struct ap_kpm_scan_ctx ctx = { .names = names }; @@ -1496,38 +1557,126 @@ int load_ap_kpm_modules(void) for (int i = 0; i < count; i++) { char *id = names + i * AP_KPM_NAME_LEN; - char path[AP_KPM_NAME_LEN * 2 + sizeof(AP_KPM_DIR) + 8]; - char disable[AP_KPM_NAME_LEN + sizeof(AP_KPM_DIR) + 16]; - int path_len = snprintf(path, sizeof(path), AP_KPM_DIR "%s/%s.kpm", id, id); - int disable_len = snprintf(disable, sizeof(disable), AP_KPM_DIR "%s/disable", id); - struct file *marker; + char path[AP_KPM_NAME_LEN * 2 + 128]; + char disable[AP_KPM_NAME_LEN + 128]; + int path_len, disable_len; - if (path_len <= 0 || path_len >= sizeof(path) || disable_len <= 0 || disable_len >= sizeof(disable)) { + if (path_has_suffix(id, ".disable")) { skipped++; continue; } - set_priv_sel_allow(current, true); - marker = filp_open(disable, O_RDONLY | O_NOFOLLOW, 0); - if (marker && !IS_ERR(marker)) { - filp_close(marker, 0); - set_priv_sel_allow(current, false); - log_boot("skip disabled AP KPM: %s\n", id); + + if (path_has_suffix(id, ".kpm")) { + path_len = snprintf(path, sizeof(path), "%s/%s", kpm_dir, id); + disable_len = snprintf(disable, sizeof(disable), "%s/%s.disable", kpm_dir, id); + } else { + path_len = snprintf(path, sizeof(path), "%s/%s/%s.kpm", kpm_dir, id, id); + disable_len = snprintf(disable, sizeof(disable), "%s/%s/disable", kpm_dir, id); + } + + if (path_len <= 0 || path_len >= (int)sizeof(path) || disable_len <= 0 || disable_len >= (int)sizeof(disable)) { + skipped++; + continue; + } + + if (file_exists_privileged(disable)) { + log_boot("skip disabled KPM: %s\n", id); skipped++; continue; } - set_priv_sel_allow(current, false); - rc = load_module_path_event(path, 0, EXTRA_EVENT_POST_FS_DATA, 0); - log_boot("load AP KPM: %s, event: %s, rc: %d\n", path, EXTRA_EVENT_POST_FS_DATA, rc); + if (path_has_suffix(id, ".kpm")) { + char disable2[AP_KPM_NAME_LEN + 128]; + int stem_len = (int)strlen(id) - 4; + if (stem_len > 0) { + int disable2_len = snprintf(disable2, sizeof(disable2), "%s/%.*s.disable", kpm_dir, stem_len, id); + if (disable2_len > 0 && disable2_len < (int)sizeof(disable2)) { + if (file_exists_privileged(disable2)) { + log_boot("skip disabled KPM: %s\n", id); + skipped++; + continue; + } + } + } + } + + set_priv_sel_allow(current, true); + rc = load_module_path_event(path, 0, event, 0); + set_priv_sel_allow(current, false); + log_boot("load KPM: %s, event: %s, rc: %d\n", path, event, rc); if (!rc) loaded++; } kvfree(names); - log_boot("AP KPM loading done: loaded=%d skipped=%d total=%d\n", loaded, skipped, count); + log_boot("KPM loading from %s done: loaded=%d skipped=%d total=%d\n", kpm_dir, loaded, skipped, count); return loaded; } + +static volatile int adb_kpm_loaded = 0; +static volatile int ap_kpm_loaded = 0; + +int load_ap_kpm_modules(void) +{ + if (xchg(&ap_kpm_loaded, 1)) return 0; + return scan_and_load_kpm_dir(AP_KPM_DIR, EXTRA_EVENT_POST_FS_DATA); +} KP_EXPORT_SYMBOL(load_ap_kpm_modules); +#ifdef CONFIG_KP_AUTOLOAD_KPM +static volatile int kpm_autoload_done = 0; + +static bool dir_exists_privileged(const char *path) +{ + struct file *f; + set_priv_sel_allow(current, true); + f = filp_open(path, O_RDONLY | O_NOFOLLOW | O_DIRECTORY, 0); + set_priv_sel_allow(current, false); + if (f && !IS_ERR(f)) { + filp_close(f, 0); + return true; + } + return false; +} + +int autoload_kpm_modules(void) +{ + int loaded = 0; + + if (!xchg(&adb_kpm_loaded, 1)) { + int rc = scan_and_load_kpm_dir(ADB_KPM_DIR, EXTRA_EVENT_POST_FS_DATA); + if (rc > 0) { + loaded += rc; + } + } + + if (!xchg(&ap_kpm_loaded, 1)) { + int rc = scan_and_load_kpm_dir(AP_KPM_DIR, EXTRA_EVENT_POST_FS_DATA); + if (rc > 0) { + loaded += rc; + } + } + + if (loaded > 0) { + log_boot("autoload KPM done: %d loaded\n", loaded); + } + return loaded; +} + +static void try_autoload_post_fs_data(void) +{ + if (kpm_autoload_done) return; + + /* Check if /data is mounted: /data/system probably always exists on mounted /data + * Also check /data/adb/ in case it exists. */ + if (dir_exists_privileged("/data/system") || dir_exists_privileged(ADB_FOLDER)) { + if (!xchg(&kpm_autoload_done, 1)) { + log_boot("post-fs-data detected, autoloading KPM...\n"); + autoload_kpm_modules(); + } + } +} +#endif + static void pre_user_exec_init() { extra_event_init(EXTRA_EVENT_PRE_EXEC_INIT); @@ -1560,10 +1709,21 @@ static void post_init_second_stage() static void on_first_app_process() { +#ifdef CONFIG_KP_AUTOLOAD_KPM + if (!adb_kpm_loaded || !ap_kpm_loaded) { + autoload_kpm_modules(); + } +#endif +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER /* Refresh the trusted-manager state (APK scan) synchronously here. The scan * itself is two-phase so it cannot deadlock on the /data/app inode lock. */ int rc = refresh_trusted_manager_state(); log_boot("on_first_app_process: trusted manager refresh rc=%d\n", rc); +#endif + +#if defined(CONFIG_KP_NO_ROOT) && defined(CONFIG_KP_NO_OFFICIAL_MANAGER) + unhook_bypass_selinux(); +#endif } static void handle_before_execve(hook_local_t *hook_local, char **__user u_filename_p, char **__user uargv, @@ -1575,11 +1735,15 @@ static void handle_before_execve(hook_local_t *hook_local, char **__user u_filen hook_local->data2 = 0; // Check if current process is trusted manager, set auto-su flag +#ifndef CONFIG_KP_NO_ROOT if (is_trusted_manager_uid(current_uid())) { hook_local->data0 = 1; } else { hook_local->data0 = 0; } +#else + hook_local->data0 = 0; +#endif if (current_uid() != 0 && !hook_local->data0) return; @@ -1655,6 +1819,12 @@ static void handle_before_execve(hook_local_t *hook_local, char **__user u_filen hook_local->data7 = 1; return; } + +#ifdef CONFIG_KP_AUTOLOAD_KPM + if (init_second_stage_executed && !kpm_autoload_done) { + try_autoload_post_fs_data(); + } +#endif } static void before_execve(hook_fargs3_t *args, void *udata); @@ -1665,9 +1835,11 @@ static void after_execveat(hook_fargs5_t *args, void *udata); static void handle_after_execve(hook_local_t *hook_local, long ret) { // Auto-su for processes executed by trusted manager +#ifndef CONFIG_KP_NO_ROOT if (hook_local->data0 && ret >= 0) { commit_su(0, all_allow_sctx); } +#endif if (ret >= 0) { if (hook_local->data1) { @@ -1718,6 +1890,7 @@ static void after_execveat(hook_fargs5_t *args, void *udata) handle_after_execve(&args->local, args->ret); } +#ifndef CONFIG_KP_NO_ROOT // https://elixir.bootlin.com/linux/v6.1/source/fs/open.c#L1337 // SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, umode_t, mode) static void before_openat(hook_fargs4_t *args, void *udata) @@ -1825,7 +1998,9 @@ static void after_openat(hook_fargs4_t *args, void *udata) unhook_syscalln(__NR_openat, before_openat, after_openat); } } +#endif /* CONFIG_KP_NO_ROOT */ +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER typedef char *(*kp_dentry_path_raw_t)(struct dentry *dentry, char *buf, int buflen); static kp_dentry_path_raw_t kp_dentry_path_raw; @@ -1869,6 +2044,11 @@ static void after_security_inode_rename(hook_fargs5_t *args, void *udata) static void hook_rename_lsm(void) { + if (trusted_managers[0].package[0] == '\0') { + log_boot("no official manager configured, skipping rename hook\n"); + return; + } + unsigned long addr; hook_err_t rc; @@ -1894,6 +2074,7 @@ static void hook_rename_lsm(void) log_boot("no symbol: security_path_rename/security_inode_rename\n"); } +#endif /* CONFIG_KP_NO_OFFICIAL_MANAGER */ #define EV_KEY 0x01 #define KEY_VOLUMEDOWN 114 @@ -1930,11 +2111,15 @@ int android_user_init() log_boot("hook __NR_execveat rc: %d\n", rc); ret |= rc; +#ifndef CONFIG_KP_NO_ROOT rc = hook_syscalln(__NR_openat, 4, before_openat, after_openat, 0); log_boot("hook __NR_openat rc: %d\n", rc); ret |= rc; +#endif +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER hook_rename_lsm(); +#endif unsigned long input_handle_event_addr = patch_config->input_handle_event; if (input_handle_event_addr) { diff --git a/kernel/patch/common/accctl.c b/kernel/patch/common/accctl.c index 62232e49..99fcd307 100644 --- a/kernel/patch/common/accctl.c +++ b/kernel/patch/common/accctl.c @@ -28,6 +28,7 @@ extern void kp_debug_write(const char *fmt, ...); +#ifndef CONFIG_KP_NO_ROOT char all_allow_sctx[SUPERCALL_SCONTEXT_LEN] = { '\0' }; uint32_t all_allow_sid = SECSID_NULL; @@ -174,6 +175,7 @@ int task_su(pid_t pid, uid_t to_uid, const char *sctx) out: return rc; } +#endif /* CONFIG_KP_NO_ROOT */ static int (*avc_denied_backup)(struct selinux_state *state, void *ssid, void *tsid, void *tclass, void *requested, void *driver, void *xperm, void *flags, struct av_decision *avd) = 0; @@ -181,6 +183,7 @@ static int (*avc_denied_backup)(struct selinux_state *state, void *ssid, void *t static int avc_denied_replace(struct selinux_state *_state, void *_ssid, void *_tsid, void *_tclass, void *_requested, void *_driver, void *_xperm, void *_flags, struct av_decision *_avd) { +#ifndef CONFIG_KP_NO_ROOT if (all_allow_sid != SECSID_NULL) { u32 ssid = (u32)(u64)_ssid; if ((uint64_t)_state <= 0xffffffffL) { @@ -195,6 +198,12 @@ static int avc_denied_replace(struct selinux_state *_state, void *_ssid, void *_ if (unlikely(task_ext_valid(ext) && (ext->sel_allow || ext->priv_sel_allow))) { goto allow; } +#else + struct task_ext *ext = get_current_task_ext(); + if (unlikely(task_ext_valid(ext) && ext->priv_sel_allow)) { + goto allow; + } +#endif int rc = avc_denied_backup(_state, _ssid, _tsid, _tclass, _requested, _driver, _xperm, _flags, _avd); return rc; @@ -218,6 +227,7 @@ static int slow_avc_audit_replace(struct selinux_state *_state, void *_ssid, voi void *_requested, void *_audited, void *_denied, void *_result, struct common_audit_data *_a) { +#ifndef CONFIG_KP_NO_ROOT if (all_allow_sid != SECSID_NULL) { u32 ssid = (u64)_ssid; if ((uint64_t)_state <= 0xffffffffL) { @@ -232,6 +242,12 @@ static int slow_avc_audit_replace(struct selinux_state *_state, void *_ssid, voi if (unlikely(task_ext_valid(ext) && (ext->sel_allow || ext->priv_sel_allow))) { return 0; } +#else + struct task_ext *ext = get_current_task_ext(); + if (unlikely(task_ext_valid(ext) && ext->priv_sel_allow)) { + return 0; + } +#endif int rc = slow_avc_audit_backup(_state, _ssid, _tsid, _tclass, _requested, _audited, _denied, _result, _a); return rc; @@ -258,3 +274,21 @@ int bypass_selinux() return 0; } + +#if defined(CONFIG_KP_NO_ROOT) && defined(CONFIG_KP_NO_OFFICIAL_MANAGER) +void unhook_bypass_selinux(void) +{ + unsigned long avc_denied_addr = patch_config->avc_denied; + if (avc_denied_addr && avc_denied_backup) { + unhook((void *)avc_denied_addr); + avc_denied_backup = 0; + } + + unsigned long slow_avc_audit_addr = patch_config->slow_avc_audit; + if (slow_avc_audit_addr && slow_avc_audit_backup) { + unhook((void *)slow_avc_audit_addr); + slow_avc_audit_backup = 0; + } + log_boot("unhooked bypass_selinux (SELinux restored to pristine state)\n"); +} +#endif diff --git a/kernel/patch/common/kstorage.c b/kernel/patch/common/kstorage.c index 93043d7d..ed5c285c 100644 --- a/kernel/patch/common/kstorage.c +++ b/kernel/patch/common/kstorage.c @@ -60,8 +60,8 @@ int kstorage_group_size(int gid) int write_kstorage(int gid, long did, void *data, int offset, int len, bool data_is_user) { - int rc = -ENOENT; - if (gid < 0 || gid >= KSTRORAGE_MAX_GROUP_NUM) return rc; + if (gid < 0 || gid >= KSTRORAGE_MAX_GROUP_NUM) return -ENOENT; + if (offset < 0 || len < 0) return -EINVAL; struct hlist_head *bucket = kstorage_bucket(gid, did); spinlock_t *lock = &kstorage_glocks[gid]; @@ -179,6 +179,11 @@ int read_kstorage(int gid, long did, void *data, int offset, int len, bool data_ return PTR_ERR(pos); } + if (offset < 0 || offset > pos->dlen || len < 0) { + rcu_read_unlock(); + return -EINVAL; + } + int min_len = pos->dlen - offset > len ? len : pos->dlen - offset; if (data_is_user) { diff --git a/kernel/patch/common/selinux_hide.c b/kernel/patch/common/selinux_hide.c index 4b1be5f3..9e12105a 100644 --- a/kernel/patch/common/selinux_hide.c +++ b/kernel/patch/common/selinux_hide.c @@ -385,7 +385,7 @@ static int selinux_hide_install_hooks(void) }else{ log_boot("selinux_hide: using fp_hook to hook write_op\n"); - write_op = lookup_name_with_suffix("write_op"); + write_op = (sel_write_op_fn *)lookup_name_with_suffix("write_op"); if (!write_op) { rc = -ENOENT; log_boot("selinux_hide: write_op not found\n"); diff --git a/kernel/patch/common/supercall.c b/kernel/patch/common/supercall.c index 5caa9892..5723d61f 100644 --- a/kernel/patch/common/supercall.c +++ b/kernel/patch/common/supercall.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,14 @@ #include +#ifdef CONFIG_KP_NO_ROOT +int is_su_allow_uid(uid_t uid) +{ + return uid == 0; +} +KP_EXPORT_SYMBOL(is_su_allow_uid); +#endif + static long call_test(long arg1, long arg2, long arg3) { return 0; @@ -132,6 +141,7 @@ static long call_kpm_info(const char *__user uname, char *__user out_info, int o return sz; } +#ifndef CONFIG_KP_NO_ROOT static long call_su(struct su_profile *__user uprofile) { struct su_profile *profile = memdup_user(uprofile, sizeof(struct su_profile)); @@ -151,6 +161,7 @@ static long call_su_task(pid_t pid, struct su_profile *__user uprofile) kvfree(profile); return rc; } +#endif /* CONFIG_KP_NO_ROOT */ static long call_skey_get(char *__user out_key, int out_len) { @@ -176,6 +187,25 @@ static long call_skey_root_enable(int enable) return 0; } +#ifdef ANDROID +extern int android_is_safe_mode; +static long call_su_get_safemode() +{ + int result = android_is_safe_mode; + logkfd("[call_su_get_safemode] %d\n", result); + return result; +} + +extern int load_ap_package_config(void); +static long call_ap_load_package_config() +{ + int result = load_ap_package_config(); + logkfd("[call_ap_load_package_config] loaded %d entries\n", result); + return result; +} +#endif /* ANDROID */ + +#ifndef CONFIG_KP_NO_ROOT static long call_grant_uid(struct su_profile *__user uprofile) { struct su_profile *profile = memdup_user(uprofile, sizeof(struct su_profile)); @@ -195,24 +225,6 @@ static long call_su_allow_uid_nums() return su_allow_uid_nums(); } -#ifdef ANDROID -extern int android_is_safe_mode; -static long call_su_get_safemode() -{ - int result = android_is_safe_mode; - logkfd("[call_su_get_safemode] %d\n", result); - return result; -} - -extern int load_ap_package_config(void); -static long call_ap_load_package_config() -{ - int result = load_ap_package_config(); - logkfd("[call_ap_load_package_config] loaded %d entries\n", result); - return result; -} -#endif - static long call_su_list_allow_uid(uid_t *__user uids, int num) { return su_allow_uids(1, uids, num); @@ -251,6 +263,7 @@ static long call_su_set_allow_sctx(char *__user usctx) if (len >= SUPERCALL_SCONTEXT_LEN && buf[SUPERCALL_SCONTEXT_LEN - 1]) return -E2BIG; return set_all_allow_sctx(buf); } +#endif /* CONFIG_KP_NO_ROOT */ static long call_kstorage_read(int gid, long did, void *out_data, int offset, int dlen) { @@ -286,12 +299,15 @@ static long supercall(int is_authed, long cmd, long arg1, long arg2, long arg3, return kver; case SUPERCALL_BUILD_TIME: return call_buildtime((char *__user)arg1, (int)arg2); - #ifdef ANDROID +#ifdef ANDROID case SUPERCALL_AP_LOAD_PACKAGE_CONFIG: return call_ap_load_package_config(); - #endif + case SUPERCALL_SU_GET_SAFEMODE: + return call_su_get_safemode(); +#endif } +#ifndef CONFIG_KP_NO_ROOT switch (cmd) { case SUPERCALL_SU: return call_su((struct su_profile * __user) arg1); @@ -317,6 +333,12 @@ static long supercall(int is_authed, long cmd, long arg1, long arg2, long arg3, case SUPERCALL_SU_SET_ALLOW_SCTX: return call_su_set_allow_sctx((char *__user)arg1); + default: + break; + } +#endif /* CONFIG_KP_NO_ROOT */ + + switch (cmd) { case SUPERCALL_KSTORAGE_READ: return call_kstorage_read((int)arg1, (long)arg2, (void *)arg3, (int)((long)arg4 >> 32), (long)arg4 << 32 >> 32); case SUPERCALL_KSTORAGE_WRITE: @@ -330,15 +352,6 @@ static long supercall(int is_authed, long cmd, long arg1, long arg2, long arg3, case SUPERCALL_CONTROL_FEATURE: return kp_control_feature_sc((const char __user *)arg1, (int)(long)arg2); -#ifdef ANDROID - case SUPERCALL_SU_GET_SAFEMODE: - return call_su_get_safemode(); -#endif - default: - break; - } - - switch (cmd) { case SUPERCALL_BOOTLOG: return call_bootlog(); case SUPERCALL_PANIC: @@ -358,7 +371,6 @@ static long supercall(int is_authed, long cmd, long arg1, long arg2, long arg3, return call_skey_set((char *__user)arg1); case SUPERCALL_SKEY_ROOT_ENABLE: return call_skey_root_enable((int)arg1); - break; } switch (cmd) { @@ -386,16 +398,21 @@ static long supercall(int is_authed, long cmd, long arg1, long arg2, long arg3, int is_trusted_manager_uid(uid_t uid) { - #ifdef ANDROID +#ifdef ANDROID return is_trusted_manager_uid_android(uid); - #endif +#else return 0; +#endif } static void before(hook_fargs6_t *args, void *udata) { int uid = current_uid(); +#ifndef CONFIG_KP_NO_ROOT if (get_ap_mod_exclude(uid)) return; +#else + if (uid != 0) return; +#endif int is_trusted_caller = 0; int is_authed = 0; @@ -408,6 +425,7 @@ static void before(hook_fargs6_t *args, void *udata) is_authed = !auth_superkey(key); is_trusted_caller = is_authed; } + if (is_trusted_manager_uid(uid)) { is_trusted_caller = 1; is_authed = 1; diff --git a/kernel/patch/common/supercmd.c b/kernel/patch/common/supercmd.c index 9cceaddf..4af0c20e 100644 --- a/kernel/patch/common/supercmd.c +++ b/kernel/patch/common/supercmd.c @@ -330,10 +330,12 @@ void handle_supercmd(char **__user u_filename_p, char **__user uargv) { int is_key_auth = 0; int is_trusted_manager = 0; +#if ANDROID is_trusted_manager = is_trusted_manager_uid(current_uid()); if (is_trusted_manager) { is_key_auth = 1; } +#endif /* ANDROID */ // key const char __user *p1 = get_user_arg_ptr(0, *uargv, 1); if (!p1 || IS_ERR(p1)) return; diff --git a/kernel/patch/include/accctl.h b/kernel/patch/include/accctl.h index 46235423..fd1d6b2a 100644 --- a/kernel/patch/include/accctl.h +++ b/kernel/patch/include/accctl.h @@ -15,6 +15,9 @@ #include #include +#include + +#ifndef CONFIG_KP_NO_ROOT extern char all_allow_sctx[SUPERCALL_SCONTEXT_LEN]; extern uint32_t all_allow_sid; @@ -23,6 +26,18 @@ int commit_kernel_su(); int commit_common_su(uid_t to_uid, const char *sctx); int commit_su(uid_t uid, const char *sctx); int task_su(pid_t pid, uid_t to_uid, const char *sctx); +#else +static inline int set_all_allow_sctx(const char *sctx) { (void)sctx; return -ENOSYS; } +static inline int commit_kernel_su(void) { return -ENOSYS; } +static inline int commit_common_su(uid_t to_uid, const char *sctx) { (void)to_uid; (void)sctx; return -ENOSYS; } +static inline int commit_su(uid_t uid, const char *sctx) { (void)uid; (void)sctx; return -ENOSYS; } +static inline int task_su(pid_t pid, uid_t to_uid, const char *sctx) { (void)pid; (void)to_uid; (void)sctx; return -ENOSYS; } +#endif /* CONFIG_KP_NO_ROOT */ + +int bypass_selinux(void); +#if defined(CONFIG_KP_NO_ROOT) && defined(CONFIG_KP_NO_OFFICIAL_MANAGER) +void unhook_bypass_selinux(void); +#endif /** * @brief Whether to make the current task bypass all selinux permission checks. diff --git a/kernel/patch/include/sucompat.h b/kernel/patch/include/sucompat.h index f5ad9a5f..4c8c077b 100644 --- a/kernel/patch/include/sucompat.h +++ b/kernel/patch/include/sucompat.h @@ -10,6 +10,9 @@ #include #include +#include + +#ifndef CONFIG_KP_NO_ROOT extern const char sh_path[]; extern const char default_su_path[]; extern const char legacy_su_path[]; @@ -46,5 +49,23 @@ void sucompat_unregister_path_probe_hooks(void); /* Supercall handler: control a feature by name. * state: 1=enable, 0=disable, -1=query current state. */ long kp_control_feature_sc(const char __user *uname, int state); +#else +int is_su_allow_uid(uid_t uid); +static inline int su_compat_init(void) { return 0; } +static inline void sucompat_init(void) {} +static inline int su_add_allow_uid(uid_t uid, uid_t to_uid, const char *scontext) { (void)uid; (void)to_uid; (void)scontext; return -ENOSYS; } +static inline int su_remove_allow_uid(uid_t uid) { (void)uid; return -ENOSYS; } +static inline int su_allow_uid_nums(void) { return 0; } +static inline int su_allow_uids(int is_user, uid_t *out_uids, int out_num) { (void)is_user; (void)out_uids; (void)out_num; return 0; } +static inline int su_allow_uid_profile(int is_user, uid_t uid, struct su_profile *profile) { (void)is_user; (void)uid; (void)profile; return -ENOSYS; } +static inline int su_reset_path(const char *path) { (void)path; return -ENOSYS; } +static inline const char *su_get_path(void) { return ""; } +static inline int get_ap_mod_exclude(uid_t uid) { (void)uid; return 0; } +static inline int set_ap_mod_exclude(uid_t uid, int exclude) { (void)uid; (void)exclude; return -ENOSYS; } +static inline int list_ap_mod_exclude(uid_t *uids, int len) { (void)uids; (void)len; return 0; } +static inline void sucompat_register_path_probe_hooks(void) {} +static inline void sucompat_unregister_path_probe_hooks(void) {} +static inline long kp_control_feature_sc(const char __user *uname, int state) { (void)uname; (void)state; return -ENOSYS; } +#endif /* CONFIG_KP_NO_ROOT */ #endif diff --git a/kernel/patch/include/userd.h b/kernel/patch/include/userd.h index af259898..e79492ac 100644 --- a/kernel/patch/include/userd.h +++ b/kernel/patch/include/userd.h @@ -10,12 +10,16 @@ int load_ap_package_config(void); int load_ap_kpm_modules(void); +#ifdef CONFIG_KP_AUTOLOAD_KPM +int autoload_kpm_modules(void); +#endif #ifdef ANDROID int refresh_trusted_manager_uid(void); int refresh_trusted_manager_state(void); - +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER uid_t get_trusted_manager_uid(void); +#endif int is_trusted_manager_uid_android(uid_t uid); #endif diff --git a/kernel/patch/patch.c b/kernel/patch/patch.c index 6fe3b034..b98282c7 100644 --- a/kernel/patch/patch.c +++ b/kernel/patch/patch.c @@ -12,6 +12,7 @@ #include #include #include +#include void print_bootlog() { @@ -53,8 +54,10 @@ int supercall_install(); void module_init(); void syscall_init(); int kstorage_init(); +#ifndef CONFIG_KP_NO_ROOT int su_compat_init(); // int selinux_hide_init(); +#endif #ifdef ANDROID int android_user_init(); @@ -88,11 +91,13 @@ static void before_rest_init(hook_fargs4_t *args, void *udata) rc = kstorage_init(); log_boot("kstorage_init done: %d\n", rc); +#ifndef CONFIG_KP_NO_ROOT rc = su_compat_init(); log_boot("su_compat_init done: %d\n", rc); // rc = selinux_hide_init(); // log_boot("selinux_hide_init done: %d\n", rc); +#endif rc = resolve_pt_regs(); log_boot("resolve_pt_regs done: %d\n", rc); @@ -139,14 +144,33 @@ void extra_event_init(const char *event) } KP_EXPORT_SYMBOL(extra_event_init); +// Guard against multiple executions: some vendor kernels (e.g. MediaTek with +// bootprof/mtprof instrumentation) or symbol collision cases may call into the +// resolved kernel_init address repeatedly. pre-kernel-init and post-kernel-init +// are strictly one-shot lifecycle events; this guard ensures they fire exactly +// once across all SMP cores without requiring unsafe in-trampoline hook unregistration. +// Note: We use the kernel's xchg() macro () instead of GCC's __atomic +// builtins because -moutline-atomics generates calls to libgcc's __aarch64_swp4_acq_rel, +// which does not exist in our nostdlib bare-metal environment. +static volatile int kernel_init_done = 0; + static void before_kernel_init(hook_fargs4_t *args, void *udata) { + args->local.data0 = 0; + if (kernel_init_done) return; + + if (xchg(&kernel_init_done, 1)) return; + + args->local.data0 = 1; extra_event_init(EXTRA_EVENT_PRE_KERNEL_INIT); } static void after_kernel_init(hook_fargs4_t *args, void *udata) { - extra_event_init(EXTRA_EVENT_POST_KERNEL_INIT); + if (args->local.data0) { + args->local.data0 = 0; + extra_event_init(EXTRA_EVENT_POST_KERNEL_INIT); + } } int patch()