Kernel: Implement sulog for enhanced logging of SU attempts and permissions
This commit is contained in:
@@ -9,6 +9,7 @@ kernelsu-objs += ksud.o
|
|||||||
kernelsu-objs += embed_ksud.o
|
kernelsu-objs += embed_ksud.o
|
||||||
kernelsu-objs += kernel_compat.o
|
kernelsu-objs += kernel_compat.o
|
||||||
kernelsu-objs += throne_comm.o
|
kernelsu-objs += throne_comm.o
|
||||||
|
kernelsu-objs += sulog.o
|
||||||
ifeq ($(CONFIG_KSU_MANUAL_SU), y)
|
ifeq ($(CONFIG_KSU_MANUAL_SU), y)
|
||||||
kernelsu-objs += manual_su.o
|
kernelsu-objs += manual_su.o
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -47,6 +47,7 @@
|
|||||||
#include "throne_comm.h"
|
#include "throne_comm.h"
|
||||||
#include "kernel_compat.h"
|
#include "kernel_compat.h"
|
||||||
#include "dynamic_manager.h"
|
#include "dynamic_manager.h"
|
||||||
|
#include "sulog.h"
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_MANUAL_SU
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
#include "manual_su.h"
|
#include "manual_su.h"
|
||||||
@@ -148,6 +149,7 @@ static void disable_seccomp()
|
|||||||
void escape_to_root(void)
|
void escape_to_root(void)
|
||||||
{
|
{
|
||||||
struct cred *cred;
|
struct cred *cred;
|
||||||
|
uid_t original_uid = current_euid().val;
|
||||||
|
|
||||||
cred = prepare_creds();
|
cred = prepare_creds();
|
||||||
if (!cred) {
|
if (!cred) {
|
||||||
@@ -157,6 +159,7 @@ void escape_to_root(void)
|
|||||||
|
|
||||||
if (cred->euid.val == 0) {
|
if (cred->euid.val == 0) {
|
||||||
pr_warn("Already root, don't escape!\n");
|
pr_warn("Already root, don't escape!\n");
|
||||||
|
ksu_sulog_report_su_grant(original_uid, NULL, "escape_to_root_failed");
|
||||||
abort_creds(cred);
|
abort_creds(cred);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -200,6 +203,8 @@ void escape_to_root(void)
|
|||||||
spin_unlock_irq(¤t->sighand->siglock);
|
spin_unlock_irq(¤t->sighand->siglock);
|
||||||
|
|
||||||
setup_selinux(profile->selinux_domain);
|
setup_selinux(profile->selinux_domain);
|
||||||
|
|
||||||
|
ksu_sulog_report_su_grant(original_uid, NULL, "escape_to_root");
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_MANUAL_SU
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
@@ -242,6 +247,7 @@ void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid)
|
|||||||
if (!target_task) {
|
if (!target_task) {
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
pr_err("cmd_su: target task not found for PID: %d\n", target_pid);
|
pr_err("cmd_su: target task not found for PID: %d\n", target_pid);
|
||||||
|
ksu_sulog_report_su_grant(target_uid, "cmd_su", "target_not_found");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
get_task_struct(target_task);
|
get_task_struct(target_task);
|
||||||
@@ -256,6 +262,7 @@ void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid)
|
|||||||
newcreds = prepare_kernel_cred(target_task);
|
newcreds = prepare_kernel_cred(target_task);
|
||||||
if (newcreds == NULL) {
|
if (newcreds == NULL) {
|
||||||
pr_err("cmd_su: failed to allocate new cred for PID: %d\n", target_pid);
|
pr_err("cmd_su: failed to allocate new cred for PID: %d\n", target_pid);
|
||||||
|
ksu_sulog_report_su_grant(target_uid, "cmd_su", "cred_alloc_failed");
|
||||||
put_task_struct(target_task);
|
put_task_struct(target_task);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -306,6 +313,7 @@ void escape_to_root_for_cmd_su(uid_t target_uid, pid_t target_pid)
|
|||||||
|
|
||||||
put_task_struct(target_task);
|
put_task_struct(target_task);
|
||||||
|
|
||||||
|
ksu_sulog_report_su_grant(target_uid, "cmd_su", "manual_escalation");
|
||||||
pr_info("cmd_su: privilege escalation completed for UID: %d, PID: %d\n", target_uid, target_pid);
|
pr_info("cmd_su: privilege escalation completed for UID: %d, PID: %d\n", target_uid, target_pid);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -474,7 +482,10 @@ skip_check:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (arg2 == CMD_GRANT_ROOT) {
|
if (arg2 == CMD_GRANT_ROOT) {
|
||||||
if (is_allow_su()) {
|
bool is_allowed = is_allow_su();
|
||||||
|
ksu_sulog_report_permission_check(current_uid().val, current->comm, is_allowed);
|
||||||
|
|
||||||
|
if (is_allowed) {
|
||||||
pr_info("allow root for: %d\n", current_uid().val);
|
pr_info("allow root for: %d\n", current_uid().val);
|
||||||
escape_to_root();
|
escape_to_root();
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||||
@@ -578,6 +589,7 @@ skip_check:
|
|||||||
post_fs_data_lock = true;
|
post_fs_data_lock = true;
|
||||||
pr_info("post-fs-data triggered\n");
|
pr_info("post-fs-data triggered\n");
|
||||||
on_post_fs_data();
|
on_post_fs_data();
|
||||||
|
ksu_sulog_init();
|
||||||
// Initialize UID scanner if enabled
|
// Initialize UID scanner if enabled
|
||||||
init_uid_scanner();
|
init_uid_scanner();
|
||||||
// Initializing Dynamic Signatures
|
// Initializing Dynamic Signatures
|
||||||
@@ -813,6 +825,9 @@ skip_check:
|
|||||||
|
|
||||||
// todo: validate the params
|
// todo: validate the params
|
||||||
if (ksu_set_app_profile(&profile, true)) {
|
if (ksu_set_app_profile(&profile, true)) {
|
||||||
|
ksu_sulog_report_manager_operation("SET_APP_PROFILE",
|
||||||
|
current_uid().val, profile.current_uid);
|
||||||
|
|
||||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||||
}
|
}
|
||||||
@@ -1376,6 +1391,7 @@ void ksu_core_exit(void)
|
|||||||
{
|
{
|
||||||
ksu_uid_exit();
|
ksu_uid_exit();
|
||||||
ksu_throne_comm_exit();
|
ksu_throne_comm_exit();
|
||||||
|
ksu_sulog_exit();
|
||||||
#ifdef CONFIG_KPROBE
|
#ifdef CONFIG_KPROBE
|
||||||
pr_info("ksu_core_kprobe_exit\n");
|
pr_info("ksu_core_kprobe_exit\n");
|
||||||
// we dont use this now
|
// we dont use this now
|
||||||
|
|||||||
@@ -92,3 +92,40 @@ long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr,
|
|||||||
{
|
{
|
||||||
return strncpy_from_user_nofault(dst, unsafe_addr, count);
|
return strncpy_from_user_nofault(dst, unsafe_addr, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int ksu_vfs_unlink(struct inode *dir, struct dentry *dentry)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
|
||||||
|
struct inode *delegated_inode = NULL;
|
||||||
|
return vfs_unlink(&nop_mnt_idmap, dir, dentry, &delegated_inode);
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||||
|
struct inode *delegated_inode = NULL;
|
||||||
|
return vfs_unlink(&init_user_ns, dir, dentry, &delegated_inode);
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
|
struct inode *delegated_inode = NULL;
|
||||||
|
return vfs_unlink(dir, dentry, &delegated_inode);
|
||||||
|
#else
|
||||||
|
return vfs_unlink(dir, dentry);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
|
struct inode *new_dir, struct dentry *new_dentry)
|
||||||
|
{
|
||||||
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
|
||||||
|
struct renamedata rd = {
|
||||||
|
.old_dir = old_dir,
|
||||||
|
.old_dentry = old_dentry,
|
||||||
|
.new_dir = new_dir,
|
||||||
|
.new_dentry = new_dentry,
|
||||||
|
.delegated_inode = NULL,
|
||||||
|
.flags = 0,
|
||||||
|
};
|
||||||
|
return vfs_rename(&rd);
|
||||||
|
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)
|
||||||
|
struct inode *delegated_inode = NULL;
|
||||||
|
return vfs_rename(old_dir, old_dentry, new_dir, new_dentry, &delegated_inode, 0);
|
||||||
|
#else
|
||||||
|
return vfs_rename(old_dir, old_dentry, new_dir, new_dentry);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|||||||
@@ -94,4 +94,8 @@ static long ksu_copy_from_user_retry(void *to,
|
|||||||
return copy_from_user(to, from, count);
|
return copy_from_user(to, from, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern int ksu_vfs_unlink(struct inode *dir, struct dentry *dentry);
|
||||||
|
extern int ksu_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,
|
||||||
|
struct inode *new_dir, struct dentry *new_dentry);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -16,6 +16,7 @@
|
|||||||
#include "klog.h" // IWYU pragma: keep
|
#include "klog.h" // IWYU pragma: keep
|
||||||
#include "ksud.h"
|
#include "ksud.h"
|
||||||
#include "kernel_compat.h"
|
#include "kernel_compat.h"
|
||||||
|
#include "sulog.h"
|
||||||
|
|
||||||
#define SU_PATH "/system/bin/su"
|
#define SU_PATH "/system/bin/su"
|
||||||
#define SH_PATH "/system/bin/sh"
|
#define SH_PATH "/system/bin/sh"
|
||||||
@@ -133,6 +134,8 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
|||||||
int *__never_use_flags)
|
int *__never_use_flags)
|
||||||
{
|
{
|
||||||
struct filename *filename;
|
struct filename *filename;
|
||||||
|
uid_t current_uid_val = current_uid().val;
|
||||||
|
bool is_allowed = ksu_is_allow_uid(current_uid_val);
|
||||||
const char sh[] = KSUD_PATH;
|
const char sh[] = KSUD_PATH;
|
||||||
const char su[] = SU_PATH;
|
const char su[] = SU_PATH;
|
||||||
|
|
||||||
@@ -152,8 +155,11 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
|||||||
if (likely(memcmp(filename->name, su, sizeof(su))))
|
if (likely(memcmp(filename->name, su, sizeof(su))))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!ksu_is_allow_uid(current_uid().val))
|
ksu_sulog_report_su_attempt(current_uid_val, NULL, filename->name, is_allowed);
|
||||||
|
|
||||||
|
if (!is_allowed) {
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
pr_info("do_execveat_common su found\n");
|
pr_info("do_execveat_common su found\n");
|
||||||
memcpy((void *)filename->name, sh, sizeof(sh));
|
memcpy((void *)filename->name, sh, sizeof(sh));
|
||||||
@@ -167,6 +173,8 @@ 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)
|
||||||
{
|
{
|
||||||
|
uid_t current_uid_val = current_uid().val;
|
||||||
|
bool is_allowed = ksu_is_allow_uid(current_uid_val);
|
||||||
const char su[] = SU_PATH;
|
const char su[] = SU_PATH;
|
||||||
char path[sizeof(su) + 1];
|
char path[sizeof(su) + 1];
|
||||||
|
|
||||||
@@ -183,8 +191,10 @@ int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
|||||||
|
|
||||||
if (likely(memcmp(path, su, sizeof(su))))
|
if (likely(memcmp(path, su, sizeof(su))))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
ksu_sulog_report_su_attempt(current_uid_val, NULL, path, is_allowed);
|
||||||
|
|
||||||
if (!ksu_is_allow_uid(current_uid().val))
|
if (!is_allowed)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
pr_info("sys_execve su found\n");
|
pr_info("sys_execve su found\n");
|
||||||
|
|||||||
393
kernel/sulog.c
Normal file
393
kernel/sulog.c
Normal file
@@ -0,0 +1,393 @@
|
|||||||
|
#include <linux/fs.h>
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
#include <linux/printk.h>
|
||||||
|
#include <linux/slab.h>
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/time.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/uaccess.h>
|
||||||
|
#include <linux/workqueue.h>
|
||||||
|
#include <linux/cred.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
|
||||||
|
#include "klog.h"
|
||||||
|
#include "kernel_compat.h"
|
||||||
|
#include "sulog.h"
|
||||||
|
#include "ksu.h"
|
||||||
|
|
||||||
|
#define SULOG_PATH "/data/adb/ksu/log/sulog.log"
|
||||||
|
#define SULOG_OLD_PATH "/data/adb/ksu/log/sulog.log.old"
|
||||||
|
#define SULOG_MAX_SIZE (16 * 1024 * 1024) // 16MB
|
||||||
|
#define SULOG_ENTRY_MAX_LEN 512
|
||||||
|
#define SULOG_COMM_LEN 256
|
||||||
|
|
||||||
|
struct sulog_entry {
|
||||||
|
struct list_head list;
|
||||||
|
char content[SULOG_ENTRY_MAX_LEN];
|
||||||
|
};
|
||||||
|
|
||||||
|
static LIST_HEAD(sulog_queue);
|
||||||
|
static DEFINE_MUTEX(sulog_mutex);
|
||||||
|
static struct workqueue_struct *sulog_workqueue;
|
||||||
|
static struct work_struct sulog_work;
|
||||||
|
static bool sulog_enabled = true;
|
||||||
|
|
||||||
|
static void get_timestamp(char *buf, size_t len)
|
||||||
|
{
|
||||||
|
struct timespec64 ts, boottime;
|
||||||
|
struct tm tm;
|
||||||
|
s64 real_time;
|
||||||
|
|
||||||
|
ktime_get_boottime_ts64(&boottime);
|
||||||
|
ktime_get_ts64(&ts);
|
||||||
|
|
||||||
|
real_time = boottime.tv_sec;
|
||||||
|
if (real_time < 946684800) {
|
||||||
|
real_time = ts.tv_sec;
|
||||||
|
}
|
||||||
|
|
||||||
|
time64_to_tm(real_time, 0, &tm);
|
||||||
|
|
||||||
|
snprintf(buf, len, "%04ld-%02d-%02d %02d:%02d:%02d",
|
||||||
|
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
|
||||||
|
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void get_full_comm(char *comm_buf, size_t buf_len)
|
||||||
|
{
|
||||||
|
struct mm_struct *mm;
|
||||||
|
char *cmdline = NULL;
|
||||||
|
unsigned long arg_start, arg_end;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
mm = get_task_mm(current);
|
||||||
|
if (mm) {
|
||||||
|
arg_start = mm->arg_start;
|
||||||
|
arg_end = mm->arg_end;
|
||||||
|
|
||||||
|
if (arg_end > arg_start) {
|
||||||
|
len = arg_end - arg_start;
|
||||||
|
if (len > 0 && len < buf_len) {
|
||||||
|
cmdline = kmalloc(len + 1, GFP_ATOMIC);
|
||||||
|
if (cmdline) {
|
||||||
|
if (ksu_copy_from_user_retry(cmdline, (void __user *)arg_start, len) == 0) {
|
||||||
|
cmdline[len] = '\0';
|
||||||
|
char *space = strchr(cmdline, ' ');
|
||||||
|
if (space) *space = '\0';
|
||||||
|
|
||||||
|
char *slash = strrchr(cmdline, '/');
|
||||||
|
if (slash && *(slash + 1)) {
|
||||||
|
strncpy(comm_buf, slash + 1, buf_len - 1);
|
||||||
|
} else {
|
||||||
|
strncpy(comm_buf, cmdline, buf_len - 1);
|
||||||
|
}
|
||||||
|
comm_buf[buf_len - 1] = '\0';
|
||||||
|
kfree(cmdline);
|
||||||
|
mmput(mm);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
kfree(cmdline);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mmput(mm);
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(comm_buf, current->comm, buf_len - 1);
|
||||||
|
comm_buf[buf_len - 1] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sulog_work_handler(struct work_struct *work)
|
||||||
|
{
|
||||||
|
struct file *fp;
|
||||||
|
struct sulog_entry *entry, *tmp;
|
||||||
|
LIST_HEAD(local_queue);
|
||||||
|
loff_t pos = 0;
|
||||||
|
|
||||||
|
mutex_lock(&sulog_mutex);
|
||||||
|
list_splice_init(&sulog_queue, &local_queue);
|
||||||
|
mutex_unlock(&sulog_mutex);
|
||||||
|
|
||||||
|
if (list_empty(&local_queue))
|
||||||
|
return;
|
||||||
|
|
||||||
|
fp = ksu_filp_open_compat(SULOG_PATH, O_WRONLY | O_CREAT | O_APPEND, 0640);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("sulog: failed to open log file: %ld\n", PTR_ERR(fp));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fp->f_inode->i_size > SULOG_MAX_SIZE) {
|
||||||
|
pr_info("sulog: rotating log file, size: %lld\n", fp->f_inode->i_size);
|
||||||
|
filp_close(fp, 0);
|
||||||
|
|
||||||
|
struct path old_path;
|
||||||
|
if (!kern_path(SULOG_OLD_PATH, 0, &old_path)) {
|
||||||
|
ksu_vfs_unlink(old_path.dentry->d_parent->d_inode, old_path.dentry);
|
||||||
|
path_put(&old_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct path current_path, parent_path;
|
||||||
|
if (!kern_path(SULOG_PATH, 0, ¤t_path)) {
|
||||||
|
parent_path = current_path;
|
||||||
|
path_get(&parent_path);
|
||||||
|
parent_path.dentry = current_path.dentry->d_parent;
|
||||||
|
|
||||||
|
struct dentry *old_dentry = lookup_one_len("sulog.log.old",
|
||||||
|
parent_path.dentry, strlen("sulog.log.old"));
|
||||||
|
if (!IS_ERR(old_dentry)) {
|
||||||
|
ksu_vfs_rename(parent_path.dentry->d_inode, current_path.dentry,
|
||||||
|
parent_path.dentry->d_inode, old_dentry);
|
||||||
|
dput(old_dentry);
|
||||||
|
}
|
||||||
|
path_put(¤t_path);
|
||||||
|
path_put(&parent_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
fp = ksu_filp_open_compat(SULOG_PATH, O_WRONLY | O_CREAT | O_EXCL, 0640);
|
||||||
|
if (IS_ERR(fp)) {
|
||||||
|
pr_err("sulog: failed to create new log file: %ld\n", PTR_ERR(fp));
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *rotate_msg = "=== Log file rotated, old log saved as sulog.log.old ===\n";
|
||||||
|
ksu_kernel_write_compat(fp, rotate_msg, strlen(rotate_msg), &pos);
|
||||||
|
} else {
|
||||||
|
pos = fp->f_inode->i_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &local_queue, list) {
|
||||||
|
ksu_kernel_write_compat(fp, entry->content, strlen(entry->content), &pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
vfs_fsync(fp, 0);
|
||||||
|
filp_close(fp, 0);
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
list_for_each_entry_safe(entry, tmp, &local_queue, list) {
|
||||||
|
list_del(&entry->list);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sulog_add_entry(const char *content)
|
||||||
|
{
|
||||||
|
struct sulog_entry *entry;
|
||||||
|
|
||||||
|
if (!sulog_enabled || !content)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entry = kmalloc(sizeof(*entry), GFP_ATOMIC);
|
||||||
|
if (!entry) {
|
||||||
|
pr_err("sulog: failed to allocate memory for log entry\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
strncpy(entry->content, content, SULOG_ENTRY_MAX_LEN - 1);
|
||||||
|
entry->content[SULOG_ENTRY_MAX_LEN - 1] = '\0';
|
||||||
|
|
||||||
|
mutex_lock(&sulog_mutex);
|
||||||
|
list_add_tail(&entry->list, &sulog_queue);
|
||||||
|
mutex_unlock(&sulog_mutex);
|
||||||
|
|
||||||
|
if (sulog_workqueue)
|
||||||
|
queue_work(sulog_workqueue, &sulog_work);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method)
|
||||||
|
{
|
||||||
|
char *timestamp, *full_comm, *log_buf;
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
timestamp = kmalloc(32, GFP_ATOMIC);
|
||||||
|
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
||||||
|
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
||||||
|
|
||||||
|
if (!timestamp || !full_comm || !log_buf) {
|
||||||
|
pr_err("sulog: failed to allocate memory for su_grant log\n");
|
||||||
|
goto cleanup_grant;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_timestamp(timestamp, 32);
|
||||||
|
|
||||||
|
if (comm && strlen(comm) > 0) {
|
||||||
|
strncpy(full_comm, comm, SULOG_COMM_LEN - 1);
|
||||||
|
full_comm[SULOG_COMM_LEN - 1] = '\0';
|
||||||
|
} else {
|
||||||
|
get_full_comm(full_comm, SULOG_COMM_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
||||||
|
"[%s] SU_GRANT: UID=%d COMM=%s METHOD=%s PID=%d\n",
|
||||||
|
timestamp, uid, full_comm,
|
||||||
|
method ? method : "unknown", current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf);
|
||||||
|
pr_info("sulog: %s", log_buf);
|
||||||
|
|
||||||
|
cleanup_grant:
|
||||||
|
if (timestamp) kfree(timestamp);
|
||||||
|
if (full_comm) kfree(full_comm);
|
||||||
|
if (log_buf) kfree(log_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success)
|
||||||
|
{
|
||||||
|
char *timestamp, *full_comm, *log_buf;
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
timestamp = kmalloc(32, GFP_ATOMIC);
|
||||||
|
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
||||||
|
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
||||||
|
|
||||||
|
if (!timestamp || !full_comm || !log_buf) {
|
||||||
|
pr_err("sulog: failed to allocate memory for su_attempt log\n");
|
||||||
|
goto cleanup_attempt;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_timestamp(timestamp, 32);
|
||||||
|
|
||||||
|
if (comm && strlen(comm) > 0) {
|
||||||
|
strncpy(full_comm, comm, SULOG_COMM_LEN - 1);
|
||||||
|
full_comm[SULOG_COMM_LEN - 1] = '\0';
|
||||||
|
} else {
|
||||||
|
get_full_comm(full_comm, SULOG_COMM_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
||||||
|
"[%s] SU_EXEC: UID=%d COMM=%s TARGET=%s RESULT=%s PID=%d\n",
|
||||||
|
timestamp, uid, full_comm,
|
||||||
|
target_path ? target_path : "unknown",
|
||||||
|
success ? "SUCCESS" : "DENIED", current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf);
|
||||||
|
pr_info("sulog: %s", log_buf);
|
||||||
|
|
||||||
|
cleanup_attempt:
|
||||||
|
if (timestamp) kfree(timestamp);
|
||||||
|
if (full_comm) kfree(full_comm);
|
||||||
|
if (log_buf) kfree(log_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed)
|
||||||
|
{
|
||||||
|
char *timestamp, *full_comm, *log_buf;
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
timestamp = kmalloc(32, GFP_ATOMIC);
|
||||||
|
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
||||||
|
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
||||||
|
|
||||||
|
if (!timestamp || !full_comm || !log_buf) {
|
||||||
|
pr_err("sulog: failed to allocate memory for permission_check log\n");
|
||||||
|
goto cleanup_perm;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_timestamp(timestamp, 32);
|
||||||
|
|
||||||
|
if (comm && strlen(comm) > 0) {
|
||||||
|
strncpy(full_comm, comm, SULOG_COMM_LEN - 1);
|
||||||
|
full_comm[SULOG_COMM_LEN - 1] = '\0';
|
||||||
|
} else {
|
||||||
|
get_full_comm(full_comm, SULOG_COMM_LEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
||||||
|
"[%s] PERM_CHECK: UID=%d COMM=%s RESULT=%s PID=%d\n",
|
||||||
|
timestamp, uid, full_comm,
|
||||||
|
allowed ? "ALLOWED" : "DENIED", current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf);
|
||||||
|
|
||||||
|
cleanup_perm:
|
||||||
|
if (timestamp) kfree(timestamp);
|
||||||
|
if (full_comm) kfree(full_comm);
|
||||||
|
if (log_buf) kfree(log_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid)
|
||||||
|
{
|
||||||
|
char *timestamp, *full_comm, *log_buf;
|
||||||
|
|
||||||
|
if (!sulog_enabled)
|
||||||
|
return;
|
||||||
|
|
||||||
|
timestamp = kmalloc(32, GFP_ATOMIC);
|
||||||
|
full_comm = kmalloc(SULOG_COMM_LEN, GFP_ATOMIC);
|
||||||
|
log_buf = kmalloc(SULOG_ENTRY_MAX_LEN, GFP_ATOMIC);
|
||||||
|
|
||||||
|
if (!timestamp || !full_comm || !log_buf) {
|
||||||
|
pr_err("sulog: failed to allocate memory for manager_operation log\n");
|
||||||
|
goto cleanup_mgr;
|
||||||
|
}
|
||||||
|
|
||||||
|
get_timestamp(timestamp, 32);
|
||||||
|
get_full_comm(full_comm, SULOG_COMM_LEN);
|
||||||
|
|
||||||
|
snprintf(log_buf, SULOG_ENTRY_MAX_LEN,
|
||||||
|
"[%s] MANAGER_OP: OP=%s MANAGER_UID=%d TARGET_UID=%d PID=%d\n",
|
||||||
|
timestamp, operation ? operation : "unknown",
|
||||||
|
manager_uid, target_uid, current->pid);
|
||||||
|
|
||||||
|
sulog_add_entry(log_buf);
|
||||||
|
pr_info("sulog: %s", log_buf);
|
||||||
|
|
||||||
|
cleanup_mgr:
|
||||||
|
if (timestamp) kfree(timestamp);
|
||||||
|
if (full_comm) kfree(full_comm);
|
||||||
|
if (log_buf) kfree(log_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_set_enabled(bool enabled)
|
||||||
|
{
|
||||||
|
sulog_enabled = enabled;
|
||||||
|
pr_info("sulog: logging %s\n", enabled ? "enabled" : "disabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ksu_sulog_is_enabled(void)
|
||||||
|
{
|
||||||
|
return sulog_enabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ksu_sulog_init(void)
|
||||||
|
{
|
||||||
|
sulog_workqueue = create_singlethread_workqueue("ksu_sulog");
|
||||||
|
if (!sulog_workqueue) {
|
||||||
|
pr_err("sulog: failed to create workqueue\n");
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
INIT_WORK(&sulog_work, sulog_work_handler);
|
||||||
|
|
||||||
|
pr_info("sulog: initialized successfully\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ksu_sulog_exit(void)
|
||||||
|
{
|
||||||
|
struct sulog_entry *entry, *tmp;
|
||||||
|
|
||||||
|
sulog_enabled = false;
|
||||||
|
|
||||||
|
if (sulog_workqueue) {
|
||||||
|
flush_workqueue(sulog_workqueue);
|
||||||
|
destroy_workqueue(sulog_workqueue);
|
||||||
|
sulog_workqueue = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
mutex_lock(&sulog_mutex);
|
||||||
|
list_for_each_entry_safe(entry, tmp, &sulog_queue, list) {
|
||||||
|
list_del(&entry->list);
|
||||||
|
kfree(entry);
|
||||||
|
}
|
||||||
|
mutex_unlock(&sulog_mutex);
|
||||||
|
|
||||||
|
pr_info("sulog: cleaned up successfully\n");
|
||||||
|
}
|
||||||
20
kernel/sulog.h
Normal file
20
kernel/sulog.h
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
#ifndef __KSU_SULOG_H
|
||||||
|
#define __KSU_SULOG_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
void ksu_sulog_report_su_grant(uid_t uid, const char *comm, const char *method);
|
||||||
|
|
||||||
|
void ksu_sulog_report_su_attempt(uid_t uid, const char *comm, const char *target_path, bool success);
|
||||||
|
|
||||||
|
void ksu_sulog_report_permission_check(uid_t uid, const char *comm, bool allowed);
|
||||||
|
|
||||||
|
void ksu_sulog_report_manager_operation(const char *operation, uid_t manager_uid, uid_t target_uid);
|
||||||
|
|
||||||
|
void ksu_sulog_set_enabled(bool enabled);
|
||||||
|
bool ksu_sulog_is_enabled(void);
|
||||||
|
|
||||||
|
int ksu_sulog_init(void);
|
||||||
|
void ksu_sulog_exit(void);
|
||||||
|
|
||||||
|
#endif /* __KSU_SULOG_H */
|
||||||
Reference in New Issue
Block a user