kernel: Remove prctl; use netlink communication to control manual_su
This commit is contained in:
@@ -13,6 +13,7 @@ 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 += sulog.o
|
kernelsu-objs += sulog.o
|
||||||
|
kernelsu-objs += ksu_netlink.o
|
||||||
|
|
||||||
ifeq ($(CONFIG_KSU_MANUAL_SU), y)
|
ifeq ($(CONFIG_KSU_MANUAL_SU), y)
|
||||||
ccflags-y += -DCONFIG_KSU_MANUAL_SU
|
ccflags-y += -DCONFIG_KSU_MANUAL_SU
|
||||||
@@ -166,7 +167,7 @@ ccflags-y += -DKSU_COMPAT_HAS_PROC_OPS
|
|||||||
endif
|
endif
|
||||||
|
|
||||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||||
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function -Wno-unused-variable
|
||||||
|
|
||||||
all:
|
all:
|
||||||
make -C $(KDIR) M=$(MDIR) modules
|
make -C $(KDIR) M=$(MDIR) modules
|
||||||
|
|||||||
@@ -389,81 +389,6 @@ bool is_system_uid(void)
|
|||||||
return caller_uid <= 2000;
|
return caller_uid <= 2000;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if __SULOG_GATE
|
|
||||||
static void sulog_prctl_cmd(uid_t uid, unsigned long cmd)
|
|
||||||
{
|
|
||||||
const char *name = NULL;
|
|
||||||
|
|
||||||
switch (cmd) {
|
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_MANUAL_SU
|
|
||||||
case CMD_MANUAL_SU_REQUEST: name = "prctl_manual_su_request"; break;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
default: name = "prctl_unknown"; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
ksu_sulog_report_syscall(uid, NULL, name, NULL);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|
||||||
unsigned long arg4, unsigned long arg5)
|
|
||||||
{
|
|
||||||
// if success, we modify the arg5 as result!
|
|
||||||
__maybe_unused u32 *result = (u32 *)arg5;
|
|
||||||
__maybe_unused u32 reply_ok = KERNEL_SU_OPTION;
|
|
||||||
|
|
||||||
if (likely(ksu_is_current_proc_umounted()))
|
|
||||||
return 0; // prevent side channel attack in ksu side
|
|
||||||
|
|
||||||
if (KERNEL_SU_OPTION != option)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
#if __SULOG_GATE
|
|
||||||
sulog_prctl_cmd(current_uid().val, arg2);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (!is_system_uid()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_info("option: 0x%x, cmd: %ld\n", option, arg2);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_MANUAL_SU
|
|
||||||
if (arg2 == CMD_MANUAL_SU_REQUEST) {
|
|
||||||
struct manual_su_request request;
|
|
||||||
int su_option = (int)arg3;
|
|
||||||
|
|
||||||
if (copy_from_user(&request, (void __user *)arg4, sizeof(request))) {
|
|
||||||
pr_err("manual_su: failed to copy request from user\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ret = ksu_handle_manual_su_request(su_option, &request);
|
|
||||||
|
|
||||||
// Copy back result for token generation
|
|
||||||
if (ret == 0 && su_option == MANUAL_SU_OP_GENERATE_TOKEN) {
|
|
||||||
if (copy_to_user((void __user *)arg4, &request, sizeof(request))) {
|
|
||||||
pr_err("manual_su: failed to copy request back to user\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret == 0) {
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
|
||||||
pr_err("manual_su: prctl reply error\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is_appuid(kuid_t uid)
|
static bool is_appuid(kuid_t uid)
|
||||||
{
|
{
|
||||||
#define PER_USER_RANGE 100000
|
#define PER_USER_RANGE 100000
|
||||||
@@ -723,26 +648,7 @@ static struct kprobe cap_task_fix_setuid_kp = {
|
|||||||
.pre_handler = cap_task_fix_setuid_handler_pre,
|
.pre_handler = cap_task_fix_setuid_handler_pre,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 3. prctl hook for handling ksu prctl commands
|
// 3.inode_permission hook for handling devpts
|
||||||
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|
||||||
{
|
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
|
||||||
int option = (int)PT_REGS_PARM1(real_regs);
|
|
||||||
unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs);
|
|
||||||
unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs);
|
|
||||||
// PRCTL_SYMBOL is the arch-specificed one, which receive raw pt_regs from syscall
|
|
||||||
unsigned long arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs);
|
|
||||||
unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs);
|
|
||||||
|
|
||||||
return ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct kprobe prctl_kp = {
|
|
||||||
.symbol_name = PRCTL_SYMBOL,
|
|
||||||
.pre_handler = handler_pre,
|
|
||||||
};
|
|
||||||
|
|
||||||
// 4.inode_permission hook for handling devpts
|
|
||||||
static int ksu_inode_permission_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int ksu_inode_permission_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct inode *inode = (struct inode *)PT_REGS_PARM1(regs);
|
struct inode *inode = (struct inode *)PT_REGS_PARM1(regs);
|
||||||
@@ -761,7 +667,7 @@ static struct kprobe ksu_inode_permission_kp = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// 5. bprm_check_security hook for handling ksud compatibility
|
// 4. bprm_check_security hook for handling ksud compatibility
|
||||||
static int ksu_bprm_check_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int ksu_bprm_check_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct linux_binprm *bprm = (struct linux_binprm *)PT_REGS_PARM1(regs);
|
struct linux_binprm *bprm = (struct linux_binprm *)PT_REGS_PARM1(regs);
|
||||||
@@ -797,7 +703,7 @@ static struct kprobe ksu_bprm_check_kp = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_MANUAL_SU
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
// 6. task_alloc hook for handling manual su escalation
|
// 5. task_alloc hook for handling manual su escalation
|
||||||
static int ksu_task_alloc_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int ksu_task_alloc_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
struct task_struct *task = (struct task_struct *)PT_REGS_PARM1(regs);
|
struct task_struct *task = (struct task_struct *)PT_REGS_PARM1(regs);
|
||||||
@@ -831,15 +737,6 @@ __maybe_unused int ksu_kprobe_init(void)
|
|||||||
} else {
|
} else {
|
||||||
pr_info("cap_task_fix_setuid_kp kprobe registered successfully\n");
|
pr_info("cap_task_fix_setuid_kp kprobe registered successfully\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Register prctl kprobe
|
|
||||||
rc = register_kprobe(&prctl_kp);
|
|
||||||
if (rc) {
|
|
||||||
pr_info("prctl kprobe failed: %d.\n", rc);
|
|
||||||
} else {
|
|
||||||
pr_info("prctl kprobe registered successfully.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Register inode_permission kprobe
|
// Register inode_permission kprobe
|
||||||
rc = register_kprobe(&ksu_inode_permission_kp);
|
rc = register_kprobe(&ksu_inode_permission_kp);
|
||||||
@@ -874,7 +771,6 @@ __maybe_unused int ksu_kprobe_exit(void)
|
|||||||
{
|
{
|
||||||
unregister_kprobe(&reboot_kp);
|
unregister_kprobe(&reboot_kp);
|
||||||
unregister_kprobe(&cap_task_fix_setuid_kp);
|
unregister_kprobe(&cap_task_fix_setuid_kp);
|
||||||
unregister_kprobe(&prctl_kp);
|
|
||||||
unregister_kprobe(&ksu_inode_permission_kp);
|
unregister_kprobe(&ksu_inode_permission_kp);
|
||||||
unregister_kprobe(&ksu_bprm_check_kp);
|
unregister_kprobe(&ksu_bprm_check_kp);
|
||||||
#ifdef CONFIG_KSU_MANUAL_SU
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
@@ -894,6 +790,9 @@ void __init ksu_core_init(void)
|
|||||||
if (ksu_register_feature_handler(&kernel_umount_handler)) {
|
if (ksu_register_feature_handler(&kernel_umount_handler)) {
|
||||||
pr_err("Failed to register umount feature handler\n");
|
pr_err("Failed to register umount feature handler\n");
|
||||||
}
|
}
|
||||||
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
|
ksu_netlink_init();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_core_exit(void)
|
void ksu_core_exit(void)
|
||||||
@@ -908,4 +807,7 @@ void ksu_core_exit(void)
|
|||||||
ksu_kprobe_exit();
|
ksu_kprobe_exit();
|
||||||
#endif
|
#endif
|
||||||
ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT);
|
ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT);
|
||||||
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
|
ksu_netlink_exit();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,6 @@
|
|||||||
|
|
||||||
extern bool ksu_uid_scanner_enabled;
|
extern bool ksu_uid_scanner_enabled;
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_MANUAL_SU
|
|
||||||
#define CMD_MANUAL_SU_REQUEST 50
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define EVENT_POST_FS_DATA 1
|
#define EVENT_POST_FS_DATA 1
|
||||||
#define EVENT_BOOT_COMPLETED 2
|
#define EVENT_BOOT_COMPLETED 2
|
||||||
#define EVENT_MODULE_MOUNTED 3
|
#define EVENT_MODULE_MOUNTED 3
|
||||||
|
|||||||
119
kernel/ksu_netlink.c
Normal file
119
kernel/ksu_netlink.c
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/skbuff.h>
|
||||||
|
#include <net/sock.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include "ksu_netlink.h"
|
||||||
|
#include "manual_su.h"
|
||||||
|
#include "ksu.h"
|
||||||
|
|
||||||
|
static struct sock *ksu_nl_sock = NULL;
|
||||||
|
|
||||||
|
extern int ksu_handle_manual_su_request(int option, struct manual_su_request *request);
|
||||||
|
|
||||||
|
static void ksu_netlink_recv_msg(struct sk_buff *skb)
|
||||||
|
{
|
||||||
|
struct nlmsghdr *nlh;
|
||||||
|
struct ksu_netlink_msg *msg;
|
||||||
|
struct ksu_netlink_msg reply;
|
||||||
|
struct sk_buff *skb_out;
|
||||||
|
int msg_size;
|
||||||
|
int res;
|
||||||
|
u32 pid;
|
||||||
|
|
||||||
|
if (!skb) {
|
||||||
|
pr_err("ksu_netlink: received NULL skb\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlh = (struct nlmsghdr *)skb->data;
|
||||||
|
pid = nlh->nlmsg_pid;
|
||||||
|
|
||||||
|
if (!nlh || nlh->nlmsg_len < NLMSG_HDRLEN + sizeof(struct ksu_netlink_msg)) {
|
||||||
|
pr_err("ksu_netlink: invalid message size\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg = (struct ksu_netlink_msg *)nlmsg_data(nlh);
|
||||||
|
|
||||||
|
if (msg->cmd != KSU_NETLINK_CMD_MANUAL_SU) {
|
||||||
|
pr_warn("ksu_netlink: unknown command %d\n", msg->cmd);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("ksu_netlink: received manual_su request, option=%d, uid=%d, pid=%d\n",
|
||||||
|
msg->option, msg->target_uid, msg->target_pid);
|
||||||
|
|
||||||
|
memset(&reply, 0, sizeof(reply));
|
||||||
|
reply.cmd = msg->cmd;
|
||||||
|
reply.option = msg->option;
|
||||||
|
reply.target_uid = msg->target_uid;
|
||||||
|
reply.target_pid = msg->target_pid;
|
||||||
|
|
||||||
|
struct manual_su_request request = {
|
||||||
|
.target_uid = msg->target_uid,
|
||||||
|
.target_pid = msg->target_pid
|
||||||
|
};
|
||||||
|
|
||||||
|
if (msg->option == MANUAL_SU_OP_GENERATE_TOKEN ||
|
||||||
|
msg->option == MANUAL_SU_OP_ESCALATE) {
|
||||||
|
memcpy(request.token_buffer, msg->token_buffer, KSU_TOKEN_LENGTH + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
res = ksu_handle_manual_su_request(msg->option, &request);
|
||||||
|
|
||||||
|
reply.result = res;
|
||||||
|
if (msg->option == MANUAL_SU_OP_GENERATE_TOKEN && res == 0) {
|
||||||
|
memcpy(reply.token_buffer, request.token_buffer, KSU_TOKEN_LENGTH + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_size = sizeof(struct ksu_netlink_msg);
|
||||||
|
skb_out = nlmsg_new(msg_size, GFP_KERNEL);
|
||||||
|
if (!skb_out) {
|
||||||
|
pr_err("ksu_netlink: failed to allocate reply skb\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, msg_size, 0);
|
||||||
|
if (!nlh) {
|
||||||
|
pr_err("ksu_netlink: nlmsg_put failed\n");
|
||||||
|
kfree_skb(skb_out);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
NETLINK_CB(skb_out).dst_group = 0;
|
||||||
|
memcpy(nlmsg_data(nlh), &reply, sizeof(reply));
|
||||||
|
|
||||||
|
res = nlmsg_unicast(ksu_nl_sock, skb_out, pid);
|
||||||
|
if (res < 0) {
|
||||||
|
pr_err("ksu_netlink: failed to send reply: %d\n", res);
|
||||||
|
} else {
|
||||||
|
pr_info("ksu_netlink: reply sent successfully\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_netlink_init(void)
|
||||||
|
{
|
||||||
|
struct netlink_kernel_cfg cfg = {
|
||||||
|
.input = ksu_netlink_recv_msg,
|
||||||
|
};
|
||||||
|
|
||||||
|
ksu_nl_sock = netlink_kernel_create(&init_net, KSU_NETLINK_PROTOCOL, &cfg);
|
||||||
|
if (!ksu_nl_sock) {
|
||||||
|
pr_err("ksu_netlink: failed to create netlink socket\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("ksu_netlink: initialized with protocol %d\n", KSU_NETLINK_PROTOCOL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_netlink_exit(void)
|
||||||
|
{
|
||||||
|
if (ksu_nl_sock) {
|
||||||
|
netlink_kernel_release(ksu_nl_sock);
|
||||||
|
ksu_nl_sock = NULL;
|
||||||
|
pr_info("ksu_netlink: released\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
23
kernel/ksu_netlink.h
Normal file
23
kernel/ksu_netlink.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#ifndef __KSU_NETLINK_H
|
||||||
|
#define __KSU_NETLINK_H
|
||||||
|
|
||||||
|
#include <linux/netlink.h>
|
||||||
|
#include <linux/skbuff.h>
|
||||||
|
#include <net/sock.h>
|
||||||
|
|
||||||
|
#define KSU_NETLINK_PROTOCOL 2
|
||||||
|
#define KSU_NETLINK_CMD_MANUAL_SU 50
|
||||||
|
|
||||||
|
struct ksu_netlink_msg {
|
||||||
|
int cmd;
|
||||||
|
int option;
|
||||||
|
uid_t target_uid;
|
||||||
|
pid_t target_pid;
|
||||||
|
char token_buffer[33];
|
||||||
|
int result;
|
||||||
|
};
|
||||||
|
|
||||||
|
int ksu_netlink_init(void);
|
||||||
|
void ksu_netlink_exit(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -430,11 +430,11 @@ void track_throne(void)
|
|||||||
{
|
{
|
||||||
struct list_head uid_list;
|
struct list_head uid_list;
|
||||||
struct uid_data *np, *n;
|
struct uid_data *np, *n;
|
||||||
__maybe_unused struct file *fp;
|
struct file *fp;
|
||||||
__maybe_unused char chr = 0;
|
char chr = 0;
|
||||||
__maybe_unused loff_t pos = 0;
|
loff_t pos = 0;
|
||||||
__maybe_unused loff_t line_start = 0;
|
loff_t line_start = 0;
|
||||||
__maybe_unused char buf[KSU_MAX_PACKAGE_NAME];
|
char buf[KSU_MAX_PACKAGE_NAME];
|
||||||
static bool manager_exist = false;
|
static bool manager_exist = false;
|
||||||
static bool dynamic_manager_exist = false;
|
static bool dynamic_manager_exist = false;
|
||||||
int current_manager_uid = ksu_get_manager_uid() % 100000;
|
int current_manager_uid = ksu_get_manager_uid() % 100000;
|
||||||
|
|||||||
Reference in New Issue
Block a user