|
4 | 4 | #include "linux/err.h"
|
5 | 5 | #include "linux/init.h"
|
6 | 6 | #include "linux/init_task.h"
|
| 7 | +#include "linux/kallsyms.h" |
7 | 8 | #include "linux/kernel.h"
|
8 | 9 | #include "linux/kprobes.h"
|
| 10 | +#include "linux/list.h" |
9 | 11 | #include "linux/lsm_hooks.h"
|
10 | 12 | #include "linux/module.h"
|
| 13 | +#include "linux/mm.h" |
| 14 | +#include "linux/mm_types.h" |
11 | 15 | #include "linux/nsproxy.h"
|
12 | 16 | #include "linux/path.h"
|
13 | 17 | #include "linux/printk.h"
|
| 18 | +#include "linux/sched.h" |
| 19 | +#include "linux/security.h" |
| 20 | +#include "linux/stddef.h" |
| 21 | +#include "linux/types.h" |
14 | 22 | #include "linux/uaccess.h"
|
15 | 23 | #include "linux/uidgid.h"
|
16 | 24 | #include "linux/version.h"
|
|
28 | 36 | #include "klog.h" // IWYU pragma: keep
|
29 | 37 | #include "ksu.h"
|
30 | 38 | #include "ksud.h"
|
| 39 | +#include "linux/vmalloc.h" |
31 | 40 | #include "manager.h"
|
32 | 41 | #include "selinux/selinux.h"
|
33 | 42 | #include "uid_observer.h"
|
@@ -243,7 +252,7 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
243 | 252 | #ifdef CONFIG_KSU_DEBUG
|
244 | 253 | pr_info("manager already exist: %d\n",
|
245 | 254 | ksu_get_manager_uid());
|
246 |
| -#endif |
| 255 | +#endif |
247 | 256 | return 0;
|
248 | 257 | }
|
249 | 258 |
|
@@ -735,14 +744,181 @@ void __init ksu_lsm_hook_init(void)
|
735 | 744 | #endif
|
736 | 745 | }
|
737 | 746 |
|
| 747 | +#ifdef MODULE |
| 748 | +static int override_security_head(void *head, const void *new_head, size_t len) |
| 749 | +{ |
| 750 | + unsigned long base = (unsigned long)head & PAGE_MASK; |
| 751 | + unsigned long offset = offset_in_page(head); |
| 752 | + |
| 753 | + // this is impossible for our case because the page alignment |
| 754 | + // but be careful for other cases! |
| 755 | + BUG_ON(offset + len > PAGE_SIZE); |
| 756 | + struct page *page = phys_to_page(__pa(base)); |
| 757 | + if (!page) { |
| 758 | + return -EFAULT; |
| 759 | + } |
| 760 | + |
| 761 | + void *addr = vmap(&page, 1, VM_MAP, PAGE_KERNEL); |
| 762 | + if (!addr) { |
| 763 | + return -ENOMEM; |
| 764 | + } |
| 765 | + memcpy(addr + offset, new_head, len); |
| 766 | + vunmap(addr); |
| 767 | + return 0; |
| 768 | +} |
| 769 | + |
| 770 | +static void free_security_hook_list(struct hlist_head *head) |
| 771 | +{ |
| 772 | + struct hlist_node *temp; |
| 773 | + struct security_hook_list *entry; |
| 774 | + |
| 775 | + if (!head) |
| 776 | + return; |
| 777 | + |
| 778 | + hlist_for_each_entry_safe (entry, temp, head, list) { |
| 779 | + hlist_del(&entry->list); |
| 780 | + kfree(entry); |
| 781 | + } |
| 782 | + |
| 783 | + kfree(head); |
| 784 | +} |
| 785 | + |
| 786 | +struct hlist_head *copy_security_hlist(struct hlist_head *orig) |
| 787 | +{ |
| 788 | + struct hlist_head *new_head = kmalloc(sizeof(*new_head), GFP_KERNEL); |
| 789 | + if (!new_head) |
| 790 | + return NULL; |
| 791 | + |
| 792 | + INIT_HLIST_HEAD(new_head); |
| 793 | + |
| 794 | + struct security_hook_list *entry; |
| 795 | + struct security_hook_list *new_entry; |
| 796 | + |
| 797 | + hlist_for_each_entry (entry, orig, list) { |
| 798 | + new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL); |
| 799 | + if (!new_entry) { |
| 800 | + free_security_hook_list(new_head); |
| 801 | + return NULL; |
| 802 | + } |
| 803 | + |
| 804 | + *new_entry = *entry; |
| 805 | + |
| 806 | + hlist_add_tail_rcu(&new_entry->list, new_head); |
| 807 | + } |
| 808 | + |
| 809 | + return new_head; |
| 810 | +} |
| 811 | + |
| 812 | +#define LSM_SEARCH_MAX 180 // This should be enough to iterate |
| 813 | +static void *find_head_addr(void *security_ptr, int *index) |
| 814 | +{ |
| 815 | + if (!security_ptr) { |
| 816 | + return NULL; |
| 817 | + } |
| 818 | + struct hlist_head *head_start = |
| 819 | + (struct hlist_head *)&security_hook_heads; |
| 820 | + |
| 821 | + for (int i = 0; i < LSM_SEARCH_MAX; i++) { |
| 822 | + struct hlist_head *head = head_start + i; |
| 823 | + struct security_hook_list *pos; |
| 824 | + hlist_for_each_entry (pos, head, list) { |
| 825 | + if (pos->hook.capget == security_ptr) { |
| 826 | + if (index) { |
| 827 | + *index = i; |
| 828 | + } |
| 829 | + return head; |
| 830 | + } |
| 831 | + } |
| 832 | + } |
| 833 | + |
| 834 | + return NULL; |
| 835 | +} |
| 836 | + |
| 837 | +#define GET_SYMBOL_ADDR(sym) \ |
| 838 | + ({ \ |
| 839 | + void *addr = kallsyms_lookup_name(#sym ".cfi_jt"); \ |
| 840 | + if (!addr) { \ |
| 841 | + addr = kallsyms_lookup_name(#sym); \ |
| 842 | + } \ |
| 843 | + addr; \ |
| 844 | + }) |
| 845 | + |
| 846 | +#define KSU_LSM_HOOK_HACK_INIT(head_ptr, name, func) \ |
| 847 | + do { \ |
| 848 | + static struct security_hook_list hook = { \ |
| 849 | + .hook = { .name = func } \ |
| 850 | + }; \ |
| 851 | + hook.head = head_ptr; \ |
| 852 | + hook.lsm = "ksu"; \ |
| 853 | + struct hlist_head *new_head = copy_security_hlist(hook.head); \ |
| 854 | + if (!new_head) { \ |
| 855 | + pr_err("Failed to copy security list: %s\n", #name); \ |
| 856 | + break; \ |
| 857 | + } \ |
| 858 | + hlist_add_tail_rcu(&hook.list, new_head); \ |
| 859 | + if (override_security_head(hook.head, new_head, \ |
| 860 | + sizeof(*new_head))) { \ |
| 861 | + free_security_hook_list(new_head); \ |
| 862 | + pr_err("Failed to hack lsm for: %s\n", #name); \ |
| 863 | + } \ |
| 864 | + } while (0) |
| 865 | + |
| 866 | +void __init ksu_lsm_hook_init_hack(void) |
| 867 | +{ |
| 868 | + void *cap_prctl = GET_SYMBOL_ADDR(cap_task_prctl); |
| 869 | + void *prctl_head = find_head_addr(cap_prctl, NULL); |
| 870 | + if (prctl_head) { |
| 871 | + if (prctl_head != &security_hook_heads.task_prctl) { |
| 872 | + pr_warn("prctl's address has shifted!\n"); |
| 873 | + } |
| 874 | + KSU_LSM_HOOK_HACK_INIT(prctl_head, task_prctl, ksu_task_prctl); |
| 875 | + } else { |
| 876 | + pr_warn("Failed to find task_prctl!\n"); |
| 877 | + } |
| 878 | + |
| 879 | + int inode_killpriv_index = -1; |
| 880 | + void *cap_killpriv = GET_SYMBOL_ADDR(cap_inode_killpriv); |
| 881 | + find_head_addr(cap_killpriv, &inode_killpriv_index); |
| 882 | + if (inode_killpriv_index < 0) { |
| 883 | + pr_warn("Failed to find inode_rename, use kprobe instead!\n"); |
| 884 | + register_kprobe(&renameat_kp); |
| 885 | + } else { |
| 886 | + int inode_rename_index = inode_killpriv_index + |
| 887 | + &security_hook_heads.inode_rename - |
| 888 | + &security_hook_heads.inode_killpriv; |
| 889 | + struct hlist_head *head_start = |
| 890 | + (struct hlist_head *)&security_hook_heads; |
| 891 | + void *inode_rename_head = head_start + inode_rename_index; |
| 892 | + if (inode_rename_head != &security_hook_heads.inode_rename) { |
| 893 | + pr_warn("inode_rename's address has shifted!\n"); |
| 894 | + } |
| 895 | + KSU_LSM_HOOK_HACK_INIT(inode_rename_head, inode_rename, |
| 896 | + ksu_inode_rename); |
| 897 | + } |
| 898 | + void *cap_setuid = GET_SYMBOL_ADDR(cap_task_fix_setuid); |
| 899 | + void *setuid_head = find_head_addr(cap_setuid, NULL); |
| 900 | + if (setuid_head) { |
| 901 | + if (setuid_head != &security_hook_heads.task_fix_setuid) { |
| 902 | + pr_warn("setuid's address has shifted!\n"); |
| 903 | + } |
| 904 | + KSU_LSM_HOOK_HACK_INIT(setuid_head, task_fix_setuid, |
| 905 | + ksu_task_fix_setuid); |
| 906 | + } else { |
| 907 | + pr_warn("Failed to find task_fix_setuid!\n"); |
| 908 | + } |
| 909 | + smp_mb(); |
| 910 | +} |
| 911 | +#endif |
| 912 | + |
738 | 913 | void __init ksu_core_init(void)
|
739 | 914 | {
|
740 | 915 | #ifndef MODULE
|
741 | 916 | pr_info("ksu_lsm_hook_init\n");
|
742 | 917 | ksu_lsm_hook_init();
|
| 918 | + |
743 | 919 | #else
|
744 |
| - pr_info("ksu_kprobe_init\n"); |
745 |
| - ksu_kprobe_init(); |
| 920 | + pr_info("ksu_lsm_hook_init hack!!!!\n"); |
| 921 | + ksu_lsm_hook_init_hack(); |
746 | 922 | #endif
|
747 | 923 | }
|
748 | 924 |
|
|
0 commit comments