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);
|
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);
|
bool ksu_uid_should_umount(uid_t uid);
|
||||||
struct root_profile *ksu_get_root_profile(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
|
#endif
|
||||||
|
|||||||
@@ -343,19 +343,10 @@ static inline void nuke_ext4_sysfs() { }
|
|||||||
|
|
||||||
static bool is_system_bin_su()
|
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
|
// quick af check
|
||||||
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();
|
||||||
@@ -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_root = 0 == current_uid().val;
|
||||||
bool from_manager = is_manager();
|
bool from_manager = is_manager();
|
||||||
|
|
||||||
|
if (!current->mm || current->in_execve) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (arg2 == CMD_SU_ESCALATION_REQUEST) {
|
if (arg2 == CMD_SU_ESCALATION_REQUEST) {
|
||||||
uid_t target_uid = (uid_t)arg3;
|
uid_t target_uid = (uid_t)arg3;
|
||||||
pid_t target_pid = (pid_t)arg4;
|
struct su_request_arg __user *user_req = (struct su_request_arg __user *)arg4;
|
||||||
const char __user *user_password = (const char __user *)arg5;
|
|
||||||
|
|
||||||
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 (ret == 0) {
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
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;
|
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
|
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
|
||||||
@@ -1005,6 +1024,15 @@ int ksu_inode_permission(struct inode *inode, int mask)
|
|||||||
return 0;
|
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
|
#ifdef CONFIG_COMPAT
|
||||||
bool ksu_is_compat __read_mostly = false;
|
bool ksu_is_compat __read_mostly = false;
|
||||||
#endif
|
#endif
|
||||||
@@ -1030,6 +1058,8 @@ int ksu_bprm_check(struct linux_binprm *bprm)
|
|||||||
|
|
||||||
ksu_handle_pre_ksud(filename);
|
ksu_handle_pre_ksud(filename);
|
||||||
|
|
||||||
|
ksu_try_escalate_for_uid(current_uid().val);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1041,6 +1071,13 @@ static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|||||||
return -ENOSYS;
|
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,
|
static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry,
|
||||||
struct inode *new_inode, struct dentry *new_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(inode_rename, ksu_inode_rename),
|
||||||
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),
|
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),
|
||||||
LSM_HOOK_INIT(inode_permission, ksu_inode_permission),
|
LSM_HOOK_INIT(inode_permission, ksu_inode_permission),
|
||||||
|
LSM_HOOK_INIT(task_alloc, ksu_task_alloc),
|
||||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||||
LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check),
|
LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check),
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
#define CMD_ENABLE_SU 15
|
#define CMD_ENABLE_SU 15
|
||||||
|
|
||||||
#define CMD_SU_ESCALATION_REQUEST 50
|
#define CMD_SU_ESCALATION_REQUEST 50
|
||||||
|
#define CMD_ADD_PENDING_ROOT 51
|
||||||
|
|
||||||
#define CMD_GET_FULL_VERSION 0xC0FFEE1A
|
#define CMD_GET_FULL_VERSION 0xC0FFEE1A
|
||||||
|
|
||||||
|
|||||||
@@ -10,9 +10,23 @@
|
|||||||
#include "ksu.h"
|
#include "ksu.h"
|
||||||
#include "allowlist.h"
|
#include "allowlist.h"
|
||||||
#include "manager.h"
|
#include "manager.h"
|
||||||
|
#include "allowlist.h"
|
||||||
|
|
||||||
static const char *ksu_su_password = "zakozako";
|
static const char *ksu_su_password = "zakozako";
|
||||||
extern void escape_to_root_for_cmd_su(uid_t, pid_t);
|
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,
|
int ksu_manual_su_escalate(uid_t target_uid, pid_t target_pid,
|
||||||
const char __user *user_password)
|
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();
|
ksu_mark_current_verified();
|
||||||
|
|
||||||
allowed:
|
allowed:
|
||||||
|
current_verified = true;
|
||||||
escape_to_root_for_cmd_su(target_uid, target_pid);
|
escape_to_root_for_cmd_su(target_uid, target_pid);
|
||||||
return 0;
|
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)
|
#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)
|
static inline bool ksu_is_current_verified(void)
|
||||||
{
|
{
|
||||||
return ((unsigned long)(current->security) & KSU_SU_VERIFIED_BIT) != 0;
|
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,
|
int ksu_manual_su_escalate(uid_t target_uid, pid_t target_pid,
|
||||||
const char __user *user_password);
|
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
|
#endif
|
||||||
Reference in New Issue
Block a user