Files
SukiSU-Ultra/kernel/ksu_netlink.c
ShirkNeko 5b6c82db32 fix
2025-11-05 16:18:59 +08:00

208 lines
5.5 KiB
C

#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)
{
if (!current->mm || current->in_execve) {
return 0;
}
uid_t caller_uid = current_uid().val;
return caller_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");
}
}