From a9a10466b373a67f6d1b95e04d268865213f3adb Mon Sep 17 00:00:00 2001 From: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com> Date: Mon, 29 Sep 2025 04:28:31 +0800 Subject: [PATCH] Kernel: Enhanced temporary record UID functionality and elevated privileges --- kernel/allowlist.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++ kernel/allowlist.h | 3 ++ kernel/core_hook.c | 62 +++++++++++++++++++++++++++++-------- kernel/ksu.h | 1 + kernel/manual_su.c | 71 ++++++++++++++++++++++++++++++++++++++++++- kernel/manual_su.h | 10 ++++++ 6 files changed, 210 insertions(+), 13 deletions(-) diff --git a/kernel/allowlist.c b/kernel/allowlist.c index 97455675..291ffc7b 100644 --- a/kernel/allowlist.c +++ b/kernel/allowlist.c @@ -524,3 +524,79 @@ void ksu_allowlist_exit(void) } mutex_unlock(&allowlist_mutex); } + +bool ksu_temp_grant_root_once(uid_t uid) +{ + struct app_profile profile = { + .version = KSU_APP_PROFILE_VER, + .allow_su = true, + .current_uid = uid, + }; + + const char *default_key = "com.temp.once"; + + struct perm_data *p = NULL; + struct list_head *pos = NULL; + bool found = false; + + list_for_each (pos, &allow_list) { + p = list_entry(pos, struct perm_data, list); + if (p->profile.current_uid == uid) { + strcpy(profile.key, p->profile.key); + found = true; + break; + } + } + + if (!found) { + strcpy(profile.key, default_key); + } + + profile.rp_config.profile.uid = default_root_profile.uid; + profile.rp_config.profile.gid = default_root_profile.gid; + profile.rp_config.profile.groups_count = default_root_profile.groups_count; + memcpy(profile.rp_config.profile.groups, default_root_profile.groups, sizeof(default_root_profile.groups)); + memcpy(&profile.rp_config.profile.capabilities, &default_root_profile.capabilities, sizeof(default_root_profile.capabilities)); + profile.rp_config.profile.namespaces = default_root_profile.namespaces; + strcpy(profile.rp_config.profile.selinux_domain, default_root_profile.selinux_domain); + + bool ok = ksu_set_app_profile(&profile, false); + if (ok) + pr_info("pending_root: UID=%d granted and persisted\n", uid); + return ok; +} + +void ksu_temp_revoke_root_once(uid_t uid) +{ + struct app_profile profile = { + .version = KSU_APP_PROFILE_VER, + .allow_su = false, + .current_uid = uid, + }; + + const char *default_key = "com.temp.once"; + + struct perm_data *p = NULL; + struct list_head *pos = NULL; + bool found = false; + + list_for_each (pos, &allow_list) { + p = list_entry(pos, struct perm_data, list); + if (p->profile.current_uid == uid) { + strcpy(profile.key, p->profile.key); + found = true; + break; + } + } + + if (!found) { + strcpy(profile.key, default_key); + } + + profile.nrp_config.profile.umount_modules = default_non_root_profile.umount_modules; + strcpy(profile.rp_config.profile.selinux_domain, KSU_DEFAULT_SELINUX_DOMAIN); + + ksu_set_app_profile(&profile, false); + persistent_allow_list(); + pr_info("pending_root: UID=%d removed and persist updated\n", uid); +} diff --git a/kernel/allowlist.h b/kernel/allowlist.h index e89bf71f..1b14b0b0 100644 --- a/kernel/allowlist.h +++ b/kernel/allowlist.h @@ -24,4 +24,7 @@ bool ksu_set_app_profile(struct app_profile *, bool persist); bool ksu_uid_should_umount(uid_t uid); struct root_profile *ksu_get_root_profile(uid_t uid); + +bool ksu_temp_grant_root_once(uid_t uid); +void ksu_temp_revoke_root_once(uid_t uid); #endif diff --git a/kernel/core_hook.c b/kernel/core_hook.c index b40ddbbf..2cbd0164 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -343,19 +343,10 @@ static inline void nuke_ext4_sysfs() { } static bool is_system_bin_su() { - // YES in_execve becomes 0 when it succeeds. - if (!current->mm || current->in_execve) - return false; - // quick af check return (current->mm->exe_file && !strcmp(current->mm->exe_file->f_path.dentry->d_name.name, "su")); } -int handle_cmd_su_escalation(uid_t uid, pid_t pid, const char __user *pwd) -{ - return ksu_manual_su_escalate(uid, pid, pwd); -} - static void init_uid_scanner(void) { ksu_uid_init(); @@ -392,12 +383,23 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, bool from_root = 0 == current_uid().val; bool from_manager = is_manager(); + if (!current->mm || current->in_execve) { + return 0; + } + if (arg2 == CMD_SU_ESCALATION_REQUEST) { uid_t target_uid = (uid_t)arg3; - pid_t target_pid = (pid_t)arg4; - const char __user *user_password = (const char __user *)arg5; + struct su_request_arg __user *user_req = (struct su_request_arg __user *)arg4; - int ret = handle_cmd_su_escalation(target_uid, target_pid, user_password); + pid_t target_pid; + const char __user *user_password; + + if (copy_from_user(&target_pid, &user_req->target_pid, sizeof(target_pid))) + return -EFAULT; + if (copy_from_user(&user_password, &user_req->user_password, sizeof(user_password))) + return -EFAULT; + + int ret = ksu_manual_su_escalate(target_uid, target_pid, user_password); if (ret == 0) { if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { @@ -407,6 +409,23 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3, return 0; } + if (arg2 == CMD_ADD_PENDING_ROOT) { + uid_t uid = (uid_t)arg3; + + if (!is_current_verified()) { + pr_warn("CMD_ADD_PENDING_ROOT: denied, password not verified\n"); + return -EPERM; + } + + add_pending_root(uid); + current_verified = false; + pr_info("prctl: pending root added for UID %d\n", uid); + + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) + pr_err("prctl: CMD_ADD_PENDING_ROOT reply error\n"); + return 0; + } + if (!from_root && !from_manager && !(is_allow_su() && is_system_bin_su())) { // only root or manager can access this interface @@ -1005,6 +1024,15 @@ int ksu_inode_permission(struct inode *inode, int mask) return 0; } +static void ksu_try_escalate_for_uid(uid_t uid) +{ + if (!is_pending_root(uid)) + return; + + pr_info("pending_root: UID=%d temporarily allowed\n", uid); + remove_pending_root(uid); +} + #ifdef CONFIG_COMPAT bool ksu_is_compat __read_mostly = false; #endif @@ -1030,6 +1058,8 @@ int ksu_bprm_check(struct linux_binprm *bprm) ksu_handle_pre_ksud(filename); + ksu_try_escalate_for_uid(current_uid().val); + return 0; } @@ -1041,6 +1071,13 @@ static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3, return -ENOSYS; } +static int ksu_task_alloc(struct task_struct *task, + unsigned long clone_flags) +{ + ksu_try_escalate_for_uid(task_uid(task).val); + return 0; +} + static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) { @@ -1059,6 +1096,7 @@ static struct security_hook_list ksu_hooks[] = { LSM_HOOK_INIT(inode_rename, ksu_inode_rename), LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid), LSM_HOOK_INIT(inode_permission, ksu_inode_permission), + LSM_HOOK_INIT(task_alloc, ksu_task_alloc), #ifndef CONFIG_KSU_KPROBES_HOOK LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check), #endif diff --git a/kernel/ksu.h b/kernel/ksu.h index 716317f9..8c051648 100644 --- a/kernel/ksu.h +++ b/kernel/ksu.h @@ -25,6 +25,7 @@ #define CMD_ENABLE_SU 15 #define CMD_SU_ESCALATION_REQUEST 50 +#define CMD_ADD_PENDING_ROOT 51 #define CMD_GET_FULL_VERSION 0xC0FFEE1A diff --git a/kernel/manual_su.c b/kernel/manual_su.c index f4ef2ae2..9a87b21a 100644 --- a/kernel/manual_su.c +++ b/kernel/manual_su.c @@ -10,9 +10,23 @@ #include "ksu.h" #include "allowlist.h" #include "manager.h" +#include "allowlist.h" static const char *ksu_su_password = "zakozako"; extern void escape_to_root_for_cmd_su(uid_t, pid_t); +#define MAX_PENDING 16 +#define REMOVE_DELAY_CALLS 150 + +struct pending_uid { + uid_t uid; + int use_count; + int remove_calls; +}; + +static struct pending_uid pending_uids[MAX_PENDING] = {0}; +static int pending_cnt = 0; + +bool current_verified = false; int ksu_manual_su_escalate(uid_t target_uid, pid_t target_pid, const char __user *user_password) @@ -40,6 +54,61 @@ int ksu_manual_su_escalate(uid_t target_uid, pid_t target_pid, ksu_mark_current_verified(); allowed: + current_verified = true; escape_to_root_for_cmd_su(target_uid, target_pid); return 0; -} \ No newline at end of file +} + +bool is_current_verified(void) +{ + return current_verified; +} + +bool is_pending_root(uid_t uid) +{ + for (int i = 0; i < pending_cnt; i++) { + if (pending_uids[i].uid == uid) { + pending_uids[i].use_count++; + pending_uids[i].remove_calls++; + return true; + } + } + return false; +} + +void remove_pending_root(uid_t uid) +{ + for (int i = 0; i < pending_cnt; i++) { + if (pending_uids[i].uid == uid) { + pending_uids[i].remove_calls++; + + if (pending_uids[i].remove_calls >= REMOVE_DELAY_CALLS) { + pending_uids[i] = pending_uids[--pending_cnt]; + pr_info("pending_root: removed UID %d after %d calls\n", uid, REMOVE_DELAY_CALLS); + ksu_temp_revoke_root_once(uid); + } else { + pr_info("pending_root: UID %d remove_call=%d (<%d)\n", + uid, pending_uids[i].remove_calls, REMOVE_DELAY_CALLS); + } + return; + } + } +} + +void add_pending_root(uid_t uid) +{ + if (pending_cnt >= MAX_PENDING) { + pr_warn("pending_root: cache full\n"); + return; + } + for (int i = 0; i < pending_cnt; i++) { + if (pending_uids[i].uid == uid) { + pending_uids[i].use_count = 0; + pending_uids[i].remove_calls = 0; + return; + } + } + pending_uids[pending_cnt++] = (struct pending_uid){uid, 0}; + ksu_temp_grant_root_once(uid); + pr_info("pending_root: cached UID %d\n", uid); +} diff --git a/kernel/manual_su.h b/kernel/manual_su.h index 5a756fca..11867b83 100644 --- a/kernel/manual_su.h +++ b/kernel/manual_su.h @@ -6,6 +6,11 @@ #define KSU_SU_VERIFIED_BIT (1UL << 0) +struct su_request_arg { + pid_t target_pid; + const char __user *user_password; +}; + static inline bool ksu_is_current_verified(void) { return ((unsigned long)(current->security) & KSU_SU_VERIFIED_BIT) != 0; @@ -19,4 +24,9 @@ static inline void ksu_mark_current_verified(void) int ksu_manual_su_escalate(uid_t target_uid, pid_t target_pid, const char __user *user_password); +bool is_pending_root(uid_t uid); +void remove_pending_root(uid_t uid); +void add_pending_root(uid_t uid); +bool is_current_verified(void); +extern bool current_verified; #endif \ No newline at end of file