From 247f7d4aeede66b40b63957f1a84266180b3da06 Mon Sep 17 00:00:00 2001 From: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com> Date: Wed, 5 Nov 2025 15:41:14 +0800 Subject: [PATCH] Kernel: Enable processes with corresponding UIDs to utilise netlink, and optimise netlink functionality --- kernel/ksu_netlink.c | 161 +++++++++++++++++++++++++++++++---------- kernel/ksu_netlink.h | 32 ++++++-- kernel/selinux/rules.c | 2 + 3 files changed, 152 insertions(+), 43 deletions(-) diff --git a/kernel/ksu_netlink.c b/kernel/ksu_netlink.c index 230416bf..bd9e9388 100644 --- a/kernel/ksu_netlink.c +++ b/kernel/ksu_netlink.c @@ -1,26 +1,90 @@ #include #include -#include -#include -#include #include +#include "kernel_compat.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 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; +} + +// Manual SU +static int handle_manual_su(struct sk_buff *skb, struct nlmsghdr *nlh, void *msg_data) +{ + struct ksu_netlink_manual_su *msg = (struct ksu_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; +} + +// Command handlers mapping table +static const struct ksu_netlink_cmd_handler ksu_netlink_handlers[] = { + { + .cmd = KSU_NETLINK_CMD_MANUAL_SU, + .msg_size = sizeof(struct ksu_netlink_manual_su), + .name = "MANUAL_SU", + .handler = handle_manual_su, + .perm_check = system_uid + }, + { .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL } +}; static void ksu_netlink_recv_msg(struct sk_buff *skb) { struct nlmsghdr *nlh; - struct ksu_netlink_msg *msg; - struct ksu_netlink_msg reply; + struct ksu_netlink_hdr *hdr; struct sk_buff *skb_out; - int msg_size; + 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"); @@ -29,67 +93,88 @@ static void ksu_netlink_recv_msg(struct sk_buff *skb) 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_msg)) { + if (!nlh || nlh->nlmsg_len < NLMSG_HDRLEN + sizeof(struct ksu_netlink_hdr)) { pr_err("ksu_netlink: invalid message size\n"); return; } - msg = (struct ksu_netlink_msg *)nlmsg_data(nlh); + hdr = (struct ksu_netlink_hdr *)nlmsg_data(nlh); - if (msg->cmd != KSU_NETLINK_CMD_MANUAL_SU) { - pr_warn("ksu_netlink: unknown command %d\n", msg->cmd); + // 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; } - 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); + // 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; } - 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); + // 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; } - msg_size = sizeof(struct ksu_netlink_msg); - skb_out = nlmsg_new(msg_size, GFP_KERNEL); + // 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, msg_size, 0); + 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), &reply, sizeof(reply)); + 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 successfully\n"); + pr_info("ksu_netlink: reply sent for cmd %s\n", handler_entry->name); } } diff --git a/kernel/ksu_netlink.h b/kernel/ksu_netlink.h index bbfa2c3f..ff1f7db1 100644 --- a/kernel/ksu_netlink.h +++ b/kernel/ksu_netlink.h @@ -4,17 +4,39 @@ #include #include #include +#include -#define KSU_NETLINK_PROTOCOL 2 -#define KSU_NETLINK_CMD_MANUAL_SU 50 +#define KSU_NETLINK_PROTOCOL 11 -struct ksu_netlink_msg { - int cmd; +#define KSU_NETLINK_CMD_MANUAL_SU 100 + +struct ksu_netlink_hdr { + int cmd; // Command ID + int result; // Result code (output) +}; + +struct ksu_netlink_manual_su { + struct ksu_netlink_hdr hdr; int option; uid_t target_uid; pid_t target_pid; char token_buffer[33]; - int result; +}; + +union ksu_netlink_msg { + struct ksu_netlink_hdr hdr; + struct ksu_netlink_manual_su manual_su; +}; + +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); diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index c8db9fc3..62df8caa 100644 --- a/kernel/selinux/rules.c +++ b/kernel/selinux/rules.c @@ -127,6 +127,8 @@ void apply_kernelsu_rules() ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid"); ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill"); + ksu_allow(db, "shell", "shell", "netlink_connector_socket", ALL); + // https://android-review.googlesource.com/c/platform/system/logging/+/3725346 ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");