Update tasktracker
@@ -102,7 +102,7 @@ | ||
102 | 102 | Version 0.1 2014/04/15 Initial backport. |
103 | 103 | |
104 | 104 | Backported the version posted to Linux Security Module ML (readable |
105 | - at http://lwn.net/Articles/575044/ ) as a loadable kernel module. | |
105 | + at https://lwn.net/Articles/575044/ ) as a loadable kernel module. | |
106 | 106 | |
107 | 107 | Version 0.2 2014/04/20 Bug fix. |
108 | 108 |
@@ -18,6 +18,29 @@ | ||
18 | 18 | |
19 | 19 | #include "probe.h" |
20 | 20 | |
21 | +static void *__init check_function_address(void *ptr, char *symbol) | |
22 | +{ | |
23 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) | |
24 | + static char buf[KSYM_SYMBOL_LEN]; | |
25 | + const int len = strlen(symbol); | |
26 | +#endif | |
27 | + | |
28 | + if (!ptr) { | |
29 | + printk(KERN_ERR "Can't resolve %s().\n", symbol); | |
30 | + return NULL; | |
31 | + } | |
32 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) | |
33 | + snprintf(buf, sizeof(buf), "%pS", ptr); | |
34 | + if (strncmp(buf, symbol, len) || strncmp(buf + len, "+0x0/", 5)) { | |
35 | + printk(KERN_ERR "Guessed %s is %s\n", symbol, buf); | |
36 | + return NULL; | |
37 | + } | |
38 | +#else | |
39 | + printk(KERN_INFO "%s=%p\n", symbol, ptr); | |
40 | +#endif | |
41 | + return ptr; | |
42 | +} | |
43 | + | |
21 | 44 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) |
22 | 45 | |
23 | 46 | /** |
@@ -41,6 +64,7 @@ | ||
41 | 64 | mm_segment_t old_fs; |
42 | 65 | unsigned long pos = file->f_pos; |
43 | 66 | int result; |
67 | + | |
44 | 68 | file->f_pos = offset; |
45 | 69 | old_fs = get_fs(); |
46 | 70 | set_fs(get_ds()); |
@@ -48,8 +72,12 @@ | ||
48 | 72 | set_fs(old_fs); |
49 | 73 | file->f_pos = pos; |
50 | 74 | return result; |
75 | +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) | |
76 | + return kernel_read(file, offset, addr, count); | |
51 | 77 | #else |
52 | - return kernel_read(file, offset, addr, count); | |
78 | + loff_t pos = offset; | |
79 | + | |
80 | + return kernel_read(file, addr, count, &pos); | |
53 | 81 | #endif |
54 | 82 | } |
55 | 83 |
@@ -65,6 +93,7 @@ | ||
65 | 93 | struct file *file = NULL; |
66 | 94 | char *buf; |
67 | 95 | unsigned long entry = 0; |
96 | + | |
68 | 97 | { |
69 | 98 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) |
70 | 99 | struct file_system_type *fstype = get_fs_type("proc"); |
@@ -78,6 +107,7 @@ | ||
78 | 107 | #endif |
79 | 108 | struct dentry *root; |
80 | 109 | struct dentry *dentry; |
110 | + | |
81 | 111 | /* |
82 | 112 | * We embed put_filesystem() here because it is not exported. |
83 | 113 | */ |
@@ -86,7 +116,11 @@ | ||
86 | 116 | if (IS_ERR(mnt)) |
87 | 117 | goto out; |
88 | 118 | root = dget(mnt->mnt_root); |
89 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) | |
119 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) | |
120 | + inode_lock(root->d_inode); | |
121 | + dentry = lookup_one_len("kallsyms", root, 8); | |
122 | + inode_unlock(root->d_inode); | |
123 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) | |
90 | 124 | mutex_lock(&root->d_inode->i_mutex); |
91 | 125 | dentry = lookup_one_len("kallsyms", root, 8); |
92 | 126 | mutex_unlock(&root->d_inode->i_mutex); |
@@ -100,7 +134,8 @@ | ||
100 | 134 | mntput(mnt); |
101 | 135 | else { |
102 | 136 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) |
103 | - struct path path = { mnt, dentry }; | |
137 | + struct path path = { .mnt = mnt, .dentry = dentry }; | |
138 | + | |
104 | 139 | file = dentry_open(&path, O_RDONLY, current_cred()); |
105 | 140 | #else |
106 | 141 | file = dentry_open(dentry, mnt, O_RDONLY |
@@ -117,9 +152,11 @@ | ||
117 | 152 | if (buf) { |
118 | 153 | int len; |
119 | 154 | int offset = 0; |
155 | + | |
120 | 156 | while ((len = probe_kernel_read(file, offset, buf, |
121 | 157 | PAGE_SIZE - 1)) > 0) { |
122 | 158 | char *cp; |
159 | + | |
123 | 160 | buf[len] = '\0'; |
124 | 161 | cp = strrchr(buf, '\n'); |
125 | 162 | if (!cp) |
@@ -144,13 +181,13 @@ | ||
144 | 181 | |
145 | 182 | #endif |
146 | 183 | |
147 | -#if defined(CONFIG_SECURITY_COMPOSER_MAX) | |
184 | +#if defined(LSM_HOOK_INIT) | |
148 | 185 | |
149 | 186 | /* |
150 | 187 | * Dummy variable for finding location of |
151 | - * "struct list_head lsm_hooks[LSM_MAX_HOOKS]". | |
188 | + * "struct security_hook_heads security_hook_heads". | |
152 | 189 | */ |
153 | -struct list_head probe_lsm_hooks[LSM_MAX_HOOKS]; | |
190 | +struct security_hook_heads probe_dummy_security_hook_heads; | |
154 | 191 | |
155 | 192 | /** |
156 | 193 | * probe_security_bprm_committed_creds - Dummy function which does identical to security_bprm_committed_creds() in security/security.c. |
@@ -162,15 +199,96 @@ | ||
162 | 199 | void probe_security_bprm_committed_creds(struct linux_binprm *bprm) |
163 | 200 | { |
164 | 201 | do { |
165 | - struct security_operations *sop; | |
166 | - | |
167 | - list_for_each_entry(sop, | |
168 | - &probe_lsm_hooks[lsm_bprm_committed_creds], | |
169 | - list[lsm_bprm_committed_creds]) | |
170 | - sop->bprm_committed_creds(bprm); | |
202 | + struct security_hook_list *p; | |
203 | + | |
204 | + list_for_each_entry(p, &probe_dummy_security_hook_heads. | |
205 | + bprm_committed_creds, list) | |
206 | + p->hook.bprm_committed_creds(bprm); | |
171 | 207 | } while (0); |
172 | 208 | } |
173 | 209 | |
210 | +#if defined(CONFIG_ARM64) | |
211 | + | |
212 | +/** | |
213 | + * probe_security_hook_heads_on_arm - Find security_hook_heads on ARM64. | |
214 | + * | |
215 | + * @base: Address of security_bprm_committed_creds(). | |
216 | + * | |
217 | + * Returns address of security_hook_heads.bprm_committed_creds on success, | |
218 | + * NULL otherwise. | |
219 | + */ | |
220 | +static void * __init probe_security_hook_heads_on_arm64(unsigned int *base) | |
221 | +{ | |
222 | + static unsigned int *ip4ret; | |
223 | + int i; | |
224 | + unsigned int *ip = (unsigned int *) base; | |
225 | + | |
226 | + for (i = 0; i < 32; ip++, i++) { | |
227 | + unsigned long tmp; | |
228 | + unsigned long offset; | |
229 | + | |
230 | + /* | |
231 | + * Find | |
232 | + * adrp Xd, #imm21 | |
233 | + * add Xd, Xn, #uimm12 | |
234 | + * add Xd, Xn, #uimm12 | |
235 | + * sequence. | |
236 | + * But don't mandate the last one in case it was optimized. | |
237 | + */ | |
238 | + if ((*ip & 0x98000000) != 0x90000000 || | |
239 | + (*(ip + 1) & 0xFFC00000) != 0x91000000) | |
240 | + continue; | |
241 | + tmp = ((unsigned long) ip) & ~0xFFFUL; | |
242 | + offset = (unsigned long) (((((*ip >> 5) & 0x007FFFF) << 2) | | |
243 | + ((*ip >> 29) & 0x3))) << 12; | |
244 | + if (offset & 0x100000000UL) | |
245 | + offset |= 0xFFFFFFFF00000000UL; | |
246 | + tmp += offset; | |
247 | + offset = (*(ip + 1) >> 10) & 0xFFF; | |
248 | + tmp += offset; | |
249 | + if ((*(ip + 2) & 0xFFC00000) == 0x91000000) { | |
250 | + offset = (*(ip + 2) >> 10) & 0xFFF; | |
251 | + tmp += offset; | |
252 | + } | |
253 | + ip4ret = (unsigned int *) tmp; | |
254 | + return &ip4ret; | |
255 | + } | |
256 | + return NULL; | |
257 | +} | |
258 | + | |
259 | +#endif | |
260 | + | |
261 | +#if defined(CONFIG_ARM) | |
262 | + | |
263 | +/** | |
264 | + * probe_security_hook_heads_on_arm - Find security_hook_heads on ARM. | |
265 | + * | |
266 | + * @base: Address of security_bprm_committed_creds(). | |
267 | + * | |
268 | + * Returns address of security_hook_heads.bprm_committed_creds on success, | |
269 | + * NULL otherwise. | |
270 | + */ | |
271 | +static void * __init probe_security_hook_heads_on_arm(unsigned int *base) | |
272 | +{ | |
273 | + static unsigned int *ip4ret; | |
274 | + int i; | |
275 | + const unsigned long addr = (unsigned long) &probe_dummy_security_hook_heads; | |
276 | + const unsigned long offset = (unsigned long) &probe_dummy_security_hook_heads.bprm_committed_creds - addr; | |
277 | + unsigned int *ip = (unsigned int *) probe_security_bprm_committed_creds; | |
278 | + | |
279 | + for (i = 0; i < 32; ip++, i++) { | |
280 | + if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) | |
281 | + continue; | |
282 | + ip = base + i; | |
283 | + ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); | |
284 | + ip4ret += offset >> 2; | |
285 | + return &ip4ret; | |
286 | + } | |
287 | + return NULL; | |
288 | +} | |
289 | + | |
290 | +#endif | |
291 | + | |
174 | 292 | #elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) |
175 | 293 | |
176 | 294 | /* |
@@ -206,6 +324,7 @@ | ||
206 | 324 | int i; |
207 | 325 | const unsigned long addr = (unsigned long) &probe_dummy_security_ops; |
208 | 326 | unsigned int *ip = (unsigned int *) probe_security_file_alloc; |
327 | + | |
209 | 328 | for (i = 0; i < 32; ip++, i++) { |
210 | 329 | if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) |
211 | 330 | continue; |
@@ -255,8 +374,10 @@ | ||
255 | 374 | unsigned int *base) |
256 | 375 | { |
257 | 376 | int i; |
377 | + | |
258 | 378 | for (i = 0; i < 32; ip++, i++) { |
259 | 379 | static unsigned int *ip4ret; |
380 | + | |
260 | 381 | if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) |
261 | 382 | continue; |
262 | 383 | ip = base + i; |
@@ -287,6 +408,7 @@ | ||
287 | 408 | int i; |
288 | 409 | u8 *base; |
289 | 410 | u8 *cp = function; |
411 | + | |
290 | 412 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) |
291 | 413 | if (*symbol == ' ') |
292 | 414 | base = probe_find_symbol(symbol); |
@@ -295,7 +417,15 @@ | ||
295 | 417 | base = __symbol_get(symbol); |
296 | 418 | if (!base) |
297 | 419 | return NULL; |
298 | -#if defined(CONFIG_ARM) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) && !defined(CONFIG_SECURITY_COMPOSER_MAX) | |
420 | +#if defined(CONFIG_ARM64) && defined(LSM_HOOK_INIT) | |
421 | + if (function == probe_security_bprm_committed_creds) | |
422 | + return probe_security_hook_heads_on_arm64((unsigned int *) base); | |
423 | +#endif | |
424 | +#if defined(CONFIG_ARM) && defined(LSM_HOOK_INIT) | |
425 | + if (function == probe_security_bprm_committed_creds) | |
426 | + return probe_security_hook_heads_on_arm((unsigned int *) base); | |
427 | +#endif | |
428 | +#if defined(CONFIG_ARM) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) && !defined(LSM_HOOK_INIT) | |
299 | 429 | if (function == probe_security_file_alloc) |
300 | 430 | return probe_security_ops_on_arm((unsigned int *) base); |
301 | 431 | #endif |
@@ -315,6 +445,7 @@ | ||
315 | 445 | for (i = 0; i < 128; i++) { |
316 | 446 | if ((unsigned long) (cp + (*(int *) cp) * 2 - 2) == addr) { |
317 | 447 | static void *cp4ret; |
448 | + | |
318 | 449 | cp = base + i; |
319 | 450 | cp += (*(int *) cp) * 2 - 2; |
320 | 451 | cp4ret = cp; |
@@ -327,6 +458,7 @@ | ||
327 | 458 | for (i = 0; i < 128; i++) { |
328 | 459 | if ((unsigned long) (cp + sizeof(int) + *(int *) cp) == addr) { |
329 | 460 | static void *cp4ret; |
461 | + | |
330 | 462 | cp = base + i; |
331 | 463 | cp += sizeof(int) + *(int *) cp; |
332 | 464 | cp4ret = cp; |
@@ -338,6 +470,7 @@ | ||
338 | 470 | for (i = 0; i < 128; i++) { |
339 | 471 | if ((unsigned long) (long) (*(int *) cp) == addr) { |
340 | 472 | static void *cp4ret; |
473 | + | |
341 | 474 | cp = base + i; |
342 | 475 | cp = (void *) (long) (*(int *) cp); |
343 | 476 | cp4ret = cp; |
@@ -348,47 +481,43 @@ | ||
348 | 481 | return NULL; |
349 | 482 | } |
350 | 483 | |
351 | -#if defined(CONFIG_SECURITY_COMPOSER_MAX) | |
484 | +#if defined(LSM_HOOK_INIT) | |
352 | 485 | |
353 | 486 | /** |
354 | - * probe_lsm_hooks_list - Find address of "struct list_head lsm_hooks[LSM_MAX_HOOKS]". | |
487 | + * probe_security_hook_heads - Find address of "struct security_hook_heads security_hook_heads". | |
355 | 488 | * |
356 | - * Returns pointer to "struct security_operations" on success, NULL otherwise. | |
489 | + * Returns pointer to "struct security_hook_heads" on success, NULL otherwise. | |
357 | 490 | */ |
358 | -struct list_head * __init probe_lsm_hooks_list(void) | |
491 | +struct security_hook_heads * __init probe_security_hook_heads(void) | |
359 | 492 | { |
360 | - unsigned int offset = 0; | |
493 | + const unsigned int offset = offsetof(struct security_hook_heads, | |
494 | + bprm_committed_creds); | |
361 | 495 | void *cp; |
362 | - /* Guess "struct list_head lsm_hooks[LSM_MAX_HOOKS];". */ | |
363 | - /* Try without offset. GCC 4.x seems to use this one. */ | |
496 | + struct security_hook_heads *shh; | |
497 | + struct security_hook_list *entry; | |
498 | + void *cap = probe_find_symbol(" cap_bprm_set_creds\n"); | |
499 | + | |
500 | + /* Get location of cap_bprm_set_creds(). */ | |
501 | + cap = check_function_address(cap, "cap_bprm_set_creds"); | |
502 | + if (!cap) | |
503 | + return NULL; | |
504 | + /* Guess "struct security_hook_heads security_hook_heads;". */ | |
364 | 505 | cp = probe_find_variable(probe_security_bprm_committed_creds, |
365 | - (unsigned long) probe_lsm_hooks, | |
506 | + ((unsigned long) | |
507 | + &probe_dummy_security_hook_heads) + offset, | |
366 | 508 | " security_bprm_committed_creds\n"); |
367 | 509 | if (!cp) { |
368 | - /* Retry with offset. GCC 3.x seems to use this one. */ | |
369 | - offset = offsetof(struct security_operations, | |
370 | - list[lsm_bprm_committed_creds]); | |
371 | - cp = probe_find_variable(probe_security_bprm_committed_creds, | |
372 | - ((unsigned long) probe_lsm_hooks) | |
373 | - + offset, | |
374 | - " security_bprm_committed_creds\n"); | |
375 | - } | |
376 | - if (!cp) { | |
377 | 510 | printk(KERN_ERR |
378 | 511 | "Can't resolve security_bprm_committed_creds().\n"); |
379 | - goto out; | |
512 | + return NULL; | |
380 | 513 | } |
381 | - /* This should be "struct list_head lsm_hooks[LSM_MAX_HOOKS];". */ | |
382 | - cp = (struct list_head *) (*(unsigned long *) cp); | |
383 | - if (!cp) { | |
384 | - printk(KERN_ERR "Can't resolve lsm_hooks array.\n"); | |
385 | - goto out; | |
386 | - } | |
387 | - /* Adjust if offset is used. */ | |
388 | - cp -= offset; | |
389 | - printk(KERN_INFO "lsm_hooks=%p\n", cp); | |
390 | - return cp; | |
391 | -out: | |
514 | + /* This should be "struct security_hook_heads security_hook_heads;". */ | |
515 | + shh = ((void *) (*(unsigned long *) cp)) - offset; | |
516 | + list_for_each_entry(entry, &shh->bprm_set_creds, list) | |
517 | + if (entry->hook.bprm_set_creds == cap) | |
518 | + return shh; | |
519 | + printk(KERN_ERR "Guessed security_hook_heads is 0x%lx\n", | |
520 | + (unsigned long) shh); | |
392 | 521 | return NULL; |
393 | 522 | } |
394 | 523 |
@@ -405,6 +534,7 @@ | ||
405 | 534 | struct security_operations *ops; |
406 | 535 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) |
407 | 536 | void *cp; |
537 | + | |
408 | 538 | /* Guess "struct security_operations *security_ops;". */ |
409 | 539 | cp = probe_find_variable(probe_security_file_alloc, (unsigned long) |
410 | 540 | &probe_dummy_security_ops, |
@@ -444,17 +574,13 @@ | ||
444 | 574 | void * __init probe_find_task_by_vpid(void) |
445 | 575 | { |
446 | 576 | void *ptr; |
577 | + | |
447 | 578 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) |
448 | 579 | ptr = probe_find_symbol(" find_task_by_vpid\n"); |
449 | 580 | #else |
450 | 581 | ptr = __symbol_get("find_task_by_vpid"); |
451 | 582 | #endif |
452 | - if (!ptr) { | |
453 | - printk(KERN_ERR "Can't resolve find_task_by_vpid().\n"); | |
454 | - return NULL; | |
455 | - } | |
456 | - printk(KERN_INFO "find_task_by_vpid=%p\n", ptr); | |
457 | - return ptr; | |
583 | + return check_function_address(ptr, "find_task_by_vpid"); | |
458 | 584 | } |
459 | 585 | |
460 | 586 | /** |
@@ -465,17 +591,13 @@ | ||
465 | 591 | void * __init probe_find_task_by_pid_ns(void) |
466 | 592 | { |
467 | 593 | void *ptr; |
594 | + | |
468 | 595 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) |
469 | 596 | ptr = probe_find_symbol(" find_task_by_pid_ns\n"); |
470 | 597 | #else |
471 | 598 | ptr = __symbol_get("find_task_by_pid_ns"); |
472 | 599 | #endif |
473 | - if (!ptr) { | |
474 | - printk(KERN_ERR "Can't resolve find_task_by_pid_ns().\n"); | |
475 | - return NULL; | |
476 | - } | |
477 | - printk(KERN_INFO "find_task_by_pid_ns=%p\n", ptr); | |
478 | - return ptr; | |
600 | + return check_function_address(ptr, "find_task_by_pid_ns"); | |
479 | 601 | } |
480 | 602 | |
481 | 603 | #endif |
@@ -504,6 +626,7 @@ | ||
504 | 626 | static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) |
505 | 627 | { |
506 | 628 | unsigned long tmp = ((unsigned long) mnt / L1_CACHE_BYTES); |
629 | + | |
507 | 630 | tmp += ((unsigned long) dentry / L1_CACHE_BYTES); |
508 | 631 | tmp = tmp + (tmp >> probe_hash_bits); |
509 | 632 | return tmp & probe_hash_mask; |
@@ -549,6 +672,7 @@ | ||
549 | 672 | { |
550 | 673 | void *cp; |
551 | 674 | spinlock_t *ptr; |
675 | + | |
552 | 676 | /* Guess "spinlock_t vfsmount_lock;". */ |
553 | 677 | cp = probe_find_variable(probe_lookup_mnt, (unsigned long) |
554 | 678 | &probe_dummy_vfsmount_lock, " lookup_mnt\n"); |
@@ -583,6 +707,7 @@ | ||
583 | 707 | { |
584 | 708 | struct vfsmount *parent; |
585 | 709 | struct dentry *mountpoint; |
710 | + | |
586 | 711 | spin_lock(&probe_dummy_vfsmount_lock); |
587 | 712 | parent = (*mnt)->mnt_parent; |
588 | 713 | if (parent == *mnt) { |
@@ -608,6 +733,7 @@ | ||
608 | 733 | { |
609 | 734 | void *cp; |
610 | 735 | spinlock_t *ptr; |
736 | + | |
611 | 737 | /* Guess "spinlock_t vfsmount_lock;". */ |
612 | 738 | cp = probe_find_variable(probe_follow_up, (unsigned long) |
613 | 739 | &probe_dummy_vfsmount_lock, "follow_up"); |
@@ -653,6 +779,7 @@ | ||
653 | 779 | { |
654 | 780 | void *cp; |
655 | 781 | spinlock_t *ptr; |
782 | + | |
656 | 783 | /* Guess "spinlock_t vfsmount_lock;". */ |
657 | 784 | cp = probe_find_variable(probe_mnt_pin, (unsigned long) |
658 | 785 | &probe_dummy_vfsmount_lock, "mnt_pin"); |
@@ -704,12 +831,8 @@ | ||
704 | 831 | void * __init probe___d_path(void) |
705 | 832 | { |
706 | 833 | void *ptr = probe_find_symbol(" __d_path\n"); |
707 | - if (!ptr) { | |
708 | - printk(KERN_ERR "Can't resolve __d_path().\n"); | |
709 | - return NULL; | |
710 | - } | |
711 | - printk(KERN_INFO "__d_path=%p\n", ptr); | |
712 | - return ptr; | |
834 | + | |
835 | + return check_function_address(ptr, "__d_path"); | |
713 | 836 | } |
714 | 837 | |
715 | 838 | #endif |
@@ -724,12 +847,8 @@ | ||
724 | 847 | void * __init probe_d_absolute_path(void) |
725 | 848 | { |
726 | 849 | void *ptr = probe_find_symbol(" d_absolute_path\n"); |
727 | - if (!ptr) { | |
728 | - printk(KERN_ERR "Can't resolve d_absolute_path().\n"); | |
729 | - return NULL; | |
730 | - } | |
731 | - printk(KERN_INFO "d_absolute_path=%p\n", ptr); | |
732 | - return ptr; | |
850 | + | |
851 | + return check_function_address(ptr, "d_absolute_path"); | |
733 | 852 | } |
734 | 853 | |
735 | 854 | #endif |
@@ -9,6 +9,9 @@ | ||
9 | 9 | #ifndef __init |
10 | 10 | #include <linux/init.h> |
11 | 11 | #endif |
12 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) | |
13 | +#include <linux/cred.h> | |
14 | +#endif | |
12 | 15 | |
13 | 16 | #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) |
14 | 17 | #error This module supports only 2.6.0 and later kernels. |
@@ -25,10 +28,14 @@ | ||
25 | 28 | #ifndef CONFIG_MODULES |
26 | 29 | #error You must choose CONFIG_MODULES=y for building this module. |
27 | 30 | #endif |
31 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26) | |
32 | +#include <linux/kallsyms.h> | |
33 | +#endif | |
28 | 34 | |
29 | -#if defined(CONFIG_SECURITY_COMPOSER_MAX) | |
30 | -struct list_head; | |
31 | -struct list_head * __init probe_lsm_hooks_list(void); | |
35 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0) && !defined(SECURITY_NAME_MAX) | |
36 | +#include <linux/lsm_hooks.h> | |
37 | +extern struct security_hook_heads probe_dummy_security_hook_heads; | |
38 | +struct security_hook_heads * __init probe_security_hook_heads(void); | |
32 | 39 | #else |
33 | 40 | struct security_operations; |
34 | 41 | struct security_operations * __init probe_security_ops(void); |
@@ -32,8 +32,17 @@ | ||
32 | 32 | static DEFINE_SPINLOCK(tt_record_list_lock); |
33 | 33 | /* List of "struct tt_record" for "struct cred". */ |
34 | 34 | static struct list_head tt_record_list[TT_MAX_RECORD_HASH]; |
35 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) | |
35 | 36 | /* Function pointers originally registered by register_security(). */ |
36 | 37 | static struct security_operations original_security_ops /* = *security_ops; */; |
38 | +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) | |
39 | +/* Original hooks. */ | |
40 | +static union security_list_options original_cred_prepare; | |
41 | +static union security_list_options original_cred_alloc_blank; | |
42 | +#else | |
43 | +/* Original hooks. */ | |
44 | +static union security_list_options original_task_alloc; | |
45 | +#endif | |
37 | 46 | |
38 | 47 | /* Structure for representing YYYY/MM/DD hh/mm/ss. */ |
39 | 48 | struct tt_time { |
@@ -70,10 +79,10 @@ | ||
70 | 79 | time /= 60; |
71 | 80 | stamp->hour = time % 24; |
72 | 81 | time /= 24; |
73 | - if (time >= 16436) { | |
74 | - /* Start from 2015/01/01 rather than 1970/01/01. */ | |
75 | - time -= 16436; | |
76 | - y += 45; | |
82 | + if (time >= 17532) { | |
83 | + /* Start from 2018/01/01 rather than 1970/01/01. */ | |
84 | + time -= 17532; | |
85 | + y += 48; | |
77 | 86 | } |
78 | 87 | while (1) { |
79 | 88 | const unsigned short days = (y & 3) ? 365 : 366; |
@@ -222,7 +231,7 @@ | ||
222 | 231 | return NULL; |
223 | 232 | record->key = key; |
224 | 233 | tt_update_record(record); |
225 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) | |
234 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) | |
226 | 235 | /* |
227 | 236 | * Special handling is needed because "struct cred" might be shared |
228 | 237 | * between multiple threads. We need to allocate history only once for |
@@ -288,7 +297,7 @@ | ||
288 | 297 | call_rcu(&ptr->rcu, tt_rcu_free); |
289 | 298 | } |
290 | 299 | |
291 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) | |
300 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) | |
292 | 301 | |
293 | 302 | /** |
294 | 303 | * tt_cred_prepare - Allocate memory for new credentials. |
@@ -312,8 +321,16 @@ | ||
312 | 321 | new_record->key = new; |
313 | 322 | strcpy(new_record->history, old_record->history); |
314 | 323 | tt_add_record(new_record); |
315 | - while (!original_security_ops.cred_prepare); | |
324 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) | |
325 | + while (!original_security_ops.cred_prepare) | |
326 | + smp_rmb(); | |
316 | 327 | rc = original_security_ops.cred_prepare(new, old, gfp); |
328 | +#else | |
329 | + if (original_cred_prepare.cred_prepare) | |
330 | + rc = original_cred_prepare.cred_prepare(new, old, gfp); | |
331 | + else | |
332 | + rc = 0; | |
333 | +#endif | |
317 | 334 | if (rc) |
318 | 335 | tt_del_record(new_record); |
319 | 336 | return rc; |
@@ -328,8 +345,11 @@ | ||
328 | 345 | */ |
329 | 346 | static void tt_cred_free(struct cred *cred) |
330 | 347 | { |
331 | - while (!original_security_ops.cred_free); | |
348 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) | |
349 | + while (!original_security_ops.cred_free) | |
350 | + smp_rmb(); | |
332 | 351 | original_security_ops.cred_free(cred); |
352 | +#endif | |
333 | 353 | tt_del_record(tt_find_record(cred)); |
334 | 354 | } |
335 | 355 |
@@ -351,8 +371,16 @@ | ||
351 | 371 | return -ENOMEM; |
352 | 372 | record->key = new; |
353 | 373 | tt_add_record(record); |
354 | - while (!original_security_ops.cred_alloc_blank); | |
374 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) | |
375 | + while (!original_security_ops.cred_alloc_blank) | |
376 | + smp_rmb(); | |
355 | 377 | rc = original_security_ops.cred_alloc_blank(new, gfp); |
378 | +#else | |
379 | + if (original_cred_alloc_blank.cred_alloc_blank) | |
380 | + rc = original_cred_alloc_blank.cred_alloc_blank(new, gfp); | |
381 | + else | |
382 | + rc = 0; | |
383 | +#endif | |
356 | 384 | if (rc) |
357 | 385 | tt_del_record(record); |
358 | 386 | return rc; |
@@ -370,8 +398,11 @@ | ||
370 | 398 | { |
371 | 399 | struct tt_record *new_record; |
372 | 400 | struct tt_record *old_record; |
373 | - while (!original_security_ops.cred_transfer); | |
401 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) | |
402 | + while (!original_security_ops.cred_transfer) | |
403 | + smp_rmb(); | |
374 | 404 | original_security_ops.cred_transfer(new, old); |
405 | +#endif | |
375 | 406 | new_record = tt_find_record(new); |
376 | 407 | old_record = tt_find_record(old); |
377 | 408 | if (new_record && old_record) |
@@ -382,6 +413,7 @@ | ||
382 | 413 | |
383 | 414 | #else |
384 | 415 | |
416 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) | |
385 | 417 | /** |
386 | 418 | * tt_task_alloc_security - Allocate memory for new tasks. |
387 | 419 | * |
@@ -401,12 +433,43 @@ | ||
401 | 433 | new->key = p; |
402 | 434 | strcpy(new->history, old->history); |
403 | 435 | tt_add_record(new); |
404 | - while (!original_security_ops.task_alloc_security); | |
436 | + while (!original_security_ops.task_alloc_security) | |
437 | + smp_rmb(); | |
405 | 438 | rc = original_security_ops.task_alloc_security(p); |
406 | 439 | if (rc) |
407 | 440 | tt_del_record(new); |
408 | 441 | return rc; |
409 | 442 | } |
443 | +#else | |
444 | +/** | |
445 | + * tt_task_alloc_security - Allocate memory for new tasks. | |
446 | + * | |
447 | + * @p: Pointer to "struct task_struct". | |
448 | + * @clone_flags: Flags passed to clone(). | |
449 | + * | |
450 | + * Returns 0 on success, negative value otherwise. | |
451 | + */ | |
452 | +static int tt_task_alloc_security(struct task_struct *p, unsigned long clone_flags) | |
453 | +{ | |
454 | + int rc; | |
455 | + struct tt_record *old = tt_current_record(current, GFP_KERNEL); | |
456 | + struct tt_record *new = kzalloc(sizeof(*new), GFP_KERNEL); | |
457 | + if (!old || !new) { | |
458 | + kfree(new); | |
459 | + return -ENOMEM; | |
460 | + } | |
461 | + new->key = p; | |
462 | + strcpy(new->history, old->history); | |
463 | + tt_add_record(new); | |
464 | + if (original_task_alloc.task_alloc) | |
465 | + rc = original_task_alloc.task_alloc(p, clone_flags); | |
466 | + else | |
467 | + rc = 0; | |
468 | + if (rc) | |
469 | + tt_del_record(new); | |
470 | + return rc; | |
471 | +} | |
472 | +#endif | |
410 | 473 | |
411 | 474 | /** |
412 | 475 | * tt_task_free_security - Release memory for "struct task_struct". |
@@ -417,8 +480,11 @@ | ||
417 | 480 | */ |
418 | 481 | static void tt_task_free_security(struct task_struct *p) |
419 | 482 | { |
420 | - while (!original_security_ops.task_free_security); | |
483 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) | |
484 | + while (!original_security_ops.task_free_security) | |
485 | + smp_rmb(); | |
421 | 486 | original_security_ops.task_free_security(p); |
487 | +#endif | |
422 | 488 | tt_del_record(tt_find_record(p)); |
423 | 489 | } |
424 | 490 |
@@ -436,7 +502,8 @@ | ||
436 | 502 | */ |
437 | 503 | static void tt_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) |
438 | 504 | { |
439 | - while (!original_security_ops.bprm_apply_creds); | |
505 | + while (!original_security_ops.bprm_apply_creds) | |
506 | + smp_rmb(); | |
440 | 507 | original_security_ops.bprm_apply_creds(bprm, unsafe); |
441 | 508 | tt_update_record(tt_find_record(current)); |
442 | 509 | } |
@@ -452,9 +519,16 @@ | ||
452 | 519 | */ |
453 | 520 | static void tt_bprm_committing_creds(struct linux_binprm *bprm) |
454 | 521 | { |
455 | - while (!original_security_ops.bprm_committing_creds); | |
522 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 2, 0) | |
523 | + while (!original_security_ops.bprm_committing_creds) | |
524 | + smp_rmb(); | |
456 | 525 | original_security_ops.bprm_committing_creds(bprm); |
457 | 526 | tt_update_record(tt_find_record(bprm->cred)); |
527 | +#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) | |
528 | + tt_update_record(tt_find_record(bprm->cred)); | |
529 | +#else | |
530 | + tt_update_record(tt_find_record(current)); | |
531 | +#endif | |
458 | 532 | } |
459 | 533 | |
460 | 534 | #endif |
@@ -492,7 +566,7 @@ | ||
492 | 566 | * tt_current_record() is called and tt_current_record() failed to |
493 | 567 | * allocate memory for current thread's record. |
494 | 568 | */ |
495 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) | |
569 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) && LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) | |
496 | 570 | record = tt_current_record(current->real_cred, GFP_ATOMIC); |
497 | 571 | #else |
498 | 572 | record = tt_current_record(current, GFP_ATOMIC); |
@@ -510,6 +584,115 @@ | ||
510 | 584 | return 0; |
511 | 585 | } |
512 | 586 | |
587 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) | |
588 | + | |
589 | +#define MY_HOOK_INIT(HEAD, HOOK) \ | |
590 | + { .head = &probe_dummy_security_hook_heads.HEAD, \ | |
591 | + .hook = { .HEAD = HOOK } } | |
592 | + | |
593 | +static struct security_hook_list tt_hooks[] = { | |
594 | + MY_HOOK_INIT(bprm_committing_creds, tt_bprm_committing_creds), | |
595 | + MY_HOOK_INIT(secid_to_secctx, tt_secid_to_secctx), | |
596 | + MY_HOOK_INIT(task_getsecid, tt_task_getsecid), | |
597 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) | |
598 | + MY_HOOK_INIT(task_free, tt_task_free_security), | |
599 | + MY_HOOK_INIT(task_alloc, tt_task_alloc_security), | |
600 | +#else | |
601 | + MY_HOOK_INIT(cred_free, tt_cred_free), | |
602 | + MY_HOOK_INIT(cred_transfer, tt_cred_transfer), | |
603 | + MY_HOOK_INIT(cred_prepare, tt_cred_prepare), | |
604 | + MY_HOOK_INIT(cred_alloc_blank, tt_cred_alloc_blank), | |
605 | +#endif | |
606 | +}; | |
607 | + | |
608 | +static inline void add_hook(struct security_hook_list *hook) | |
609 | +{ | |
610 | + list_add_tail_rcu(&hook->list, hook->head); | |
611 | +} | |
612 | + | |
613 | +static void __init swap_hook(struct security_hook_list *hook, | |
614 | + union security_list_options *original) | |
615 | +{ | |
616 | + struct list_head *list = hook->head; | |
617 | + if (list_empty(list)) { | |
618 | + add_hook(hook); | |
619 | + } else { | |
620 | + struct security_hook_list *shp = | |
621 | + list_last_entry(list, struct security_hook_list, list); | |
622 | + *original = shp->hook; | |
623 | + smp_wmb(); | |
624 | + shp->hook = hook->hook; | |
625 | + } | |
626 | +} | |
627 | + | |
628 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) | |
629 | +#if defined(CONFIG_STRICT_KERNEL_RWX) && !defined(SECURITY_WRITABLE_HOOKS) | |
630 | +#include <linux/uaccess.h> /* probe_kernel_write() */ | |
631 | +#define NEED_TO_CHECK_HOOKS_ARE_WRITABLE | |
632 | + | |
633 | +#if defined(CONFIG_X86) | |
634 | +#define MAX_RO_PAGES 1024 | |
635 | +static struct page *ro_pages[MAX_RO_PAGES] __initdata; | |
636 | +static unsigned int ro_pages_len __initdata; | |
637 | + | |
638 | +static bool __init lsm_test_page_ro(void *addr) | |
639 | +{ | |
640 | + unsigned int i; | |
641 | + int unused; | |
642 | + struct page *page; | |
643 | + | |
644 | + page = (struct page *) lookup_address((unsigned long) addr, &unused); | |
645 | + if (!page) | |
646 | + return false; | |
647 | + if (test_bit(_PAGE_BIT_RW, &(page->flags))) | |
648 | + return true; | |
649 | + for (i = 0; i < ro_pages_len; i++) | |
650 | + if (page == ro_pages[i]) | |
651 | + return true; | |
652 | + if (ro_pages_len == MAX_RO_PAGES) | |
653 | + return false; | |
654 | + ro_pages[ro_pages_len++] = page; | |
655 | + return true; | |
656 | +} | |
657 | + | |
658 | +static bool __init check_ro_pages(struct security_hook_heads *hooks) | |
659 | +{ | |
660 | + int i; | |
661 | + struct list_head *list = (struct list_head *) hooks; | |
662 | + | |
663 | + if (!probe_kernel_write(&list->next, list->next, sizeof(void *))) | |
664 | + return true; | |
665 | + for (i = 0; i < ARRAY_SIZE(tt_hooks); i++) { | |
666 | + const unsigned int idx = | |
667 | + ((unsigned long) tt_hooks[i].head | |
668 | + - (unsigned long) hooks) | |
669 | + / sizeof(struct list_head); | |
670 | + struct list_head *self = &list[idx]; | |
671 | + struct list_head *prev = self->prev; | |
672 | + | |
673 | + if (!lsm_test_page_ro(&prev->next) || | |
674 | + !lsm_test_page_ro(&self->prev)) | |
675 | + return false; | |
676 | + if (!list_empty(self) && | |
677 | + !lsm_test_page_ro(&list_last_entry | |
678 | + (self, struct security_hook_list, | |
679 | + list)->hook)) | |
680 | + return false; | |
681 | + } | |
682 | + return true; | |
683 | +} | |
684 | +#else | |
685 | +static bool __init check_ro_pages(struct security_hook_heads *hooks) | |
686 | +{ | |
687 | + return !probe_kernel_write(&((struct list_head *) hooks)->next, | |
688 | + ((struct list_head *) hooks)->next, | |
689 | + sizeof(void *)); | |
690 | +} | |
691 | +#endif | |
692 | +#endif | |
693 | +#endif | |
694 | + | |
695 | +#else | |
513 | 696 | /** |
514 | 697 | * tt_update_security_ops - Overwrite original "struct security_operations". |
515 | 698 | * |
@@ -556,6 +739,7 @@ | ||
556 | 739 | ops->bprm_committing_creds = tt_bprm_committing_creds; |
557 | 740 | #endif |
558 | 741 | } |
742 | +#endif | |
559 | 743 | |
560 | 744 | /** |
561 | 745 | * tt_init - Initialize this module. |
@@ -565,6 +749,21 @@ | ||
565 | 749 | static int __init tt_init(void) |
566 | 750 | { |
567 | 751 | int idx; |
752 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) | |
753 | + struct security_hook_heads *hooks = probe_security_hook_heads(); | |
754 | + if (!hooks) | |
755 | + return -EINVAL; | |
756 | + for (idx = 0; idx < ARRAY_SIZE(tt_hooks); idx++) | |
757 | + tt_hooks[idx].head = ((void *) hooks) | |
758 | + + ((unsigned long) tt_hooks[idx].head) | |
759 | + - ((unsigned long) &probe_dummy_security_hook_heads); | |
760 | +#if defined(NEED_TO_CHECK_HOOKS_ARE_WRITABLE) | |
761 | + if (!check_ro_pages(hooks)) { | |
762 | + printk(KERN_INFO "Can't update security_hook_heads due to write protected. Retry with rodata=0 kernel command line option added.\n"); | |
763 | + return -EINVAL; | |
764 | + } | |
765 | +#endif | |
766 | +#else | |
568 | 767 | struct security_operations *ops = probe_security_ops(); |
569 | 768 | if (!ops) |
570 | 769 | return -EINVAL; |
@@ -574,9 +773,30 @@ | ||
574 | 773 | "command line option.\n", ops->name); |
575 | 774 | return -EINVAL; |
576 | 775 | } |
776 | +#endif | |
577 | 777 | for (idx = 0; idx < TT_MAX_RECORD_HASH; idx++) |
578 | 778 | INIT_LIST_HEAD(&tt_record_list[idx]); |
779 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) | |
780 | +#if defined(NEED_TO_CHECK_HOOKS_ARE_WRITABLE) && defined(CONFIG_X86) | |
781 | + for (idx = 0; idx < ro_pages_len; idx++) | |
782 | + set_bit(_PAGE_BIT_RW, &(ro_pages[idx]->flags)); | |
783 | +#endif | |
784 | + for (idx = 0; idx < 4; idx++) | |
785 | + add_hook(&tt_hooks[idx]); | |
786 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) | |
787 | + swap_hook(&tt_hooks[4], &original_task_alloc); | |
788 | +#else | |
789 | + add_hook(&tt_hooks[4]); | |
790 | + swap_hook(&tt_hooks[5], &original_cred_prepare); | |
791 | + swap_hook(&tt_hooks[6], &original_cred_alloc_blank); | |
792 | +#endif | |
793 | +#if defined(NEED_TO_CHECK_HOOKS_ARE_WRITABLE) && defined(CONFIG_X86) | |
794 | + for (idx = 0; idx < ro_pages_len; idx++) | |
795 | + clear_bit(_PAGE_BIT_RW, &(ro_pages[idx]->flags)); | |
796 | +#endif | |
797 | +#else | |
579 | 798 | tt_update_security_ops(ops); |
799 | +#endif | |
580 | 800 | printk(KERN_INFO "TaskTracker: 0.3 2015/01/11\n"); |
581 | 801 | return 0; |
582 | 802 | } |