kernel: Add the ability to manually elevate privileges for programs using prctl by specifying UID or PID.

This commit is contained in:
ShirkNeko
2025-09-28 19:33:08 +08:00
parent e552163d9e
commit 65d5d6a494
5 changed files with 172 additions and 0 deletions

View File

@@ -9,6 +9,7 @@ kernelsu-objs += ksud.o
kernelsu-objs += embed_ksud.o kernelsu-objs += embed_ksud.o
kernelsu-objs += kernel_compat.o kernelsu-objs += kernel_compat.o
kernelsu-objs += throne_comm.o kernelsu-objs += throne_comm.o
kernelsu-objs += manual_su.o
ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y) ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y)
kernelsu-objs += ksu_trace.o kernelsu-objs += ksu_trace.o

View File

@@ -22,6 +22,7 @@
#include <linux/version.h> #include <linux/version.h>
#include <linux/mount.h> #include <linux/mount.h>
#include <linux/binfmts.h> #include <linux/binfmts.h>
#include <linux/tty.h>
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/namei.h> #include <linux/namei.h>
@@ -46,6 +47,7 @@
#include "throne_comm.h" #include "throne_comm.h"
#include "kernel_compat.h" #include "kernel_compat.h"
#include "dynamic_manager.h" #include "dynamic_manager.h"
#include "manual_su.h"
#ifdef CONFIG_KPM #ifdef CONFIG_KPM
#include "kpm/kpm.h" #include "kpm/kpm.h"
@@ -193,6 +195,86 @@ void escape_to_root(void)
setup_selinux(profile->selinux_domain); setup_selinux(profile->selinux_domain);
} }
void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid)
{
struct cred *newcreds;
struct task_struct *target_task;
pr_info("cmd_su: escape_to_root_for_cmd_su called for UID: %d, PID: %d\n", target_uid, target_pid);
// Find target task by PID
rcu_read_lock();
target_task = pid_task(find_vpid(target_pid), PIDTYPE_PID);
if (!target_task) {
rcu_read_unlock();
pr_err("cmd_su: target task not found for PID: %d\n", target_pid);
return;
}
get_task_struct(target_task);
rcu_read_unlock();
if (task_uid(target_task).val == 0) {
pr_warn("cmd_su: target task is already root, PID: %d\n", target_pid);
put_task_struct(target_task);
return;
}
newcreds = prepare_kernel_cred(target_task);
if (newcreds == NULL) {
pr_err("cmd_su: failed to allocate new cred for PID: %d\n", target_pid);
put_task_struct(target_task);
return;
}
struct root_profile *profile = ksu_get_root_profile(target_uid);
newcreds->uid.val = profile->uid;
newcreds->suid.val = profile->uid;
newcreds->euid.val = profile->uid;
newcreds->fsuid.val = profile->uid;
newcreds->gid.val = profile->gid;
newcreds->fsgid.val = profile->gid;
newcreds->sgid.val = profile->gid;
newcreds->egid.val = profile->gid;
newcreds->securebits = 0;
u64 cap_for_cmd_su = profile->capabilities.effective | CAP_DAC_READ_SEARCH | CAP_SETUID | CAP_SETGID;
memcpy(&newcreds->cap_effective, &cap_for_cmd_su, sizeof(newcreds->cap_effective));
memcpy(&newcreds->cap_permitted, &profile->capabilities.effective, sizeof(newcreds->cap_permitted));
memcpy(&newcreds->cap_bset, &profile->capabilities.effective, sizeof(newcreds->cap_bset));
setup_groups(profile, newcreds);
task_lock(target_task);
const struct cred *old_creds = get_task_cred(target_task);
rcu_assign_pointer(target_task->real_cred, newcreds);
rcu_assign_pointer(target_task->cred, get_cred(newcreds));
task_unlock(target_task);
if (target_task->sighand) {
spin_lock_irq(&target_task->sighand->siglock);
disable_seccomp(target_task);
spin_unlock_irq(&target_task->sighand->siglock);
}
setup_selinux(profile->selinux_domain);
put_cred(old_creds);
wake_up_process(target_task);
if (target_task->signal->tty) {
struct inode *inode = target_task->signal->tty->driver_data;
if (inode && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC) {
__ksu_handle_devpts(inode);
}
}
put_task_struct(target_task);
pr_info("cmd_su: privilege escalation completed for UID: %d, PID: %d\n", target_uid, target_pid);
}
int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry) int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
{ {
if (!current->mm) { if (!current->mm) {
@@ -269,6 +351,11 @@ static bool is_system_bin_su()
return (current->mm->exe_file && !strcmp(current->mm->exe_file->f_path.dentry->d_name.name, "su")); 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) static void init_uid_scanner(void)
{ {
ksu_uid_init(); ksu_uid_init();
@@ -305,6 +392,21 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
bool from_root = 0 == current_uid().val; bool from_root = 0 == current_uid().val;
bool from_manager = is_manager(); bool from_manager = is_manager();
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;
int ret = handle_cmd_su_escalation(target_uid, target_pid, user_password);
if (ret == 0) {
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("cmd_su_escalation: prctl reply error\n");
}
}
return 0;
}
if (!from_root && !from_manager if (!from_root && !from_manager
&& !(is_allow_su() && is_system_bin_su())) { && !(is_allow_su() && is_system_bin_su())) {
// only root or manager can access this interface // only root or manager can access this interface

View File

@@ -24,6 +24,8 @@
#define CMD_IS_SU_ENABLED 14 #define CMD_IS_SU_ENABLED 14
#define CMD_ENABLE_SU 15 #define CMD_ENABLE_SU 15
#define CMD_SU_ESCALATION_REQUEST 50
#define CMD_GET_FULL_VERSION 0xC0FFEE1A #define CMD_GET_FULL_VERSION 0xC0FFEE1A
#define CMD_ENABLE_KPM 100 #define CMD_ENABLE_KPM 100

45
kernel/manual_su.c Normal file
View File

@@ -0,0 +1,45 @@
#include <linux/string.h>
#include <linux/uaccess.h>
#include <linux/printk.h>
#include <linux/cred.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/file.h>
#include "kernel_compat.h"
#include "manual_su.h"
#include "ksu.h"
#include "allowlist.h"
#include "manager.h"
static const char *ksu_su_password = "zakozako";
extern void escape_to_root_for_cmd_su(uid_t, pid_t);
int ksu_manual_su_escalate(uid_t target_uid, pid_t target_pid,
const char __user *user_password)
{
if (ksu_is_current_verified())
goto allowed;
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");
return -EACCES;
}
char buf[64];
if (strncpy_from_user(buf, user_password, sizeof(buf) - 1) < 0)
return -EFAULT;
buf[sizeof(buf) - 1] = '\0';
if (strcmp(buf, ksu_su_password) != 0) {
pr_warn("manual_su: wrong password\n");
return -EACCES;
}
ksu_mark_current_verified();
allowed:
escape_to_root_for_cmd_su(target_uid, target_pid);
return 0;
}

22
kernel/manual_su.h Normal file
View File

@@ -0,0 +1,22 @@
#ifndef __KSU_MANUAL_SU_H
#define __KSU_MANUAL_SU_H
#include <linux/types.h>
#include <linux/sched.h>
#define KSU_SU_VERIFIED_BIT (1UL << 0)
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);
#endif