Kernel: Enhanced temporary record UID functionality and elevated privileges

This commit is contained in:
ShirkNeko
2025-09-29 04:28:31 +08:00
parent 65d5d6a494
commit a9a10466b3
6 changed files with 210 additions and 13 deletions

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}
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);
}

View File

@@ -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