kernel: Add the ability to manually elevate privileges for programs using prctl by specifying UID or PID.
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
45
kernel/manual_su.c
Normal 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
22
kernel/manual_su.h
Normal 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
|
||||||
Reference in New Issue
Block a user