Kernel: Enhanced temporary record UID functionality and elevated privileges
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user