kernel: Migrate manual_su to ioctl

This commit is contained in:
ShirkNeko
2025-11-06 02:52:14 +08:00
parent 77fbfb7796
commit ee9c20f62a
7 changed files with 59 additions and 259 deletions

View File

@@ -12,7 +12,6 @@ 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

View File

@@ -1586,7 +1586,6 @@ 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");
} }
ksu_netlink_init();
} }
void ksu_core_exit(void) void ksu_core_exit(void)
@@ -1602,5 +1601,4 @@ 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);
ksu_netlink_exit();
} }

View File

@@ -1,202 +0,0 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include "kernel_compat.h"
#include "ksu_netlink.h"
#include "manual_su.h"
#include "ksu.h"
static struct sock *ksu_nl_sock = NULL;
static bool manager_only(uid_t uid)
{
return is_manager();
}
static bool manager_or_allowed(uid_t uid)
{
return is_manager() || ksu_is_allow_uid(uid);
}
static bool always_allow(uid_t uid)
{
return true;
}
static bool system_uid(uid_t uid)
{
return uid <= 2000;
}
#ifdef CONFIG_KSU_MANUAL_SU
// Manual SU
static int handle_manual_su(struct sk_buff *skb, struct nlmsghdr *nlh, void *msg_data)
{
struct netlink_manual_su *msg = (struct netlink_manual_su *)msg_data;
struct manual_su_request request;
int res;
pr_info("ksu_netlink: manual_su request, option=%d, uid=%d, pid=%d\n",
msg->option, msg->target_uid, msg->target_pid);
memset(&request, 0, sizeof(request));
request.target_uid = msg->target_uid;
request.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, sizeof(request.token_buffer));
}
res = ksu_handle_manual_su_request(msg->option, &request);
msg->hdr.result = res;
if (msg->option == MANUAL_SU_OP_GENERATE_TOKEN && res == 0) {
memcpy(msg->token_buffer, request.token_buffer, sizeof(msg->token_buffer));
}
return 0;
}
#endif
// Command handlers mapping table
static const struct ksu_netlink_cmd_handler ksu_netlink_handlers[] = {
#ifdef CONFIG_KSU_MANUAL_SU
{ .cmd = KSU_NETLINK_CMD_MANUAL_SU,
.msg_size = sizeof(struct netlink_manual_su),
.name = "MANUAL_SU",
.handler = handle_manual_su,
.perm_check = system_uid
},
#endif
{ .cmd = 0, .msg_size = NULL, .name = NULL, .handler = NULL, .perm_check = NULL }
};
static void ksu_netlink_recv_msg(struct sk_buff *skb)
{
struct nlmsghdr *nlh;
struct ksu_netlink_hdr *hdr;
struct sk_buff *skb_out;
const struct ksu_netlink_cmd_handler *handler_entry = NULL;
void *msg_data;
int res;
u32 pid;
uid_t sender_uid;
int i;
if (!skb) {
pr_err("ksu_netlink: received NULL skb\n");
return;
}
nlh = (struct nlmsghdr *)skb->data;
pid = nlh->nlmsg_pid;
sender_uid = NETLINK_CB(skb).creds.uid.val;
if (!nlh || nlh->nlmsg_len < NLMSG_HDRLEN + sizeof(struct ksu_netlink_hdr)) {
pr_err("ksu_netlink: invalid message size\n");
return;
}
hdr = (struct ksu_netlink_hdr *)nlmsg_data(nlh);
// Find command handler
for (i = 0; ksu_netlink_handlers[i].handler; i++) {
if (hdr->cmd == ksu_netlink_handlers[i].cmd) {
handler_entry = &ksu_netlink_handlers[i];
break;
}
}
if (!handler_entry) {
pr_warn("ksu_netlink: unknown command %d\n", hdr->cmd);
return;
}
// Validate message size
if (nlh->nlmsg_len < NLMSG_HDRLEN + handler_entry->msg_size) {
pr_err("ksu_netlink: invalid message size for cmd %s\n", handler_entry->name);
return;
}
// Permission check
if (handler_entry->perm_check && !handler_entry->perm_check(sender_uid)) {
pr_warn("ksu_netlink: permission denied for cmd %s from uid %d\n",
handler_entry->name, sender_uid);
hdr->result = -EPERM;
goto send_reply;
}
// Allocate response buffer (reuse input data for response)
msg_data = kmalloc(handler_entry->msg_size, GFP_KERNEL);
if (!msg_data) {
pr_err("ksu_netlink: failed to allocate message buffer\n");
return;
}
memcpy(msg_data, hdr, handler_entry->msg_size);
// Execute handler
res = handler_entry->handler(skb, nlh, msg_data);
if (res < 0) {
pr_err("ksu_netlink: handler for cmd %s failed: %d\n", handler_entry->name, res);
kfree(msg_data);
return;
}
send_reply:
// Send reply
skb_out = nlmsg_new(handler_entry->msg_size, GFP_KERNEL);
if (!skb_out) {
pr_err("ksu_netlink: failed to allocate reply skb\n");
if (msg_data)
kfree(msg_data);
return;
}
nlh = nlmsg_put(skb_out, 0, 0, NLMSG_DONE, handler_entry->msg_size, 0);
if (!nlh) {
pr_err("ksu_netlink: nlmsg_put failed\n");
kfree_skb(skb_out);
if (msg_data)
kfree(msg_data);
return;
}
NETLINK_CB(skb_out).dst_group = 0;
memcpy(nlmsg_data(nlh), msg_data ? msg_data : hdr, handler_entry->msg_size);
if (msg_data)
kfree(msg_data);
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 for cmd %s\n", handler_entry->name);
}
}
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");
}
}

View File

@@ -1,49 +0,0 @@
#ifndef __KSU_NETLINK_H
#define __KSU_NETLINK_H
#include <linux/netlink.h>
#include <linux/skbuff.h>
#include <net/sock.h>
#include <linux/net.h>
#define KSU_NETLINK_PROTOCOL 11
#define KSU_NETLINK_CMD_MANUAL_SU 100
struct ksu_netlink_hdr {
int cmd; // Command ID
int result; // Result code (output)
};
#ifdef CONFIG_KSU_MANUAL_SU
struct netlink_manual_su {
struct ksu_netlink_hdr hdr;
int option;
uid_t target_uid;
pid_t target_pid;
char token_buffer[33];
};
#endif
union ksu_netlink_msg {
struct ksu_netlink_hdr hdr;
#ifdef CONFIG_KSU_MANUAL_SU
struct netlink_manual_su manual_su;
#endif
};
typedef int (*ksu_netlink_handler_t)(struct sk_buff *skb, struct nlmsghdr *nlh, void *msg_data);
typedef bool (*ksu_netlink_perm_check_t)(uid_t uid);
struct ksu_netlink_cmd_handler {
int cmd;
size_t msg_size;
const char *name;
ksu_netlink_handler_t handler;
ksu_netlink_perm_check_t perm_check;
};
int ksu_netlink_init(void);
void ksu_netlink_exit(void);
#endif

View File

@@ -139,10 +139,6 @@ void apply_kernelsu_rules(void)
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid"); ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill"); ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
#ifdef CONFIG_KSU_MANUAL_SU
ksu_allow(db, "shell", "shell", "netlink_connector_socket", ALL);
#endif
// https://android-review.googlesource.com/c/platform/system/logging/+/3725346 // https://android-review.googlesource.com/c/platform/system/logging/+/3725346
ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr"); ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");

View File

@@ -520,6 +520,49 @@ static int do_enable_uid_scanner(void __user *arg)
return 0; return 0;
} }
#ifdef CONFIG_KSU_MANUAL_SU
static bool system_uid_check(void)
{
return current_uid().val <= 2000;
}
static int do_manual_su(void __user *arg)
{
struct ksu_manual_su_cmd cmd;
struct manual_su_request request;
int res;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("manual_su: copy_from_user failed\n");
return -EFAULT;
}
pr_info("manual_su request, option=%d, uid=%d, pid=%d\n",
cmd.option, cmd.target_uid, cmd.target_pid);
memset(&request, 0, sizeof(request));
request.target_uid = cmd.target_uid;
request.target_pid = cmd.target_pid;
if (cmd.option == MANUAL_SU_OP_GENERATE_TOKEN ||
cmd.option == MANUAL_SU_OP_ESCALATE) {
memcpy(request.token_buffer, cmd.token_buffer, sizeof(request.token_buffer));
}
res = ksu_handle_manual_su_request(cmd.option, &request);
if (cmd.option == MANUAL_SU_OP_GENERATE_TOKEN && res == 0) {
memcpy(cmd.token_buffer, request.token_buffer, sizeof(cmd.token_buffer));
if (copy_to_user(arg, &cmd, sizeof(cmd))) {
pr_err("manual_su: copy_to_user failed\n");
return -EFAULT;
}
}
return res;
}
#endif
// IOCTL handlers mapping table // IOCTL handlers mapping table
static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = { static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
{ .cmd = KSU_IOCTL_GRANT_ROOT, .name = "GRANT_ROOT", .handler = do_grant_root, .perm_check = allowed_for_su }, { .cmd = KSU_IOCTL_GRANT_ROOT, .name = "GRANT_ROOT", .handler = do_grant_root, .perm_check = allowed_for_su },
@@ -542,6 +585,9 @@ static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
{ .cmd = KSU_IOCTL_DYNAMIC_MANAGER, .name = "SET_DYNAMIC_MANAGER", .handler = do_dynamic_manager, .perm_check = manager_or_root}, { .cmd = KSU_IOCTL_DYNAMIC_MANAGER, .name = "SET_DYNAMIC_MANAGER", .handler = do_dynamic_manager, .perm_check = manager_or_root},
{ .cmd = KSU_IOCTL_GET_MANAGERS, .name = "GET_MANAGERS", .handler = do_get_managers, .perm_check = manager_or_root}, { .cmd = KSU_IOCTL_GET_MANAGERS, .name = "GET_MANAGERS", .handler = do_get_managers, .perm_check = manager_or_root},
{ .cmd = KSU_IOCTL_ENABLE_UID_SCANNER, .name = "SET_ENABLE_UID_SCANNER", .handler = do_enable_uid_scanner, .perm_check = manager_or_root}, { .cmd = KSU_IOCTL_ENABLE_UID_SCANNER, .name = "SET_ENABLE_UID_SCANNER", .handler = do_enable_uid_scanner, .perm_check = manager_or_root},
#ifdef CONFIG_KSU_MANUAL_SU
{ .cmd = KSU_IOCTL_MANUAL_SU, .name = "MANUAL_SU", .handler = do_manual_su, .perm_check = system_uid_check},
#endif
#ifdef CONFIG_KPM #ifdef CONFIG_KPM
{ .cmd = KSU_IOCTL_KPM, .name = "KPM_OPERATION", .handler = do_kpm, .perm_check = manager_or_root}, { .cmd = KSU_IOCTL_KPM, .name = "KPM_OPERATION", .handler = do_kpm, .perm_check = manager_or_root},
#endif #endif

View File

@@ -104,6 +104,15 @@ struct ksu_enable_uid_scanner_cmd {
void __user *status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS) void __user *status_ptr; // Input: pointer to store status (for UID_SCANNER_OP_GET_STATUS)
}; };
#ifdef CONFIG_KSU_MANUAL_SU
struct ksu_manual_su_cmd {
__u32 option; // Input: operation type (MANUAL_SU_OP_GENERATE_TOKEN, MANUAL_SU_OP_ESCALATE, MANUAL_SU_OP_ADD_PENDING)
__u32 target_uid; // Input: target UID
__u32 target_pid; // Input: target PID
char token_buffer[33]; // Input/Output: token buffer
};
#endif
// IOCTL command definitions // IOCTL command definitions
#define KSU_IOCTL_GRANT_ROOT _IOC(_IOC_NONE, 'K', 1, 0) #define KSU_IOCTL_GRANT_ROOT _IOC(_IOC_NONE, 'K', 1, 0)
#define KSU_IOCTL_GET_INFO _IOC(_IOC_READ, 'K', 2, 0) #define KSU_IOCTL_GET_INFO _IOC(_IOC_READ, 'K', 2, 0)
@@ -126,6 +135,9 @@ struct ksu_enable_uid_scanner_cmd {
#define KSU_IOCTL_DYNAMIC_MANAGER _IOC(_IOC_READ|_IOC_WRITE, 'K', 103, 0) #define KSU_IOCTL_DYNAMIC_MANAGER _IOC(_IOC_READ|_IOC_WRITE, 'K', 103, 0)
#define KSU_IOCTL_GET_MANAGERS _IOC(_IOC_READ|_IOC_WRITE, 'K', 104, 0) #define KSU_IOCTL_GET_MANAGERS _IOC(_IOC_READ|_IOC_WRITE, 'K', 104, 0)
#define KSU_IOCTL_ENABLE_UID_SCANNER _IOC(_IOC_READ|_IOC_WRITE, 'K', 105, 0) #define KSU_IOCTL_ENABLE_UID_SCANNER _IOC(_IOC_READ|_IOC_WRITE, 'K', 105, 0)
#ifdef CONFIG_KSU_MANUAL_SU
#define KSU_IOCTL_MANUAL_SU _IOC(_IOC_READ|_IOC_WRITE, 'K', 106, 0)
#endif
// IOCTL handler types // IOCTL handler types
typedef int (*ksu_ioctl_handler_t)(void __user *arg); typedef int (*ksu_ioctl_handler_t)(void __user *arg);