* clean unused header * on_module_mounted in ksud.c * refact: use app_profile * unified hook manager * add zygote to hook target * move reboot hook to supercall.c * refactor: kernel_umount setuid_hook * update mark rules, add init mark tracker * remove reboot from check_syscall_fastpath * update setuid_hook, remove uneeded sucompat enable * log freely * kernel: Migrate kprobe hook configuration items * kernel: fix build * cli: add ksud debug mark * Fix rustfmt warning --------- Co-authored-by: Ylarod <me@ylarod.cn> Co-authored-by: Wang Han <416810799@qq.com> Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
265 lines
7.2 KiB
C
265 lines
7.2 KiB
C
#include <linux/sched.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/task_work.h>
|
|
#include <linux/cred.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/mount.h>
|
|
#include <linux/namei.h>
|
|
#include <linux/nsproxy.h>
|
|
#include <linux/path.h>
|
|
#include <linux/printk.h>
|
|
#include <linux/types.h>
|
|
|
|
#include "kernel_umount.h"
|
|
#include "klog.h" // IWYU pragma: keep
|
|
#include "allowlist.h"
|
|
#include "selinux/selinux.h"
|
|
#include "feature.h"
|
|
#include "ksud.h"
|
|
|
|
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 bool should_umount(struct path *path)
|
|
{
|
|
if (!path) {
|
|
return false;
|
|
}
|
|
|
|
if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
|
|
pr_info("ignore global mnt namespace process: %d\n", current_uid().val);
|
|
return false;
|
|
}
|
|
|
|
if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) {
|
|
const char *fstype = path->mnt->mnt_sb->s_type->name;
|
|
return strcmp(fstype, "overlay") == 0;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
extern int path_umount(struct path *path, int flags);
|
|
|
|
static void ksu_umount_mnt(struct path *path, int flags)
|
|
{
|
|
int err = path_umount(path, flags);
|
|
if (err) {
|
|
pr_info("umount %s failed: %d\n", path->dentry->d_iname, err);
|
|
}
|
|
}
|
|
|
|
static void try_umount(const char *mnt, bool check_mnt, int flags)
|
|
{
|
|
struct path path;
|
|
int err = kern_path(mnt, 0, &path);
|
|
if (err) {
|
|
return;
|
|
}
|
|
|
|
if (path.dentry != path.mnt->mnt_root) {
|
|
// it is not root mountpoint, maybe umounted by others already.
|
|
path_put(&path);
|
|
return;
|
|
}
|
|
|
|
// we are only interest in some specific mounts
|
|
if (check_mnt && !should_umount(&path)) {
|
|
path_put(&path);
|
|
return;
|
|
}
|
|
|
|
ksu_umount_mnt(&path, flags);
|
|
}
|
|
|
|
struct umount_tw {
|
|
struct callback_head cb;
|
|
const struct cred *old_cred;
|
|
};
|
|
|
|
#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
|
|
static void umount_tw_func(struct callback_head *cb)
|
|
{
|
|
struct umount_tw *tw = container_of(cb, struct umount_tw, cb);
|
|
const struct cred *saved = NULL;
|
|
if (tw->old_cred) {
|
|
saved = override_creds(tw->old_cred);
|
|
}
|
|
|
|
uid_t uid = current_uid().val;
|
|
|
|
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
|
// filter the mountpoint whose target is `/data/adb`
|
|
try_umount("/odm", true, 0, uid);
|
|
try_umount("/system", true, 0, uid);
|
|
try_umount("/vendor", true, 0, uid);
|
|
try_umount("/product", true, 0, uid);
|
|
try_umount("/system_ext", true, 0, uid);
|
|
try_umount("/data/adb/modules", false, MNT_DETACH, uid);
|
|
try_umount("/data/adb/kpm", false, MNT_DETACH, uid);
|
|
|
|
// try umount ksu temp path
|
|
try_umount("/debug_ramdisk", false, MNT_DETACH, uid);
|
|
try_umount("/sbin", false, MNT_DETACH, uid);
|
|
|
|
// try umount lsposed dex2oat bins
|
|
try_umount("/system/etc/hosts", false, MNT_DETACH, uid);
|
|
|
|
// try umount lsposed dex2oat bins
|
|
try_umount("/apex/com.android.art/bin/dex2oat64", false, MNT_DETACH, uid);
|
|
try_umount("/apex/com.android.art/bin/dex2oat32", false, MNT_DETACH, uid);
|
|
|
|
if (saved)
|
|
revert_creds(saved);
|
|
|
|
if (tw->old_cred)
|
|
put_cred(tw->old_cred);
|
|
|
|
kfree(tw);
|
|
}
|
|
#else
|
|
static void umount_tw_func(struct callback_head *cb)
|
|
{
|
|
struct umount_tw *tw = container_of(cb, struct umount_tw, cb);
|
|
const struct cred *saved = NULL;
|
|
if (tw->old_cred) {
|
|
saved = override_creds(tw->old_cred);
|
|
}
|
|
|
|
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
|
// filter the mountpoint whose target is `/data/adb`
|
|
try_umount("/odm", true, 0);
|
|
try_umount("/system", true, 0);
|
|
try_umount("/vendor", true, 0);
|
|
try_umount("/product", true, 0);
|
|
try_umount("/system_ext", true, 0);
|
|
try_umount("/data/adb/modules", false, MNT_DETACH);
|
|
try_umount("/data/adb/kpm", false, MNT_DETACH);
|
|
// try umount ksu temp path
|
|
try_umount("/debug_ramdisk", false, MNT_DETACH);
|
|
try_umount("/sbin", false, MNT_DETACH);
|
|
|
|
try_umount("/system/etc/hosts", false, MNT_DETACH);
|
|
// try umount lsposed dex2oat bins
|
|
try_umount("/apex/com.android.art/bin/dex2oat64", false, MNT_DETACH);
|
|
try_umount("/apex/com.android.art/bin/dex2oat32", false, MNT_DETACH);
|
|
|
|
if (saved)
|
|
revert_creds(saved);
|
|
|
|
if (tw->old_cred)
|
|
put_cred(tw->old_cred);
|
|
|
|
kfree(tw);
|
|
}
|
|
|
|
static inline bool is_appuid(uid_t uid)
|
|
{
|
|
#define PER_USER_RANGE 100000
|
|
#define FIRST_APPLICATION_UID 10000
|
|
#define LAST_APPLICATION_UID 19999
|
|
|
|
uid_t appid = uid % PER_USER_RANGE;
|
|
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
|
|
}
|
|
|
|
static inline bool is_unsupported_uid(uid_t uid)
|
|
{
|
|
#define LAST_APPLICATION_UID 19999
|
|
uid_t appid = uid % 100000;
|
|
return appid > LAST_APPLICATION_UID;
|
|
}
|
|
|
|
int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
|
|
{
|
|
struct umount_tw *tw;
|
|
|
|
// this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it!
|
|
if (!ksu_module_mounted) {
|
|
return 0;
|
|
}
|
|
|
|
if (!ksu_kernel_umount_enabled) {
|
|
return 0;
|
|
}
|
|
|
|
if (!is_appuid(new_uid) || is_unsupported_uid(new_uid)) {
|
|
pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid);
|
|
return 0;
|
|
}
|
|
|
|
if (!ksu_uid_should_umount(new_uid)) {
|
|
return 0;
|
|
} else {
|
|
pr_info("uid: %d should not umount!\n", current_uid().val);
|
|
}
|
|
|
|
// check old process's selinux context, if it is not zygote, ignore it!
|
|
// because some su apps may setuid to untrusted_app but they are in global mount namespace
|
|
// when we umount for such process, that is a disaster!
|
|
bool is_zygote_child = is_zygote(get_current_cred());
|
|
if (!is_zygote_child) {
|
|
pr_info("handle umount ignore non zygote child: %d\n", current->pid);
|
|
return 0;
|
|
}
|
|
// umount the target mnt
|
|
pr_info("handle umount for uid: %d, pid: %d\n", new_uid, current->pid);
|
|
|
|
tw = kmalloc(sizeof(*tw), GFP_ATOMIC);
|
|
if (!tw)
|
|
return 0;
|
|
|
|
tw->old_cred = get_current_cred();
|
|
tw->cb.func = umount_tw_func;
|
|
|
|
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
|
|
int err = task_work_add(current, &tw->cb, TWA_RESUME);
|
|
#else
|
|
int err = task_work_add(current, &tw->cb, true);
|
|
#endif
|
|
if (err) {
|
|
if (tw->old_cred) {
|
|
put_cred(tw->old_cred);
|
|
}
|
|
kfree(tw);
|
|
pr_warn("unmount add task_work failed\n");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ksu_kernel_umount_init(void)
|
|
{
|
|
int rc = 0;
|
|
rc = ksu_umount_manager_init();
|
|
if (rc) {
|
|
pr_err("Failed to initialize umount manager: %d\n", rc);
|
|
}
|
|
if (ksu_register_feature_handler(&kernel_umount_handler)) {
|
|
pr_err("Failed to register kernel_umount feature handler\n");
|
|
}
|
|
}
|
|
|
|
void ksu_kernel_umount_exit(void)
|
|
{
|
|
ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT);
|
|
} |