From 0c322a33bc22e30a9fa44a5630b9f412d601376e Mon Sep 17 00:00:00 2001 From: f19 <58457605+F-19-F@users.noreply.github.com> Date: Sun, 5 Feb 2023 07:14:59 +0800 Subject: [PATCH] kernel: fix filp_open on older kernel's kworker (#205) On older kernel, kworker missing keyring from init process , and this keyring is related to FBE , which causes filp_open return ENOKEY or other errors.To fix this,just install init's keyring to per kworkers.This works on Kernel 4.4 and 4.9. --- kernel/allowlist.c | 9 ++------- kernel/core_hook.c | 25 ++++++++++++++++++++++++- kernel/kernel_compat.c | 6 +++++- kernel/kernel_compat.h | 36 +++++++++++++++++++++++++++++++++++- kernel/uid_observer.c | 1 + 5 files changed, 67 insertions(+), 10 deletions(-) diff --git a/kernel/allowlist.c b/kernel/allowlist.c index 1c4a3756..d8d5007f 100644 --- a/kernel/allowlist.c +++ b/kernel/allowlist.c @@ -22,12 +22,7 @@ struct perm_data { static struct list_head allow_list; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) #define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist" -#else -// filp_open return error if under encryption dir on Kernel4.4 -#define KERNEL_SU_ALLOWLIST "/data/user_de/.ksu_allowlist" -#endif static struct work_struct ksu_save_work; static struct work_struct ksu_load_work; @@ -124,7 +119,7 @@ void do_persistent_allow_list(struct work_struct *work) struct perm_data *p = NULL; struct list_head *pos = NULL; loff_t off = 0; - + KWORKER_INSTALL_KEYRING(); struct file *fp = filp_open(KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT, 0644); @@ -164,7 +159,7 @@ void do_load_allow_list(struct work_struct *work) struct file *fp = NULL; u32 magic; u32 version; - + KWORKER_INSTALL_KEYRING(); fp = filp_open("/data/adb", O_RDONLY, 0); if (IS_ERR(fp)) { int errno = PTR_ERR(fp); diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 64f371d1..1090be5e 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -22,6 +22,7 @@ #include "manager.h" #include "selinux/selinux.h" #include "uid_observer.h" +#include "kernel_compat.h" extern int handle_sepolicy(unsigned long arg3, void __user *arg4); @@ -356,7 +357,26 @@ static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3, ksu_handle_prctl(option, arg2, arg3, arg4, arg5); return -ENOSYS; } - +// kernel 4.4 and 4.9 +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) +static int ksu_key_permission(key_ref_t key_ref, + const struct cred *cred, + unsigned perm) +{ + if (init_session_keyring != NULL) + { + return 0; + } + if (strcmp(current->comm, "init")) + { + // we are only interested in `init` process + return 0; + } + init_session_keyring = cred->session_keyring; + pr_info("kernel_compat: got init_session_keyring"); + return 0; +} +#endif static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) { @@ -366,6 +386,9 @@ static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry, static struct security_hook_list ksu_hooks[] = { LSM_HOOK_INIT(task_prctl, ksu_task_prctl), LSM_HOOK_INIT(inode_rename, ksu_inode_rename), +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) + LSM_HOOK_INIT(key_permission, ksu_key_permission) +#endif }; void __init ksu_lsm_hook_init(void) diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c index 59057eea..84bdbe81 100644 --- a/kernel/kernel_compat.c +++ b/kernel/kernel_compat.c @@ -1,6 +1,10 @@ #include "linux/version.h" -#include "linux/init.h" #include "linux/fs.h" +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) +#include "linux/key.h" +#include "linux/errno.h" +struct key *init_session_keyring = NULL; +#endif ssize_t kernel_read_compat(struct file *p, void *buf, size_t count, loff_t *pos){ #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) return kernel_read(p, buf, count, pos); diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h index bf8ee9fc..c2fe695f 100644 --- a/kernel/kernel_compat.h +++ b/kernel/kernel_compat.h @@ -2,7 +2,41 @@ #define __KSU_H_KERNEL_COMPAT #include "linux/fs.h" +#include "linux/key.h" +#include "linux/version.h" -extern ssize_t kernel_read_compat(struct file *p, void* buf, size_t count, loff_t *pos); +extern struct key *init_session_keyring; + +extern ssize_t kernel_read_compat(struct file *p, void *buf, size_t count, loff_t *pos); extern ssize_t kernel_write_compat(struct file *p, const void *buf, size_t count, loff_t *pos); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) +static inline int install_session_keyring(struct key *keyring) +{ + struct cred *new; + int ret; + + new = prepare_creds(); + if (!new) + return -ENOMEM; + + ret = install_session_keyring_to_cred(new, keyring); + if (ret < 0) { + abort_creds(new); + return ret; + } + + return commit_creds(new); +} +#define KWORKER_INSTALL_KEYRING() \ + static bool keyring_installed = false; \ + if (init_session_keyring != NULL && !keyring_installed) \ + { \ + install_session_keyring(init_session_keyring); \ + keyring_installed = true; \ + } +#else +#define KWORKER_INSTALL_KEYRING() #endif + +#endif \ No newline at end of file diff --git a/kernel/uid_observer.c b/kernel/uid_observer.c index 7b9fcfd7..5af99a01 100644 --- a/kernel/uid_observer.c +++ b/kernel/uid_observer.c @@ -39,6 +39,7 @@ static bool is_uid_exist(uid_t uid, void *data) static void do_update_uid(struct work_struct *work) { + KWORKER_INSTALL_KEYRING(); struct file *fp = filp_open(SYSTEM_PACKAGES_LIST_PATH, O_RDONLY, 0); if (IS_ERR(fp)) { pr_err("do_update_uid, open " SYSTEM_PACKAGES_LIST_PATH