diff --git a/kernel/Makefile b/kernel/Makefile index 9fec3ef387b7..669297563b1d 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -29,6 +29,14 @@ $(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git subm ccflags-y += -DKSU_VERSION=16 endif +ifeq ($(shell grep -q " current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0) +ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID +endif + +ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0) +ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE +endif + ifndef KSU_EXPECTED_SIZE KSU_EXPECTED_SIZE := 0x033b endif @@ -48,6 +56,13 @@ $(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH)) ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE) ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\" +ifeq ($(shell grep -q "int path_umount" $(srctree)/fs/namespace.c; echo $$?),0) +ccflags-y += -DKSU_UMOUNT +else +$(info -- Did you know you can backport path_umount to fs/namespace.c from 5.9?) +$(info -- Read: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#how-to-backport-path-umount) +endif + ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat ccflags-y += -Wno-declaration-after-statement -Wno-unused-function diff --git a/kernel/allowlist.c b/kernel/allowlist.c index 9daceef2011b..4fbba9355949 100644 --- a/kernel/allowlist.c +++ b/kernel/allowlist.c @@ -7,7 +7,9 @@ #include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) #include +#endif #include "ksu.h" #include "klog.h" // IWYU pragma: keep diff --git a/kernel/arch.h b/kernel/arch.h index eec38c289e71..f36ec5f509fa 100644 --- a/kernel/arch.h +++ b/kernel/arch.h @@ -18,11 +18,19 @@ #define __PT_SP_REG sp #define __PT_IP_REG pc +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) #define PRCTL_SYMBOL "__arm64_sys_prctl" #define SYS_READ_SYMBOL "__arm64_sys_read" #define SYS_NEWFSTATAT_SYMBOL "__arm64_sys_newfstatat" #define SYS_FACCESSAT_SYMBOL "__arm64_sys_faccessat" #define SYS_EXECVE_SYMBOL "__arm64_sys_execve" +#else +#define PRCTL_SYMBOL "sys_prctl" +#define SYS_READ_SYMBOL "sys_read" +#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat" +#define SYS_FACCESSAT_SYMBOL "sys_faccessat" +#define SYS_EXECVE_SYMBOL "sys_execve" +#endif #elif defined(__x86_64__) @@ -39,11 +47,19 @@ #define __PT_RC_REG ax #define __PT_SP_REG sp #define __PT_IP_REG ip +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) #define PRCTL_SYMBOL "__x64_sys_prctl" #define SYS_READ_SYMBOL "__x64_sys_read" #define SYS_NEWFSTATAT_SYMBOL "__x64_sys_newfstatat" #define SYS_FACCESSAT_SYMBOL "__x64_sys_faccessat" #define SYS_EXECVE_SYMBOL "__x64_sys_execve" +#else +#define PRCTL_SYMBOL "sys_prctl" +#define SYS_READ_SYMBOL "sys_read" +#define SYS_NEWFSTATAT_SYMBOL "sys_newfstatat" +#define SYS_FACCESSAT_SYMBOL "sys_faccessat" +#define SYS_EXECVE_SYMBOL "sys_execve" +#endif #else #error "Unsupported arch" @@ -67,6 +83,10 @@ #define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG) #define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) #define PT_REAL_REGS(regs) ((struct pt_regs *)PT_REGS_PARM1(regs)) +#else +#define PT_REAL_REGS(regs) ((regs)) +#endif #endif diff --git a/kernel/core_hook.c b/kernel/core_hook.c index b63ea0b1d6fd..429ba3306ea3 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -98,7 +98,11 @@ static void setup_groups(struct root_profile *profile, struct cred *cred) put_group_info(group_info); return; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 9, 0) group_info->gid[i] = kgid; +#else + GROUP_AT(group_info, i) = kgid; +#endif } groups_sort(group_info); @@ -445,12 +449,14 @@ static bool should_umount(struct path *path) return false; } -static void ksu_umount_mnt(struct path *path, int flags) +static int 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); - } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || defined(KSU_UMOUNT) + return path_umount(path, flags); +#else + // TODO: umount for non GKI kernel + return -ENOSYS; +#endif } static void try_umount(const char *mnt, bool check_mnt, int flags) @@ -471,7 +477,10 @@ static void try_umount(const char *mnt, bool check_mnt, int flags) return; } - ksu_umount_mnt(&path, flags); + err = ksu_umount_mnt(&path, flags); + if (err) { + pr_warn("umount %s failed: %d\n", mnt, err); + } } int ksu_handle_setuid(struct cred *new, const struct cred *old) @@ -548,8 +557,14 @@ static int handler_pre(struct kprobe *p, struct pt_regs *regs) int option = (int)PT_REGS_PARM1(real_regs); unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs); unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0) // PRCTL_SYMBOL is the arch-specificed one, which receive raw pt_regs from syscall unsigned long arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs); +#else + // PRCTL_SYMBOL is the common one, called by C convention in do_syscall_64 + // https://elixir.bootlin.com/linux/v4.15.18/source/arch/x86/entry/common.c#L287 + unsigned long arg4 = (unsigned long)PT_REGS_CCALL_PARM4(real_regs); +#endif unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs); return ksu_handle_prctl(option, arg2, arg3, arg4, arg5); @@ -609,7 +624,23 @@ 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) || defined(CONFIG_IS_HW_HISI) +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\n"); + return 0; +} +#endif static int ksu_inode_rename(struct inode *old_inode, struct dentry *old_dentry, struct inode *new_inode, struct dentry *new_dentry) { @@ -627,11 +658,19 @@ static struct security_hook_list ksu_hooks[] = { LSM_HOOK_INIT(task_prctl, ksu_task_prctl), LSM_HOOK_INIT(inode_rename, ksu_inode_rename), LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid), +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) + LSM_HOOK_INIT(key_permission, ksu_key_permission) +#endif }; void __init ksu_lsm_hook_init(void) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks), "ksu"); +#else + // https://elixir.bootlin.com/linux/v4.10.17/source/include/linux/lsm_hooks.h#L1892 + security_add_hooks(ksu_hooks, ARRAY_SIZE(ksu_hooks)); +#endif } #else diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c index d4ee546d6ef1..b242bc637398 100644 --- a/kernel/kernel_compat.c +++ b/kernel/kernel_compat.c @@ -1,10 +1,39 @@ #include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 10, 0) #include +#else +#include +#endif #include #include "klog.h" // IWYU pragma: keep -#include "kernel_compat.h" +#include "kernel_compat.h" // Add check Huawei Device + +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) +#include +#include +#include +struct key *init_session_keyring = NULL; + +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); +} +#endif extern struct task_struct init_task; @@ -50,6 +79,13 @@ void ksu_android_ns_fs_check() struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) + if (init_session_keyring != NULL && !current_cred()->session_keyring && + (current->flags & PF_WQ_WORKER)) { + pr_info("installing init session keyring for older kernel\n"); + install_session_keyring(init_session_keyring); + } +#endif // switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns struct ksu_ns_fs_saved saved; if (android_context_saved_enabled) { @@ -72,17 +108,69 @@ struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode) ssize_t ksu_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); +#else + loff_t offset = pos ? *pos : 0; + ssize_t result = kernel_read(p, offset, (char *)buf, count); + if (pos && result > 0) { + *pos = offset + result; + } + return result; +#endif } ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, loff_t *pos) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) return kernel_write(p, buf, count, pos); +#else + loff_t offset = pos ? *pos : 0; + ssize_t result = kernel_write(p, buf, count, offset); + if (pos && result > 0) { + *pos = offset + result; + } + return result; +#endif } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, long count) { return strncpy_from_user_nofault(dst, unsafe_addr, count); } +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) +long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, + long count) +{ + return strncpy_from_unsafe_user(dst, unsafe_addr, count); +} +#else +// Copied from: https://elixir.bootlin.com/linux/v4.9.337/source/mm/maccess.c#L201 +long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, + long count) +{ + mm_segment_t old_fs = get_fs(); + long ret; + + if (unlikely(count <= 0)) + return 0; + + set_fs(USER_DS); + pagefault_disable(); + ret = strncpy_from_user(dst, unsafe_addr, count); + pagefault_enable(); + set_fs(old_fs); + + if (ret >= count) { + ret = count; + dst[ret - 1] = '\0'; + } else if (ret > 0) { + ret++; + } + + return ret; +} +#endif diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h index 4bcfbf389318..ba9981857fd1 100644 --- a/kernel/kernel_compat.h +++ b/kernel/kernel_compat.h @@ -24,6 +24,10 @@ extern long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, long count); +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) +extern struct key *init_session_keyring; +#endif + extern void ksu_android_ns_fs_check(); extern struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode); diff --git a/kernel/ksu.c b/kernel/ksu.c index d517c3b54396..3639edc21503 100644 --- a/kernel/ksu.c +++ b/kernel/ksu.c @@ -94,4 +94,7 @@ module_exit(kernelsu_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("weishu"); MODULE_DESCRIPTION("Android KernelSU"); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) MODULE_IMPORT_NS(VFS_internal_I_am_really_a_filesystem_and_am_NOT_a_driver); +#endif diff --git a/kernel/ksud.c b/kernel/ksud.c index 98fee107bf5e..68e473524284 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -6,7 +6,14 @@ #include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0) #include +#else +#include +#endif +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 1, 0) +#include +#endif #include #include #include @@ -463,6 +470,27 @@ bool ksu_is_safe_mode() #ifdef CONFIG_KPROBES +// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864 +static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + int *fd = (int *)&PT_REGS_PARM1(regs); + struct filename **filename_ptr = + (struct filename **)&PT_REGS_PARM2(regs); + struct user_arg_ptr argv; +#ifdef CONFIG_COMPAT + argv.is_compat = PT_REGS_PARM3(regs); + if (unlikely(argv.is_compat)) { + argv.ptr.compat = PT_REGS_CCALL_PARM4(regs); + } else { + argv.ptr.native = PT_REGS_CCALL_PARM4(regs); + } +#else + argv.ptr.native = PT_REGS_PARM3(regs); +#endif + + return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL); +} + static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) { struct pt_regs *real_regs = PT_REAL_REGS(regs); @@ -486,6 +514,18 @@ static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) NULL); } +// remove this later! +__maybe_unused static int vfs_read_handler_pre(struct kprobe *p, + struct pt_regs *regs) +{ + struct file **file_ptr = (struct file **)&PT_REGS_PARM1(regs); + char __user **buf_ptr = (char **)&PT_REGS_PARM2(regs); + size_t *count_ptr = (size_t *)&PT_REGS_PARM3(regs); + loff_t **pos_ptr = (loff_t **)&PT_REGS_CCALL_PARM4(regs); + + return ksu_handle_vfs_read(file_ptr, buf_ptr, count_ptr, pos_ptr); +} + static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs) { struct pt_regs *real_regs = PT_REAL_REGS(regs); @@ -505,16 +545,35 @@ static int input_handle_event_handler_pre(struct kprobe *p, return ksu_handle_input_handle_event(type, code, value); } +#if 1 static struct kprobe execve_kp = { .symbol_name = SYS_EXECVE_SYMBOL, .pre_handler = sys_execve_handler_pre, }; +#else +static struct kprobe execve_kp = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + .symbol_name = "do_execveat_common", +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + .symbol_name = "__do_execve_file", +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + .symbol_name = "do_execveat_common", +#endif + .pre_handler = execve_handler_pre, +}; +#endif +#if 1 static struct kprobe vfs_read_kp = { .symbol_name = SYS_READ_SYMBOL, .pre_handler = sys_read_handler_pre, }; - +#else +static struct kprobe vfs_read_kp = { + .symbol_name = "vfs_read", + .pre_handler = vfs_read_handler_pre, +}; +#endif static struct kprobe input_event_kp = { .symbol_name = "input_event", diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index b4e6eae0c1d7..1ba6d853f2a9 100644 --- a/kernel/selinux/rules.c +++ b/kernel/selinux/rules.c @@ -9,7 +9,9 @@ #include "linux/lsm_audit.h" #include "xfrm.h" +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) #define SELINUX_POLICY_INSTEAD_SELINUX_SS +#endif #define KERNEL_SU_DOMAIN "su" #define KERNEL_SU_FILE "ksu_file" @@ -19,8 +21,18 @@ static struct policydb *get_policydb(void) { struct policydb *db; +// selinux_state does not exists before 4.19 +#ifdef KSU_COMPAT_USE_SELINUX_STATE +#ifdef SELINUX_POLICY_INSTEAD_SELINUX_SS struct selinux_policy *policy = rcu_dereference(selinux_state.policy); db = &policy->policydb; +#else + struct selinux_ss *ss = rcu_dereference(selinux_state.ss); + db = &ss->policydb; +#endif +#else + db = &policydb; +#endif return db; } @@ -169,7 +181,8 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz, // reset avc cache table, otherwise the new rules will not take effect if already denied static void reset_avc_cache() { -#if (LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)) +#if ((!defined(KSU_COMPAT_USE_SELINUX_STATE)) || \ + LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0)) avc_ss_reset(0); selnl_notify_policyload(0); selinux_status_update_policyload(0); diff --git a/kernel/selinux/selinux.c b/kernel/selinux/selinux.c index 74095362089c..c333e8a800e6 100644 --- a/kernel/selinux/selinux.c +++ b/kernel/selinux/selinux.c @@ -2,6 +2,9 @@ #include "objsec.h" #include "linux/version.h" #include "../klog.h" // IWYU pragma: keep +#ifndef KSU_COMPAT_USE_SELINUX_STATE +#include "avc.h" +#endif #define KERNEL_SU_DOMAIN "u:r:su:s0" @@ -52,20 +55,32 @@ if (!is_domain_permissive) { void setenforce(bool enforce) { #ifdef CONFIG_SECURITY_SELINUX_DEVELOP +#ifdef KSU_COMPAT_USE_SELINUX_STATE selinux_state.enforcing = enforce; +#else + selinux_enforcing = enforce; +#endif #endif } bool getenforce() { #ifdef CONFIG_SECURITY_SELINUX_DISABLE +#ifdef KSU_COMPAT_USE_SELINUX_STATE if (selinux_state.disabled) { +#else + if (selinux_disabled) { +#endif return false; } #endif #ifdef CONFIG_SECURITY_SELINUX_DEVELOP +#ifdef KSU_COMPAT_USE_SELINUX_STATE return selinux_state.enforcing; +#else + return selinux_enforcing; +#endif #else return true; #endif diff --git a/kernel/selinux/selinux.h b/kernel/selinux/selinux.h index 88f1e7d30961..07120c253268 100644 --- a/kernel/selinux/selinux.h +++ b/kernel/selinux/selinux.h @@ -4,6 +4,10 @@ #include "linux/types.h" #include "linux/version.h" +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || defined(KSU_COMPAT_HAS_SELINUX_STATE) +#define KSU_COMPAT_USE_SELINUX_STATE +#endif + void setup_selinux(const char *); void setenforce(bool); diff --git a/kernel/selinux/sepolicy.c b/kernel/selinux/sepolicy.c index 7759602c765e..acdc45ad81f7 100644 --- a/kernel/selinux/sepolicy.c +++ b/kernel/selinux/sepolicy.c @@ -524,6 +524,7 @@ static bool add_filename_trans(struct policydb *db, const char *s, return false; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) struct filename_trans_key key; key.ttype = tgt->value; key.tclass = cls->value; @@ -531,8 +532,13 @@ static bool add_filename_trans(struct policydb *db, const char *s, struct filename_trans_datum *last = NULL; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) struct filename_trans_datum *trans = policydb_filenametr_search(db, &key); +#else + struct filename_trans_datum *trans = + hashtab_search(&db->filename_trans, &key); +#endif while (trans) { if (ebitmap_get_bit(&trans->stypes, src->value - 1)) { // Duplicate, overwrite existing data and return @@ -561,6 +567,39 @@ static bool add_filename_trans(struct policydb *db, const char *s, db->compat_filename_trans_count++; return ebitmap_set_bit(&trans->stypes, src->value - 1, 1) == 0; +#else // < 5.7.0, has no filename_trans_key, but struct filename_trans + + struct filename_trans key; + key.ttype = tgt->value; + key.tclass = cls->value; + key.name = (char *)o; + + struct filename_trans_datum *trans = + hashtab_search(db->filename_trans, &key); + + if (trans == NULL) { + trans = (struct filename_trans_datum *)kcalloc(sizeof(*trans), + 1, GFP_ATOMIC); + if (!trans) { + pr_err("add_filename_trans: Failed to alloc datum\n"); + return false; + } + struct filename_trans *new_key = + (struct filename_trans *)kmalloc(sizeof(*new_key), + GFP_ATOMIC); + if (!new_key) { + pr_err("add_filename_trans: Failed to alloc new_key\n"); + return false; + } + *new_key = key; + new_key->name = kstrdup(key.name, GFP_ATOMIC); + trans->otype = def->value; + hashtab_insert(db->filename_trans, new_key, trans); + } + + return ebitmap_set_bit(&db->filename_trans_ttypes, src->value - 1, 1) == + 0; +#endif } static bool add_genfscon(struct policydb *db, const char *fs_name, @@ -587,6 +626,7 @@ static void *ksu_realloc(void *old, size_t new_size, size_t old_size) static bool add_type(struct policydb *db, const char *type_name, bool attr) { +#ifdef KSU_SUPPORT_ADD_TYPE struct type_datum *type = symtab_search(&db->p_types, type_name); if (type) { pr_warn("Type %s already exists\n", type_name); @@ -616,6 +656,7 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr) return false; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) struct ebitmap *new_type_attr_map_array = ksu_realloc(db->type_attr_map_array, value * sizeof(struct ebitmap), @@ -662,6 +703,171 @@ static bool add_type(struct policydb *db, const char *type_name, bool attr) } return true; +#elif defined(CONFIG_IS_HW_HISI) + /* + * Huawei use type_attr_map and type_val_to_struct. + * And use ebitmap not flex_array. + */ + size_t new_size = sizeof(struct ebitmap) * db->p_types.nprim; + struct ebitmap *new_type_attr_map = + (krealloc(db->type_attr_map, new_size, GFP_ATOMIC)); + + struct type_datum **new_type_val_to_struct = + krealloc(db->type_val_to_struct, + sizeof(*db->type_val_to_struct) * db->p_types.nprim, + GFP_ATOMIC); + + if (!new_type_attr_map) { + pr_err("add_type: alloc type_attr_map failed\n"); + return false; + } + + if (!new_type_val_to_struct) { + pr_err("add_type: alloc type_val_to_struct failed\n"); + return false; + } + + char **new_val_to_name_types = + krealloc(db->sym_val_to_name[SYM_TYPES], + sizeof(char *) * db->symtab[SYM_TYPES].nprim, + GFP_KERNEL); + if (!new_val_to_name_types) { + pr_err("add_type: alloc val_to_name failed\n"); + return false; + } + + db->type_attr_map = new_type_attr_map; + ebitmap_init(&db->type_attr_map[value - 1], HISI_SELINUX_EBITMAP_RO); + ebitmap_set_bit(&db->type_attr_map[value - 1], value - 1, 1); + + db->type_val_to_struct = new_type_val_to_struct; + db->type_val_to_struct[value - 1] = type; + + db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types; + db->sym_val_to_name[SYM_TYPES][value - 1] = key; + + int i; + for (i = 0; i < db->p_roles.nprim; ++i) { + ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1, + 1); + } + + return true; +#else + // flex_array is not extensible, we need to create a new bigger one instead + struct flex_array *new_type_attr_map_array = + flex_array_alloc(sizeof(struct ebitmap), db->p_types.nprim, + GFP_ATOMIC | __GFP_ZERO); + + struct flex_array *new_type_val_to_struct = + flex_array_alloc(sizeof(struct type_datum *), db->p_types.nprim, + GFP_ATOMIC | __GFP_ZERO); + + struct flex_array *new_val_to_name_types = + flex_array_alloc(sizeof(char *), db->symtab[SYM_TYPES].nprim, + GFP_ATOMIC | __GFP_ZERO); + + if (!new_type_attr_map_array) { + pr_err("add_type: alloc type_attr_map_array failed\n"); + return false; + } + + if (!new_type_val_to_struct) { + pr_err("add_type: alloc type_val_to_struct failed\n"); + return false; + } + + if (!new_val_to_name_types) { + pr_err("add_type: alloc val_to_name failed\n"); + return false; + } + + // preallocate so we don't have to worry about the put ever failing + if (flex_array_prealloc(new_type_attr_map_array, 0, db->p_types.nprim, + GFP_ATOMIC | __GFP_ZERO)) { + pr_err("add_type: prealloc type_attr_map_array failed\n"); + return false; + } + + if (flex_array_prealloc(new_type_val_to_struct, 0, db->p_types.nprim, + GFP_ATOMIC | __GFP_ZERO)) { + pr_err("add_type: prealloc type_val_to_struct_array failed\n"); + return false; + } + + if (flex_array_prealloc(new_val_to_name_types, 0, + db->symtab[SYM_TYPES].nprim, + GFP_ATOMIC | __GFP_ZERO)) { + pr_err("add_type: prealloc val_to_name_types failed\n"); + return false; + } + + int j; + void *old_elem; + // copy the old data or pointers to new flex arrays + for (j = 0; j < db->type_attr_map_array->total_nr_elements; j++) { + old_elem = flex_array_get(db->type_attr_map_array, j); + if (old_elem) + flex_array_put(new_type_attr_map_array, j, old_elem, + GFP_ATOMIC | __GFP_ZERO); + } + + for (j = 0; j < db->type_val_to_struct_array->total_nr_elements; j++) { + old_elem = flex_array_get_ptr(db->type_val_to_struct_array, j); + if (old_elem) + flex_array_put_ptr(new_type_val_to_struct, j, old_elem, + GFP_ATOMIC | __GFP_ZERO); + } + + for (j = 0; j < db->symtab[SYM_TYPES].nprim; j++) { + old_elem = + flex_array_get_ptr(db->sym_val_to_name[SYM_TYPES], j); + if (old_elem) + flex_array_put_ptr(new_val_to_name_types, j, old_elem, + GFP_ATOMIC | __GFP_ZERO); + } + + // store the pointer of old flex arrays first, when assigning new ones we + // should free it + struct flex_array *old_fa; + + old_fa = db->type_attr_map_array; + db->type_attr_map_array = new_type_attr_map_array; + if (old_fa) { + flex_array_free(old_fa); + } + + ebitmap_init(flex_array_get(db->type_attr_map_array, value - 1)); + ebitmap_set_bit(flex_array_get(db->type_attr_map_array, value - 1), + value - 1, 1); + + old_fa = db->type_val_to_struct_array; + db->type_val_to_struct_array = new_type_val_to_struct; + if (old_fa) { + flex_array_free(old_fa); + } + flex_array_put_ptr(db->type_val_to_struct_array, value - 1, type, + GFP_ATOMIC | __GFP_ZERO); + + old_fa = db->sym_val_to_name[SYM_TYPES]; + db->sym_val_to_name[SYM_TYPES] = new_val_to_name_types; + if (old_fa) { + flex_array_free(old_fa); + } + flex_array_put_ptr(db->sym_val_to_name[SYM_TYPES], value - 1, key, + GFP_ATOMIC | __GFP_ZERO); + + int i; + for (i = 0; i < db->p_roles.nprim; ++i) { + ebitmap_set_bit(&db->role_val_to_struct[i]->types, value - 1, + 1); + } + return true; +#endif + +#else + return false; +#endif } static bool set_type_state(struct policydb *db, const char *type_name, @@ -696,7 +902,18 @@ static bool set_type_state(struct policydb *db, const char *type_name, static void add_typeattribute_raw(struct policydb *db, struct type_datum *type, struct type_datum *attr) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) struct ebitmap *sattr = &db->type_attr_map_array[type->value - 1]; +#elif defined(CONFIG_IS_HW_HISI) + /* + * HISI_SELINUX_EBITMAP_RO is Huawei's unique features. + */ + struct ebitmap *sattr = &db->type_attr_map[type->value - 1], + HISI_SELINUX_EBITMAP_RO; +#else + struct ebitmap *sattr = + flex_array_get(db->type_attr_map_array, type->value - 1); +#endif ebitmap_set_bit(sattr, attr->value - 1, 1); struct hashtab_node *node; diff --git a/kernel/sucompat.c b/kernel/sucompat.c index 966cbf8fa854..9b45cd0d9490 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -8,7 +8,11 @@ #include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) #include +#else +#include +#endif #include "objsec.h" #include "allowlist.h" @@ -180,7 +184,12 @@ int ksu_handle_devpts(struct inode *inode) return 0; if (ksu_devpts_sid) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) struct inode_security_struct *sec = selinux_inode(inode); +#else + struct inode_security_struct *sec = + (struct inode_security_struct *)inode->i_security; +#endif if (sec) { sec->sid = ksu_devpts_sid; } @@ -191,6 +200,18 @@ int ksu_handle_devpts(struct inode *inode) #ifdef CONFIG_KPROBES +__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); + int *mode = (int *)&PT_REGS_PARM3(regs); + // Both sys_ and do_ is C function + int *flags = (int *)&PT_REGS_CCALL_PARM4(regs); + + return ksu_handle_faccessat(dfd, filename_user, mode, flags); +} + static int sys_faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) { struct pt_regs *real_regs = PT_REAL_REGS(regs); @@ -202,16 +223,43 @@ static int sys_faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) return ksu_handle_faccessat(dfd, filename_user, mode, NULL); } +__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) + int *flags = (int *)&PT_REGS_PARM3(regs); +#else + // int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,int flag) + int *flags = (int *)&PT_REGS_CCALL_PARM4(regs); +#endif + + return ksu_handle_stat(dfd, filename_user, flags); +} + 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); } +// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864 +static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) +{ + int *fd = (int *)&PT_REGS_PARM1(regs); + struct filename **filename_ptr = + (struct filename **)&PT_REGS_PARM2(regs); + + return ksu_handle_execveat_sucompat(fd, filename_ptr, NULL, NULL, NULL); +} + static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) { struct pt_regs *real_regs = PT_REAL_REGS(regs); @@ -222,26 +270,65 @@ static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs) NULL); } +#if 1 static struct kprobe faccessat_kp = { .symbol_name = SYS_FACCESSAT_SYMBOL, .pre_handler = sys_faccessat_handler_pre, }; +#else +static struct kprobe faccessat_kp = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) + .symbol_name = "do_faccessat", +#else + .symbol_name = "sys_faccessat", +#endif + .pre_handler = faccessat_handler_pre, +}; +#endif +#if 1 static struct kprobe newfstatat_kp = { .symbol_name = SYS_NEWFSTATAT_SYMBOL, .pre_handler = sys_newfstatat_handler_pre, }; +#else +static struct kprobe newfstatat_kp = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) + .symbol_name = "vfs_statx", +#else + .symbol_name = "vfs_fstatat", +#endif + .pre_handler = newfstatat_handler_pre, +}; +#endif +#if 1 static struct kprobe execve_kp = { .symbol_name = SYS_EXECVE_SYMBOL, .pre_handler = sys_execve_handler_pre, }; +#else +static struct kprobe execve_kp = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) + .symbol_name = "do_execveat_common", +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(4, 19, 0) + .symbol_name = "__do_execve_file", +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(3, 19, 0) + .symbol_name = "do_execveat_common", +#endif + .pre_handler = execve_handler_pre, +}; +#endif static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs) { struct inode *inode; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0) struct file *file = (struct file *)PT_REGS_PARM2(regs); inode = file->f_path.dentry->d_inode; +#else + inode = (struct inode *)PT_REGS_PARM2(regs); +#endif return ksu_handle_devpts(inode); } diff --git a/kernel/throne_tracker.c b/kernel/throne_tracker.c index eb04c8b0f344..c709a6921a23 100644 --- a/kernel/throne_tracker.c +++ b/kernel/throne_tracker.c @@ -170,7 +170,11 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name, } else { if ((namelen == 8) && (strncmp(name, "base.apk", namelen) == 0)) { struct apk_path_hash *pos, *n; +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 8, 0) + unsigned int hash = full_name_hash(dirpath, strlen(dirpath)); +#else unsigned int hash = full_name_hash(NULL, dirpath, strlen(dirpath)); +#endif list_for_each_entry(pos, &apk_path_hash_list, list) { if (hash == pos->hash) { pos->exists = true;