kernel: Undo some changes
This commit is contained in:
518
kernel/ksud.c
518
kernel/ksud.c
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
#include <linux/rcupdate.h>
|
#include <linux/rcupdate.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/task_work.h>
|
#include <linux/task_work.h>
|
||||||
@@ -33,8 +32,6 @@
|
|||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksud.h"
|
#include "ksud.h"
|
||||||
|
|
||||||
#include "kernel_compat.h"
|
|
||||||
#include "selinux/selinux.h"
|
#include "selinux/selinux.h"
|
||||||
#include "syscall_hook_manager.h"
|
#include "syscall_hook_manager.h"
|
||||||
|
|
||||||
@@ -74,9 +71,9 @@ static struct work_struct stop_execve_hook_work;
|
|||||||
static struct work_struct stop_input_hook_work;
|
static struct work_struct stop_input_hook_work;
|
||||||
#else
|
#else
|
||||||
bool ksu_vfs_read_hook __read_mostly = true;
|
bool ksu_vfs_read_hook __read_mostly = true;
|
||||||
|
bool ksu_execveat_hook __read_mostly = true;
|
||||||
bool ksu_input_hook __read_mostly = true;
|
bool ksu_input_hook __read_mostly = true;
|
||||||
#endif
|
#endif
|
||||||
bool ksu_execveat_hook __read_mostly = true;
|
|
||||||
|
|
||||||
u32 ksu_file_sid;
|
u32 ksu_file_sid;
|
||||||
|
|
||||||
@@ -87,11 +84,11 @@ void on_post_fs_data(void)
|
|||||||
{
|
{
|
||||||
static bool done = false;
|
static bool done = false;
|
||||||
if (done) {
|
if (done) {
|
||||||
pr_info("%s already done\n", __func__);
|
pr_info("on_post_fs_data already done\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
done = true;
|
done = true;
|
||||||
pr_info("%s!\n", __func__);
|
pr_info("on_post_fs_data!\n");
|
||||||
ksu_load_allow_list();
|
ksu_load_allow_list();
|
||||||
pr_info("mark tif for running process\n");
|
pr_info("mark tif for running process\n");
|
||||||
ksu_mark_running_process();
|
ksu_mark_running_process();
|
||||||
@@ -106,18 +103,6 @@ void on_post_fs_data(void)
|
|||||||
pr_info("ksu_file sid: %d\n", ksu_file_sid);
|
pr_info("ksu_file sid: %d\n", ksu_file_sid);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct user_arg_ptr {
|
|
||||||
#ifdef CONFIG_COMPAT
|
|
||||||
bool is_compat;
|
|
||||||
#endif
|
|
||||||
union {
|
|
||||||
const char __user *const __user *native;
|
|
||||||
#ifdef CONFIG_COMPAT
|
|
||||||
const compat_uptr_t __user *compat;
|
|
||||||
#endif
|
|
||||||
} ptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern void ext4_unregister_sysfs(struct super_block *sb);
|
extern void ext4_unregister_sysfs(struct super_block *sb);
|
||||||
static void nuke_ext4_sysfs(void)
|
static void nuke_ext4_sysfs(void)
|
||||||
{
|
{
|
||||||
@@ -157,18 +142,95 @@ void on_boot_completed(void){
|
|||||||
ksu_mark_running_process();
|
ksu_mark_running_process();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define MAX_ARG_STRINGS 0x7FFFFFFF
|
||||||
|
struct user_arg_ptr {
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
bool is_compat;
|
||||||
|
#endif
|
||||||
|
union {
|
||||||
|
const char __user *const __user *native;
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
const compat_uptr_t __user *compat;
|
||||||
|
#endif
|
||||||
|
} ptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr)
|
||||||
|
{
|
||||||
|
const char __user *native;
|
||||||
|
|
||||||
|
#ifdef CONFIG_COMPAT
|
||||||
|
if (unlikely(argv.is_compat)) {
|
||||||
|
compat_uptr_t compat;
|
||||||
|
|
||||||
|
if (get_user(compat, argv.ptr.compat + nr))
|
||||||
|
return ERR_PTR(-EFAULT);
|
||||||
|
|
||||||
|
return compat_ptr(compat);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (get_user(native, argv.ptr.native + nr))
|
||||||
|
return ERR_PTR(-EFAULT);
|
||||||
|
|
||||||
|
return native;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* count() counts the number of strings in array ARGV.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Make sure old GCC compiler can use __maybe_unused,
|
||||||
|
* Test passed in 4.4.x ~ 4.9.x when use GCC.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int __maybe_unused count(struct user_arg_ptr argv, int max)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
if (argv.ptr.native != NULL) {
|
||||||
|
for (;;) {
|
||||||
|
const char __user *p = get_user_arg_ptr(argv, i);
|
||||||
|
|
||||||
|
if (!p)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (IS_ERR(p))
|
||||||
|
return -EFAULT;
|
||||||
|
|
||||||
|
if (i >= max)
|
||||||
|
return -E2BIG;
|
||||||
|
++i;
|
||||||
|
|
||||||
|
if (fatal_signal_pending(current))
|
||||||
|
return -ERESTARTNOHAND;
|
||||||
|
cond_resched();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
static void on_post_fs_data_cbfun(struct callback_head *cb)
|
static void on_post_fs_data_cbfun(struct callback_head *cb)
|
||||||
{
|
{
|
||||||
on_post_fs_data();
|
on_post_fs_data();
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct callback_head on_post_fs_data_cb = { .func = on_post_fs_data_cbfun };
|
static struct callback_head on_post_fs_data_cb = { .func =
|
||||||
|
on_post_fs_data_cbfun };
|
||||||
|
|
||||||
// since _ksud handler only uses argv and envp for comparisons
|
// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version
|
||||||
// this can probably work
|
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||||
// adapted from ksu_handle_execveat_ksud
|
struct user_arg_ptr *argv,
|
||||||
static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const char *envp, size_t envp_len)
|
struct user_arg_ptr *envp, int *flags)
|
||||||
{
|
{
|
||||||
|
#ifndef KSU_KPROBES_HOOK
|
||||||
|
if (!ksu_execveat_hook) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
struct filename *filename;
|
||||||
|
|
||||||
static const char app_process[] = "/system/bin/app_process";
|
static const char app_process[] = "/system/bin/app_process";
|
||||||
static bool first_app_process = true;
|
static bool first_app_process = true;
|
||||||
|
|
||||||
@@ -178,89 +240,101 @@ static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const c
|
|||||||
static const char old_system_init[] = "/init";
|
static const char old_system_init[] = "/init";
|
||||||
static bool init_second_stage_executed = false;
|
static bool init_second_stage_executed = false;
|
||||||
|
|
||||||
// return early when disabled
|
if (!filename_ptr)
|
||||||
if (!ksu_execveat_hook)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!filename)
|
filename = *filename_ptr;
|
||||||
|
if (IS_ERR(filename)) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// debug! remove me!
|
if (unlikely(!memcmp(filename->name, system_bin_init,
|
||||||
pr_info("%s: filename: %s argv1: %s envp_len: %zu\n", __func__, filename, argv1, envp_len);
|
sizeof(system_bin_init) - 1) &&
|
||||||
|
argv)) {
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
// /system/bin/init executed
|
||||||
const char *envp_n = envp;
|
int argc = count(*argv, MAX_ARG_STRINGS);
|
||||||
unsigned int envc = 1;
|
pr_info("/system/bin/init argc: %d\n", argc);
|
||||||
do {
|
if (argc > 1 && !init_second_stage_executed) {
|
||||||
pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n);
|
const char __user *p = get_user_arg_ptr(*argv, 1);
|
||||||
envp_n += strlen(envp_n) + 1;
|
if (p && !IS_ERR(p)) {
|
||||||
envc++;
|
char first_arg[16];
|
||||||
} while (envp_n < envp + 256);
|
strncpy_from_user_nofault(first_arg, p, sizeof(first_arg));
|
||||||
#endif
|
pr_info("/system/bin/init first arg: %s\n", first_arg);
|
||||||
|
if (!strcmp(first_arg, "second_stage")) {
|
||||||
if (init_second_stage_executed)
|
pr_info("/system/bin/init second_stage executed\n");
|
||||||
goto first_app_process;
|
apply_kernelsu_rules();
|
||||||
|
init_second_stage_executed = true;
|
||||||
// /system/bin/init with argv1
|
}
|
||||||
if (!init_second_stage_executed
|
} else {
|
||||||
&& (!memcmp(filename, system_bin_init, sizeof(system_bin_init) - 1))) {
|
pr_err("/system/bin/init parse args err!\n");
|
||||||
if (argv1 && !strcmp(argv1, "second_stage")) {
|
}
|
||||||
pr_info("%s: /system/bin/init second_stage executed\n", __func__);
|
}
|
||||||
apply_kernelsu_rules();
|
} else if (unlikely(!memcmp(filename->name, old_system_init,
|
||||||
init_second_stage_executed = true;
|
sizeof(old_system_init) - 1) &&
|
||||||
|
argv)) {
|
||||||
|
// /init executed
|
||||||
|
int argc = count(*argv, MAX_ARG_STRINGS);
|
||||||
|
pr_info("/init argc: %d\n", argc);
|
||||||
|
if (argc > 1 && !init_second_stage_executed) {
|
||||||
|
/* This applies to versions between Android 6 ~ 7 */
|
||||||
|
const char __user *p = get_user_arg_ptr(*argv, 1);
|
||||||
|
if (p && !IS_ERR(p)) {
|
||||||
|
char first_arg[16];
|
||||||
|
strncpy_from_user_nofault(first_arg, p, sizeof(first_arg));
|
||||||
|
pr_info("/init first arg: %s\n", first_arg);
|
||||||
|
if (!strcmp(first_arg, "--second-stage")) {
|
||||||
|
pr_info("/init second_stage executed\n");
|
||||||
|
apply_kernelsu_rules();
|
||||||
|
init_second_stage_executed = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
pr_err("/init parse args err!\n");
|
||||||
|
}
|
||||||
|
} else if (argc == 1 && !init_second_stage_executed && envp) {
|
||||||
|
/* This applies to versions between Android 8 ~ 9 */
|
||||||
|
int envc = count(*envp, MAX_ARG_STRINGS);
|
||||||
|
if (envc > 0) {
|
||||||
|
int n;
|
||||||
|
for (n = 1; n <= envc; n++) {
|
||||||
|
const char __user *p = get_user_arg_ptr(*envp, n);
|
||||||
|
if (!p || IS_ERR(p)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
char env[256];
|
||||||
|
// Reading environment variable strings from user space
|
||||||
|
if (strncpy_from_user_nofault(env, p, sizeof(env)) < 0)
|
||||||
|
continue;
|
||||||
|
// Parsing environment variable names and values
|
||||||
|
char *env_name = env;
|
||||||
|
char *env_value = strchr(env, '=');
|
||||||
|
if (env_value == NULL)
|
||||||
|
continue;
|
||||||
|
// Replace equal sign with string terminator
|
||||||
|
*env_value = '\0';
|
||||||
|
env_value++;
|
||||||
|
// Check if the environment variable name and value are matching
|
||||||
|
if (!strcmp(env_name, "INIT_SECOND_STAGE") &&
|
||||||
|
(!strcmp(env_value, "1") ||
|
||||||
|
!strcmp(env_value, "true"))) {
|
||||||
|
pr_info("/init second_stage executed\n");
|
||||||
|
apply_kernelsu_rules();
|
||||||
|
init_second_stage_executed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// /init with argv1
|
if (unlikely(first_app_process && !memcmp(filename->name, app_process,
|
||||||
if (!init_second_stage_executed
|
sizeof(app_process) - 1))) {
|
||||||
&& (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) {
|
|
||||||
if (argv1 && !strcmp(argv1, "--second-stage")) {
|
|
||||||
pr_info("%s: /init --second-stage executed\n", __func__);
|
|
||||||
apply_kernelsu_rules();
|
|
||||||
init_second_stage_executed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!envp || !envp_len)
|
|
||||||
goto first_app_process;
|
|
||||||
|
|
||||||
// /init without argv1/useless-argv1 but usable envp
|
|
||||||
// untested! TODO: test and debug me!
|
|
||||||
if (!init_second_stage_executed && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) {
|
|
||||||
|
|
||||||
// we hunt for "INIT_SECOND_STAGE"
|
|
||||||
const char *envp_n = envp;
|
|
||||||
unsigned int envc = 1;
|
|
||||||
do {
|
|
||||||
if (strstarts(envp_n, "INIT_SECOND_STAGE"))
|
|
||||||
break;
|
|
||||||
envp_n += strlen(envp_n) + 1;
|
|
||||||
envc++;
|
|
||||||
} while (envp_n < envp + envp_len);
|
|
||||||
pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n);
|
|
||||||
|
|
||||||
if (!strcmp(envp_n, "INIT_SECOND_STAGE=1")
|
|
||||||
|| !strcmp(envp_n, "INIT_SECOND_STAGE=true") ) {
|
|
||||||
pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__);
|
|
||||||
apply_kernelsu_rules();
|
|
||||||
init_second_stage_executed = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
first_app_process:
|
|
||||||
if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) {
|
|
||||||
first_app_process = false;
|
first_app_process = false;
|
||||||
pr_info("%s: exec app_process, /data prepared, second_stage: %d\n", __func__, init_second_stage_executed);
|
pr_info("exec app_process, /data prepared, second_stage: %d\n",
|
||||||
|
init_second_stage_executed);
|
||||||
struct task_struct *init_task;
|
struct task_struct *init_task;
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
init_task = rcu_dereference(current->parent);
|
init_task = rcu_dereference(current->real_parent);
|
||||||
if (init_task) {
|
if (init_task) {
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
|
||||||
task_work_add(init_task, &on_post_fs_data_cb, TWA_RESUME);
|
task_work_add(init_task, &on_post_fs_data_cb, TWA_RESUME);
|
||||||
#else
|
|
||||||
task_work_add(init_task, &on_post_fs_data_cb, true);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
@@ -270,81 +344,18 @@ first_app_process:
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_pre_ksud(const char *filename)
|
|
||||||
{
|
|
||||||
if (likely(!ksu_execveat_hook))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy)
|
|
||||||
// return 0;
|
|
||||||
if (likely(strcmp(filename, "/system/bin/init") && strcmp(filename, "/init")
|
|
||||||
&& !strstarts(filename, "/system/bin/app_process") ))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!current || !current->mm)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429
|
|
||||||
// unsigned long arg_start, arg_end, env_start, env_end;
|
|
||||||
unsigned long arg_start = current->mm->arg_start;
|
|
||||||
unsigned long arg_end = current->mm->arg_end;
|
|
||||||
unsigned long env_start = current->mm->env_start;
|
|
||||||
unsigned long env_end = current->mm->env_end;
|
|
||||||
|
|
||||||
size_t arg_len = arg_end - arg_start;
|
|
||||||
size_t envp_len = env_end - env_start;
|
|
||||||
|
|
||||||
if (arg_len <= 0 || envp_len <= 0) // this wont make sense, filter it
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
#define ARGV_MAX 32 // this is enough for argv1
|
|
||||||
#define ENVP_MAX 256 // this is enough for INIT_SECOND_STAGE
|
|
||||||
char args[ARGV_MAX];
|
|
||||||
size_t argv_copy_len = (arg_len > ARGV_MAX) ? ARGV_MAX : arg_len;
|
|
||||||
char envp[ENVP_MAX];
|
|
||||||
size_t envp_copy_len = (envp_len > ENVP_MAX) ? ENVP_MAX : envp_len;
|
|
||||||
|
|
||||||
// we cant use strncpy on here, else it will truncate once it sees \0
|
|
||||||
if (ksu_copy_from_user_retry(args, (void __user *)arg_start, argv_copy_len))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (ksu_copy_from_user_retry(envp, (void __user *)env_start, envp_copy_len))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
args[ARGV_MAX - 1] = '\0';
|
|
||||||
envp[ENVP_MAX - 1] = '\0';
|
|
||||||
|
|
||||||
// we only need argv1 !
|
|
||||||
// abuse strlen here since it only gets length up to \0
|
|
||||||
char *argv1 = args + strlen(args) + 1;
|
|
||||||
if (argv1 >= args + argv_copy_len) // out of bounds!
|
|
||||||
argv1 = "";
|
|
||||||
|
|
||||||
return ksu_handle_bprm_ksud(filename, argv1, envp, envp_copy_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
|
||||||
struct user_arg_ptr *argv, struct user_arg_ptr *envp,
|
|
||||||
int *flags)
|
|
||||||
{
|
|
||||||
// this is now handled via security_bprm_check
|
|
||||||
// we only keep this for the sake of old hooks.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *);
|
static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *);
|
||||||
static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *);
|
static ssize_t (*orig_read_iter)(struct kiocb *, struct iov_iter *);
|
||||||
static struct file_operations fops_proxy;
|
static struct file_operations fops_proxy;
|
||||||
static ssize_t read_count_append = 0;
|
static ssize_t read_count_append = 0;
|
||||||
|
|
||||||
static ssize_t read_proxy(struct file *file, char __user *buf, size_t count,
|
static ssize_t read_proxy(struct file *file, char __user *buf, size_t count,
|
||||||
loff_t *pos)
|
loff_t *pos)
|
||||||
{
|
{
|
||||||
bool first_read = file->f_pos == 0;
|
bool first_read = file->f_pos == 0;
|
||||||
ssize_t ret = orig_read(file, buf, count, pos);
|
ssize_t ret = orig_read(file, buf, count, pos);
|
||||||
if (first_read) {
|
if (first_read) {
|
||||||
pr_info("read_proxy append %ld + %ld\n", ret,
|
pr_info("read_proxy append %ld + %ld\n", ret, read_count_append);
|
||||||
read_count_append);
|
|
||||||
ret += read_count_append;
|
ret += read_count_append;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
@@ -355,15 +366,14 @@ static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to)
|
|||||||
bool first_read = iocb->ki_pos == 0;
|
bool first_read = iocb->ki_pos == 0;
|
||||||
ssize_t ret = orig_read_iter(iocb, to);
|
ssize_t ret = orig_read_iter(iocb, to);
|
||||||
if (first_read) {
|
if (first_read) {
|
||||||
pr_info("read_iter_proxy append %ld + %ld\n", ret,
|
pr_info("read_iter_proxy append %ld + %ld\n", ret, read_count_append);
|
||||||
read_count_append);
|
|
||||||
ret += read_count_append;
|
ret += read_count_append;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
static int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||||
size_t *count_ptr, loff_t **pos)
|
size_t *count_ptr, loff_t **pos)
|
||||||
{
|
{
|
||||||
#ifndef KSU_KPROBES_HOOK
|
#ifndef KSU_KPROBES_HOOK
|
||||||
if (!ksu_vfs_read_hook) {
|
if (!ksu_vfs_read_hook) {
|
||||||
@@ -421,7 +431,7 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
|||||||
size_t rc_count = strlen(KERNEL_SU_RC);
|
size_t rc_count = strlen(KERNEL_SU_RC);
|
||||||
|
|
||||||
pr_info("vfs_read: %s, comm: %s, count: %zu, rc_count: %zu\n", dpath,
|
pr_info("vfs_read: %s, comm: %s, count: %zu, rc_count: %zu\n", dpath,
|
||||||
current->comm, count, rc_count);
|
current->comm, count, rc_count);
|
||||||
|
|
||||||
if (count < rc_count) {
|
if (count < rc_count) {
|
||||||
pr_err("count: %zu < rc_count: %zu\n", count, rc_count);
|
pr_err("count: %zu < rc_count: %zu\n", count, rc_count);
|
||||||
@@ -456,8 +466,8 @@ int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr,
|
static int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr,
|
||||||
size_t *count_ptr)
|
size_t *count_ptr)
|
||||||
{
|
{
|
||||||
struct file *file = fget(fd);
|
struct file *file = fget(fd);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
@@ -476,7 +486,7 @@ static bool is_volumedown_enough(unsigned int count)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||||
int *value)
|
int *value)
|
||||||
{
|
{
|
||||||
#ifndef KSU_KPROBES_HOOK
|
#ifndef KSU_KPROBES_HOOK
|
||||||
if (!ksu_input_hook) {
|
if (!ksu_input_hook) {
|
||||||
@@ -521,123 +531,27 @@ bool ksu_is_safe_mode()
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef KSU_KPROBES_HOOK
|
#ifdef KSU_KPROBES_HOOK
|
||||||
|
|
||||||
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
asmlinkage int sys_execve(const char __user *filenamei,
|
|
||||||
const char __user *const __user *argv,
|
|
||||||
const char __user *const __user *envp, struct pt_regs *regs)
|
|
||||||
*/
|
|
||||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||||
const char __user *filename_user = (const char __user *)PT_REGS_PARM1(real_regs);
|
const char __user **filename_user =
|
||||||
const char __user *const __user *__argv = (const char __user *const __user *)PT_REGS_PARM2(real_regs);
|
(const char **)&PT_REGS_PARM1(real_regs);
|
||||||
const char __user *const __user *__envp = (const char __user *const __user *)PT_REGS_PARM3(real_regs);
|
const char __user *const __user *__argv =
|
||||||
|
(const char __user *const __user *)PT_REGS_PARM2(real_regs);
|
||||||
|
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||||
|
struct filename filename_in, *filename_p;
|
||||||
char path[32];
|
char path[32];
|
||||||
|
|
||||||
if (!filename_user)
|
if (!filename_user)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// filename stage
|
memset(path, 0, sizeof(path));
|
||||||
if (ksu_copy_from_user_retry(path, filename_user, sizeof(path)))
|
strncpy_from_user_nofault(path, *filename_user, 32);
|
||||||
return 0;
|
filename_in.name = path;
|
||||||
|
|
||||||
path[sizeof(path) - 1] = '\0';
|
filename_p = &filename_in;
|
||||||
|
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, NULL);
|
||||||
// not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy)
|
|
||||||
// we dont care !!
|
|
||||||
if (likely(strcmp(path, "/system/bin/init") && strcmp(path, "/init")
|
|
||||||
&& !strstarts(path, "/system/bin/app_process") ))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
// argv stage
|
|
||||||
char argv1[32] = {0};
|
|
||||||
// memzero_explicit(argv1, 32);
|
|
||||||
if (__argv) {
|
|
||||||
const char __user *arg1_user = NULL;
|
|
||||||
// grab argv[1] pointer
|
|
||||||
// this looks like
|
|
||||||
/*
|
|
||||||
* 0x1000 ./program << this is __argv
|
|
||||||
* 0x1001 -o
|
|
||||||
* 0x1002 arg
|
|
||||||
*/
|
|
||||||
if (ksu_copy_from_user_retry(&arg1_user, __argv + 1, sizeof(arg1_user)))
|
|
||||||
goto no_argv1; // copy argv[1] pointer fail, probably no argv1 !!
|
|
||||||
|
|
||||||
if (arg1_user)
|
|
||||||
ksu_copy_from_user_retry(argv1, arg1_user, sizeof(argv1));
|
|
||||||
}
|
|
||||||
|
|
||||||
no_argv1:
|
|
||||||
argv1[sizeof(argv1) - 1] = '\0';
|
|
||||||
|
|
||||||
// envp stage
|
|
||||||
#define ENVP_MAX 256
|
|
||||||
char envp[ENVP_MAX] = {0};
|
|
||||||
char *dst = envp;
|
|
||||||
size_t envp_len = 0;
|
|
||||||
int i = 0; // to track user pointer offset from __envp
|
|
||||||
|
|
||||||
// memzero_explicit(envp, ENVP_MAX);
|
|
||||||
|
|
||||||
if (__envp) {
|
|
||||||
do {
|
|
||||||
const char __user *env_entry_user = NULL;
|
|
||||||
// this is also like argv above
|
|
||||||
/*
|
|
||||||
* 0x1001 PATH=/bin
|
|
||||||
* 0x1002 VARIABLE=value
|
|
||||||
* 0x1002 some_more_env_var=1
|
|
||||||
*/
|
|
||||||
|
|
||||||
// check if pointer exists
|
|
||||||
if (ksu_copy_from_user_retry(&env_entry_user, __envp + i, sizeof(env_entry_user)))
|
|
||||||
break;
|
|
||||||
|
|
||||||
// check if no more env entry
|
|
||||||
if (!env_entry_user)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// probably redundant to while condition but ok
|
|
||||||
if (envp_len >= ENVP_MAX - 1)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// copy strings from env_entry_user pointer that we collected
|
|
||||||
// also break if failed
|
|
||||||
if (ksu_copy_from_user_retry(dst, env_entry_user, ENVP_MAX - envp_len))
|
|
||||||
break;
|
|
||||||
|
|
||||||
// get the length of that new copy above
|
|
||||||
// get lngth of dst as far as ENVP_MAX - current collected envp_len
|
|
||||||
size_t len = strnlen(dst, ENVP_MAX - envp_len);
|
|
||||||
if (envp_len + len + 1 > ENVP_MAX)
|
|
||||||
break; // if more than 255 bytes, bail
|
|
||||||
|
|
||||||
dst[len] = '\0';
|
|
||||||
// collect total number of copied strings
|
|
||||||
envp_len = envp_len + len + 1;
|
|
||||||
// increment dst address since we need to put something on next iter
|
|
||||||
dst = dst + len + 1;
|
|
||||||
// pointer walk, __envp + i
|
|
||||||
i++;
|
|
||||||
} while (envp_len < ENVP_MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
at this point, we shoul've collected envp from
|
|
||||||
* 0x1001 PATH=/bin
|
|
||||||
* 0x1002 VARIABLE=value
|
|
||||||
* 0x1002 some_more_env_var=1
|
|
||||||
to
|
|
||||||
* 0x1234 PATH=/bin\0VARIABLE=value\0some_more_env_var=1\0\0\0\0
|
|
||||||
*/
|
|
||||||
|
|
||||||
envp[ENVP_MAX - 1] = '\0';
|
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_DEBUG
|
|
||||||
pr_info("%s: filename: %s argv[1]:%s envp_len: %zu\n", __func__, path, argv1, envp_len);
|
|
||||||
#endif
|
|
||||||
return ksu_handle_bprm_ksud(path, argv1, envp, envp_len);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||||
@@ -651,7 +565,7 @@ static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int input_handle_event_handler_pre(struct kprobe *p,
|
static int input_handle_event_handler_pre(struct kprobe *p,
|
||||||
struct pt_regs *regs)
|
struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs);
|
unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs);
|
||||||
unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs);
|
unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs);
|
||||||
@@ -688,51 +602,6 @@ static void do_stop_input_hook(struct work_struct *work)
|
|||||||
{
|
{
|
||||||
unregister_kprobe(&input_event_kp);
|
unregister_kprobe(&input_event_kp);
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
static int ksu_execve_ksud_common(const char __user *filename_user,
|
|
||||||
struct user_arg_ptr *argv)
|
|
||||||
{
|
|
||||||
struct filename filename_in, *filename_p;
|
|
||||||
char path[32];
|
|
||||||
long len;
|
|
||||||
|
|
||||||
// return early if disabled.
|
|
||||||
if (!ksu_execveat_hook) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filename_user)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
len = ksu_strncpy_from_user_nofault(path, filename_user, 32);
|
|
||||||
if (len <= 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
path[sizeof(path) - 1] = '\0';
|
|
||||||
|
|
||||||
// this is because ksu_handle_execveat_ksud calls it filename->name
|
|
||||||
filename_in.name = path;
|
|
||||||
filename_p = &filename_in;
|
|
||||||
|
|
||||||
return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, argv, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
int __maybe_unused ksu_handle_execve_ksud(const char __user *filename_user,
|
|
||||||
const char __user *const __user *__argv)
|
|
||||||
{
|
|
||||||
struct user_arg_ptr argv = { .ptr.native = __argv };
|
|
||||||
return ksu_execve_ksud_common(filename_user, &argv);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CONFIG_COMPAT) && defined(CONFIG_64BIT)
|
|
||||||
int __maybe_unused ksu_handle_compat_execve_ksud(const char __user *filename_user,
|
|
||||||
const compat_uptr_t __user *__argv)
|
|
||||||
{
|
|
||||||
struct user_arg_ptr argv = { .ptr.compat = __argv };
|
|
||||||
return ksu_execve_ksud_common(filename_user, &argv);
|
|
||||||
}
|
|
||||||
#endif /* COMPAT & 64BIT */
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void stop_vfs_read_hook(void)
|
static void stop_vfs_read_hook(void)
|
||||||
@@ -752,23 +621,22 @@ static void stop_execve_hook(void)
|
|||||||
bool ret = schedule_work(&stop_execve_hook_work);
|
bool ret = schedule_work(&stop_execve_hook_work);
|
||||||
pr_info("unregister execve kprobe: %d!\n", ret);
|
pr_info("unregister execve kprobe: %d!\n", ret);
|
||||||
#else
|
#else
|
||||||
pr_info("stop execve_hook\n");
|
|
||||||
ksu_execveat_hook = false;
|
ksu_execveat_hook = false;
|
||||||
|
pr_info("stop execve_hook\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static void stop_input_hook(void)
|
static void stop_input_hook(void)
|
||||||
{
|
{
|
||||||
#ifdef KSU_KPROBES_HOOK
|
|
||||||
static bool input_hook_stopped = false;
|
static bool input_hook_stopped = false;
|
||||||
if (input_hook_stopped) {
|
if (input_hook_stopped) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
input_hook_stopped = true;
|
input_hook_stopped = true;
|
||||||
|
#ifdef KSU_KPROBES_HOOK
|
||||||
bool ret = schedule_work(&stop_input_hook_work);
|
bool ret = schedule_work(&stop_input_hook_work);
|
||||||
pr_info("unregister input kprobe: %d!\n", ret);
|
pr_info("unregister input kprobe: %d!\n", ret);
|
||||||
#else
|
#else
|
||||||
if (!ksu_input_hook) { return; }
|
|
||||||
ksu_input_hook = false;
|
ksu_input_hook = false;
|
||||||
pr_info("stop input_hook\n");
|
pr_info("stop input_hook\n");
|
||||||
#endif
|
#endif
|
||||||
@@ -805,4 +673,4 @@ void ksu_ksud_exit(void)
|
|||||||
#endif
|
#endif
|
||||||
is_boot_phase = false;
|
is_boot_phase = false;
|
||||||
volumedown_pressed_count = 0;
|
volumedown_pressed_count = 0;
|
||||||
}
|
}
|
||||||
@@ -18,7 +18,4 @@ extern u32 ksu_file_sid;
|
|||||||
extern bool ksu_module_mounted;
|
extern bool ksu_module_mounted;
|
||||||
extern bool ksu_boot_completed;
|
extern bool ksu_boot_completed;
|
||||||
|
|
||||||
extern bool ksu_execveat_hook __read_mostly;
|
|
||||||
extern int ksu_handle_pre_ksud(const char *filename);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
#include "selinux.h"
|
#include "selinux.h"
|
||||||
#include "sepolicy.h"
|
#include "sepolicy.h"
|
||||||
#include "ss/services.h"
|
#include "ss/services.h"
|
||||||
#include "linux/lsm_audit.h" // IWYU pragma: keep
|
#include "linux/lsm_audit.h"
|
||||||
#include "xfrm.h"
|
#include "xfrm.h"
|
||||||
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
@@ -141,7 +141,7 @@ void apply_kernelsu_rules(void)
|
|||||||
|
|
||||||
// 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");
|
||||||
|
|
||||||
mutex_unlock(&ksu_rules);
|
mutex_unlock(&ksu_rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,39 +157,16 @@ void apply_kernelsu_rules(void)
|
|||||||
#define CMD_TYPE_CHANGE 8
|
#define CMD_TYPE_CHANGE 8
|
||||||
#define CMD_GENFSCON 9
|
#define CMD_GENFSCON 9
|
||||||
|
|
||||||
// keep it!
|
|
||||||
extern bool ksu_is_compat __read_mostly;
|
|
||||||
|
|
||||||
// armv7l kernel compat
|
|
||||||
#ifdef CONFIG_64BIT
|
|
||||||
#define usize u64
|
|
||||||
#else
|
|
||||||
#define usize u32
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct sepol_data {
|
struct sepol_data {
|
||||||
u32 cmd;
|
uint32_t cmd;
|
||||||
u32 subcmd;
|
uint32_t subcmd;
|
||||||
usize field_sepol1;
|
uint64_t sepol1;
|
||||||
usize field_sepol2;
|
uint64_t sepol2;
|
||||||
usize field_sepol3;
|
uint64_t sepol3;
|
||||||
usize field_sepol4;
|
uint64_t sepol4;
|
||||||
usize field_sepol5;
|
uint64_t sepol5;
|
||||||
usize field_sepol6;
|
uint64_t sepol6;
|
||||||
usize field_sepol7;
|
uint64_t sepol7;
|
||||||
};
|
|
||||||
|
|
||||||
// ksud 32-bit on arm64 kernel
|
|
||||||
struct __maybe_unused sepol_data_compat {
|
|
||||||
u32 cmd;
|
|
||||||
u32 subcmd;
|
|
||||||
u32 field_sepol1;
|
|
||||||
u32 field_sepol2;
|
|
||||||
u32 field_sepol3;
|
|
||||||
u32 field_sepol4;
|
|
||||||
u32 field_sepol5;
|
|
||||||
u32 field_sepol6;
|
|
||||||
u32 field_sepol7;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
||||||
@@ -208,15 +185,18 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz,
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0))
|
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \
|
||||||
|
!defined(KSU_COMPAT_USE_SELINUX_STATE)
|
||||||
extern int avc_ss_reset(u32 seqno);
|
extern int avc_ss_reset(u32 seqno);
|
||||||
#else
|
#else
|
||||||
extern int avc_ss_reset(struct selinux_avc *avc, u32 seqno);
|
extern int avc_ss_reset(struct selinux_avc *avc, u32 seqno);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
// reset avc cache table, otherwise the new rules will not take effect if already denied
|
||||||
static void reset_avc_cache(void)
|
static void reset_avc_cache(void)
|
||||||
{
|
{
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \
|
||||||
!defined(KSU_COMPAT_USE_SELINUX_STATE)
|
!defined(KSU_COMPAT_USE_SELINUX_STATE)
|
||||||
avc_ss_reset(0);
|
avc_ss_reset(0);
|
||||||
selnl_notify_policyload(0);
|
selnl_notify_policyload(0);
|
||||||
@@ -242,76 +222,55 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
pr_info("SELinux permissive or disabled when handle policy!\n");
|
pr_info("SELinux permissive or disabled when handle policy!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 cmd, subcmd;
|
struct sepol_data data = { 0 };
|
||||||
char __user *sepol1, *sepol2, *sepol3, *sepol4, *sepol5, *sepol6, *sepol7;
|
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
||||||
|
pr_err("sepol: copy sepol_data failed.\n");
|
||||||
if (unlikely(ksu_is_compat)) {
|
return -EINVAL;
|
||||||
struct sepol_data_compat data_compat;
|
|
||||||
if (copy_from_user(&data_compat, arg4, sizeof(struct sepol_data_compat))) {
|
|
||||||
pr_err("sepol: copy sepol_data failed.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
pr_info("sepol: running in compat mode!\n");
|
|
||||||
sepol1 = compat_ptr(data_compat.field_sepol1);
|
|
||||||
sepol2 = compat_ptr(data_compat.field_sepol2);
|
|
||||||
sepol3 = compat_ptr(data_compat.field_sepol3);
|
|
||||||
sepol4 = compat_ptr(data_compat.field_sepol4);
|
|
||||||
sepol5 = compat_ptr(data_compat.field_sepol5);
|
|
||||||
sepol6 = compat_ptr(data_compat.field_sepol6);
|
|
||||||
sepol7 = compat_ptr(data_compat.field_sepol7);
|
|
||||||
cmd = data_compat.cmd;
|
|
||||||
subcmd = data_compat.subcmd;
|
|
||||||
} else {
|
|
||||||
struct sepol_data data;
|
|
||||||
if (copy_from_user(&data, arg4, sizeof(struct sepol_data))) {
|
|
||||||
pr_err("sepol: copy sepol_data failed.\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
sepol1 = data.field_sepol1;
|
|
||||||
sepol2 = data.field_sepol2;
|
|
||||||
sepol3 = data.field_sepol3;
|
|
||||||
sepol4 = data.field_sepol4;
|
|
||||||
sepol5 = data.field_sepol5;
|
|
||||||
sepol6 = data.field_sepol6;
|
|
||||||
sepol7 = data.field_sepol7;
|
|
||||||
cmd = data.cmd;
|
|
||||||
subcmd = data.subcmd;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
u32 cmd = data.cmd;
|
||||||
|
u32 subcmd = data.subcmd;
|
||||||
|
|
||||||
mutex_lock(&ksu_rules);
|
mutex_lock(&ksu_rules);
|
||||||
|
|
||||||
db = get_policydb();
|
db = get_policydb();
|
||||||
|
|
||||||
int ret = -EINVAL;
|
int ret = -EINVAL;
|
||||||
if (cmd == CMD_NORMAL_PERM) {
|
|
||||||
|
switch (cmd) {
|
||||||
|
case CMD_NORMAL_PERM: {
|
||||||
char src_buf[MAX_SEPOL_LEN];
|
char src_buf[MAX_SEPOL_LEN];
|
||||||
char tgt_buf[MAX_SEPOL_LEN];
|
char tgt_buf[MAX_SEPOL_LEN];
|
||||||
char cls_buf[MAX_SEPOL_LEN];
|
char cls_buf[MAX_SEPOL_LEN];
|
||||||
char perm_buf[MAX_SEPOL_LEN];
|
char perm_buf[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
char *s, *t, *c, *p;
|
char *s, *t, *c, *p;
|
||||||
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
|
if (get_object(src_buf, (void __user *)data.sepol1,
|
||||||
|
sizeof(src_buf), &s) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
|
if (get_object(tgt_buf, (void __user *)data.sepol2,
|
||||||
|
sizeof(tgt_buf), &t) < 0) {
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
|
if (get_object(cls_buf, (void __user *)data.sepol3,
|
||||||
|
sizeof(cls_buf), &c) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy cls failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get_object(perm_buf, sepol4, sizeof(perm_buf), &p) <
|
if (get_object(perm_buf, (void __user *)data.sepol4,
|
||||||
0) {
|
sizeof(perm_buf), &p) < 0) {
|
||||||
pr_err("sepol: copy perm failed.\n");
|
pr_err("sepol: copy perm failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
if (subcmd == 1) {
|
if (subcmd == 1) {
|
||||||
success = ksu_allow(db, s, t, c, p);
|
success = ksu_allow(db, s, t, c, p);
|
||||||
} else if (subcmd == 2) {
|
} else if (subcmd == 2) {
|
||||||
@@ -324,8 +283,9 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
}
|
}
|
||||||
ret = success ? 0 : -EINVAL;
|
ret = success ? 0 : -EINVAL;
|
||||||
|
break;
|
||||||
} else if (cmd == CMD_XPERM) {
|
}
|
||||||
|
case CMD_XPERM: {
|
||||||
char src_buf[MAX_SEPOL_LEN];
|
char src_buf[MAX_SEPOL_LEN];
|
||||||
char tgt_buf[MAX_SEPOL_LEN];
|
char tgt_buf[MAX_SEPOL_LEN];
|
||||||
char cls_buf[MAX_SEPOL_LEN];
|
char cls_buf[MAX_SEPOL_LEN];
|
||||||
@@ -335,25 +295,28 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
char perm_set[MAX_SEPOL_LEN];
|
char perm_set[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
char *s, *t, *c;
|
char *s, *t, *c;
|
||||||
if (get_object(src_buf, sepol1, sizeof(src_buf), &s) < 0) {
|
if (get_object(src_buf, (void __user *)data.sepol1,
|
||||||
|
sizeof(src_buf), &s) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (get_object(tgt_buf, sepol2, sizeof(tgt_buf), &t) < 0) {
|
if (get_object(tgt_buf, (void __user *)data.sepol2,
|
||||||
|
sizeof(tgt_buf), &t) < 0) {
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (get_object(cls_buf, sepol3, sizeof(cls_buf), &c) < 0) {
|
if (get_object(cls_buf, (void __user *)data.sepol3,
|
||||||
|
sizeof(cls_buf), &c) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy cls failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(operation, sepol4,
|
if (strncpy_from_user(operation, (void __user *)data.sepol4,
|
||||||
sizeof(operation)) < 0) {
|
sizeof(operation)) < 0) {
|
||||||
pr_err("sepol: copy operation failed.\n");
|
pr_err("sepol: copy operation failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(perm_set, sepol5, sizeof(perm_set)) <
|
if (strncpy_from_user(perm_set, (void __user *)data.sepol5,
|
||||||
0) {
|
sizeof(perm_set)) < 0) {
|
||||||
pr_err("sepol: copy perm_set failed.\n");
|
pr_err("sepol: copy perm_set failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -369,10 +332,13 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
pr_err("sepol: unknown subcmd: %d\n", subcmd);
|
||||||
}
|
}
|
||||||
ret = success ? 0 : -EINVAL;
|
ret = success ? 0 : -EINVAL;
|
||||||
} else if (cmd == CMD_TYPE_STATE) {
|
break;
|
||||||
|
}
|
||||||
|
case CMD_TYPE_STATE: {
|
||||||
char src[MAX_SEPOL_LEN];
|
char src[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
if (strncpy_from_user(src, (void __user *)data.sepol1,
|
||||||
|
sizeof(src)) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -387,16 +353,20 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
}
|
}
|
||||||
if (success)
|
if (success)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
break;
|
||||||
} else if (cmd == CMD_TYPE || cmd == CMD_TYPE_ATTR) {
|
}
|
||||||
|
case CMD_TYPE:
|
||||||
|
case CMD_TYPE_ATTR: {
|
||||||
char type[MAX_SEPOL_LEN];
|
char type[MAX_SEPOL_LEN];
|
||||||
char attr[MAX_SEPOL_LEN];
|
char attr[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(type, sepol1, sizeof(type)) < 0) {
|
if (strncpy_from_user(type, (void __user *)data.sepol1,
|
||||||
|
sizeof(type)) < 0) {
|
||||||
pr_err("sepol: copy type failed.\n");
|
pr_err("sepol: copy type failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(attr, sepol2, sizeof(attr)) < 0) {
|
if (strncpy_from_user(attr, (void __user *)data.sepol2,
|
||||||
|
sizeof(attr)) < 0) {
|
||||||
pr_err("sepol: copy attr failed.\n");
|
pr_err("sepol: copy attr failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -412,11 +382,13 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
break;
|
||||||
} else if (cmd == CMD_ATTR) {
|
}
|
||||||
|
case CMD_ATTR: {
|
||||||
char attr[MAX_SEPOL_LEN];
|
char attr[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(attr, sepol1, sizeof(attr)) < 0) {
|
if (strncpy_from_user(attr, (void __user *)data.sepol1,
|
||||||
|
sizeof(attr)) < 0) {
|
||||||
pr_err("sepol: copy attr failed.\n");
|
pr_err("sepol: copy attr failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -425,36 +397,41 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
break;
|
||||||
} else if (cmd == CMD_TYPE_TRANSITION) {
|
}
|
||||||
|
case CMD_TYPE_TRANSITION: {
|
||||||
char src[MAX_SEPOL_LEN];
|
char src[MAX_SEPOL_LEN];
|
||||||
char tgt[MAX_SEPOL_LEN];
|
char tgt[MAX_SEPOL_LEN];
|
||||||
char cls[MAX_SEPOL_LEN];
|
char cls[MAX_SEPOL_LEN];
|
||||||
char default_type[MAX_SEPOL_LEN];
|
char default_type[MAX_SEPOL_LEN];
|
||||||
char object[MAX_SEPOL_LEN];
|
char object[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
if (strncpy_from_user(src, (void __user *)data.sepol1,
|
||||||
|
sizeof(src)) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
|
if (strncpy_from_user(tgt, (void __user *)data.sepol2,
|
||||||
|
sizeof(tgt)) < 0) {
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
|
if (strncpy_from_user(cls, (void __user *)data.sepol3,
|
||||||
|
sizeof(cls)) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy cls failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(default_type, sepol4,
|
if (strncpy_from_user(default_type, (void __user *)data.sepol4,
|
||||||
sizeof(default_type)) < 0) {
|
sizeof(default_type)) < 0) {
|
||||||
pr_err("sepol: copy default_type failed.\n");
|
pr_err("sepol: copy default_type failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
char *real_object;
|
char *real_object;
|
||||||
if (sepol5 == NULL) {
|
if ((void __user *)data.sepol5 == NULL) {
|
||||||
real_object = NULL;
|
real_object = NULL;
|
||||||
} else {
|
} else {
|
||||||
if (strncpy_from_user(object, sepol5,
|
if (strncpy_from_user(object,
|
||||||
|
(void __user *)data.sepol5,
|
||||||
sizeof(object)) < 0) {
|
sizeof(object)) < 0) {
|
||||||
pr_err("sepol: copy object failed.\n");
|
pr_err("sepol: copy object failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -466,26 +443,30 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
default_type, real_object);
|
default_type, real_object);
|
||||||
if (success)
|
if (success)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
break;
|
||||||
} else if (cmd == CMD_TYPE_CHANGE) {
|
}
|
||||||
|
case CMD_TYPE_CHANGE: {
|
||||||
char src[MAX_SEPOL_LEN];
|
char src[MAX_SEPOL_LEN];
|
||||||
char tgt[MAX_SEPOL_LEN];
|
char tgt[MAX_SEPOL_LEN];
|
||||||
char cls[MAX_SEPOL_LEN];
|
char cls[MAX_SEPOL_LEN];
|
||||||
char default_type[MAX_SEPOL_LEN];
|
char default_type[MAX_SEPOL_LEN];
|
||||||
|
|
||||||
if (strncpy_from_user(src, sepol1, sizeof(src)) < 0) {
|
if (strncpy_from_user(src, (void __user *)data.sepol1,
|
||||||
|
sizeof(src)) < 0) {
|
||||||
pr_err("sepol: copy src failed.\n");
|
pr_err("sepol: copy src failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(tgt, sepol2, sizeof(tgt)) < 0) {
|
if (strncpy_from_user(tgt, (void __user *)data.sepol2,
|
||||||
|
sizeof(tgt)) < 0) {
|
||||||
pr_err("sepol: copy tgt failed.\n");
|
pr_err("sepol: copy tgt failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(cls, sepol3, sizeof(cls)) < 0) {
|
if (strncpy_from_user(cls, (void __user *)data.sepol3,
|
||||||
|
sizeof(cls)) < 0) {
|
||||||
pr_err("sepol: copy cls failed.\n");
|
pr_err("sepol: copy cls failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(default_type, sepol4,
|
if (strncpy_from_user(default_type, (void __user *)data.sepol4,
|
||||||
sizeof(default_type)) < 0) {
|
sizeof(default_type)) < 0) {
|
||||||
pr_err("sepol: copy default_type failed.\n");
|
pr_err("sepol: copy default_type failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
@@ -502,20 +483,24 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
}
|
}
|
||||||
if (success)
|
if (success)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
} else if (cmd == CMD_GENFSCON) {
|
break;
|
||||||
|
}
|
||||||
|
case CMD_GENFSCON: {
|
||||||
char name[MAX_SEPOL_LEN];
|
char name[MAX_SEPOL_LEN];
|
||||||
char path[MAX_SEPOL_LEN];
|
char path[MAX_SEPOL_LEN];
|
||||||
char context[MAX_SEPOL_LEN];
|
char context[MAX_SEPOL_LEN];
|
||||||
if (strncpy_from_user(name, sepol1, sizeof(name)) < 0) {
|
if (strncpy_from_user(name, (void __user *)data.sepol1,
|
||||||
|
sizeof(name)) < 0) {
|
||||||
pr_err("sepol: copy name failed.\n");
|
pr_err("sepol: copy name failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(path, sepol2, sizeof(path)) < 0) {
|
if (strncpy_from_user(path, (void __user *)data.sepol2,
|
||||||
|
sizeof(path)) < 0) {
|
||||||
pr_err("sepol: copy path failed.\n");
|
pr_err("sepol: copy path failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
if (strncpy_from_user(context, sepol3, sizeof(context)) <
|
if (strncpy_from_user(context, (void __user *)data.sepol3,
|
||||||
0) {
|
sizeof(context)) < 0) {
|
||||||
pr_err("sepol: copy context failed.\n");
|
pr_err("sepol: copy context failed.\n");
|
||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
@@ -525,8 +510,12 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
|||||||
goto exit;
|
goto exit;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
} else {
|
break;
|
||||||
|
}
|
||||||
|
default: {
|
||||||
pr_err("sepol: unknown cmd: %d\n", cmd);
|
pr_err("sepol: unknown cmd: %d\n", cmd);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exit:
|
exit:
|
||||||
@@ -537,4 +526,4 @@ exit:
|
|||||||
reset_avc_cache();
|
reset_avc_cache();
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -150,60 +150,6 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// the call from execve_handler_pre won't provided correct value for __never_use_argument, use them after fix execve_handler_pre, keeping them for consistence for manually patched code
|
|
||||||
int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
|
||||||
void *__never_use_argv, void *__never_use_envp,
|
|
||||||
int *__never_use_flags)
|
|
||||||
{
|
|
||||||
struct filename *filename;
|
|
||||||
const char sh[] = KSUD_PATH;
|
|
||||||
const char su[] = SU_PATH;
|
|
||||||
|
|
||||||
#ifdef KSU_MANUAL_HOOK
|
|
||||||
if (!ksu_su_compat_enabled) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (unlikely(!filename_ptr))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
filename = *filename_ptr;
|
|
||||||
if (IS_ERR(filename)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (likely(memcmp(filename->name, su, sizeof(su))))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
#if __SULOG_GATE
|
|
||||||
bool is_allowed = ksu_is_allow_uid_for_current(current_uid().val);
|
|
||||||
ksu_sulog_report_syscall(current_uid().val, NULL, "execve", filename->name);
|
|
||||||
|
|
||||||
if (!is_allowed) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ksu_sulog_report_su_attempt(current_uid().val, NULL, filename->name, is_allowed);
|
|
||||||
#else
|
|
||||||
if (!ksu_is_allow_uid_for_current(current_uid().val)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
pr_info("do_execveat_common su found\n");
|
|
||||||
memcpy((void *)filename->name, sh, sizeof(sh));
|
|
||||||
|
|
||||||
escape_with_root_profile();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
|
|
||||||
void *envp, int *flags)
|
|
||||||
{
|
|
||||||
return ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
||||||
void *__never_use_argv, void *__never_use_envp,
|
void *__never_use_argv, void *__never_use_envp,
|
||||||
int *__never_use_flags)
|
int *__never_use_flags)
|
||||||
|
|||||||
@@ -209,7 +209,6 @@ static inline bool check_syscall_fastpath(int nr)
|
|||||||
case __NR_execve:
|
case __NR_execve:
|
||||||
case __NR_setresuid:
|
case __NR_setresuid:
|
||||||
case __NR_faccessat2:
|
case __NR_faccessat2:
|
||||||
case __NR_execveat:
|
|
||||||
case __NR_clone:
|
case __NR_clone:
|
||||||
case __NR_clone3:
|
case __NR_clone3:
|
||||||
return true;
|
return true;
|
||||||
@@ -242,10 +241,6 @@ int ksu_handle_init_mark_tracker(int *fd, const char __user **filename_user,
|
|||||||
#include "manual_su.h"
|
#include "manual_su.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
|
||||||
bool ksu_is_compat __read_mostly = false;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LOOKUP_FOLLOW
|
#ifndef LOOKUP_FOLLOW
|
||||||
#define LOOKUP_FOLLOW 0x0001
|
#define LOOKUP_FOLLOW 0x0001
|
||||||
#endif
|
#endif
|
||||||
@@ -266,48 +261,6 @@ static inline void ksu_handle_inode_permission(struct pt_regs *regs)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void ksu_handle_bprm_check_security(struct pt_regs *regs, long id)
|
|
||||||
{
|
|
||||||
const char __user *filename;
|
|
||||||
char path_buf[256];
|
|
||||||
|
|
||||||
if (id == __NR_execve)
|
|
||||||
filename = (const char __user *)PT_REGS_PARM1(regs);
|
|
||||||
else /* __NR_execveat */
|
|
||||||
filename = (const char __user *)PT_REGS_PARM2(regs);
|
|
||||||
|
|
||||||
if (!ksu_execveat_hook)
|
|
||||||
return;
|
|
||||||
|
|
||||||
memset(path_buf, 0, sizeof(path_buf));
|
|
||||||
strncpy_from_user_nofault(path_buf, filename, sizeof(path_buf));
|
|
||||||
|
|
||||||
#ifdef CONFIG_COMPAT
|
|
||||||
static bool compat_check_done __read_mostly = false;
|
|
||||||
if (unlikely(!compat_check_done) &&
|
|
||||||
unlikely(!strcmp(path_buf, "/data/adb/ksud"))) {
|
|
||||||
char buf[4];
|
|
||||||
struct file *file = filp_open(path_buf, O_RDONLY, 0);
|
|
||||||
if (!IS_ERR(file)) {
|
|
||||||
loff_t pos = 0;
|
|
||||||
kernel_read(file, buf, 4, &pos);
|
|
||||||
if (!memcmp(buf, "\x7f\x45\x4c\x46", 4)) {
|
|
||||||
char elf_class;
|
|
||||||
pos = 4;
|
|
||||||
kernel_read(file, &elf_class, 1, &pos);
|
|
||||||
if (elf_class == 0x01)
|
|
||||||
ksu_is_compat = true;
|
|
||||||
pr_info("%s: %s ELF magic found! ksu_is_compat: %d\n",
|
|
||||||
__func__, path_buf, ksu_is_compat);
|
|
||||||
compat_check_done = true;
|
|
||||||
}
|
|
||||||
filp_close(file, NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
ksu_handle_pre_ksud(path_buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void ksu_handle_task_alloc(struct pt_regs *regs)
|
static inline void ksu_handle_task_alloc(struct pt_regs *regs)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_KSU_MANUAL_SU
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
@@ -371,10 +324,6 @@ static void ksu_sys_enter_handler(void *data, struct pt_regs *regs, long id)
|
|||||||
if (id == __NR_faccessat || id == __NR_faccessat2)
|
if (id == __NR_faccessat || id == __NR_faccessat2)
|
||||||
return ksu_handle_inode_permission(regs);
|
return ksu_handle_inode_permission(regs);
|
||||||
|
|
||||||
// Handle bprm_check_security via execve/execveat
|
|
||||||
if (id == __NR_execve || id == __NR_execveat)
|
|
||||||
return ksu_handle_bprm_check_security(regs, id);
|
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_MANUAL_SU
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
// Handle task_alloc via clone/fork
|
// Handle task_alloc via clone/fork
|
||||||
if (id == __NR_clone || id == __NR_clone3)
|
if (id == __NR_clone || id == __NR_clone3)
|
||||||
|
|||||||
@@ -15,8 +15,6 @@
|
|||||||
#define DEVPTS_SUPER_MAGIC 0x1cd1
|
#define DEVPTS_SUPER_MAGIC 0x1cd1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern bool ksu_is_compat __read_mostly;
|
|
||||||
|
|
||||||
extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c
|
extern int __ksu_handle_devpts(struct inode *inode); // sucompat.c
|
||||||
|
|
||||||
// Hook manager initialization and cleanup
|
// Hook manager initialization and cleanup
|
||||||
|
|||||||
Reference in New Issue
Block a user