diff --git a/kernel/ksud.c b/kernel/ksud.c index efa16398..68e47352 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -64,6 +64,8 @@ bool ksu_execveat_hook __read_mostly = true; bool ksu_input_hook __read_mostly = true; #endif +u32 ksu_devpts_sid; + void on_post_fs_data(void) { static bool done = false; @@ -76,6 +78,9 @@ void on_post_fs_data(void) ksu_load_allow_list(); // sanity check, this may influence the performance stop_input_hook(); + + ksu_devpts_sid = ksu_get_devpts_sid(); + pr_info("devpts sid: %d\n", ksu_devpts_sid); } #define MAX_ARG_STRINGS 0x7FFFFFFF diff --git a/kernel/ksud.h b/kernel/ksud.h index eafb3147..cc2df243 100644 --- a/kernel/ksud.h +++ b/kernel/ksud.h @@ -9,4 +9,6 @@ void on_post_fs_data(void); bool ksu_is_safe_mode(void); +extern u32 ksu_devpts_sid; + #endif diff --git a/kernel/selinux/selinux.c b/kernel/selinux/selinux.c index 40a92631..c333e8a8 100644 --- a/kernel/selinux/selinux.c +++ b/kernel/selinux/selinux.c @@ -129,4 +129,17 @@ bool is_zygote(void *sec) result = strncmp("u:r:zygote:s0", domain, seclen) == 0; security_release_secctx(domain, seclen); return result; +} + +#define DEVPTS_DOMAIN "u:object_r:devpts:s0" + +u32 ksu_get_devpts_sid() +{ + u32 devpts_sid = 0; + int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN), + &devpts_sid); + if (err) { + pr_info("get devpts sid err %d\n", err); + } + return devpts_sid; } \ No newline at end of file diff --git a/kernel/selinux/selinux.h b/kernel/selinux/selinux.h index 0c497856..07120c25 100644 --- a/kernel/selinux/selinux.h +++ b/kernel/selinux/selinux.h @@ -20,4 +20,6 @@ bool is_zygote(void *cred); void apply_kernelsu_rules(); +u32 ksu_get_devpts_sid(); + #endif diff --git a/kernel/sucompat.c b/kernel/sucompat.c index f43c2063..66f4532a 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -12,6 +14,7 @@ #include #endif +#include "objsec.h" #include "allowlist.h" #include "arch.h" #include "klog.h" // IWYU pragma: keep @@ -47,7 +50,7 @@ static char __user *ksud_user_path(void) } int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, - int * __unused_flags) + int *__unused_flags) { const char su[] = SU_PATH; @@ -87,7 +90,7 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags) // it becomes a `struct filename *` after 5.18 // https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216 const char sh[] = SH_PATH; - struct filename *filename = * ((struct filename **) filename_user); + struct filename *filename = *((struct filename **)filename_user); if (IS_ERR(filename)) { return 0; } @@ -109,7 +112,8 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags) // 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) + void *__never_use_argv, void *__never_use_envp, + int *__never_use_flags) { struct filename *filename; const char sh[] = KSUD_PATH; @@ -138,7 +142,8 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, } int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user, - void *__never_use_argv, void *__never_use_envp, int *__never_use_flags) + void *__never_use_argv, void *__never_use_envp, + int *__never_use_flags) { const char su[] = SU_PATH; char path[sizeof(su) + 1]; @@ -165,7 +170,8 @@ int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user, #ifdef CONFIG_KPROBES -__maybe_unused static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) +__maybe_unused static int faccessat_handler_pre(struct kprobe *p, + struct pt_regs *regs) { int *dfd = (int *)&PT_REGS_PARM1(regs); const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs); @@ -180,21 +186,23 @@ static int sys_faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) { struct pt_regs *real_regs = PT_REAL_REGS(regs); int *dfd = (int *)&PT_REGS_PARM1(real_regs); - const char __user **filename_user = (const char **)&PT_REGS_PARM2(real_regs); + const char __user **filename_user = + (const char **)&PT_REGS_PARM2(real_regs); int *mode = (int *)&PT_REGS_PARM3(real_regs); return ksu_handle_faccessat(dfd, filename_user, mode, NULL); } -__maybe_unused static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs) +__maybe_unused static int newfstatat_handler_pre(struct kprobe *p, + struct pt_regs *regs) { int *dfd = (int *)&PT_REGS_PARM1(regs); const char __user **filename_user = (const char **)&PT_REGS_PARM2(regs); #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) -// static int vfs_statx(int dfd, const char __user *filename, int flags, struct kstat *stat, u32 request_mask) + // static int vfs_statx(int dfd, const char __user *filename, int flags, struct kstat *stat, u32 request_mask) int *flags = (int *)&PT_REGS_PARM3(regs); #else -// int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,int flag) + // int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,int flag) int *flags = (int *)&PT_REGS_CCALL_PARM4(regs); #endif @@ -205,7 +213,8 @@ static int sys_newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs) { struct pt_regs *real_regs = PT_REAL_REGS(regs); int *dfd = (int *)&PT_REGS_PARM1(real_regs); - const char __user **filename_user = (const char **)&PT_REGS_PARM2(real_regs); + const char __user **filename_user = + (const char **)&PT_REGS_PARM2(real_regs); int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs); return ksu_handle_stat(dfd, filename_user, flags); @@ -224,9 +233,11 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) { struct pt_regs *real_regs = PT_REAL_REGS(regs); - const char __user **filename_user = (const char **)&PT_REGS_PARM1(real_regs); + const char __user **filename_user = + (const char **)&PT_REGS_PARM1(real_regs); - return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL, NULL); + return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL, + NULL); } #if 1 @@ -279,6 +290,45 @@ static struct kprobe execve_kp = { }; #endif +static int devpts_get_priv_pre(struct kprobe *p, struct pt_regs *regs) +{ + if (!current->mm) { + return 0; + } + + uid_t uid = current_uid().val; + if (uid % 100000 < 10000) { + // not untrusted_app, ignore it + return 0; + } + + if (!ksu_is_allow_uid(uid)) + return 0; + + struct inode *inode; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) + struct dentry *dentry = (struct dentry *)PT_REGS_PARM1(regs); + inode = dentry->d_inode; +#else + inode = (struct inode *)PT_REGS_PARM1(real_regs); +#endif + + if (ksu_devpts_sid) { + struct inode_security_struct *sec = selinux_inode(inode); + if (sec) { + sec->sid = ksu_devpts_sid; + inode->i_uid.val = 0; + inode->i_gid.val = 0; + } + } + + return 0; +} + +static struct kprobe devpts_get_priv_kp = { .symbol_name = "devpts_get_priv", + .pre_handler = + devpts_get_priv_pre }; + #endif // sucompat: permited process can execute 'su' to gain root access. @@ -292,13 +342,17 @@ void ksu_sucompat_init() pr_info("sucompat: newfstatat_kp: %d\n", ret); ret = register_kprobe(&faccessat_kp); pr_info("sucompat: faccessat_kp: %d\n", ret); + ret = register_kprobe(&devpts_get_priv_kp); + pr_info("sucompat: devpts_kp: %d\n", ret); #endif } -void ksu_sucompat_exit() { +void ksu_sucompat_exit() +{ #ifdef CONFIG_KPROBES unregister_kprobe(&execve_kp); unregister_kprobe(&newfstatat_kp); unregister_kprobe(&faccessat_kp); + unregister_kprobe(&devpts_get_priv_kp); #endif }