diff --git a/kernel/pkg_observer.c b/kernel/pkg_observer.c new file mode 100644 index 00000000..e78d7ba7 --- /dev/null +++ b/kernel/pkg_observer.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include +#include +#include +#include +#include "klog.h" // IWYU pragma: keep +#include "throne_tracker.h" + +#define MASK_SYSTEM (FS_CREATE | FS_MOVE | FS_EVENT_ON_CHILD) + +struct watch_dir { + const char *path; + u32 mask; + struct path kpath; + struct inode *inode; + struct fsnotify_mark *mark; +}; + +static struct fsnotify_group *g; + +#include "pkg_observer_defs.h" // KSU_DECL_FSNOTIFY_OPS +static KSU_DECL_FSNOTIFY_OPS(ksu_handle_generic_event) +{ + if (!file_name || (mask & FS_ISDIR)) + return 0; + + if (ksu_fname_len(file_name) == 13 && + !memcmp(ksu_fname_arg(file_name), "packages.list", 13)) { + pr_info("packages.list detected (mask=%d)\n", mask); + track_throne(); + } + return 0; +} + +static const struct fsnotify_ops ksu_ops = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + .handle_inode_event = ksu_handle_generic_event, +#else + .handle_event = ksu_handle_generic_event, +#endif +}; + +static void __maybe_unused m_free(struct fsnotify_mark *m) +{ + if (m) { + kfree(m); + } +} + +static int add_mark_on_inode(struct inode *inode, u32 mask, + struct fsnotify_mark **out) +{ + struct fsnotify_mark *m; + int ret; + + m = kzalloc(sizeof(*m), GFP_KERNEL); + if (!m) + return -ENOMEM; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) + fsnotify_init_mark(m, m_free); + m->mask = mask; + ret = fsnotify_add_mark(m, g, inode, NULL, 0); +#else + fsnotify_init_mark(m, g); + m->mask = mask; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) + ret = fsnotify_add_inode_mark(m, inode, 0); +#else + ret = fsnotify_add_mark(m, inode, NULL, 0); +#endif +#endif + + if (ret < 0) { + fsnotify_put_mark(m); + return ret; + } + + *out = m; + return 0; +} + +static int watch_one_dir(struct watch_dir *wd) +{ + int ret = kern_path(wd->path, LOOKUP_FOLLOW, &wd->kpath); + if (ret) { + pr_info("path not ready: %s (%d)\n", wd->path, ret); + return ret; + } + wd->inode = d_inode(wd->kpath.dentry); + ihold(wd->inode); + + ret = add_mark_on_inode(wd->inode, wd->mask, &wd->mark); + if (ret) { + pr_err("Add mark failed for %s (%d)\n", wd->path, ret); + path_put(&wd->kpath); + iput(wd->inode); + wd->inode = NULL; + return ret; + } + pr_info("watching %s\n", wd->path); + return 0; +} + +static void unwatch_one_dir(struct watch_dir *wd) +{ + if (wd->mark) { + fsnotify_destroy_mark(wd->mark, g); + fsnotify_put_mark(wd->mark); + wd->mark = NULL; + } + if (wd->inode) { + iput(wd->inode); + wd->inode = NULL; + } + if (wd->kpath.dentry) { + path_put(&wd->kpath); + memset(&wd->kpath, 0, sizeof(wd->kpath)); + } +} + +static struct watch_dir g_watch = { .path = "/data/system", + .mask = MASK_SYSTEM }; + +int ksu_observer_init(void) +{ + int ret = 0; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + g = fsnotify_alloc_group(&ksu_ops, 0); +#else + g = fsnotify_alloc_group(&ksu_ops); +#endif + if (IS_ERR(g)) + return PTR_ERR(g); + + ret = watch_one_dir(&g_watch); + pr_info("%s: done.\n", __func__); + return 0; +} + +void ksu_observer_exit(void) +{ + unwatch_one_dir(&g_watch); + fsnotify_put_group(g); + pr_info("%s: done.\n", __func__); +} diff --git a/kernel/pkg_observer_defs.h b/kernel/pkg_observer_defs.h new file mode 100644 index 00000000..9758deb9 --- /dev/null +++ b/kernel/pkg_observer_defs.h @@ -0,0 +1,42 @@ +// This header should not be used outside of pkg_observer.c! + +#include + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0) +typedef const struct qstr *ksu_fname_t; +#define ksu_fname_len(f) ((f)->len) +#define ksu_fname_arg(f) ((f)->name) +#else +typedef const unsigned char *ksu_fname_t; +#define ksu_fname_len(f) (strlen(f)) +#define ksu_fname_arg(f) (f) +#endif + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) +#define KSU_DECL_FSNOTIFY_OPS(name) \ + int name(struct fsnotify_mark *mark, u32 mask, struct inode *inode, \ + struct inode *dir, const struct qstr *file_name, u32 cookie) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 2, 0) +#define KSU_DECL_FSNOTIFY_OPS(name) \ + int name(struct fsnotify_group *group, struct inode *inode, u32 mask, \ + const void *data, int data_type, ksu_fname_t file_name, \ + u32 cookie, struct fsnotify_iter_info *iter_info) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 18, 0) +#define KSU_DECL_FSNOTIFY_OPS(name) \ + int name(struct fsnotify_group *group, struct inode *inode, u32 mask, \ + const void *data, int data_type, ksu_fname_t file_name, \ + u32 cookie, struct fsnotify_iter_info *iter_info) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) +#define KSU_DECL_FSNOTIFY_OPS(name) \ + int name(struct fsnotify_group *group, struct inode *inode, \ + struct fsnotify_mark *inode_mark, \ + struct fsnotify_mark *vfsmount_mark, u32 mask, \ + const void *data, int data_type, ksu_fname_t file_name, \ + u32 cookie, struct fsnotify_iter_info *iter_info) +#else +#define KSU_DECL_FSNOTIFY_OPS(name) \ + int name(struct fsnotify_group *group, struct inode *inode, \ + struct fsnotify_mark *inode_mark, \ + struct fsnotify_mark *vfsmount_mark, u32 mask, void *data, \ + int data_type, ksu_fname_t file_name, u32 cookie) +#endif diff --git a/kernel/sucompat.c b/kernel/sucompat.c index b06c6439..1503fa6d 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -189,7 +189,7 @@ int ksu_handle_devpts(struct inode *inode) int __ksu_handle_devpts(struct inode *inode) { #ifndef CONFIG_KSU_KPROBES_HOOK - if (!ksu_sucompat_hook_state) + if (!ksu_su_compat_enabled) return 0; #endif