kernel: Enhanced user data scanning
Added filesystem type checks to prevent dangerous paths
This commit is contained in:
@@ -8,18 +8,114 @@
|
|||||||
#include <linux/stat.h>
|
#include <linux/stat.h>
|
||||||
#include <linux/namei.h>
|
#include <linux/namei.h>
|
||||||
#include <linux/sched.h>
|
#include <linux/sched.h>
|
||||||
|
#include <linux/mount.h>
|
||||||
|
#include <linux/magic.h>
|
||||||
|
#include <linux/jiffies.h>
|
||||||
|
|
||||||
#include "klog.h"
|
#include "klog.h"
|
||||||
#include "ksu.h"
|
#include "ksu.h"
|
||||||
#include "kernel_compat.h"
|
#include "kernel_compat.h"
|
||||||
#include "user_data_scanner.h"
|
#include "user_data_scanner.h"
|
||||||
|
|
||||||
|
#define KERN_PATH_TIMEOUT_MS 100
|
||||||
|
#define MAX_FUSE_CHECK_RETRIES 3
|
||||||
|
|
||||||
struct work_buffers *get_work_buffer(void)
|
struct work_buffers *get_work_buffer(void)
|
||||||
{
|
{
|
||||||
static struct work_buffers global_buffer;
|
static struct work_buffers global_buffer;
|
||||||
return &global_buffer;
|
return &global_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check the file system type
|
||||||
|
static bool is_dangerous_fs_magic(unsigned long magic)
|
||||||
|
{
|
||||||
|
switch (magic) {
|
||||||
|
case 0x65735546:
|
||||||
|
case 0x794c7630:
|
||||||
|
case 0x01021994:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool is_path_for_kern_path(const char *path, struct super_block *expected_sb)
|
||||||
|
{
|
||||||
|
if (fatal_signal_pending(current)) {
|
||||||
|
pr_warn("Fatal signal pending, skip path: %s\n", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (need_resched()) {
|
||||||
|
cond_resched();
|
||||||
|
if (fatal_signal_pending(current))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (expected_sb && is_dangerous_fs_magic(expected_sb->s_magic)) {
|
||||||
|
pr_info("Skipping dangerous filesystem (magic=0x%lx): %s\n",
|
||||||
|
expected_sb->s_magic, path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!path || strlen(path) == 0 || strlen(path) >= PATH_MAX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strstr(path, ".tmp") || strstr(path, ".removing") || strstr(path, ".unmounting")) {
|
||||||
|
pr_debug("Path appears to be in transition state: %s\n", path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int kern_path_with_timeout(const char *path, unsigned int flags, struct path *result)
|
||||||
|
{
|
||||||
|
unsigned long start_time = jiffies;
|
||||||
|
unsigned long timeout = start_time + msecs_to_jiffies(KERN_PATH_TIMEOUT_MS);
|
||||||
|
int retries = 0;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (time_after(jiffies, timeout)) {
|
||||||
|
pr_warn("kern_path timeout for: %s\n", path);
|
||||||
|
return -ETIMEDOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fatal_signal_pending(current)) {
|
||||||
|
pr_warn("Fatal signal during kern_path: %s\n", path);
|
||||||
|
return -EINTR;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = kern_path(path, flags, result);
|
||||||
|
|
||||||
|
if (err == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == -ENOENT || err == -ENOTDIR || err == -EACCES) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err == -EBUSY || err == -EAGAIN) {
|
||||||
|
retries++;
|
||||||
|
if (retries >= MAX_FUSE_CHECK_RETRIES) {
|
||||||
|
pr_warn("Max retries reached for: %s (err=%d)\n", path, err);
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
usleep_range(1000, 2000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return err;
|
||||||
|
|
||||||
|
} while (retries < MAX_FUSE_CHECK_RETRIES);
|
||||||
|
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
FILLDIR_RETURN_TYPE scan_user_packages(struct dir_context *ctx, const char *name,
|
FILLDIR_RETURN_TYPE scan_user_packages(struct dir_context *ctx, const char *name,
|
||||||
int namelen, loff_t off, u64 ino, unsigned int d_type)
|
int namelen, loff_t off, u64 ino, unsigned int d_type)
|
||||||
{
|
{
|
||||||
@@ -32,6 +128,10 @@ FILLDIR_RETURN_TYPE scan_user_packages(struct dir_context *ctx, const char *name
|
|||||||
scan_ctx->processed_count++;
|
scan_ctx->processed_count++;
|
||||||
if (scan_ctx->processed_count % SCHEDULE_INTERVAL == 0) {
|
if (scan_ctx->processed_count % SCHEDULE_INTERVAL == 0) {
|
||||||
cond_resched();
|
cond_resched();
|
||||||
|
if (fatal_signal_pending(current)) {
|
||||||
|
pr_info("Fatal signal received, stopping scan\n");
|
||||||
|
return FILLDIR_ACTOR_STOP;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d_type != DT_DIR || namelen <= 0)
|
if (d_type != DT_DIR || namelen <= 0)
|
||||||
@@ -77,17 +177,40 @@ static int process_deferred_paths(struct list_head *deferred_paths, struct list_
|
|||||||
{
|
{
|
||||||
struct deferred_path_info *path_info, *n;
|
struct deferred_path_info *path_info, *n;
|
||||||
int success_count = 0;
|
int success_count = 0;
|
||||||
|
int skip_count = 0;
|
||||||
|
|
||||||
list_for_each_entry_safe(path_info, n, deferred_paths, list) {
|
list_for_each_entry_safe(path_info, n, deferred_paths, list) {
|
||||||
struct path path;
|
if (!is_path_for_kern_path(path_info->path, NULL)) {
|
||||||
int err = kern_path(path_info->path, LOOKUP_FOLLOW, &path);
|
pr_debug("Skipping unsafe path: %s\n", path_info->path);
|
||||||
if (err) {
|
skip_count++;
|
||||||
pr_debug("Path lookup failed: %s (%d)\n", path_info->path, err);
|
|
||||||
list_del(&path_info->list);
|
list_del(&path_info->list);
|
||||||
kfree(path_info);
|
kfree(path_info);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Retrieve path information
|
||||||
|
struct path path;
|
||||||
|
int err = kern_path_with_timeout(path_info->path, LOOKUP_FOLLOW, &path);
|
||||||
|
if (err) {
|
||||||
|
if (err != -ENOENT) {
|
||||||
|
pr_debug("Path lookup failed: %s (%d)\n", path_info->path, err);
|
||||||
|
}
|
||||||
|
list_del(&path_info->list);
|
||||||
|
kfree(path_info);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the file system type
|
||||||
|
if (is_dangerous_fs_magic(path.mnt->mnt_sb->s_magic)) {
|
||||||
|
pr_info("Skipping path on dangerous filesystem: %s (magic=0x%lx)\n",
|
||||||
|
path_info->path, path.mnt->mnt_sb->s_magic);
|
||||||
|
path_put(&path);
|
||||||
|
list_del(&path_info->list);
|
||||||
|
kfree(path_info);
|
||||||
|
skip_count++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
struct kstat stat;
|
struct kstat stat;
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0) || defined(KSU_HAS_NEW_VFS_GETATTR)
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4,11,0) || defined(KSU_HAS_NEW_VFS_GETATTR)
|
||||||
err = vfs_getattr(&path, &stat, STATX_UID, AT_STATX_SYNC_AS_STAT);
|
err = vfs_getattr(&path, &stat, STATX_UID, AT_STATX_SYNC_AS_STAT);
|
||||||
@@ -134,8 +257,16 @@ static int process_deferred_paths(struct list_head *deferred_paths, struct list_
|
|||||||
|
|
||||||
if (success_count % 10 == 0) {
|
if (success_count % 10 == 0) {
|
||||||
cond_resched();
|
cond_resched();
|
||||||
|
if (fatal_signal_pending(current)) {
|
||||||
|
pr_info("Fatal signal received, stopping path processing\n");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip_count > 0) {
|
||||||
|
pr_info("Skipped %d potentially dangerous paths for safety\n", skip_count);
|
||||||
|
}
|
||||||
|
|
||||||
return success_count;
|
return success_count;
|
||||||
}
|
}
|
||||||
@@ -159,6 +290,14 @@ static int scan_primary_user_apps(struct list_head *uid_list,
|
|||||||
return PTR_ERR(dir_file);
|
return PTR_ERR(dir_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check the file system type
|
||||||
|
if (is_dangerous_fs_magic(dir_file->f_inode->i_sb->s_magic)) {
|
||||||
|
pr_err("Primary user path is on dangerous filesystem (magic=0x%lx), aborting\n",
|
||||||
|
dir_file->f_inode->i_sb->s_magic);
|
||||||
|
filp_close(dir_file, NULL);
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
struct user_scan_ctx scan_ctx = {
|
struct user_scan_ctx scan_ctx = {
|
||||||
.deferred_paths = &deferred_paths,
|
.deferred_paths = &deferred_paths,
|
||||||
.user_id = 0,
|
.user_id = 0,
|
||||||
@@ -195,6 +334,8 @@ FILLDIR_RETURN_TYPE collect_user_ids(struct dir_context *ctx, const char *name,
|
|||||||
uctx->processed_count++;
|
uctx->processed_count++;
|
||||||
if (uctx->processed_count % SCHEDULE_INTERVAL == 0) {
|
if (uctx->processed_count % SCHEDULE_INTERVAL == 0) {
|
||||||
cond_resched();
|
cond_resched();
|
||||||
|
if (fatal_signal_pending(current))
|
||||||
|
return FILLDIR_ACTOR_STOP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d_type != DT_DIR || namelen <= 0)
|
if (d_type != DT_DIR || namelen <= 0)
|
||||||
@@ -229,6 +370,14 @@ static int get_all_active_users(struct work_buffers *work_buf, size_t *found_cou
|
|||||||
return PTR_ERR(dir_file);
|
return PTR_ERR(dir_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check the file system type of the base path
|
||||||
|
if (is_dangerous_fs_magic(dir_file->f_inode->i_sb->s_magic)) {
|
||||||
|
pr_err("User data base path is on dangerous filesystem (magic=0x%lx), aborting\n",
|
||||||
|
dir_file->f_inode->i_sb->s_magic);
|
||||||
|
filp_close(dir_file, NULL);
|
||||||
|
return -EOPNOTSUPP;
|
||||||
|
}
|
||||||
|
|
||||||
struct user_id_ctx uctx = {
|
struct user_id_ctx uctx = {
|
||||||
.ctx.actor = collect_user_ids,
|
.ctx.actor = collect_user_ids,
|
||||||
.user_ids = work_buf->user_ids_buffer,
|
.user_ids = work_buf->user_ids_buffer,
|
||||||
@@ -260,6 +409,11 @@ static int scan_secondary_users_apps(struct list_head *uid_list,
|
|||||||
*total_pkg_count = *total_error_count = 0;
|
*total_pkg_count = *total_error_count = 0;
|
||||||
|
|
||||||
for (size_t i = 0; i < user_count; i++) {
|
for (size_t i = 0; i < user_count; i++) {
|
||||||
|
if (fatal_signal_pending(current)) {
|
||||||
|
pr_info("Fatal signal received, stopping secondary user scan\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Skip the main user since it was already scanned in the first step
|
// Skip the main user since it was already scanned in the first step
|
||||||
if (work_buf->user_ids_buffer[i] == 0)
|
if (work_buf->user_ids_buffer[i] == 0)
|
||||||
continue;
|
continue;
|
||||||
@@ -278,6 +432,14 @@ static int scan_secondary_users_apps(struct list_head *uid_list,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check the file system type of the user directory
|
||||||
|
if (is_dangerous_fs_magic(dir_file->f_inode->i_sb->s_magic)) {
|
||||||
|
pr_info("User path %s is on dangerous filesystem (magic=0x%lx), skipping\n",
|
||||||
|
work_buf->path_buffer, dir_file->f_inode->i_sb->s_magic);
|
||||||
|
filp_close(dir_file, NULL);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
struct user_scan_ctx scan_ctx = {
|
struct user_scan_ctx scan_ctx = {
|
||||||
.deferred_paths = &deferred_paths,
|
.deferred_paths = &deferred_paths,
|
||||||
.user_id = work_buf->user_ids_buffer[i],
|
.user_id = work_buf->user_ids_buffer[i],
|
||||||
|
|||||||
Reference in New Issue
Block a user