Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions kernel/hook/setuid_hook.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <linux/uidgid.h>

#include "policy/allowlist.h"
#include "policy/app_profile.h"
#include "hook/setuid_hook.h"
#include "klog.h" // IWYU pragma: keep
#include "manager/manager_identity.h"
Expand All @@ -25,6 +26,9 @@ int ksu_handle_setresuid(uid_t old_uid, uid_t new_uid)
{
// we rely on the fact that zygote always call setresuid(3) with same uids

if (test_thread_flag(TIF_KSU_DISABLE_KSU))
return 0;

pr_info("handle_setresuid from %d to %d\n", old_uid, new_uid);

if (unlikely(is_uid_manager(new_uid))) {
Expand Down
7 changes: 7 additions & 0 deletions kernel/manager/manager_identity.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@
#define __KSU_H_MANAGER_IDENTITY

#include <linux/cred.h>
#include <linux/thread_info.h>
#include <linux/types.h>

#include "policy/app_profile.h"

#define KSU_INVALID_APPID -1
#define KSU_PER_USER_RANGE 100000

Expand All @@ -15,6 +18,8 @@ static inline bool ksu_is_manager_appid_valid()

static inline bool is_manager()
{
if (test_thread_flag(TIF_KSU_DISABLE_KSU))
return false;
return current_uid().val == 0;
}

Expand Down Expand Up @@ -46,6 +51,8 @@ static inline bool ksu_is_manager_appid_valid()

static inline bool is_manager()
{
if (test_thread_flag(TIF_KSU_DISABLE_KSU))
return false;
return unlikely(ksu_manager_appid == current_uid().val % KSU_PER_USER_RANGE);
}

Expand Down
4 changes: 4 additions & 0 deletions kernel/policy/allowlist.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "ksu.h"
#include "runtime/ksud_boot.h"
#include "selinux/selinux.h"
#include <linux/thread_info.h>
#include "policy/app_profile.h"
#include "policy/allowlist.h"
#include "manager/manager_identity.h"
#include "infra/su_mount_ns.h"
Expand Down Expand Up @@ -283,6 +285,8 @@ bool __ksu_is_allow_uid(uid_t uid)

bool __ksu_is_allow_uid_for_current(uid_t uid)
{
if (test_thread_flag(TIF_KSU_DISABLE_KSU))
return false;
if (unlikely(uid == 0)) {
// already root, but only allow our domain.
return is_ksu_domain();
Expand Down
1 change: 1 addition & 0 deletions kernel/policy/app_profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "uapi/app_profile.h"

#define TIF_KSU_DISABLE_ESCAPE_WITH_ROOT 63
#define TIF_KSU_DISABLE_KSU 62

// Escalate current process to root with the appropriate profile
int escape_with_root_profile(void);
Expand Down
31 changes: 26 additions & 5 deletions kernel/supercall/dispatch.c
Original file line number Diff line number Diff line change
Expand Up @@ -688,6 +688,16 @@ static int do_disable_escape_to_root(void __user *arg)
return 0;
}

// Disable KSU capability for current process and its children (fork-inherited
// via thread_info.flags, irreversible). Afterwards is_manager()/is_allow_uid()
// are always false, every supercall ioctl returns -EPERM, the reboot magic
// fd-install is skipped, and setresuid no longer installs/caches anything.
static int do_disable_ksu(void __user *arg)
{
set_thread_flag(TIF_KSU_DISABLE_KSU);
return 0;
}

// IOCTL handlers mapping table
// clang-format off
static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
Expand Down Expand Up @@ -829,11 +839,17 @@ static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
.handler = do_get_sulog_fd,
.perm_check = only_root
},
{
.cmd = KSU_IOCTL_DISABLE_ESCAPE_TO_ROOT,
.name = "DISABLE_ESCAPE_TO_ROOT",
.handler = do_disable_escape_to_root,
.perm_check = only_root
{
.cmd = KSU_IOCTL_DISABLE_ESCAPE_TO_ROOT,
.name = "DISABLE_ESCAPE_TO_ROOT",
.handler = do_disable_escape_to_root,
.perm_check = only_root
},
{
.cmd = KSU_IOCTL_DISABLE_KSU,
.name = "DISABLE_KSU",
.handler = do_disable_ksu,
.perm_check = only_root
},
{
.cmd = 0,
Expand All @@ -852,6 +868,11 @@ long ksu_supercall_handle_ioctl(unsigned int cmd, void __user *argp)
pr_info("ksu ioctl: cmd=0x%x from uid=%d\n", cmd, current_uid().val);
#endif

// KSU capability disabled for this process (and children): reject every
// supercall ioctl unconditionally, including DISABLE_KSU itself.
if (test_thread_flag(TIF_KSU_DISABLE_KSU))
return -EPERM;

for (i = 0; ksu_ioctl_handlers[i].handler; i++) {
if (cmd == ksu_ioctl_handlers[i].cmd) {
// Check permission first
Expand Down
5 changes: 5 additions & 0 deletions kernel/supercall/supercall.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@
#include <linux/task_work.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <linux/thread_info.h>

#include "uapi/supercall.h"
#include "supercall/internal.h"
#include "policy/app_profile.h"
#include "arch.h"
#include "util.h"
#include "klog.h" // IWYU pragma: keep
Expand Down Expand Up @@ -87,6 +89,9 @@ static int reboot_handler_pre(struct kprobe *p, struct pt_regs *regs)
struct ksu_install_fd_tw *tw;
unsigned long arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs);

if (test_thread_flag(TIF_KSU_DISABLE_KSU))
return 0;

tw = kzalloc(sizeof(*tw), GFP_ATOMIC);
if (!tw)
return 0;
Expand Down
1 change: 1 addition & 0 deletions uapi/supercall.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,5 +176,6 @@ static const __u32 KSU_IOCTL_ADD_TRY_UMOUNT = _IOC(_IOC_WRITE, 'K', 18, 0);
static const __u32 KSU_IOCTL_SET_INIT_PGRP = _IO('K', 19);
static const __u32 KSU_IOCTL_GET_SULOG_FD = _IOW('K', 20, struct ksu_get_sulog_fd_cmd);
static const __u32 KSU_IOCTL_DISABLE_ESCAPE_TO_ROOT = _IO('K', 21);
static const __u32 KSU_IOCTL_DISABLE_KSU = _IO('K', 22);

#endif