kernel: Migrate manual_su to ioctl
This commit is contained in:
@@ -13,7 +13,6 @@ kernelsu-objs += embed_ksud.o
|
||||
kernelsu-objs += kernel_compat.o
|
||||
kernelsu-objs += throne_comm.o
|
||||
kernelsu-objs += sulog.o
|
||||
kernelsu-objs += ksu_netlink.o
|
||||
|
||||
ifeq ($(CONFIG_KSU_MANUAL_SU), y)
|
||||
ccflags-y += -DCONFIG_KSU_MANUAL_SU
|
||||
|
||||
@@ -790,9 +790,6 @@ void __init ksu_core_init(void)
|
||||
if (ksu_register_feature_handler(&kernel_umount_handler)) {
|
||||
pr_err("Failed to register umount feature handler\n");
|
||||
}
|
||||
#ifdef CONFIG_KSU_MANUAL_SU
|
||||
ksu_netlink_init();
|
||||
#endif
|
||||
}
|
||||
|
||||
void ksu_core_exit(void)
|
||||
@@ -807,7 +804,4 @@ void ksu_core_exit(void)
|
||||
ksu_kprobe_exit();
|
||||
#endif
|
||||
ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT);
|
||||
#ifdef CONFIG_KSU_MANUAL_SU
|
||||
ksu_netlink_exit();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,203 +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");
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -127,10 +127,6 @@ void apply_kernelsu_rules()
|
||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
||||
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
|
||||
ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");
|
||||
|
||||
|
||||
@@ -481,7 +481,7 @@ static int do_enable_uid_scanner(void __user *arg)
|
||||
bool enabled = cmd.enabled;
|
||||
|
||||
if (enabled == ksu_uid_scanner_enabled) {
|
||||
pr_info("enable_uid_scanner: no need to change, already %s\n",
|
||||
pr_info("enable_uid_scanner: no need to change, already %s\n",
|
||||
enabled ? "enabled" : "disabled");
|
||||
break;
|
||||
}
|
||||
@@ -520,6 +520,49 @@ static int do_enable_uid_scanner(void __user *arg)
|
||||
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
|
||||
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 },
|
||||
@@ -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_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},
|
||||
#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
|
||||
{ .cmd = KSU_IOCTL_KPM, .name = "KPM_OPERATION", .handler = do_kpm, .perm_check = manager_or_root},
|
||||
#endif
|
||||
|
||||
@@ -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)
|
||||
};
|
||||
|
||||
#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
|
||||
#define KSU_IOCTL_GRANT_ROOT _IOC(_IOC_NONE, 'K', 1, 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_GET_MANAGERS _IOC(_IOC_READ|_IOC_WRITE, 'K', 104, 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
|
||||
typedef int (*ksu_ioctl_handler_t)(void __user *arg);
|
||||
|
||||
Reference in New Issue
Block a user