diff --git a/kernel/Makefile b/kernel/Makefile index 0a080d22..c2db7e1e 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -6,6 +6,7 @@ kernelsu-objs += sucompat.o kernelsu-objs += pkg_observer.o kernelsu-objs += core_hook.o kernelsu-objs += supercalls.o +kernelsu-objs += feature.o kernelsu-objs += ksud.o kernelsu-objs += embed_ksud.o kernelsu-objs += kernel_compat.o diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 862c43db..00f0f91b 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -50,6 +50,7 @@ #include "allowlist.h" #include "arch.h" #include "core_hook.h" +#include "feature.h" #include "klog.h" // IWYU pragma: keep #include "ksu.h" #include "ksud.h" @@ -178,6 +179,29 @@ struct ksu_umount_work { struct mnt_namespace *mnt_ns; }; +static bool ksu_kernel_umount_enabled = true; + +static int kernel_umount_feature_get(u64 *value) +{ + *value = ksu_kernel_umount_enabled ? 1 : 0; + return 0; +} + +static int kernel_umount_feature_set(u64 value) +{ + bool enable = value != 0; + ksu_kernel_umount_enabled = enable; + pr_info("kernel_umount: set to %d\n", enable); + return 0; +} + +static const struct ksu_feature_handler kernel_umount_handler = { + .feature_id = KSU_FEATURE_KERNEL_UMOUNT, + .name = "kernel_umount", + .get_handler = kernel_umount_feature_get, + .set_handler = kernel_umount_feature_set, +}; + static inline bool is_allow_su(void) { if (is_manager()) { @@ -1007,8 +1031,8 @@ static void do_umount_work(struct work_struct *work) try_umount("/apex/com.android.art/bin/dex2oat64", false, MNT_DETACH, uid); try_umount("/apex/com.android.art/bin/dex2oat32", false, MNT_DETACH, uid); + // fixme: dec refcount current->nsproxy->mnt_ns = old_mnt_ns; - put_mnt_ns(umount_work->mnt_ns); kfree(umount_work); } @@ -1052,6 +1076,10 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) return 0; } + if (!ksu_kernel_umount_enabled) { + return 0; + } + // We only interest in process spwaned by zygote if (!susfs_is_sid_equal(old->security, susfs_zygote_sid)) { return 0; @@ -1165,6 +1193,10 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) return 0; } + if (!ksu_kernel_umount_enabled) { + return 0; + } + if (!ksu_uid_should_umount(new_uid.val)) { return 0; } else { @@ -1199,8 +1231,8 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) return 0; } + // fixme: inc refcount umount_work->mnt_ns = current->nsproxy->mnt_ns; - get_mnt_ns(umount_work->mnt_ns); INIT_WORK(&umount_work->work, do_umount_work); diff --git a/kernel/feature.c b/kernel/feature.c new file mode 100644 index 00000000..001f387f --- /dev/null +++ b/kernel/feature.c @@ -0,0 +1,188 @@ +#include "feature.h" +#include "klog.h" // IWYU pragma: keep + +#include +#include + +static struct ksu_feature_handler *feature_handlers[KSU_FEATURE_MAX]; + +static DEFINE_MUTEX(feature_mutex); + +int ksu_register_feature_handler(const struct ksu_feature_handler *handler) +{ + int ret = 0; + + if (!handler) { + pr_err("feature: register handler is NULL\n"); + return -EINVAL; + } + + if (handler->feature_id >= KSU_FEATURE_MAX) { + pr_err("feature: invalid feature_id %u\n", handler->feature_id); + return -EINVAL; + } + + if (!handler->get_handler && !handler->set_handler) { + pr_err("feature: no handler provided for feature %u\n", handler->feature_id); + return -EINVAL; + } + + mutex_lock(&feature_mutex); + + if (feature_handlers[handler->feature_id]) { + pr_warn("feature: handler for %u already registered, overwriting\n", + handler->feature_id); + } + + feature_handlers[handler->feature_id] = kmalloc(sizeof(struct ksu_feature_handler), GFP_KERNEL); + if (!feature_handlers[handler->feature_id]) { + pr_err("feature: failed to allocate handler for %u\n", handler->feature_id); + ret = -ENOMEM; + goto out; + } + + memcpy(feature_handlers[handler->feature_id], handler, sizeof(struct ksu_feature_handler)); + + pr_info("feature: registered handler for %s (id=%u)\n", + handler->name ? handler->name : "unknown", handler->feature_id); + +out: + mutex_unlock(&feature_mutex); + return ret; +} + +int ksu_unregister_feature_handler(u32 feature_id) +{ + int ret = 0; + + if (feature_id >= KSU_FEATURE_MAX) { + pr_err("feature: invalid feature_id %u\n", feature_id); + return -EINVAL; + } + + mutex_lock(&feature_mutex); + + if (!feature_handlers[feature_id]) { + pr_warn("feature: no handler registered for %u\n", feature_id); + ret = -ENOENT; + goto out; + } + + kfree(feature_handlers[feature_id]); + feature_handlers[feature_id] = NULL; + + pr_info("feature: unregistered handler for id=%u\n", feature_id); + +out: + mutex_unlock(&feature_mutex); + return ret; +} + +int ksu_get_feature(u32 feature_id, u64 *value, bool *supported) +{ + int ret = 0; + struct ksu_feature_handler *handler; + + if (feature_id >= KSU_FEATURE_MAX) { + pr_err("feature: invalid feature_id %u\n", feature_id); + return -EINVAL; + } + + if (!value || !supported) { + pr_err("feature: invalid parameters\n"); + return -EINVAL; + } + + mutex_lock(&feature_mutex); + + handler = feature_handlers[feature_id]; + + if (!handler) { + *supported = false; + *value = 0; + pr_debug("feature: feature %u not supported\n", feature_id); + goto out; + } + + *supported = true; + + if (!handler->get_handler) { + pr_warn("feature: no get_handler for feature %u\n", feature_id); + ret = -EOPNOTSUPP; + goto out; + } + + ret = handler->get_handler(value); + if (ret) { + pr_err("feature: get_handler for %u failed: %d\n", feature_id, ret); + } + +out: + mutex_unlock(&feature_mutex); + return ret; +} + +int ksu_set_feature(u32 feature_id, u64 value) +{ + int ret = 0; + struct ksu_feature_handler *handler; + + if (feature_id >= KSU_FEATURE_MAX) { + pr_err("feature: invalid feature_id %u\n", feature_id); + return -EINVAL; + } + + mutex_lock(&feature_mutex); + + handler = feature_handlers[feature_id]; + + if (!handler) { + pr_err("feature: feature %u not registered\n", feature_id); + ret = -EOPNOTSUPP; + goto out; + } + + if (!handler->set_handler) { + pr_warn("feature: no set_handler for feature %u\n", feature_id); + ret = -EOPNOTSUPP; + goto out; + } + + ret = handler->set_handler(value); + if (ret) { + pr_err("feature: set_handler for %u failed: %d\n", feature_id, ret); + } + +out: + mutex_unlock(&feature_mutex); + return ret; +} + +void ksu_feature_init(void) +{ + int i; + + for (i = 0; i < KSU_FEATURE_MAX; i++) { + feature_handlers[i] = NULL; + } + + pr_info("feature: feature management initialized\n"); +} + +void ksu_feature_exit(void) +{ + int i; + + mutex_lock(&feature_mutex); + + for (i = 0; i < KSU_FEATURE_MAX; i++) { + if (feature_handlers[i]) { + kfree(feature_handlers[i]); + feature_handlers[i] = NULL; + } + } + + mutex_unlock(&feature_mutex); + + pr_info("feature: feature management cleaned up\n"); +} \ No newline at end of file diff --git a/kernel/feature.h b/kernel/feature.h new file mode 100644 index 00000000..f1a3239b --- /dev/null +++ b/kernel/feature.h @@ -0,0 +1,35 @@ +#ifndef __KSU_H_FEATURE +#define __KSU_H_FEATURE + +#include + +enum ksu_feature_id { + KSU_FEATURE_SU_COMPAT = 0, + KSU_FEATURE_KERNEL_UMOUNT = 1, + + KSU_FEATURE_MAX +}; + +typedef int (*ksu_feature_get_t)(u64 *value); +typedef int (*ksu_feature_set_t)(u64 value); + +struct ksu_feature_handler { + u32 feature_id; + const char *name; + ksu_feature_get_t get_handler; + ksu_feature_set_t set_handler; +}; + +int ksu_register_feature_handler(const struct ksu_feature_handler *handler); + +int ksu_unregister_feature_handler(u32 feature_id); + +int ksu_get_feature(u32 feature_id, u64 *value, bool *supported); + +int ksu_set_feature(u32 feature_id, u64 value); + +void ksu_feature_init(void); + +void ksu_feature_exit(void); + +#endif // __KSU_H_FEATURE \ No newline at end of file diff --git a/kernel/ksu.c b/kernel/ksu.c index 34b1e6d5..51b8a364 100644 --- a/kernel/ksu.c +++ b/kernel/ksu.c @@ -8,6 +8,7 @@ #include "allowlist.h" #include "arch.h" #include "core_hook.h" +#include "feature.h" #include "klog.h" // IWYU pragma: keep #include "ksu.h" #include "throne_tracker.h" @@ -59,6 +60,7 @@ extern void ksu_sucompat_init(void); extern void ksu_sucompat_exit(void); extern void ksu_ksud_init(void); extern void ksu_ksud_exit(void); +extern void ksu_supercalls_init(); #ifdef CONFIG_KSU_TRACEPOINT_HOOK extern void ksu_trace_register(); extern void ksu_trace_unregister(); @@ -90,6 +92,10 @@ int __init kernelsu_init(void) susfs_init(); #endif + ksu_feature_init(); + + ksu_supercalls_init(); + ksu_core_init(); ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0); @@ -144,6 +150,7 @@ void kernelsu_exit(void) ksu_sucompat_exit(); ksu_core_exit(); + ksu_feature_exit(); } module_init(kernelsu_init); diff --git a/kernel/sucompat.c b/kernel/sucompat.c index acc64e77..d76ec089 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -21,6 +21,7 @@ #include "objsec.h" #include "allowlist.h" #include "arch.h" +#include "feature.h" #include "klog.h" // IWYU pragma: keep #include "ksud.h" #include "kernel_compat.h" @@ -31,6 +32,45 @@ extern void escape_to_root(void); +void ksu_sucompat_enable(); +void ksu_sucompat_disable(); + +bool ksu_su_compat_enabled = true; + +static int su_compat_feature_get(u64 *value) +{ + *value = ksu_su_compat_enabled ? 1 : 0; + return 0; +} + +static int su_compat_feature_set(u64 value) +{ + bool enable = value != 0; + + if (enable == ksu_su_compat_enabled) { + pr_info("su_compat: no need to change\n"); + return 0; + } + + if (enable) { + ksu_sucompat_enable(); + } else { + ksu_sucompat_disable(); + } + + ksu_su_compat_enabled = enable; + pr_info("su_compat: set to %d\n", enable); + + return 0; +} + +static const struct ksu_feature_handler su_compat_handler = { + .feature_id = KSU_FEATURE_SU_COMPAT, + .name = "su_compat", + .get_handler = su_compat_feature_get, + .set_handler = su_compat_feature_set, +}; + static const char sh_path[] = "/system/bin/sh"; static const char ksud_path[] = KSUD_PATH; static const char su[] = SU_PATH; @@ -397,7 +437,7 @@ static void destroy_kprobe(struct kprobe **kp_ptr) #endif // sucompat: permited process can execute 'su' to gain root access. -void ksu_sucompat_init(void) +void ksu_sucompat_enable(void) { #ifdef CONFIG_KSU_KPROBES_HOOK su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre); @@ -412,7 +452,7 @@ void ksu_sucompat_init(void) #endif } -void ksu_sucompat_exit(void) +void ksu_sucompat_disable(void) { #ifdef CONFIG_KSU_KPROBES_HOOK int i; @@ -425,6 +465,25 @@ void ksu_sucompat_exit(void) #endif } +// sucompat: permited process can execute 'su' to gain root access. +void ksu_sucompat_init() +{ + if (ksu_register_feature_handler(&su_compat_handler)) { + pr_err("Failed to register su_compat feature handler\n"); + } + if (ksu_su_compat_enabled) { + ksu_sucompat_enable(); + } +} + +void ksu_sucompat_exit() +{ + if (ksu_su_compat_enabled) { + ksu_sucompat_disable(); + } + ksu_unregister_feature_handler(KSU_FEATURE_SU_COMPAT); +} + #ifdef CONFIG_KSU_SUSFS_SUS_SU extern bool ksu_su_compat_enabled; bool ksu_devpts_hook = false; diff --git a/kernel/supercalls.c b/kernel/supercalls.c index 189b1dc1..77b5218c 100644 --- a/kernel/supercalls.c +++ b/kernel/supercalls.c @@ -11,6 +11,7 @@ #include #include "allowlist.h" +#include "feature.h" #include "klog.h" // IWYU pragma: keep #include "ksud.h" #include "manager.h" @@ -32,29 +33,25 @@ extern int handle_sepolicy(unsigned long arg3, void __user *arg4); extern void ksu_sucompat_init(void); extern void ksu_sucompat_exit(void); -// Forward declaration for anon_ksu_fops -static const struct file_operations anon_ksu_fops; - -bool ksu_su_compat_enabled = true; bool ksu_uid_scanner_enabled = false; // Permission check functions -bool perm_check_manager(void) +bool only_manager(void) { return is_manager(); } -bool perm_check_root(void) +bool only_root(void) { return current_uid().val == 0; } -bool perm_check_basic(void) +bool manager_or_root(void) { return current_uid().val == 0 || is_manager(); } -bool perm_check_all(void) +bool always_allow(void) { return true; // No permission check } @@ -101,6 +98,7 @@ static int do_get_info(void __user *arg) if (is_manager()) { cmd.flags |= 0x2; } + cmd.features = KSU_FEATURE_MAX; if (copy_to_user(arg, &cmd, sizeof(cmd))) { pr_err("get_version: copy_to_user failed\n"); @@ -318,42 +316,49 @@ static int do_set_app_profile(void __user *arg) return 0; } -static int do_is_su_enabled(void __user *arg) +static int do_get_feature(void __user *arg) { - struct ksu_is_su_enabled_cmd cmd; + struct ksu_get_feature_cmd cmd; + bool supported; + int ret; - cmd.enabled = ksu_su_compat_enabled; + if (copy_from_user(&cmd, arg, sizeof(cmd))) { + pr_err("get_feature: copy_from_user failed\n"); + return -EFAULT; + } + + ret = ksu_get_feature(cmd.feature_id, &cmd.value, &supported); + cmd.supported = supported ? 1 : 0; + + if (ret && supported) { + pr_err("get_feature: failed for feature %u: %d\n", cmd.feature_id, ret); + return ret; + } if (copy_to_user(arg, &cmd, sizeof(cmd))) { - pr_err("is_su_enabled: copy_to_user failed\n"); + pr_err("get_feature: copy_to_user failed\n"); return -EFAULT; } return 0; } -static int do_enable_su(void __user *arg) +static int do_set_feature(void __user *arg) { - struct ksu_enable_su_cmd cmd; + struct ksu_set_feature_cmd cmd; + int ret; if (copy_from_user(&cmd, arg, sizeof(cmd))) { - pr_err("enable_su: copy_from_user failed\n"); + pr_err("set_feature: copy_from_user failed\n"); return -EFAULT; } - if (cmd.enable == ksu_su_compat_enabled) { - pr_info("enable_su: no need to change\n"); - return 0; + ret = ksu_set_feature(cmd.feature_id, cmd.value); + if (ret) { + pr_err("set_feature: failed for feature %u: %d\n", cmd.feature_id, ret); + return ret; } - if (cmd.enable) { - ksu_sucompat_init(); - } else { - ksu_sucompat_exit(); - } - - ksu_su_compat_enabled = cmd.enable; - return 0; } @@ -517,80 +522,79 @@ static int do_enable_uid_scanner(void __user *arg) // IOCTL handlers mapping table static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = { - { .cmd = KSU_IOCTL_GRANT_ROOT, .handler = do_grant_root, .perm_check = perm_check_basic, .name = "do_grant_root"}, - { .cmd = KSU_IOCTL_GET_INFO, .handler = do_get_info, .perm_check = perm_check_all, .name = "do_get_info"}, - { .cmd = KSU_IOCTL_REPORT_EVENT, .handler = do_report_event, .perm_check = perm_check_root, .name = "do_report_event"}, - { .cmd = KSU_IOCTL_SET_SEPOLICY, .handler = do_set_sepolicy, .perm_check = perm_check_root, .name = "do_set_sepolicy"}, - { .cmd = KSU_IOCTL_CHECK_SAFEMODE, .handler = do_check_safemode, .perm_check = perm_check_all, .name = "do_check_safemode"}, - { .cmd = KSU_IOCTL_GET_ALLOW_LIST, .handler = do_get_allow_list, .perm_check = perm_check_basic, .name = "do_get_allow_list"}, - { .cmd = KSU_IOCTL_GET_DENY_LIST, .handler = do_get_deny_list, .perm_check = perm_check_basic, .name = "do_get_deny_list"}, - { .cmd = KSU_IOCTL_UID_GRANTED_ROOT, .handler = do_uid_granted_root, .perm_check = perm_check_basic, .name = "do_uid_granted_root"}, - { .cmd = KSU_IOCTL_UID_SHOULD_UMOUNT, .handler = do_uid_should_umount, .perm_check = perm_check_basic, .name = "do_uid_should_umount"}, - { .cmd = KSU_IOCTL_GET_MANAGER_UID, .handler = do_get_manager_uid, .perm_check = perm_check_basic, .name = "do_get_manager_uid"}, - { .cmd = KSU_IOCTL_GET_APP_PROFILE, .handler = do_get_app_profile, .perm_check = perm_check_manager, .name = "do_get_app_profile"}, - { .cmd = KSU_IOCTL_SET_APP_PROFILE, .handler = do_set_app_profile, .perm_check = perm_check_manager, .name = "do_set_app_profile"}, - { .cmd = KSU_IOCTL_IS_SU_ENABLED, .handler = do_is_su_enabled, .perm_check = perm_check_manager, .name = "do_is_su_enabled"}, - { .cmd = KSU_IOCTL_ENABLE_SU, .handler = do_enable_su, .perm_check = perm_check_manager, .name = "do_enable_su"}, - { .cmd = KSU_IOCTL_GET_FULL_VERSION, .handler = do_get_full_version, .perm_check = perm_check_manager, .name = "do_get_full_version"}, - { .cmd = KSU_IOCTL_HOOK_TYPE, .handler = do_get_hook_type, .perm_check = perm_check_basic, .name = "do_get_hook_type"}, - { .cmd = KSU_IOCTL_ENABLE_KPM, .handler = do_enable_kpm, .perm_check = perm_check_basic, .name = "do_enable_kpm"}, - { .cmd = KSU_IOCTL_DYNAMIC_MANAGER, .handler = do_dynamic_manager, .perm_check = perm_check_basic, .name = "do_dynamic_manager"}, - { .cmd = KSU_IOCTL_GET_MANAGERS, .handler = do_get_managers, .perm_check = perm_check_basic, .name = "do_get_managers"}, - { .cmd = KSU_IOCTL_ENABLE_UID_SCANNER, .handler = do_enable_uid_scanner, .perm_check = perm_check_basic, .name = "do_enable_uid_scanner"}, - { .cmd = 0, .handler = NULL, .perm_check = NULL, .name = NULL} // Sentinel + { .cmd = KSU_IOCTL_GRANT_ROOT, .name = "GRANT_ROOT", .handler = do_grant_root, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_INFO, .name = "GET_INFO", .handler = do_get_info, .perm_check = always_allow }, + { .cmd = KSU_IOCTL_REPORT_EVENT, .name = "REPORT_EVENT", .handler = do_report_event, .perm_check = only_root }, + { .cmd = KSU_IOCTL_SET_SEPOLICY, .name = "SET_SEPOLICY", .handler = do_set_sepolicy, .perm_check = only_root }, + { .cmd = KSU_IOCTL_CHECK_SAFEMODE, .name = "CHECK_SAFEMODE", .handler = do_check_safemode, .perm_check = always_allow }, + { .cmd = KSU_IOCTL_GET_ALLOW_LIST, .name = "GET_ALLOW_LIST", .handler = do_get_allow_list, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_DENY_LIST, .name = "GET_DENY_LIST", .handler = do_get_deny_list, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_UID_GRANTED_ROOT, .name = "UID_GRANTED_ROOT", .handler = do_uid_granted_root, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_UID_SHOULD_UMOUNT, .name = "UID_SHOULD_UMOUNT", .handler = do_uid_should_umount, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_MANAGER_UID, .name = "GET_MANAGER_UID", .handler = do_get_manager_uid, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_APP_PROFILE, .name = "GET_APP_PROFILE", .handler = do_get_app_profile, .perm_check = only_manager }, + { .cmd = KSU_IOCTL_SET_APP_PROFILE, .name = "SET_APP_PROFILE", .handler = do_set_app_profile, .perm_check = only_manager }, + { .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow}, + { .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root}, + { .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .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_ENABLE_UID_SCANNER, .name = "SET_ENABLE_UID_SCANNER", .handler = do_enable_uid_scanner, .perm_check = manager_or_root}, + { .cmd = 0, .name = NULL, .handler = NULL, .perm_check = NULL} // Sentine }; +void ksu_supercalls_init(void) +{ + int i; + + pr_info("KernelSU IOCTL Commands:\n"); + for (i = 0; ksu_ioctl_handlers[i].handler; i++) { + pr_info(" %-18s = 0x%08x\n", ksu_ioctl_handlers[i].name, ksu_ioctl_handlers[i].cmd); + } +} + +static inline void ksu_ioctl_audit(unsigned int cmd, const char *cmd_name, uid_t uid, int ret) +{ +#if __SULOG_GATE + const char *result = (ret == 0) ? "SUCCESS" : + (ret == -EPERM) ? "DENIED" : "FAILED"; + ksu_sulog_report_syscall(uid, NULL, cmd_name, result); +#endif +} + // IOCTL dispatcher static long anon_ksu_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { void __user *argp = (void __user *)arg; int i; - const char *cmd_name = "unknown"; - int ret = -ENOTTY; #ifdef CONFIG_KSU_DEBUG pr_info("ksu ioctl: cmd=0x%x from uid=%d\n", cmd, current_uid().val); #endif - // Determine the command name based on the cmd value for (i = 0; ksu_ioctl_handlers[i].handler; i++) { if (cmd == ksu_ioctl_handlers[i].cmd) { - cmd_name = ksu_ioctl_handlers[i].name; - break; + // Check permission first + if (ksu_ioctl_handlers[i].perm_check && + !ksu_ioctl_handlers[i].perm_check()) { + pr_warn("ksu ioctl: permission denied for cmd=0x%x uid=%d\n", + cmd, current_uid().val); + ksu_ioctl_audit(cmd, ksu_ioctl_handlers[i].name, + current_uid().val, -EPERM); + return -EPERM; + } + // Execute handler + int ret = ksu_ioctl_handlers[i].handler(argp); + ksu_ioctl_audit(cmd, ksu_ioctl_handlers[i].name, + current_uid().val, ret); + return ret; } } - // Check permission first - if (ksu_ioctl_handlers[i].perm_check && - !ksu_ioctl_handlers[i].perm_check()) { - pr_warn("ksu ioctl: permission denied for cmd=0x%x uid=%d\n", - cmd, current_uid().val); -#if __SULOG_GATE - ksu_sulog_report_syscall(current_uid().val, NULL, cmd_name, "DENIED"); -#endif - return -EPERM; - } - - // Execute handler - ret = ksu_ioctl_handlers[i].handler(argp); - - // Log the result of the ioctl command - if (ret == 0) { -#if __SULOG_GATE - ksu_sulog_report_syscall(current_uid().val, NULL, cmd_name, "SUCCESS"); -#endif - } else { -#if __SULOG_GATE - ksu_sulog_report_syscall(current_uid().val, NULL, cmd_name, "FAILED"); -#endif - } - - if (ksu_ioctl_handlers[i].handler == NULL) { - pr_warn("ksu ioctl: unsupported command 0x%x\n", cmd); - ret = -ENOTTY; - } - - return ret; + pr_warn("ksu ioctl: unsupported command 0x%x\n", cmd); + return -ENOTTY; } // File release handler @@ -632,7 +636,7 @@ int ksu_install_fd(void) // Install fd fd_install(fd, filp); - #if __SULOG_GATE +#if __SULOG_GATE ksu_sulog_report_permission_check(current_uid().val, current->comm, fd >= 0); #endif diff --git a/kernel/supercalls.h b/kernel/supercalls.h index 7b4b14cb..023d6d3d 100644 --- a/kernel/supercalls.h +++ b/kernel/supercalls.h @@ -18,6 +18,7 @@ struct ksu_become_daemon_cmd { struct ksu_get_info_cmd { __u32 version; // Output: KERNEL_SU_VERSION __u32 flags; // Output: flags (bit 0: MODULE mode) + __u32 features; // Output: max feature ID supported }; struct ksu_report_event_cmd { @@ -61,12 +62,15 @@ struct ksu_set_app_profile_cmd { struct app_profile profile; // Input: app profile structure }; -struct ksu_is_su_enabled_cmd { - __u8 enabled; // Output: true if su compat enabled +struct ksu_get_feature_cmd { + __u32 feature_id; // Input: feature ID (enum ksu_feature_id) + __u64 value; // Output: feature value/state + __u8 supported; // Output: true if feature is supported, false otherwise }; -struct ksu_enable_su_cmd { - __u8 enable; // Input: true to enable, false to disable +struct ksu_set_feature_cmd { + __u32 feature_id; // Input: feature ID (enum ksu_feature_id) + __u64 value; // Input: feature value/state to set }; // Other command structures @@ -97,44 +101,38 @@ struct ksu_enable_uid_scanner_cmd { }; // IOCTL command definitions -#define KSU_IOCTL_GRANT_ROOT _IO('K', 1) -#define KSU_IOCTL_GET_INFO _IOR('K', 2, struct ksu_get_info_cmd) -#define KSU_IOCTL_REPORT_EVENT _IOW('K', 3, struct ksu_report_event_cmd) -#define KSU_IOCTL_SET_SEPOLICY _IOWR('K', 4, struct ksu_set_sepolicy_cmd) -#define KSU_IOCTL_CHECK_SAFEMODE _IOR('K', 5, struct ksu_check_safemode_cmd) -#define KSU_IOCTL_GET_ALLOW_LIST _IOWR('K', 6, struct ksu_get_allow_list_cmd) -#define KSU_IOCTL_GET_DENY_LIST _IOWR('K', 7, struct ksu_get_allow_list_cmd) -#define KSU_IOCTL_UID_GRANTED_ROOT _IOWR('K', 8, struct ksu_uid_granted_root_cmd) -#define KSU_IOCTL_UID_SHOULD_UMOUNT _IOWR('K', 9, struct ksu_uid_should_umount_cmd) -#define KSU_IOCTL_GET_MANAGER_UID _IOR('K', 10, struct ksu_get_manager_uid_cmd) -#define KSU_IOCTL_GET_APP_PROFILE _IOWR('K', 11, struct ksu_get_app_profile_cmd) -#define KSU_IOCTL_SET_APP_PROFILE _IOW('K', 12, struct ksu_set_app_profile_cmd) -#define KSU_IOCTL_IS_SU_ENABLED _IOR('K', 13, struct ksu_is_su_enabled_cmd) -#define KSU_IOCTL_ENABLE_SU _IOW('K', 14, struct ksu_enable_su_cmd) +#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_REPORT_EVENT _IOC(_IOC_WRITE, 'K', 3, 0) +#define KSU_IOCTL_SET_SEPOLICY _IOC(_IOC_READ|_IOC_WRITE, 'K', 4, 0) +#define KSU_IOCTL_CHECK_SAFEMODE _IOC(_IOC_READ, 'K', 5, 0) +#define KSU_IOCTL_GET_ALLOW_LIST _IOC(_IOC_READ|_IOC_WRITE, 'K', 6, 0) +#define KSU_IOCTL_GET_DENY_LIST _IOC(_IOC_READ|_IOC_WRITE, 'K', 7, 0) +#define KSU_IOCTL_UID_GRANTED_ROOT _IOC(_IOC_READ|_IOC_WRITE, 'K', 8, 0) +#define KSU_IOCTL_UID_SHOULD_UMOUNT _IOC(_IOC_READ|_IOC_WRITE, 'K', 9, 0) +#define KSU_IOCTL_GET_MANAGER_UID _IOC(_IOC_READ, 'K', 10, 0) +#define KSU_IOCTL_GET_APP_PROFILE _IOC(_IOC_READ|_IOC_WRITE, 'K', 11, 0) +#define KSU_IOCTL_SET_APP_PROFILE _IOC(_IOC_WRITE, 'K', 12, 0) +#define KSU_IOCTL_GET_FEATURE _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0) +#define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0) // Other IOCTL command definitions -#define KSU_IOCTL_GET_FULL_VERSION _IOR('K', 100, struct ksu_get_full_version_cmd) -#define KSU_IOCTL_HOOK_TYPE _IOR('K', 101, struct ksu_hook_type_cmd) -#define KSU_IOCTL_ENABLE_KPM _IOR('K', 102, struct ksu_enable_kpm_cmd) -#define KSU_IOCTL_DYNAMIC_MANAGER _IOWR('K', 103, struct ksu_dynamic_manager_cmd) -#define KSU_IOCTL_GET_MANAGERS _IOWR('K', 104, struct ksu_get_managers_cmd) -#define KSU_IOCTL_ENABLE_UID_SCANNER _IOWR('K', 105, struct ksu_enable_uid_scanner_cmd) +#define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0) +#define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0) +#define KSU_IOCTL_ENABLE_KPM _IOC(_IOC_READ, 'K', 102, 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_ENABLE_UID_SCANNER _IOC(_IOC_READ|_IOC_WRITE, 'K', 105, 0) // IOCTL handler types typedef int (*ksu_ioctl_handler_t)(void __user *arg); typedef bool (*ksu_perm_check_t)(void); -// Permission check functions -bool perm_check_manager(void); -bool perm_check_root(void); -bool perm_check_basic(void); -bool perm_check_all(void); - // IOCTL command mapping struct ksu_ioctl_cmd_map { unsigned int cmd; + const char *name; ksu_ioctl_handler_t handler; ksu_perm_check_t perm_check; // Permission check function - const char *name; // Command name for logging }; // Install KSU fd to current process