diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 53185d59..320dcc5d 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -454,6 +454,7 @@ static void sulog_prctl_cmd(uid_t uid, unsigned long cmd) #ifdef CONFIG_KSU_MANUAL_SU case CMD_SU_ESCALATION_REQUEST: name = "prctl_su_escalation_request"; break; case CMD_ADD_PENDING_ROOT: name = "prctl_add_pending_root"; break; + case CMD_GENERATE_AUTH_TOKEN: name = "prctl_generate_auth_token"; break; #endif default: name = "prctl_unknown"; break; @@ -806,23 +807,45 @@ skip_check: #ifdef CONFIG_KSU_MANUAL_SU if (arg2 == CMD_SU_ESCALATION_REQUEST) { uid_t target_uid = (uid_t)arg3; - struct su_request_arg __user *user_req = (struct su_request_arg __user *)arg4; + pid_t target_pid = (pid_t)arg4; - pid_t target_pid; - const char __user *user_password; + int ret = ksu_manual_su_escalate(target_uid, target_pid); - 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))) + if (ret == 0 && copy_to_user(result, &reply_ok, sizeof(reply_ok))) return -EFAULT; + return 0; + } - int ret = ksu_manual_su_escalate(target_uid, target_pid, user_password); + if (arg2 == CMD_GENERATE_AUTH_TOKEN) { + char __user *token_buffer = (char __user *)arg3; + size_t buffer_size = (size_t)arg4; - if (ret == 0) { - if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { - pr_err("cmd_su_escalation: prctl reply error\n"); - } + if (current_uid().val > 2000) { + pr_warn("CMD_GENERATE_AUTH_TOKEN: denied for app UID %d\n", current_uid().val); + return 0; } + + if (buffer_size < KSU_TOKEN_LENGTH + 1) { + pr_err("CMD_GENERATE_AUTH_TOKEN: buffer too small\n"); + return -EINVAL; + } + + char *new_token = ksu_generate_auth_token(); + if (!new_token) { + pr_err("CMD_GENERATE_AUTH_TOKEN: failed to generate token\n"); + return -ENOMEM; + } + + if (copy_to_user(token_buffer, new_token, KSU_TOKEN_LENGTH + 1)) { + pr_err("CMD_GENERATE_AUTH_TOKEN: failed to copy token to user\n"); + return -EFAULT; + } + + if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { + pr_err("CMD_GENERATE_AUTH_TOKEN: prctl reply error\n"); + } + + pr_info("prctl: auth token generated successfully\n"); return 0; } diff --git a/kernel/ksu.h b/kernel/ksu.h index 1c6780a3..e5115a14 100644 --- a/kernel/ksu.h +++ b/kernel/ksu.h @@ -27,6 +27,7 @@ #ifdef CONFIG_KSU_MANUAL_SU #define CMD_SU_ESCALATION_REQUEST 50 #define CMD_ADD_PENDING_ROOT 51 +#define CMD_GENERATE_AUTH_TOKEN 52 #endif #define CMD_GET_FULL_VERSION 0xC0FFEE1A diff --git a/kernel/manual_su.c b/kernel/manual_su.c index b8b61b1f..1c1370d8 100644 --- a/kernel/manual_su.c +++ b/kernel/manual_su.c @@ -5,16 +5,18 @@ #include #include #include +#include +#include #include "kernel_compat.h" #include "manual_su.h" #include "ksu.h" #include "allowlist.h" #include "manager.h" -static const char *ksu_su_password = KSU_SU_PASSWORD; extern void escape_to_root_for_cmd_su(uid_t, pid_t); #define MAX_PENDING 16 #define REMOVE_DELAY_CALLS 150 +#define MAX_TOKENS 10 struct pending_uid { uid_t uid; @@ -24,38 +26,192 @@ struct pending_uid { static struct pending_uid pending_uids[MAX_PENDING] = {0}; static int pending_cnt = 0; +static struct ksu_token_entry auth_tokens[MAX_TOKENS] = {0}; +static int token_count = 0; +static DEFINE_SPINLOCK(token_lock); bool current_verified = false; -int ksu_manual_su_escalate(uid_t target_uid, pid_t target_pid, - const char __user *user_password) +static char* get_token_from_envp(void) { - if (ksu_is_current_verified()) - goto allowed; + struct mm_struct *mm; + char *envp_start, *envp_end; + char *env_ptr, *token = NULL; + unsigned long env_len; + char *env_copy = NULL; + + if (!current->mm) + return NULL; + + mm = current->mm; + + down_read(&mm->mmap_lock); + + envp_start = (char *)mm->env_start; + envp_end = (char *)mm->env_end; + env_len = envp_end - envp_start; + + if (env_len <= 0 || env_len > PAGE_SIZE * 32) { + up_read(&mm->mmap_lock); + return NULL; + } + + env_copy = kmalloc(env_len + 1, GFP_KERNEL); + if (!env_copy) { + up_read(&mm->mmap_lock); + return NULL; + } + + if (copy_from_user(env_copy, envp_start, env_len)) { + kfree(env_copy); + up_read(&mm->mmap_lock); + return NULL; + } + + up_read(&mm->mmap_lock); + + env_copy[env_len] = '\0'; + env_ptr = env_copy; + + while (env_ptr < env_copy + env_len) { + 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_end = strchr(token_start, '\0'); + + if (token_end && (token_end - token_start) == KSU_TOKEN_LENGTH) { + token = kmalloc(KSU_TOKEN_LENGTH + 1, GFP_KERNEL); + if (token) { + memcpy(token, token_start, KSU_TOKEN_LENGTH); + token[KSU_TOKEN_LENGTH] = '\0'; + pr_info("manual_su: found auth token in environment\n"); + } + } + break; + } + + env_ptr += strlen(env_ptr) + 1; + } + + kfree(env_copy); + return token; +} +char* ksu_generate_auth_token(void) +{ + static char token_buffer[KSU_TOKEN_LENGTH + 1]; + unsigned long flags; + int i; + + ksu_cleanup_expired_tokens(); + + spin_lock_irqsave(&token_lock, flags); + + if (token_count >= MAX_TOKENS) { + for (i = 0; i < MAX_TOKENS - 1; i++) { + auth_tokens[i] = auth_tokens[i + 1]; + } + token_count = MAX_TOKENS - 1; + } + + for (i = 0; i < KSU_TOKEN_LENGTH; i++) { + u8 rand_byte; + get_random_bytes(&rand_byte, 1); + int char_type = rand_byte % 3; + if (char_type == 0) { + token_buffer[i] = 'A' + (rand_byte % 26); + } else if (char_type == 1) { + token_buffer[i] = 'a' + (rand_byte % 26); + } else { + token_buffer[i] = '0' + (rand_byte % 10); + } + } + token_buffer[KSU_TOKEN_LENGTH] = '\0'; + + strncpy(auth_tokens[token_count].token, token_buffer, KSU_TOKEN_LENGTH + 1); + auth_tokens[token_count].expire_time = jiffies + KSU_TOKEN_EXPIRE_TIME * HZ; + auth_tokens[token_count].used = false; + token_count++; + + spin_unlock_irqrestore(&token_lock, flags); + + pr_info("manual_su: generated new auth token (expires in %d seconds)\n", KSU_TOKEN_EXPIRE_TIME); + return token_buffer; +} + +bool ksu_verify_auth_token(const char *token) +{ + unsigned long flags; + bool valid = false; + int i; + + if (!token || strlen(token) != KSU_TOKEN_LENGTH) { + return false; + } + + spin_lock_irqsave(&token_lock, flags); + + for (i = 0; i < token_count; i++) { + if (!auth_tokens[i].used && + time_before(jiffies, auth_tokens[i].expire_time) && + strcmp(auth_tokens[i].token, token) == 0) { + + auth_tokens[i].used = true; + valid = true; + pr_info("manual_su: auth token verified successfully\n"); + break; + } + } + + spin_unlock_irqrestore(&token_lock, flags); + + if (!valid) { + pr_warn("manual_su: invalid or expired auth token\n"); + } + + return valid; +} + +void ksu_cleanup_expired_tokens(void) +{ + unsigned long flags; + int i, j; + + spin_lock_irqsave(&token_lock, flags); + + for (i = 0; i < token_count; ) { + if (time_after(jiffies, auth_tokens[i].expire_time) || auth_tokens[i].used) { + for (j = i; j < token_count - 1; j++) { + auth_tokens[j] = auth_tokens[j + 1]; + } + token_count--; + pr_debug("manual_su: cleaned up expired/used token\n"); + } else { + i++; + } + } + + spin_unlock_irqrestore(&token_lock, flags); +} + +int ksu_manual_su_escalate(uid_t target_uid, pid_t target_pid) +{ if (current_uid().val == 0 || is_manager() || ksu_is_allow_uid(current_uid().val)) goto allowed; - if (!user_password) { - pr_warn("manual_su: password required\n"); + char *env_token = get_token_from_envp(); + if (!env_token) { + pr_warn("manual_su: no auth token found in environment\n"); return -EACCES; } - char buf[64]; - long copied; - - copied = ksu_copy_from_user_retry(buf, user_password, sizeof(buf) - 1); - if (copied < 0) - return -EFAULT; - - buf[copied] = '\0'; - - if (strcmp(buf, ksu_su_password) != 0) { - pr_warn("manual_su: wrong password (input=%s, expect=%s)\n", buf, ksu_su_password); + + bool token_valid = ksu_verify_auth_token(env_token); + kfree(env_token); + + if (!token_valid) { + pr_warn("manual_su: token verification failed\n"); return -EACCES; } - ksu_mark_current_verified(); - allowed: current_verified = true; escape_to_root_for_cmd_su(target_uid, target_pid); diff --git a/kernel/manual_su.h b/kernel/manual_su.h index 11867b83..814a11d7 100644 --- a/kernel/manual_su.h +++ b/kernel/manual_su.h @@ -5,28 +5,24 @@ #include #define KSU_SU_VERIFIED_BIT (1UL << 0) +#define KSU_TOKEN_LENGTH 32 +#define KSU_TOKEN_ENV_NAME "KSU_AUTH_TOKEN" +#define KSU_TOKEN_EXPIRE_TIME 30 -struct su_request_arg { - pid_t target_pid; - const char __user *user_password; +struct ksu_token_entry { + char token[KSU_TOKEN_LENGTH + 1]; + unsigned long expire_time; + bool used; }; -static inline bool ksu_is_current_verified(void) -{ - return ((unsigned long)(current->security) & KSU_SU_VERIFIED_BIT) != 0; -} - -static inline void ksu_mark_current_verified(void) -{ - current->security = (void *)((unsigned long)(current->security) | KSU_SU_VERIFIED_BIT); -} - -int ksu_manual_su_escalate(uid_t target_uid, pid_t target_pid, - const char __user *user_password); +int ksu_manual_su_escalate(uid_t target_uid, pid_t target_pid); 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); +char* ksu_generate_auth_token(void); +bool ksu_verify_auth_token(const char *token); +void ksu_cleanup_expired_tokens(void); extern bool current_verified; #endif \ No newline at end of file