1 |
/* |
2 |
* probe.c |
3 |
* |
4 |
* Copyright (C) 2010-2013 Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> |
5 |
* |
6 |
* Functions in this file are doing runtime address resolution based on byte |
7 |
* code comparison in order to allow LKM-based LSM modules to access built-in |
8 |
* functions and variables which are not exported to LKMs. |
9 |
* Since functions in this file are assuming that using identical source code, |
10 |
* identical kernel config and identical compiler generates identical byte code |
11 |
* output, functions in this file may not work on some architectures and/or |
12 |
* environments. |
13 |
* |
14 |
* This file is used by AKARI and CaitSith. This file will become unnecessary |
15 |
* when LKM-based LSM module comes back and TOMOYO 2.x becomes a LKM-based LSM |
16 |
* module. |
17 |
*/ |
18 |
|
19 |
#include "probe.h" |
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 |
#endif |
39 |
return ptr; |
40 |
} |
41 |
|
42 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) |
43 |
|
44 |
/** |
45 |
* my_kernel_read - Wrapper for kernel_read(). |
46 |
* |
47 |
* @file: Pointer to "struct file". |
48 |
* @offset: Starting position. |
49 |
* @addr: Buffer. |
50 |
* @count: Size of @addr. |
51 |
* |
52 |
* Returns return value from kernel_read(). |
53 |
*/ |
54 |
static inline int my_kernel_read(struct file *file, unsigned long offset, |
55 |
char *addr, unsigned long count) |
56 |
{ |
57 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 8) |
58 |
/* |
59 |
* I can't use kernel_read() because seq_read() returns -EPIPE |
60 |
* if &pos != &file->f_pos . |
61 |
*/ |
62 |
mm_segment_t old_fs; |
63 |
unsigned long pos = file->f_pos; |
64 |
int result; |
65 |
|
66 |
file->f_pos = offset; |
67 |
old_fs = get_fs(); |
68 |
set_fs(get_ds()); |
69 |
result = vfs_read(file, (void __user *)addr, count, &file->f_pos); |
70 |
set_fs(old_fs); |
71 |
file->f_pos = pos; |
72 |
return result; |
73 |
#elif LINUX_VERSION_CODE < KERNEL_VERSION(4, 14, 0) |
74 |
return kernel_read(file, offset, addr, count); |
75 |
#elif LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0) |
76 |
loff_t pos = offset; |
77 |
|
78 |
return kernel_read(file, addr, count, &pos); |
79 |
#else |
80 |
struct kvec iov = { .iov_base = addr, .iov_len = count }; |
81 |
struct kiocb kiocb; |
82 |
struct iov_iter iter; |
83 |
|
84 |
init_sync_kiocb(&kiocb, file); |
85 |
iov_iter_kvec(&iter, READ, &iov, 1, count); |
86 |
kiocb.ki_pos = offset; |
87 |
return seq_read_iter(&kiocb, &iter); |
88 |
#endif |
89 |
} |
90 |
|
91 |
/** |
92 |
* probe_find_symbol - Find function's address from /proc/kallsyms . |
93 |
* |
94 |
* @keyline: Function to find. |
95 |
* |
96 |
* Returns address of specified function on success, NULL otherwise. |
97 |
*/ |
98 |
static void *__init probe_find_symbol(const char *keyline) |
99 |
{ |
100 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 33) && LINUX_VERSION_CODE < KERNEL_VERSION(5, 7, 0) |
101 |
char buf[128] = { }; |
102 |
char *cp; |
103 |
|
104 |
strncpy(buf, keyline + 1, sizeof(buf) - 1); |
105 |
cp = strchr(buf, '\n'); |
106 |
if (cp) |
107 |
*cp = '\0'; |
108 |
return (void *) kallsyms_lookup_name(buf); |
109 |
#else |
110 |
struct file *file = NULL; |
111 |
char *buf; |
112 |
unsigned long entry = 0; |
113 |
|
114 |
{ |
115 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) |
116 |
struct file_system_type *fstype = get_fs_type("proc"); |
117 |
struct vfsmount *mnt = vfs_kern_mount(fstype, 0, "proc", NULL); |
118 |
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) |
119 |
struct file_system_type *fstype = NULL; |
120 |
struct vfsmount *mnt = do_kern_mount("proc", 0, "proc", NULL); |
121 |
#else |
122 |
struct file_system_type *fstype = get_fs_type("proc"); |
123 |
struct vfsmount *mnt = kern_mount(fstype); |
124 |
#endif |
125 |
struct dentry *root; |
126 |
struct dentry *dentry; |
127 |
|
128 |
/* |
129 |
* We embed put_filesystem() here because it is not exported. |
130 |
*/ |
131 |
if (fstype) |
132 |
module_put(fstype->owner); |
133 |
if (IS_ERR(mnt)) |
134 |
goto out; |
135 |
root = dget(mnt->mnt_root); |
136 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) |
137 |
inode_lock(root->d_inode); |
138 |
dentry = lookup_one_len("kallsyms", root, 8); |
139 |
inode_unlock(root->d_inode); |
140 |
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) |
141 |
mutex_lock(&root->d_inode->i_mutex); |
142 |
dentry = lookup_one_len("kallsyms", root, 8); |
143 |
mutex_unlock(&root->d_inode->i_mutex); |
144 |
#else |
145 |
down(&root->d_inode->i_sem); |
146 |
dentry = lookup_one_len("kallsyms", root, 8); |
147 |
up(&root->d_inode->i_sem); |
148 |
#endif |
149 |
dput(root); |
150 |
if (IS_ERR(dentry)) |
151 |
mntput(mnt); |
152 |
else { |
153 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) |
154 |
struct path path = { .mnt = mnt, .dentry = dentry }; |
155 |
|
156 |
file = dentry_open(&path, O_RDONLY, current_cred()); |
157 |
#else |
158 |
file = dentry_open(dentry, mnt, O_RDONLY |
159 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) |
160 |
, current_cred() |
161 |
#endif |
162 |
); |
163 |
#endif |
164 |
} |
165 |
} |
166 |
if (IS_ERR(file) || !file) |
167 |
goto out; |
168 |
buf = kmalloc(PAGE_SIZE, GFP_KERNEL); |
169 |
if (buf) { |
170 |
int len; |
171 |
int offset = 0; |
172 |
|
173 |
while ((len = my_kernel_read(file, offset, buf, |
174 |
PAGE_SIZE - 1)) > 0) { |
175 |
char *cp; |
176 |
|
177 |
buf[len] = '\0'; |
178 |
cp = strrchr(buf, '\n'); |
179 |
if (!cp) |
180 |
break; |
181 |
*(cp + 1) = '\0'; |
182 |
offset += strlen(buf); |
183 |
cp = strstr(buf, keyline); |
184 |
if (!cp) |
185 |
continue; |
186 |
*cp = '\0'; |
187 |
while (cp > buf && *(cp - 1) != '\n') |
188 |
cp--; |
189 |
entry = simple_strtoul(cp, NULL, 16); |
190 |
break; |
191 |
} |
192 |
kfree(buf); |
193 |
} |
194 |
filp_close(file, NULL); |
195 |
out: |
196 |
return (void *) entry; |
197 |
#endif |
198 |
} |
199 |
|
200 |
#endif |
201 |
|
202 |
#if defined(LSM_HOOK_INIT) |
203 |
|
204 |
/* |
205 |
* Dummy variable for finding location of |
206 |
* "struct security_hook_heads security_hook_heads". |
207 |
*/ |
208 |
struct security_hook_heads probe_dummy_security_hook_heads; |
209 |
|
210 |
/** |
211 |
* probe_security_bprm_committed_creds - Dummy function which does identical to security_bprm_committed_creds() in security/security.c. |
212 |
* |
213 |
* @bprm: Pointer to "struct linux_binprm". |
214 |
* |
215 |
* Returns nothing. |
216 |
*/ |
217 |
void probe_security_bprm_committed_creds(struct linux_binprm *bprm) |
218 |
{ |
219 |
do { |
220 |
struct security_hook_list *p; |
221 |
|
222 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) |
223 |
hlist_for_each_entry(p, &probe_dummy_security_hook_heads. |
224 |
bprm_committed_creds, list) |
225 |
p->hook.bprm_committed_creds(bprm); |
226 |
#else |
227 |
list_for_each_entry(p, &probe_dummy_security_hook_heads. |
228 |
bprm_committed_creds, list) |
229 |
p->hook.bprm_committed_creds(bprm); |
230 |
#endif |
231 |
} while (0); |
232 |
} |
233 |
|
234 |
#if defined(CONFIG_ARM64) |
235 |
|
236 |
/** |
237 |
* probe_security_hook_heads_on_arm64 - Find security_hook_heads on ARM64. |
238 |
* |
239 |
* @base: Address of security_bprm_committed_creds(). |
240 |
* |
241 |
* Returns address of security_hook_heads.bprm_committed_creds on success, |
242 |
* NULL otherwise. |
243 |
*/ |
244 |
static void * __init probe_security_hook_heads_on_arm64(unsigned int *base) |
245 |
{ |
246 |
static unsigned int *ip4ret; |
247 |
int i; |
248 |
unsigned int *ip = (unsigned int *) base; |
249 |
|
250 |
for (i = 0; i < 32; ip++, i++) { |
251 |
unsigned long tmp; |
252 |
unsigned long offset; |
253 |
|
254 |
/* |
255 |
* Find |
256 |
* adrp Xd, #imm21 |
257 |
* add Xd, Xn, #uimm12 |
258 |
* sequence. |
259 |
*/ |
260 |
if ((*ip & 0x9F000000) != 0x90000000 || |
261 |
(*(ip + 1) & 0xFFC00000) != 0x91000000) |
262 |
continue; |
263 |
tmp = ((unsigned long) ip) & ~0xFFFUL; |
264 |
offset = (unsigned long) (((((*ip >> 5) & 0x007FFFF) << 2) | |
265 |
((*ip >> 29) & 0x3))) << 12; |
266 |
if (offset & 0x100000000UL) |
267 |
offset |= 0xFFFFFFFF00000000UL; |
268 |
tmp += offset; |
269 |
offset = (*(ip + 1) >> 10) & 0xFFF; |
270 |
tmp += offset; |
271 |
/* |
272 |
* Find |
273 |
* ldr Xt, [Xn, #uimm12] |
274 |
* sequence. |
275 |
*/ |
276 |
for (ip += 2; i < 32 - 2; ip++, i++) { |
277 |
if ((*ip & 0xFFC00000) != 0xF9400000) |
278 |
continue; |
279 |
offset = ((*ip >> 10) & 0xFFF) << 3; |
280 |
tmp += offset; |
281 |
ip4ret = (unsigned int *) tmp; |
282 |
return &ip4ret; |
283 |
} |
284 |
break; |
285 |
} |
286 |
for (i = 0; i < 32; ip++, i++) { |
287 |
unsigned long tmp; |
288 |
unsigned long offset; |
289 |
|
290 |
/* |
291 |
* Find |
292 |
* adrp Xd, #imm21 |
293 |
* sequence. |
294 |
*/ |
295 |
if ((*ip & 0x9F000000) != 0x90000000) |
296 |
continue; |
297 |
tmp = ((unsigned long) ip) & ~0xFFFUL; |
298 |
offset = (unsigned long) (((((*ip >> 5) & 0x007FFFF) << 2) | |
299 |
((*ip >> 29) & 0x3))) << 12; |
300 |
if (offset & 0x100000000UL) |
301 |
offset |= 0xFFFFFFFF00000000UL; |
302 |
tmp += offset; |
303 |
/* |
304 |
* Find |
305 |
* ldr Xt, [Xn, #uimm12] |
306 |
* sequence. |
307 |
*/ |
308 |
for (ip += 1; i < 32 - 1; ip++, i++) { |
309 |
if ((*ip & 0xFFC00000) != 0xF9400000) |
310 |
continue; |
311 |
offset = ((*ip >> 10) & 0xFFF) << 3; |
312 |
tmp += offset; |
313 |
ip4ret = (unsigned int *) tmp; |
314 |
return &ip4ret; |
315 |
} |
316 |
break; |
317 |
} |
318 |
return NULL; |
319 |
} |
320 |
|
321 |
#endif |
322 |
|
323 |
#if defined(CONFIG_ARM) |
324 |
|
325 |
/** |
326 |
* probe_security_hook_heads_on_arm - Find security_hook_heads on ARM. |
327 |
* |
328 |
* @base: Address of security_bprm_committed_creds(). |
329 |
* |
330 |
* Returns address of security_hook_heads.bprm_committed_creds on success, |
331 |
* NULL otherwise. |
332 |
*/ |
333 |
static void * __init probe_security_hook_heads_on_arm(unsigned int *base) |
334 |
{ |
335 |
static unsigned int *ip4ret; |
336 |
int i; |
337 |
const unsigned long addr = (unsigned long) &probe_dummy_security_hook_heads; |
338 |
const unsigned long offset = (unsigned long) &probe_dummy_security_hook_heads.bprm_committed_creds - addr; |
339 |
unsigned int *ip = (unsigned int *) probe_security_bprm_committed_creds; |
340 |
|
341 |
for (i = 0; i < 32; ip++, i++) { |
342 |
if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) |
343 |
continue; |
344 |
ip = base + i; |
345 |
ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); |
346 |
ip4ret += offset >> 2; |
347 |
return &ip4ret; |
348 |
} |
349 |
return NULL; |
350 |
} |
351 |
|
352 |
#endif |
353 |
|
354 |
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) |
355 |
|
356 |
/* |
357 |
* Dummy variable for finding address of |
358 |
* "struct security_operations *security_ops". |
359 |
*/ |
360 |
static struct security_operations *probe_dummy_security_ops; |
361 |
|
362 |
/** |
363 |
* probe_security_file_alloc - Dummy function which does identical to security_file_alloc() in security/security.c. |
364 |
* |
365 |
* @file: Pointer to "struct file". |
366 |
* |
367 |
* Returns return value from security_file_alloc(). |
368 |
*/ |
369 |
static int probe_security_file_alloc(struct file *file) |
370 |
{ |
371 |
return probe_dummy_security_ops->file_alloc_security(file); |
372 |
} |
373 |
|
374 |
#if defined(CONFIG_ARM) |
375 |
|
376 |
/** |
377 |
* probe_security_ops_on_arm - Find security_ops on ARM. |
378 |
* |
379 |
* @base: Address of security_file_alloc(). |
380 |
* |
381 |
* Returns address of security_ops on success, NULL otherwise. |
382 |
*/ |
383 |
static void * __init probe_security_ops_on_arm(unsigned int *base) |
384 |
{ |
385 |
static unsigned int *ip4ret; |
386 |
int i; |
387 |
const unsigned long addr = (unsigned long) &probe_dummy_security_ops; |
388 |
unsigned int *ip = (unsigned int *) probe_security_file_alloc; |
389 |
|
390 |
for (i = 0; i < 32; ip++, i++) { |
391 |
if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) |
392 |
continue; |
393 |
ip = base + i; |
394 |
ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); |
395 |
return &ip4ret; |
396 |
} |
397 |
ip = (unsigned int *) probe_security_file_alloc; |
398 |
for (i = 0; i < 32; ip++, i++) { |
399 |
/* |
400 |
* Find |
401 |
* ldr r3, [pc, #offset1] |
402 |
* ldr r3, [r3, #offset2] |
403 |
* sequence. |
404 |
*/ |
405 |
if ((*ip & 0xFFFFF000) != 0xE59F3000 || |
406 |
(*(ip + 1) & 0xFFFFF000) != 0xE5933000) |
407 |
continue; |
408 |
ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); |
409 |
ip4ret += (*(ip + 1) & 0xFFF) >> 2; |
410 |
if ((unsigned long) ip4ret != addr) |
411 |
continue; |
412 |
ip = base + i; |
413 |
ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); |
414 |
ip4ret += (*(ip + 1) & 0xFFF) >> 2; |
415 |
return &ip4ret; |
416 |
} |
417 |
return NULL; |
418 |
} |
419 |
|
420 |
#endif |
421 |
|
422 |
#endif |
423 |
|
424 |
#if defined(CONFIG_ARM) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) |
425 |
/** |
426 |
* probe_find_vfsmount_lock_on_arm - Find vfsmount_lock spinlock on ARM. |
427 |
* |
428 |
* @ip: Address of dummy function's entry point. |
429 |
* @addr: Address of the variable which is used within @function. |
430 |
* @base: Address of function's entry point. |
431 |
* |
432 |
* Returns address of vfsmount_lock on success, NULL otherwise. |
433 |
*/ |
434 |
static void * __init probe_find_vfsmount_lock_on_arm(unsigned int *ip, |
435 |
unsigned long addr, |
436 |
unsigned int *base) |
437 |
{ |
438 |
int i; |
439 |
|
440 |
for (i = 0; i < 32; ip++, i++) { |
441 |
static unsigned int *ip4ret; |
442 |
|
443 |
if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) |
444 |
continue; |
445 |
ip = base + i; |
446 |
ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); |
447 |
return &ip4ret; |
448 |
} |
449 |
return NULL; |
450 |
} |
451 |
#endif |
452 |
|
453 |
/** |
454 |
* probe_find_variable - Find variable's address using dummy. |
455 |
* |
456 |
* @function: Pointer to dummy function's entry point. |
457 |
* @addr: Address of the variable which is used within @function. |
458 |
* @symbol: Name of symbol to resolve. |
459 |
* |
460 |
* This trick depends on below assumptions. |
461 |
* |
462 |
* (1) @addr is found within 128 bytes from @function, even if additional |
463 |
* code (e.g. debug symbols) is added. |
464 |
* (2) It is safe to read 128 bytes from @function. |
465 |
* (3) @addr != Byte code except @addr. |
466 |
*/ |
467 |
static void * __init probe_find_variable(void *function, unsigned long addr, |
468 |
const char *symbol) |
469 |
{ |
470 |
int i; |
471 |
u8 *base; |
472 |
u8 *cp = function; |
473 |
|
474 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) |
475 |
if (*symbol == ' ') |
476 |
base = probe_find_symbol(symbol); |
477 |
else |
478 |
#endif |
479 |
base = __symbol_get(symbol); |
480 |
if (!base) |
481 |
return NULL; |
482 |
#if defined(CONFIG_ARM64) && defined(LSM_HOOK_INIT) |
483 |
if (function == probe_security_bprm_committed_creds) |
484 |
return probe_security_hook_heads_on_arm64((unsigned int *) base); |
485 |
#endif |
486 |
#if defined(CONFIG_ARM) && defined(LSM_HOOK_INIT) |
487 |
if (function == probe_security_bprm_committed_creds) |
488 |
return probe_security_hook_heads_on_arm((unsigned int *) base); |
489 |
#endif |
490 |
#if defined(CONFIG_ARM) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) && !defined(LSM_HOOK_INIT) |
491 |
if (function == probe_security_file_alloc) |
492 |
return probe_security_ops_on_arm((unsigned int *) base); |
493 |
#endif |
494 |
#if defined(CONFIG_ARM) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) |
495 |
return probe_find_vfsmount_lock_on_arm(function, addr, |
496 |
(unsigned int *) base); |
497 |
#endif |
498 |
/* First, assume absolute addressing mode is used. */ |
499 |
for (i = 0; i < 128; i++) { |
500 |
if (*(unsigned long *) cp == addr) |
501 |
return base + i; |
502 |
cp++; |
503 |
} |
504 |
/* Next, assume PC-relative addressing mode is used. */ |
505 |
#if defined(CONFIG_S390) |
506 |
cp = function; |
507 |
for (i = 0; i < 128; i++) { |
508 |
if ((unsigned long) (cp + (*(int *) cp) * 2 - 2) == addr) { |
509 |
static void *cp4ret; |
510 |
|
511 |
cp = base + i; |
512 |
cp += (*(int *) cp) * 2 - 2; |
513 |
cp4ret = cp; |
514 |
return &cp4ret; |
515 |
} |
516 |
cp++; |
517 |
} |
518 |
#endif |
519 |
cp = function; |
520 |
for (i = 0; i < 128; i++) { |
521 |
if ((unsigned long) (cp + sizeof(int) + *(int *) cp) == addr) { |
522 |
static void *cp4ret; |
523 |
|
524 |
cp = base + i; |
525 |
cp += sizeof(int) + *(int *) cp; |
526 |
cp4ret = cp; |
527 |
return &cp4ret; |
528 |
} |
529 |
cp++; |
530 |
} |
531 |
cp = function; |
532 |
for (i = 0; i < 128; i++) { |
533 |
if ((unsigned long) (long) (*(int *) cp) == addr) { |
534 |
static void *cp4ret; |
535 |
|
536 |
cp = base + i; |
537 |
cp = (void *) (long) (*(int *) cp); |
538 |
cp4ret = cp; |
539 |
return &cp4ret; |
540 |
} |
541 |
cp++; |
542 |
} |
543 |
return NULL; |
544 |
} |
545 |
|
546 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) |
547 |
|
548 |
/** |
549 |
* probe_security_hook_heads - Find address of "struct security_hook_heads security_hook_heads". |
550 |
* |
551 |
* Returns pointer to "struct security_hook_heads" on success, NULL otherwise. |
552 |
*/ |
553 |
struct security_hook_heads * __init probe_security_hook_heads(void) |
554 |
{ |
555 |
const unsigned int offset = offsetof(struct security_hook_heads, |
556 |
bprm_committed_creds); |
557 |
void *cp; |
558 |
struct security_hook_heads *shh; |
559 |
struct security_hook_list *entry; |
560 |
void *cap = probe_find_symbol(" cap_bprm_creds_from_file\n"); |
561 |
|
562 |
/* Get location of cap_bprm_set_creds(). */ |
563 |
cap = check_function_address(cap, "cap_bprm_creds_from_file"); |
564 |
if (!cap) |
565 |
return NULL; |
566 |
/* Guess "struct security_hook_heads security_hook_heads;". */ |
567 |
cp = probe_find_variable(probe_security_bprm_committed_creds, |
568 |
((unsigned long) |
569 |
&probe_dummy_security_hook_heads) + offset, |
570 |
" security_bprm_committed_creds\n"); |
571 |
if (!cp) { |
572 |
printk(KERN_ERR |
573 |
"Can't resolve security_bprm_committed_creds().\n"); |
574 |
return NULL; |
575 |
} |
576 |
/* This should be "struct security_hook_heads security_hook_heads;". */ |
577 |
shh = ((void *) (*(unsigned long *) cp)) - offset; |
578 |
hlist_for_each_entry(entry, &shh->bprm_creds_from_file, list) |
579 |
if (entry->hook.bprm_creds_from_file == cap) |
580 |
return shh; |
581 |
printk(KERN_ERR "Guessed security_hook_heads is 0x%lx\n", |
582 |
(unsigned long) shh); |
583 |
return NULL; |
584 |
} |
585 |
|
586 |
#elif defined(RHEL_MAJOR) && RHEL_MAJOR == 8 && defined(RHEL_MINOR) && RHEL_MINOR >= 6 |
587 |
|
588 |
/** |
589 |
* probe_security_hook_heads - Find address of "struct security_hook_heads security_hook_heads". |
590 |
* |
591 |
* Returns pointer to "struct security_hook_heads" on success, NULL otherwise. |
592 |
*/ |
593 |
struct security_hook_heads * __init probe_security_hook_heads(void) |
594 |
{ |
595 |
const unsigned int offset = offsetof(struct security_hook_heads, |
596 |
bprm_committed_creds); |
597 |
void *cp; |
598 |
struct security_hook_heads *shh; |
599 |
struct security_hook_list *entry; |
600 |
void *cap = probe_find_symbol(" cap_bprm_repopulate_creds\n"); |
601 |
|
602 |
/* Get location of cap_bprm_repopulate_creds(). */ |
603 |
cap = check_function_address(cap, "cap_bprm_repopulate_creds"); |
604 |
if (!cap) |
605 |
return NULL; |
606 |
/* Guess "struct security_hook_heads security_hook_heads;". */ |
607 |
cp = probe_find_variable(probe_security_bprm_committed_creds, |
608 |
((unsigned long) |
609 |
&probe_dummy_security_hook_heads) + offset, |
610 |
" security_bprm_committed_creds\n"); |
611 |
if (!cp) { |
612 |
printk(KERN_ERR |
613 |
"Can't resolve security_bprm_committed_creds().\n"); |
614 |
return NULL; |
615 |
} |
616 |
/* This should be "struct security_hook_heads security_hook_heads;". */ |
617 |
shh = ((void *) (*(unsigned long *) cp)) - offset; |
618 |
hlist_for_each_entry(entry, &shh->bprm_repopulate_creds, list) |
619 |
if (entry->hook.bprm_repopulate_creds == cap) |
620 |
return shh; |
621 |
printk(KERN_ERR "Guessed security_hook_heads is 0x%lx\n", |
622 |
(unsigned long) shh); |
623 |
return NULL; |
624 |
} |
625 |
|
626 |
#elif defined(LSM_HOOK_INIT) |
627 |
|
628 |
/** |
629 |
* probe_security_hook_heads - Find address of "struct security_hook_heads security_hook_heads". |
630 |
* |
631 |
* Returns pointer to "struct security_hook_heads" on success, NULL otherwise. |
632 |
*/ |
633 |
struct security_hook_heads * __init probe_security_hook_heads(void) |
634 |
{ |
635 |
const unsigned int offset = offsetof(struct security_hook_heads, |
636 |
bprm_committed_creds); |
637 |
void *cp; |
638 |
struct security_hook_heads *shh; |
639 |
struct security_hook_list *entry; |
640 |
void *cap = probe_find_symbol(" cap_bprm_set_creds\n"); |
641 |
|
642 |
/* Get location of cap_bprm_set_creds(). */ |
643 |
cap = check_function_address(cap, "cap_bprm_set_creds"); |
644 |
if (!cap) |
645 |
return NULL; |
646 |
/* Guess "struct security_hook_heads security_hook_heads;". */ |
647 |
cp = probe_find_variable(probe_security_bprm_committed_creds, |
648 |
((unsigned long) |
649 |
&probe_dummy_security_hook_heads) + offset, |
650 |
" security_bprm_committed_creds\n"); |
651 |
if (!cp) { |
652 |
printk(KERN_ERR |
653 |
"Can't resolve security_bprm_committed_creds().\n"); |
654 |
return NULL; |
655 |
} |
656 |
/* This should be "struct security_hook_heads security_hook_heads;". */ |
657 |
shh = ((void *) (*(unsigned long *) cp)) - offset; |
658 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 17, 0) |
659 |
hlist_for_each_entry(entry, &shh->bprm_set_creds, list) |
660 |
if (entry->hook.bprm_set_creds == cap) |
661 |
return shh; |
662 |
#else |
663 |
list_for_each_entry(entry, &shh->bprm_set_creds, list) |
664 |
if (entry->hook.bprm_set_creds == cap) |
665 |
return shh; |
666 |
#endif |
667 |
printk(KERN_ERR "Guessed security_hook_heads is 0x%lx\n", |
668 |
(unsigned long) shh); |
669 |
return NULL; |
670 |
} |
671 |
|
672 |
#else |
673 |
|
674 |
/** |
675 |
* probe_security_ops - Find address of "struct security_operations *security_ops". |
676 |
* |
677 |
* Returns pointer to "struct security_operations" on success, NULL otherwise. |
678 |
*/ |
679 |
struct security_operations * __init probe_security_ops(void) |
680 |
{ |
681 |
struct security_operations **ptr; |
682 |
struct security_operations *ops; |
683 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) |
684 |
void *cp; |
685 |
|
686 |
/* Guess "struct security_operations *security_ops;". */ |
687 |
cp = probe_find_variable(probe_security_file_alloc, (unsigned long) |
688 |
&probe_dummy_security_ops, |
689 |
" security_file_alloc\n"); |
690 |
if (!cp) { |
691 |
printk(KERN_ERR "Can't resolve security_file_alloc().\n"); |
692 |
return NULL; |
693 |
} |
694 |
/* This should be "struct security_operations *security_ops;". */ |
695 |
ptr = *(struct security_operations ***) cp; |
696 |
#else |
697 |
/* This is "struct security_operations *security_ops;". */ |
698 |
ptr = (struct security_operations **) __symbol_get("security_ops"); |
699 |
#endif |
700 |
if (!ptr) { |
701 |
printk(KERN_ERR "Can't resolve security_ops structure.\n"); |
702 |
return NULL; |
703 |
} |
704 |
ops = *ptr; |
705 |
if (!ops) { |
706 |
printk(KERN_ERR "No security_operations registered.\n"); |
707 |
return NULL; |
708 |
} |
709 |
return ops; |
710 |
} |
711 |
|
712 |
#endif |
713 |
|
714 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) |
715 |
|
716 |
/** |
717 |
* probe_find_task_by_vpid - Find address of find_task_by_vpid(). |
718 |
* |
719 |
* Returns address of find_task_by_vpid() on success, NULL otherwise. |
720 |
*/ |
721 |
void * __init probe_find_task_by_vpid(void) |
722 |
{ |
723 |
void *ptr; |
724 |
|
725 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) |
726 |
ptr = probe_find_symbol(" find_task_by_vpid\n"); |
727 |
#else |
728 |
ptr = __symbol_get("find_task_by_vpid"); |
729 |
#endif |
730 |
return check_function_address(ptr, "find_task_by_vpid"); |
731 |
} |
732 |
|
733 |
/** |
734 |
* probe_find_task_by_pid_ns - Find address of find_task_by_pid(). |
735 |
* |
736 |
* Returns address of find_task_by_pid_ns() on success, NULL otherwise. |
737 |
*/ |
738 |
void * __init probe_find_task_by_pid_ns(void) |
739 |
{ |
740 |
void *ptr; |
741 |
|
742 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) |
743 |
ptr = probe_find_symbol(" find_task_by_pid_ns\n"); |
744 |
#else |
745 |
ptr = __symbol_get("find_task_by_pid_ns"); |
746 |
#endif |
747 |
return check_function_address(ptr, "find_task_by_pid_ns"); |
748 |
} |
749 |
|
750 |
#endif |
751 |
|
752 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) |
753 |
|
754 |
#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) |
755 |
|
756 |
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) |
757 |
|
758 |
/* Dummy variable for finding address of "spinlock_t vfsmount_lock". */ |
759 |
static spinlock_t probe_dummy_vfsmount_lock __cacheline_aligned_in_smp = |
760 |
SPIN_LOCK_UNLOCKED; |
761 |
|
762 |
static struct list_head *probe_mount_hashtable; |
763 |
static int probe_hash_mask, probe_hash_bits; |
764 |
|
765 |
/** |
766 |
* hash - Copy of hash() in fs/namespace.c. |
767 |
* |
768 |
* @mnt: Pointer to "struct vfsmount". |
769 |
* @dentry: Pointer to "struct dentry". |
770 |
* |
771 |
* Returns hash value. |
772 |
*/ |
773 |
static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) |
774 |
{ |
775 |
unsigned long tmp = ((unsigned long) mnt / L1_CACHE_BYTES); |
776 |
|
777 |
tmp += ((unsigned long) dentry / L1_CACHE_BYTES); |
778 |
tmp = tmp + (tmp >> probe_hash_bits); |
779 |
return tmp & probe_hash_mask; |
780 |
} |
781 |
|
782 |
/** |
783 |
* probe_lookup_mnt - Dummy function which does identical to lookup_mnt() in fs/namespace.c. |
784 |
* |
785 |
* @mnt: Pointer to "struct vfsmount". |
786 |
* @dentry: Pointer to "struct dentry". |
787 |
* |
788 |
* Returns pointer to "struct vfsmount". |
789 |
*/ |
790 |
static struct vfsmount *probe_lookup_mnt(struct vfsmount *mnt, |
791 |
struct dentry *dentry) |
792 |
{ |
793 |
struct list_head *head = probe_mount_hashtable + hash(mnt, dentry); |
794 |
struct list_head *tmp = head; |
795 |
struct vfsmount *p, *found = NULL; |
796 |
|
797 |
spin_lock(&probe_dummy_vfsmount_lock); |
798 |
for (;;) { |
799 |
tmp = tmp->next; |
800 |
p = NULL; |
801 |
if (tmp == head) |
802 |
break; |
803 |
p = list_entry(tmp, struct vfsmount, mnt_hash); |
804 |
if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) { |
805 |
found = mntget(p); |
806 |
break; |
807 |
} |
808 |
} |
809 |
spin_unlock(&probe_dummy_vfsmount_lock); |
810 |
return found; |
811 |
} |
812 |
|
813 |
/** |
814 |
* probe_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". |
815 |
* |
816 |
* Returns address of vfsmount_lock on success, NULL otherwise. |
817 |
*/ |
818 |
void * __init probe_vfsmount_lock(void) |
819 |
{ |
820 |
void *cp; |
821 |
spinlock_t *ptr; |
822 |
|
823 |
/* Guess "spinlock_t vfsmount_lock;". */ |
824 |
cp = probe_find_variable(probe_lookup_mnt, (unsigned long) |
825 |
&probe_dummy_vfsmount_lock, " lookup_mnt\n"); |
826 |
if (!cp) { |
827 |
printk(KERN_ERR "Can't resolve lookup_mnt().\n"); |
828 |
return NULL; |
829 |
} |
830 |
/* This should be "spinlock_t *vfsmount_lock;". */ |
831 |
ptr = *(spinlock_t **) cp; |
832 |
if (!ptr) { |
833 |
printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); |
834 |
return NULL; |
835 |
} |
836 |
return ptr; |
837 |
} |
838 |
|
839 |
#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) |
840 |
|
841 |
/* Dummy variable for finding address of "spinlock_t vfsmount_lock". */ |
842 |
static spinlock_t probe_dummy_vfsmount_lock; |
843 |
|
844 |
/** |
845 |
* probe_follow_up - Dummy function which does identical to follow_up() in fs/namei.c. |
846 |
* |
847 |
* @mnt: Pointer to "struct vfsmount *". |
848 |
* @dentry: Pointer to "struct dentry *". |
849 |
* |
850 |
* Returns 1 if followed up, 0 otherwise. |
851 |
*/ |
852 |
static int probe_follow_up(struct vfsmount **mnt, struct dentry **dentry) |
853 |
{ |
854 |
struct vfsmount *parent; |
855 |
struct dentry *mountpoint; |
856 |
|
857 |
spin_lock(&probe_dummy_vfsmount_lock); |
858 |
parent = (*mnt)->mnt_parent; |
859 |
if (parent == *mnt) { |
860 |
spin_unlock(&probe_dummy_vfsmount_lock); |
861 |
return 0; |
862 |
} |
863 |
mntget(parent); |
864 |
mountpoint = dget((*mnt)->mnt_mountpoint); |
865 |
spin_unlock(&probe_dummy_vfsmount_lock); |
866 |
dput(*dentry); |
867 |
*dentry = mountpoint; |
868 |
mntput(*mnt); |
869 |
*mnt = parent; |
870 |
return 1; |
871 |
} |
872 |
|
873 |
/** |
874 |
* probe_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". |
875 |
* |
876 |
* Returns address of vfsmount_lock on success, NULL otherwise. |
877 |
*/ |
878 |
void * __init probe_vfsmount_lock(void) |
879 |
{ |
880 |
void *cp; |
881 |
spinlock_t *ptr; |
882 |
|
883 |
/* Guess "spinlock_t vfsmount_lock;". */ |
884 |
cp = probe_find_variable(probe_follow_up, (unsigned long) |
885 |
&probe_dummy_vfsmount_lock, "follow_up"); |
886 |
if (!cp) { |
887 |
printk(KERN_ERR "Can't resolve follow_up().\n"); |
888 |
return NULL; |
889 |
} |
890 |
/* This should be "spinlock_t *vfsmount_lock;". */ |
891 |
ptr = *(spinlock_t **) cp; |
892 |
if (!ptr) { |
893 |
printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); |
894 |
return NULL; |
895 |
} |
896 |
return ptr; |
897 |
} |
898 |
|
899 |
#else |
900 |
|
901 |
/* Dummy variable for finding address of "spinlock_t vfsmount_lock". */ |
902 |
static spinlock_t probe_dummy_vfsmount_lock; |
903 |
|
904 |
/** |
905 |
* probe_mnt_pin - Dummy function which does identical to mnt_pin() in fs/namespace.c. |
906 |
* |
907 |
* @mnt: Pointer to "struct vfsmount". |
908 |
* |
909 |
* Returns nothing. |
910 |
*/ |
911 |
static void probe_mnt_pin(struct vfsmount *mnt) |
912 |
{ |
913 |
spin_lock(&probe_dummy_vfsmount_lock); |
914 |
mnt->mnt_pinned++; |
915 |
spin_unlock(&probe_dummy_vfsmount_lock); |
916 |
} |
917 |
|
918 |
/** |
919 |
* probe_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". |
920 |
* |
921 |
* Returns address of vfsmount_lock on success, NULL otherwise. |
922 |
*/ |
923 |
void * __init probe_vfsmount_lock(void) |
924 |
{ |
925 |
void *cp; |
926 |
spinlock_t *ptr; |
927 |
|
928 |
/* Guess "spinlock_t vfsmount_lock;". */ |
929 |
cp = probe_find_variable(probe_mnt_pin, (unsigned long) |
930 |
&probe_dummy_vfsmount_lock, "mnt_pin"); |
931 |
if (!cp) { |
932 |
printk(KERN_ERR "Can't resolve mnt_pin().\n"); |
933 |
return NULL; |
934 |
} |
935 |
/* This should be "spinlock_t *vfsmount_lock;". */ |
936 |
ptr = *(spinlock_t **) cp; |
937 |
if (!ptr) { |
938 |
printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); |
939 |
return NULL; |
940 |
} |
941 |
return ptr; |
942 |
} |
943 |
|
944 |
#endif |
945 |
|
946 |
#else |
947 |
|
948 |
/* |
949 |
* Never mark this variable as __initdata , for this variable might be accessed |
950 |
* by caller of probe_find_vfsmount_lock(). |
951 |
*/ |
952 |
static spinlock_t probe_dummy_vfsmount_lock; |
953 |
|
954 |
/** |
955 |
* probe_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". |
956 |
* |
957 |
* Returns address of vfsmount_lock. |
958 |
*/ |
959 |
void * __init probe_vfsmount_lock(void) |
960 |
{ |
961 |
return &probe_dummy_vfsmount_lock; |
962 |
} |
963 |
|
964 |
#endif |
965 |
|
966 |
#endif |
967 |
|
968 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) |
969 |
|
970 |
/** |
971 |
* probe___d_path - Find address of "__d_path()". |
972 |
* |
973 |
* Returns address of __d_path() on success, NULL otherwise. |
974 |
*/ |
975 |
void * __init probe___d_path(void) |
976 |
{ |
977 |
void *ptr = probe_find_symbol(" __d_path\n"); |
978 |
|
979 |
return check_function_address(ptr, "__d_path"); |
980 |
} |
981 |
|
982 |
#endif |
983 |
|
984 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) |
985 |
|
986 |
/** |
987 |
* probe_d_absolute_path - Find address of "d_absolute_path()". |
988 |
* |
989 |
* Returns address of d_absolute_path() on success, NULL otherwise. |
990 |
*/ |
991 |
void * __init probe_d_absolute_path(void) |
992 |
{ |
993 |
void *ptr = probe_find_symbol(" d_absolute_path\n"); |
994 |
|
995 |
return check_function_address(ptr, "d_absolute_path"); |
996 |
} |
997 |
|
998 |
#endif |
999 |
|
1000 |
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0) |
1001 |
|
1002 |
/** |
1003 |
* probe_copy_to_kernel_nofault - Find address of "copy_to_kernel_nofault()". |
1004 |
* |
1005 |
* Returns address of copy_to_kernel_nofault() on success, NULL otherwise. |
1006 |
*/ |
1007 |
void * __init probe_copy_to_kernel_nofault(void) |
1008 |
{ |
1009 |
void *ptr = probe_find_symbol(" copy_to_kernel_nofault\n"); |
1010 |
|
1011 |
return check_function_address(ptr, "copy_to_kernel_nofault"); |
1012 |
} |
1013 |
|
1014 |
#endif |