(empty log message)
@@ -1,670 +0,0 @@ | ||
1 | -/* | |
2 | - * caitsith_test.c | |
3 | - * | |
4 | - * Copyright (C) 2010-2012 Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
5 | - */ | |
6 | -#include <linux/version.h> | |
7 | -#include <linux/module.h> | |
8 | -#include <linux/init.h> | |
9 | -#include <linux/slab.h> | |
10 | -#include <linux/security.h> | |
11 | -#include <linux/namei.h> | |
12 | -#include <linux/mount.h> | |
13 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0) | |
14 | -#include <linux/fs.h> | |
15 | -#include <linux/sched.h> | |
16 | -#endif | |
17 | - | |
18 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) | |
19 | -#error This module supports only 2.6.27 and later kernels. | |
20 | -#endif | |
21 | -#ifndef CONFIG_SECURITY | |
22 | -#error You must choose CONFIG_SECURITY=y for building this module. | |
23 | -#endif | |
24 | -#ifndef CONFIG_KALLSYMS | |
25 | -#error You must choose CONFIG_KALLSYMS=y for building this module. | |
26 | -#endif | |
27 | -#ifndef CONFIG_PROC_FS | |
28 | -#error You must choose CONFIG_PROC_FS=y for building this module. | |
29 | -#endif | |
30 | -#ifndef CONFIG_MODULES | |
31 | -#error You must choose CONFIG_MODULES=y for building this module. | |
32 | -#endif | |
33 | - | |
34 | -#ifndef bool | |
35 | -#define bool _Bool | |
36 | -#endif | |
37 | -#ifndef false | |
38 | -#define false 0 | |
39 | -#endif | |
40 | -#ifndef true | |
41 | -#define true 1 | |
42 | -#endif | |
43 | - | |
44 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) | |
45 | - | |
46 | -#include <linux/mount.h> | |
47 | -#include <linux/fs_struct.h> | |
48 | - | |
49 | -/** | |
50 | - * cs_kernel_read - Wrapper for kernel_read(). | |
51 | - * | |
52 | - * @file: Pointer to "struct file". | |
53 | - * @offset: Starting position. | |
54 | - * @addr: Buffer. | |
55 | - * @count: Size of @addr. | |
56 | - * | |
57 | - * Returns return value from kernel_read(). | |
58 | - */ | |
59 | -static int __init cs_kernel_read(struct file *file, unsigned long offset, | |
60 | - char *addr, unsigned long count) | |
61 | -{ | |
62 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 8) | |
63 | - /* | |
64 | - * I can't use kernel_read() because seq_read() returns -EPIPE | |
65 | - * if &pos != &file->f_pos . | |
66 | - */ | |
67 | - mm_segment_t old_fs; | |
68 | - unsigned long pos = file->f_pos; | |
69 | - int result; | |
70 | - file->f_pos = offset; | |
71 | - old_fs = get_fs(); | |
72 | - set_fs(get_ds()); | |
73 | - result = vfs_read(file, (void __user *)addr, count, &file->f_pos); | |
74 | - set_fs(old_fs); | |
75 | - file->f_pos = pos; | |
76 | - return result; | |
77 | -#else | |
78 | - return kernel_read(file, offset, addr, count); | |
79 | -#endif | |
80 | -} | |
81 | - | |
82 | -/** | |
83 | - * cs_find_symbol - Find function's address from /proc/kallsyms . | |
84 | - * | |
85 | - * @keyline: Function to find. | |
86 | - * | |
87 | - * Returns address of specified function on success, NULL otherwise. | |
88 | - */ | |
89 | -static void *__init cs_find_symbol(const char *keyline) | |
90 | -{ | |
91 | - struct file *file = NULL; | |
92 | - char *buf; | |
93 | - unsigned long entry = 0; | |
94 | - { | |
95 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) | |
96 | - struct file_system_type *fstype = get_fs_type("proc"); | |
97 | - struct vfsmount *mnt = vfs_kern_mount(fstype, 0, "proc", NULL); | |
98 | -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) | |
99 | - struct file_system_type *fstype = NULL; | |
100 | - struct vfsmount *mnt = do_kern_mount("proc", 0, "proc", NULL); | |
101 | -#else | |
102 | - struct file_system_type *fstype = get_fs_type("proc"); | |
103 | - struct vfsmount *mnt = kern_mount(fstype); | |
104 | -#endif | |
105 | - struct dentry *root; | |
106 | - struct dentry *dentry; | |
107 | - /* | |
108 | - * We embed put_filesystem() here because it is not exported. | |
109 | - */ | |
110 | - if (fstype) | |
111 | - module_put(fstype->owner); | |
112 | - if (IS_ERR(mnt)) | |
113 | - goto out; | |
114 | - root = dget(mnt->mnt_root); | |
115 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) | |
116 | - mutex_lock(&root->d_inode->i_mutex); | |
117 | - dentry = lookup_one_len("kallsyms", root, 8); | |
118 | - mutex_unlock(&root->d_inode->i_mutex); | |
119 | -#else | |
120 | - down(&root->d_inode->i_sem); | |
121 | - dentry = lookup_one_len("kallsyms", root, 8); | |
122 | - up(&root->d_inode->i_sem); | |
123 | -#endif | |
124 | - dput(root); | |
125 | - if (IS_ERR(dentry)) | |
126 | - mntput(mnt); | |
127 | - else { | |
128 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) | |
129 | - struct path path = { mnt, dentry }; | |
130 | - file = dentry_open(&path, O_RDONLY, current_cred()); | |
131 | -#else | |
132 | - file = dentry_open(dentry, mnt, O_RDONLY | |
133 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) | |
134 | - , current_cred() | |
135 | -#endif | |
136 | - ); | |
137 | -#endif | |
138 | - } | |
139 | - } | |
140 | - if (IS_ERR(file) || !file) | |
141 | - goto out; | |
142 | - buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | |
143 | - if (buf) { | |
144 | - int len; | |
145 | - int offset = 0; | |
146 | - while ((len = cs_kernel_read(file, offset, buf, | |
147 | - PAGE_SIZE - 1)) > 0) { | |
148 | - char *cp; | |
149 | - buf[len] = '\0'; | |
150 | - cp = strrchr(buf, '\n'); | |
151 | - if (!cp) | |
152 | - break; | |
153 | - *(cp + 1) = '\0'; | |
154 | - offset += strlen(buf); | |
155 | - cp = strstr(buf, keyline); | |
156 | - if (!cp) | |
157 | - continue; | |
158 | - *cp = '\0'; | |
159 | - while (cp > buf && *(cp - 1) != '\n') | |
160 | - cp--; | |
161 | - entry = simple_strtoul(cp, NULL, 16); | |
162 | - break; | |
163 | - } | |
164 | - kfree(buf); | |
165 | - } | |
166 | - filp_close(file, NULL); | |
167 | -out: | |
168 | - return (void *) entry; | |
169 | -} | |
170 | - | |
171 | -#endif | |
172 | - | |
173 | -#if defined(CONFIG_ARM) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
174 | -static int lsm_addr_calculator(struct file *file); | |
175 | -static void * __init cs_find_security_ops_on_arm(unsigned int *base); | |
176 | -#endif | |
177 | - | |
178 | -#if defined(CONFIG_ARM) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
179 | -/** | |
180 | - * cs_find_vfsmount_lock_on_arm - Find vfsmount_lock spinlock on ARM. | |
181 | - * | |
182 | - * @ip: Address of dummy function's entry point. | |
183 | - * @addr: Address of the variable which is used within @function. | |
184 | - * @base: Address of function's entry point. | |
185 | - * | |
186 | - * Returns address of vfsmount_lock on success, NULL otherwise. | |
187 | - */ | |
188 | -static void * __init cs_find_vfsmount_lock_on_arm(unsigned int *ip, | |
189 | - unsigned long addr, | |
190 | - unsigned int *base) | |
191 | -{ | |
192 | - int i; | |
193 | - for (i = 0; i < 32; ip++, i++) { | |
194 | - static unsigned int *ip4ret; | |
195 | - if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) | |
196 | - continue; | |
197 | - ip = base + i; | |
198 | - ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); | |
199 | - return &ip4ret; | |
200 | - } | |
201 | - return NULL; | |
202 | -} | |
203 | -#endif | |
204 | - | |
205 | -/** | |
206 | - * cs_find_variable - Find variable's address using dummy. | |
207 | - * | |
208 | - * @function: Pointer to dummy function's entry point. | |
209 | - * @addr: Address of the variable which is used within @function. | |
210 | - * @symbol: Name of symbol to resolve. | |
211 | - * | |
212 | - * This trick depends on below assumptions. | |
213 | - * | |
214 | - * (1) @addr is found within 128 bytes from @function, even if additional | |
215 | - * code (e.g. debug symbols) is added. | |
216 | - * (2) It is safe to read 128 bytes from @function. | |
217 | - * (3) @addr != Byte code except @addr. | |
218 | - */ | |
219 | -static void * __init cs_find_variable(void *function, unsigned long addr, | |
220 | - const char *symbol) | |
221 | -{ | |
222 | - int i; | |
223 | - u8 *base; | |
224 | - u8 *cp = function; | |
225 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) | |
226 | - if (*symbol == ' ') | |
227 | - base = cs_find_symbol(symbol); | |
228 | - else | |
229 | -#endif | |
230 | - base = __symbol_get(symbol); | |
231 | - if (!base) | |
232 | - return NULL; | |
233 | -#if defined(CONFIG_ARM) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
234 | - if (function == lsm_addr_calculator) | |
235 | - return cs_find_security_ops_on_arm((unsigned int *) base); | |
236 | -#endif | |
237 | -#if defined(CONFIG_ARM) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
238 | - return cs_find_vfsmount_lock_on_arm(function, addr, | |
239 | - (unsigned int *) base); | |
240 | -#endif | |
241 | - /* First, assume absolute adressing mode is used. */ | |
242 | - for (i = 0; i < 128; i++) { | |
243 | - if (*(unsigned long *) cp == addr) | |
244 | - return base + i; | |
245 | - cp++; | |
246 | - } | |
247 | - /* Next, assume PC-relative addressing mode is used. */ | |
248 | - cp = function; | |
249 | - for (i = 0; i < 128; i++) { | |
250 | - if ((unsigned long) (cp + sizeof(int) + *(int *) cp) == addr) { | |
251 | - static void *cp4ret; | |
252 | - cp = base + i; | |
253 | - cp += sizeof(int) + *(int *) cp; | |
254 | - cp4ret = cp; | |
255 | - return &cp4ret; | |
256 | - } | |
257 | - cp++; | |
258 | - } | |
259 | - cp = function; | |
260 | - for (i = 0; i < 128; i++) { | |
261 | - if ((unsigned long) (long) (*(int *) cp) == addr) { | |
262 | - static void *cp4ret; | |
263 | - cp = base + i; | |
264 | - cp = (void *) (long) (*(int *) cp); | |
265 | - cp4ret = cp; | |
266 | - return &cp4ret; | |
267 | - } | |
268 | - cp++; | |
269 | - } | |
270 | - return NULL; | |
271 | -} | |
272 | - | |
273 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
274 | - | |
275 | -/* Never mark this variable as __initdata . */ | |
276 | -static struct security_operations *cs_security_ops; | |
277 | - | |
278 | -/** | |
279 | - * lsm_addr_calculator - Dummy function which does identical to security_file_alloc() in security/security.c. | |
280 | - * | |
281 | - * @file: Pointer to "struct file". | |
282 | - * | |
283 | - * Returns return value from security_file_alloc(). | |
284 | - * | |
285 | - * Never mark this function as __init in order to make sure that compiler | |
286 | - * generates identical code for security_file_alloc() and this function. | |
287 | - */ | |
288 | -static int lsm_addr_calculator(struct file *file) | |
289 | -{ | |
290 | - return cs_security_ops->file_alloc_security(file); | |
291 | -} | |
292 | - | |
293 | -#ifdef CONFIG_ARM | |
294 | -/** | |
295 | - * cs_find_security_ops_on_arm - Find security_ops on ARM. | |
296 | - * | |
297 | - * @base: Address of security_file_alloc(). | |
298 | - * | |
299 | - * Returns address of security_ops on success, NULL otherwise. | |
300 | - */ | |
301 | -static void * __init cs_find_security_ops_on_arm(unsigned int *base) | |
302 | -{ | |
303 | - static unsigned int *ip4ret; | |
304 | - int i; | |
305 | - const unsigned long addr = (unsigned long) &cs_security_ops; | |
306 | - unsigned int *ip = (unsigned int *) lsm_addr_calculator; | |
307 | - for (i = 0; i < 32; ip++, i++) { | |
308 | - if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) | |
309 | - continue; | |
310 | - ip = base + i; | |
311 | - ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); | |
312 | - return &ip4ret; | |
313 | - } | |
314 | - ip = (unsigned int *) lsm_addr_calculator; | |
315 | - for (i = 0; i < 32; ip++, i++) { | |
316 | - /* | |
317 | - * Find | |
318 | - * ldr r3, [pc, #offset1] | |
319 | - * ldr r3, [r3, #offset2] | |
320 | - * sequence. | |
321 | - */ | |
322 | - if ((*ip & 0xFFFFF000) != 0xE59F3000 || | |
323 | - (*(ip + 1) & 0xFFFFF000) != 0xE5933000) | |
324 | - continue; | |
325 | - ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); | |
326 | - ip4ret += (*(ip + 1) & 0xFFF) >> 2; | |
327 | - if ((unsigned long) ip4ret != addr) | |
328 | - continue; | |
329 | - ip = base + i; | |
330 | - ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); | |
331 | - ip4ret += (*(ip + 1) & 0xFFF) >> 2; | |
332 | - return &ip4ret; | |
333 | - } | |
334 | - return NULL; | |
335 | -} | |
336 | -#endif | |
337 | -#endif | |
338 | - | |
339 | -/** | |
340 | - * cs_find_find_security_ops - Find address of "struct security_operations *security_ops". | |
341 | - * | |
342 | - * Returns pointer to "struct security_operations" on success, NULL otherwise. | |
343 | - */ | |
344 | -static struct security_operations * __init cs_find_security_ops(void) | |
345 | -{ | |
346 | - struct security_operations **ptr; | |
347 | - struct security_operations *ops; | |
348 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
349 | - void *cp; | |
350 | - /* Guess "struct security_operations *security_ops;". */ | |
351 | - cp = cs_find_variable(lsm_addr_calculator, (unsigned long) | |
352 | - &cs_security_ops, " security_file_alloc\n"); | |
353 | - if (!cp) { | |
354 | - printk(KERN_ERR "Can't resolve security_file_alloc().\n"); | |
355 | - goto out; | |
356 | - } | |
357 | - /* This should be "struct security_operations *security_ops;". */ | |
358 | - ptr = *(struct security_operations ***) cp; | |
359 | -#else | |
360 | - /* This is "struct security_operations *security_ops;". */ | |
361 | - ptr = (struct security_operations **) __symbol_get("security_ops"); | |
362 | -#endif | |
363 | - if (!ptr) { | |
364 | - printk(KERN_ERR "Can't resolve security_ops structure.\n"); | |
365 | - goto out; | |
366 | - } | |
367 | - printk(KERN_INFO "security_ops=%p\n", ptr); | |
368 | - ops = *ptr; | |
369 | - if (!ops) { | |
370 | - printk(KERN_ERR "No security_operations registered.\n"); | |
371 | - goto out; | |
372 | - } | |
373 | - return ops; | |
374 | -out: | |
375 | - return NULL; | |
376 | -} | |
377 | - | |
378 | -/** | |
379 | - * cs_find_find_task_by_pid - Find address of find_task_by_pid(). | |
380 | - * | |
381 | - * Returns true on success, false otherwise. | |
382 | - */ | |
383 | -static bool __init cs_find_find_task_by_pid(void) | |
384 | -{ | |
385 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
386 | - void *ptr; | |
387 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) | |
388 | - ptr = cs_find_symbol(" find_task_by_vpid\n"); | |
389 | -#else | |
390 | - ptr = __symbol_get("find_task_by_vpid"); | |
391 | -#endif | |
392 | - if (!ptr) { | |
393 | - printk(KERN_ERR "Can't resolve find_task_by_vpid().\n"); | |
394 | - goto out; | |
395 | - } | |
396 | - printk(KERN_INFO "find_task_by_vpid=%p\n", ptr); | |
397 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) | |
398 | - ptr = cs_find_symbol(" find_task_by_pid_ns\n"); | |
399 | -#else | |
400 | - ptr = __symbol_get("find_task_by_pid_ns"); | |
401 | -#endif | |
402 | - if (!ptr) { | |
403 | - printk(KERN_ERR "Can't resolve find_task_by_pid_ns().\n"); | |
404 | - goto out; | |
405 | - } | |
406 | - printk(KERN_INFO "find_task_by_pid_ns=%p\n", ptr); | |
407 | - return true; | |
408 | -out: | |
409 | - return false; | |
410 | -#else | |
411 | - return true; | |
412 | -#endif | |
413 | -} | |
414 | - | |
415 | -#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) || LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
416 | - | |
417 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) | |
418 | - | |
419 | -/* Never mark this variable as __initdata . */ | |
420 | -static spinlock_t cs_vfsmount_lock __cacheline_aligned_in_smp = | |
421 | - SPIN_LOCK_UNLOCKED; | |
422 | - | |
423 | -static struct list_head *mount_hashtable; | |
424 | -static int hash_mask, hash_bits; | |
425 | - | |
426 | -/** | |
427 | - * hash - Copy of hash() in fs/namespace.c. | |
428 | - * | |
429 | - * @mnt: Pointer to "struct vfsmount". | |
430 | - * @dentry: Pointer to "struct dentry". | |
431 | - * | |
432 | - * Returns hash value. | |
433 | - */ | |
434 | -static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) | |
435 | -{ | |
436 | - unsigned long tmp = ((unsigned long) mnt / L1_CACHE_BYTES); | |
437 | - tmp += ((unsigned long) dentry / L1_CACHE_BYTES); | |
438 | - tmp = tmp + (tmp >> hash_bits); | |
439 | - return tmp & hash_mask; | |
440 | -} | |
441 | - | |
442 | -/** | |
443 | - * lsm_lu_mnt - Dummy function which does identical to lookup_mnt() in fs/namespace.c. | |
444 | - * | |
445 | - * @mnt: Pointer to "struct vfsmount". | |
446 | - * @dentry: Pointer to "struct dentry". | |
447 | - * | |
448 | - * Returns pointer to "struct vfsmount". | |
449 | - * | |
450 | - * Never mark this function as __init in order to make sure that compiler | |
451 | - * generates identical code for lookup_mnt() and this function. | |
452 | - */ | |
453 | -static struct vfsmount *lsm_lu_mnt(struct vfsmount *mnt, struct dentry *dentry) | |
454 | -{ | |
455 | - struct list_head *head = mount_hashtable + hash(mnt, dentry); | |
456 | - struct list_head *tmp = head; | |
457 | - struct vfsmount *p, *found = NULL; | |
458 | - | |
459 | - spin_lock(&cs_vfsmount_lock); | |
460 | - for (;;) { | |
461 | - tmp = tmp->next; | |
462 | - p = NULL; | |
463 | - if (tmp == head) | |
464 | - break; | |
465 | - p = list_entry(tmp, struct vfsmount, mnt_hash); | |
466 | - if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) { | |
467 | - found = mntget(p); | |
468 | - break; | |
469 | - } | |
470 | - } | |
471 | - spin_unlock(&cs_vfsmount_lock); | |
472 | - return found; | |
473 | -} | |
474 | - | |
475 | -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) | |
476 | - | |
477 | -/* Never mark this variable as __initdata . */ | |
478 | -static spinlock_t cs_vfsmount_lock; | |
479 | - | |
480 | -/** | |
481 | - * lsm_flwup - Dummy function which does identical to follow_up() in fs/namei.c. | |
482 | - * | |
483 | - * @mnt: Pointer to "struct vfsmount *". | |
484 | - * @dentry: Pointer to "struct dentry *". | |
485 | - * | |
486 | - * Returns 1 if followed up, 0 otehrwise. | |
487 | - * | |
488 | - * Never mark this function as __init in order to make sure that compiler | |
489 | - * generates identical code for follow_up() and this function. | |
490 | - */ | |
491 | -static int lsm_flwup(struct vfsmount **mnt, struct dentry **dentry) | |
492 | -{ | |
493 | - struct vfsmount *parent; | |
494 | - struct dentry *mountpoint; | |
495 | - spin_lock(&cs_vfsmount_lock); | |
496 | - parent = (*mnt)->mnt_parent; | |
497 | - if (parent == *mnt) { | |
498 | - spin_unlock(&cs_vfsmount_lock); | |
499 | - return 0; | |
500 | - } | |
501 | - mntget(parent); | |
502 | - mountpoint = dget((*mnt)->mnt_mountpoint); | |
503 | - spin_unlock(&cs_vfsmount_lock); | |
504 | - dput(*dentry); | |
505 | - *dentry = mountpoint; | |
506 | - mntput(*mnt); | |
507 | - *mnt = parent; | |
508 | - return 1; | |
509 | -} | |
510 | - | |
511 | -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
512 | - | |
513 | -/* Never mark this variable as __initdata . */ | |
514 | -static spinlock_t cs_vfsmount_lock; | |
515 | - | |
516 | -/** | |
517 | - * lsm_pin - Dummy function which does identical to mnt_pin() in fs/namespace.c. | |
518 | - * | |
519 | - * @mnt: Pointer to "struct vfsmount". | |
520 | - * | |
521 | - * Returns nothing. | |
522 | - * | |
523 | - * Never mark this function as __init in order to make sure that compiler | |
524 | - * generates identical code for mnt_pin() and this function. | |
525 | - */ | |
526 | -static void lsm_pin(struct vfsmount *mnt) | |
527 | -{ | |
528 | - spin_lock(&cs_vfsmount_lock); | |
529 | - mnt->mnt_pinned++; | |
530 | - spin_unlock(&cs_vfsmount_lock); | |
531 | -} | |
532 | - | |
533 | -#endif | |
534 | - | |
535 | -/** | |
536 | - * cs_find_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". | |
537 | - * | |
538 | - * Returns true on success, false otherwise. | |
539 | - */ | |
540 | -static bool __init cs_find_vfsmount_lock(void) | |
541 | -{ | |
542 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) | |
543 | - void *cp; | |
544 | - spinlock_t *ptr; | |
545 | - /* Guess "spinlock_t vfsmount_lock;". */ | |
546 | - cp = cs_find_variable(lsm_lu_mnt, (unsigned long) &cs_vfsmount_lock, | |
547 | - " lookup_mnt\n"); | |
548 | - if (!cp) { | |
549 | - printk(KERN_ERR "Can't resolve lookup_mnt().\n"); | |
550 | - goto out; | |
551 | - } | |
552 | - /* This should be "spinlock_t *vfsmount_lock;". */ | |
553 | - ptr = *(spinlock_t **) cp; | |
554 | - if (!ptr) { | |
555 | - printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); | |
556 | - goto out; | |
557 | - } | |
558 | - printk(KERN_INFO "vfsmount_lock=%p\n", ptr); | |
559 | - return true; | |
560 | -out: | |
561 | - return false; | |
562 | -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) | |
563 | - void *cp; | |
564 | - spinlock_t *ptr; | |
565 | - /* Guess "spinlock_t vfsmount_lock;". */ | |
566 | - cp = cs_find_variable(lsm_flwup, (unsigned long) &cs_vfsmount_lock, | |
567 | - "follow_up"); | |
568 | - if (!cp) { | |
569 | - printk(KERN_ERR "Can't resolve follow_up().\n"); | |
570 | - goto out; | |
571 | - } | |
572 | - /* This should be "spinlock_t *vfsmount_lock;". */ | |
573 | - ptr = *(spinlock_t **) cp; | |
574 | - if (!ptr) { | |
575 | - printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); | |
576 | - goto out; | |
577 | - } | |
578 | - printk(KERN_INFO "vfsmount_lock=%p\n", ptr); | |
579 | - return true; | |
580 | -out: | |
581 | - return false; | |
582 | -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
583 | - void *cp; | |
584 | - spinlock_t *ptr; | |
585 | - /* Guess "spinlock_t vfsmount_lock;". */ | |
586 | - cp = cs_find_variable(lsm_pin, (unsigned long) &cs_vfsmount_lock, | |
587 | - "mnt_pin"); | |
588 | - if (!cp) { | |
589 | - printk(KERN_ERR "Can't resolve mnt_pin().\n"); | |
590 | - goto out; | |
591 | - } | |
592 | - /* This should be "spinlock_t *vfsmount_lock;". */ | |
593 | - ptr = *(spinlock_t **) cp; | |
594 | - if (!ptr) { | |
595 | - printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); | |
596 | - goto out; | |
597 | - } | |
598 | - printk(KERN_INFO "vfsmount_lock=%p\n", ptr); | |
599 | - return true; | |
600 | -out: | |
601 | - return false; | |
602 | -#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) | |
603 | - void *ptr = cs_find_symbol(" __d_path\n"); | |
604 | - if (!ptr) { | |
605 | - printk(KERN_ERR "Can't resolve __d_path().\n"); | |
606 | - return false; | |
607 | - } | |
608 | - printk(KERN_INFO "__d_path=%p\n", ptr); | |
609 | - return true; | |
610 | -#else | |
611 | - void *ptr = cs_find_symbol(" d_absolute_path\n"); | |
612 | - if (!ptr) { | |
613 | - printk(KERN_ERR "Can't resolve d_absolute_path().\n"); | |
614 | - return false; | |
615 | - } | |
616 | - printk(KERN_INFO "d_absolute_path=%p\n", ptr); | |
617 | - return true; | |
618 | -#endif | |
619 | -} | |
620 | - | |
621 | -#else | |
622 | - | |
623 | -/** | |
624 | - * cs_find_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". | |
625 | - * | |
626 | - * Returns true on success, false otherwise. | |
627 | - */ | |
628 | -static bool __init cs_find_vfsmount_lock(void) | |
629 | -{ | |
630 | - return true; | |
631 | -} | |
632 | - | |
633 | -#endif | |
634 | - | |
635 | -/** | |
636 | - * cs_init - Initialize this module. | |
637 | - * | |
638 | - * Returns 0 on success, -EINVAL otherwise. | |
639 | - */ | |
640 | -static int __init cs_init(void) | |
641 | -{ | |
642 | - if (!cs_find_security_ops() || !cs_find_find_task_by_pid() || | |
643 | - !cs_find_vfsmount_lock()) { | |
644 | - printk(KERN_INFO "Sorry, I couldn't guess dependent " | |
645 | - "symbols.\n"); | |
646 | - printk(KERN_INFO "I need some changes for supporting your " | |
647 | - "environment.\n"); | |
648 | - printk(KERN_INFO "Please contact the author.\n"); | |
649 | - return -EINVAL; | |
650 | - } | |
651 | - printk(KERN_INFO "All dependent symbols have been guessed.\n"); | |
652 | - printk(KERN_INFO "Please verify these addresses using System.map for " | |
653 | - "this kernel (e.g. /boot/System.map-`uname -r` ).\n"); | |
654 | - printk(KERN_INFO "If these addresses are correct, you can try loading " | |
655 | - "CaitSith module on this kernel.\n"); | |
656 | - return 0; | |
657 | -} | |
658 | - | |
659 | -/** | |
660 | - * cs_exit - Exit this module. | |
661 | - * | |
662 | - * Returns nothing. | |
663 | - */ | |
664 | -static void cs_exit(void) | |
665 | -{ | |
666 | -} | |
667 | - | |
668 | -module_init(cs_init); | |
669 | -module_exit(cs_exit); | |
670 | -MODULE_LICENSE("GPL"); |
@@ -0,0 +1,63 @@ | ||
1 | +/* | |
2 | + * test.c | |
3 | + * | |
4 | + * Copyright (C) 2010-2013 Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
5 | + */ | |
6 | +#include "probe.h" | |
7 | + | |
8 | +/** | |
9 | + * cs_init - Initialize this module. | |
10 | + * | |
11 | + * Returns 0 on success, -EINVAL otherwise. | |
12 | + */ | |
13 | +static int __init cs_init(void) | |
14 | +{ | |
15 | +#ifdef CONFIG_SECURITY_COMPOSER_MAX | |
16 | + if (!probe_lsm_hooks_list()) | |
17 | + goto out; | |
18 | +#else | |
19 | + if (!probe_security_ops()) | |
20 | + goto out; | |
21 | +#endif | |
22 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
23 | + if (!probe_find_task_by_vpid()) | |
24 | + goto out; | |
25 | + if (!probe_find_task_by_pid_ns()) | |
26 | + goto out; | |
27 | +#endif | |
28 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
29 | + if (!probe_vfsmount_lock()) | |
30 | + goto out; | |
31 | +#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) | |
32 | + if (!probe___d_path()) | |
33 | + goto out; | |
34 | +#else | |
35 | + if (!probe_d_absolute_path()) | |
36 | + goto out; | |
37 | +#endif | |
38 | + printk(KERN_INFO "All dependent symbols have been guessed.\n"); | |
39 | + printk(KERN_INFO "Please verify these addresses using System.map for " | |
40 | + "this kernel (e.g. /boot/System.map-`uname -r` ).\n"); | |
41 | + printk(KERN_INFO "If these addresses are correct, you can try loading " | |
42 | + "CaitSith module on this kernel.\n"); | |
43 | + return 0; | |
44 | +out: | |
45 | + printk(KERN_INFO "Sorry, I couldn't guess dependent symbols.\n"); | |
46 | + printk(KERN_INFO "I need some changes for supporting your " | |
47 | + "environment.\n"); | |
48 | + printk(KERN_INFO "Please contact the author.\n"); | |
49 | + return -EINVAL; | |
50 | +} | |
51 | + | |
52 | +/** | |
53 | + * cs_exit - Exit this module. | |
54 | + * | |
55 | + * Returns nothing. | |
56 | + */ | |
57 | +static void cs_exit(void) | |
58 | +{ | |
59 | +} | |
60 | + | |
61 | +module_init(cs_init); | |
62 | +module_exit(cs_exit); | |
63 | +MODULE_LICENSE("GPL"); |
@@ -0,0 +1,712 @@ | ||
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 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) | |
22 | + | |
23 | +/** | |
24 | + * probe_kernel_read - Wrapper for kernel_read(). | |
25 | + * | |
26 | + * @file: Pointer to "struct file". | |
27 | + * @offset: Starting position. | |
28 | + * @addr: Buffer. | |
29 | + * @count: Size of @addr. | |
30 | + * | |
31 | + * Returns return value from kernel_read(). | |
32 | + */ | |
33 | +static int __init probe_kernel_read(struct file *file, unsigned long offset, | |
34 | + char *addr, unsigned long count) | |
35 | +{ | |
36 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 8) | |
37 | + /* | |
38 | + * I can't use kernel_read() because seq_read() returns -EPIPE | |
39 | + * if &pos != &file->f_pos . | |
40 | + */ | |
41 | + mm_segment_t old_fs; | |
42 | + unsigned long pos = file->f_pos; | |
43 | + int result; | |
44 | + file->f_pos = offset; | |
45 | + old_fs = get_fs(); | |
46 | + set_fs(get_ds()); | |
47 | + result = vfs_read(file, (void __user *)addr, count, &file->f_pos); | |
48 | + set_fs(old_fs); | |
49 | + file->f_pos = pos; | |
50 | + return result; | |
51 | +#else | |
52 | + return kernel_read(file, offset, addr, count); | |
53 | +#endif | |
54 | +} | |
55 | + | |
56 | +/** | |
57 | + * probe_find_symbol - Find function's address from /proc/kallsyms . | |
58 | + * | |
59 | + * @keyline: Function to find. | |
60 | + * | |
61 | + * Returns address of specified function on success, NULL otherwise. | |
62 | + */ | |
63 | +static void *__init probe_find_symbol(const char *keyline) | |
64 | +{ | |
65 | + struct file *file = NULL; | |
66 | + char *buf; | |
67 | + unsigned long entry = 0; | |
68 | + { | |
69 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) | |
70 | + struct file_system_type *fstype = get_fs_type("proc"); | |
71 | + struct vfsmount *mnt = vfs_kern_mount(fstype, 0, "proc", NULL); | |
72 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) | |
73 | + struct file_system_type *fstype = NULL; | |
74 | + struct vfsmount *mnt = do_kern_mount("proc", 0, "proc", NULL); | |
75 | +#else | |
76 | + struct file_system_type *fstype = get_fs_type("proc"); | |
77 | + struct vfsmount *mnt = kern_mount(fstype); | |
78 | +#endif | |
79 | + struct dentry *root; | |
80 | + struct dentry *dentry; | |
81 | + /* | |
82 | + * We embed put_filesystem() here because it is not exported. | |
83 | + */ | |
84 | + if (fstype) | |
85 | + module_put(fstype->owner); | |
86 | + if (IS_ERR(mnt)) | |
87 | + goto out; | |
88 | + root = dget(mnt->mnt_root); | |
89 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) | |
90 | + mutex_lock(&root->d_inode->i_mutex); | |
91 | + dentry = lookup_one_len("kallsyms", root, 8); | |
92 | + mutex_unlock(&root->d_inode->i_mutex); | |
93 | +#else | |
94 | + down(&root->d_inode->i_sem); | |
95 | + dentry = lookup_one_len("kallsyms", root, 8); | |
96 | + up(&root->d_inode->i_sem); | |
97 | +#endif | |
98 | + dput(root); | |
99 | + if (IS_ERR(dentry)) | |
100 | + mntput(mnt); | |
101 | + else { | |
102 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) | |
103 | + struct path path = { mnt, dentry }; | |
104 | + file = dentry_open(&path, O_RDONLY, current_cred()); | |
105 | +#else | |
106 | + file = dentry_open(dentry, mnt, O_RDONLY | |
107 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) | |
108 | + , current_cred() | |
109 | +#endif | |
110 | + ); | |
111 | +#endif | |
112 | + } | |
113 | + } | |
114 | + if (IS_ERR(file) || !file) | |
115 | + goto out; | |
116 | + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | |
117 | + if (buf) { | |
118 | + int len; | |
119 | + int offset = 0; | |
120 | + while ((len = probe_kernel_read(file, offset, buf, | |
121 | + PAGE_SIZE - 1)) > 0) { | |
122 | + char *cp; | |
123 | + buf[len] = '\0'; | |
124 | + cp = strrchr(buf, '\n'); | |
125 | + if (!cp) | |
126 | + break; | |
127 | + *(cp + 1) = '\0'; | |
128 | + offset += strlen(buf); | |
129 | + cp = strstr(buf, keyline); | |
130 | + if (!cp) | |
131 | + continue; | |
132 | + *cp = '\0'; | |
133 | + while (cp > buf && *(cp - 1) != '\n') | |
134 | + cp--; | |
135 | + entry = simple_strtoul(cp, NULL, 16); | |
136 | + break; | |
137 | + } | |
138 | + kfree(buf); | |
139 | + } | |
140 | + filp_close(file, NULL); | |
141 | +out: | |
142 | + return (void *) entry; | |
143 | +} | |
144 | + | |
145 | +#endif | |
146 | + | |
147 | +#if defined(CONFIG_SECURITY_COMPOSER_MAX) | |
148 | + | |
149 | +/* | |
150 | + * Dummy variable for finding location of | |
151 | + * "struct list_head lsm_hooks[LSM_MAX_HOOKS]". | |
152 | + */ | |
153 | +struct list_head probe_lsm_hooks[LSM_MAX_HOOKS]; | |
154 | + | |
155 | +/** | |
156 | + * probe_security_bprm_committed_creds - Dummy function which does identical to security_bprm_committed_creds() in security/security.c. | |
157 | + * | |
158 | + * @bprm: Pointer to "struct linux_binprm". | |
159 | + * | |
160 | + * Returns nothing. | |
161 | + */ | |
162 | +void probe_security_bprm_committed_creds(struct linux_binprm *bprm) | |
163 | +{ | |
164 | + 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); | |
171 | + } while (0); | |
172 | +} | |
173 | + | |
174 | +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
175 | + | |
176 | +/* | |
177 | + * Dummy variable for finding address of | |
178 | + * "struct security_operations *security_ops". | |
179 | + */ | |
180 | +static struct security_operations *probe_dummy_security_ops; | |
181 | + | |
182 | +/** | |
183 | + * probe_security_file_alloc - Dummy function which does identical to security_file_alloc() in security/security.c. | |
184 | + * | |
185 | + * @file: Pointer to "struct file". | |
186 | + * | |
187 | + * Returns return value from security_file_alloc(). | |
188 | + */ | |
189 | +static int probe_security_file_alloc(struct file *file) | |
190 | +{ | |
191 | + return probe_dummy_security_ops->file_alloc_security(file); | |
192 | +} | |
193 | + | |
194 | +#if defined(CONFIG_ARM) | |
195 | + | |
196 | +/** | |
197 | + * probe_security_ops_on_arm - Find security_ops on ARM. | |
198 | + * | |
199 | + * @base: Address of security_file_alloc(). | |
200 | + * | |
201 | + * Returns address of security_ops on success, NULL otherwise. | |
202 | + */ | |
203 | +static void * __init probe_security_ops_on_arm(unsigned int *base) | |
204 | +{ | |
205 | + static unsigned int *ip4ret; | |
206 | + int i; | |
207 | + const unsigned long addr = (unsigned long) &probe_dummy_security_ops; | |
208 | + unsigned int *ip = (unsigned int *) probe_security_file_alloc; | |
209 | + for (i = 0; i < 32; ip++, i++) { | |
210 | + if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) | |
211 | + continue; | |
212 | + ip = base + i; | |
213 | + ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); | |
214 | + return &ip4ret; | |
215 | + } | |
216 | + ip = (unsigned int *) probe_security_file_alloc; | |
217 | + for (i = 0; i < 32; ip++, i++) { | |
218 | + /* | |
219 | + * Find | |
220 | + * ldr r3, [pc, #offset1] | |
221 | + * ldr r3, [r3, #offset2] | |
222 | + * sequence. | |
223 | + */ | |
224 | + if ((*ip & 0xFFFFF000) != 0xE59F3000 || | |
225 | + (*(ip + 1) & 0xFFFFF000) != 0xE5933000) | |
226 | + continue; | |
227 | + ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); | |
228 | + ip4ret += (*(ip + 1) & 0xFFF) >> 2; | |
229 | + if ((unsigned long) ip4ret != addr) | |
230 | + continue; | |
231 | + ip = base + i; | |
232 | + ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); | |
233 | + ip4ret += (*(ip + 1) & 0xFFF) >> 2; | |
234 | + return &ip4ret; | |
235 | + } | |
236 | + return NULL; | |
237 | +} | |
238 | + | |
239 | +#endif | |
240 | + | |
241 | +#endif | |
242 | + | |
243 | +#if defined(CONFIG_ARM) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
244 | +/** | |
245 | + * probe_find_vfsmount_lock_on_arm - Find vfsmount_lock spinlock on ARM. | |
246 | + * | |
247 | + * @ip: Address of dummy function's entry point. | |
248 | + * @addr: Address of the variable which is used within @function. | |
249 | + * @base: Address of function's entry point. | |
250 | + * | |
251 | + * Returns address of vfsmount_lock on success, NULL otherwise. | |
252 | + */ | |
253 | +static void * __init probe_find_vfsmount_lock_on_arm(unsigned int *ip, | |
254 | + unsigned long addr, | |
255 | + unsigned int *base) | |
256 | +{ | |
257 | + int i; | |
258 | + for (i = 0; i < 32; ip++, i++) { | |
259 | + static unsigned int *ip4ret; | |
260 | + if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) | |
261 | + continue; | |
262 | + ip = base + i; | |
263 | + ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); | |
264 | + return &ip4ret; | |
265 | + } | |
266 | + return NULL; | |
267 | +} | |
268 | +#endif | |
269 | + | |
270 | +/** | |
271 | + * probe_find_variable - Find variable's address using dummy. | |
272 | + * | |
273 | + * @function: Pointer to dummy function's entry point. | |
274 | + * @addr: Address of the variable which is used within @function. | |
275 | + * @symbol: Name of symbol to resolve. | |
276 | + * | |
277 | + * This trick depends on below assumptions. | |
278 | + * | |
279 | + * (1) @addr is found within 128 bytes from @function, even if additional | |
280 | + * code (e.g. debug symbols) is added. | |
281 | + * (2) It is safe to read 128 bytes from @function. | |
282 | + * (3) @addr != Byte code except @addr. | |
283 | + */ | |
284 | +static void * __init probe_find_variable(void *function, unsigned long addr, | |
285 | + const char *symbol) | |
286 | +{ | |
287 | + int i; | |
288 | + u8 *base; | |
289 | + u8 *cp = function; | |
290 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) | |
291 | + if (*symbol == ' ') | |
292 | + base = probe_find_symbol(symbol); | |
293 | + else | |
294 | +#endif | |
295 | + base = __symbol_get(symbol); | |
296 | + if (!base) | |
297 | + return NULL; | |
298 | +#if defined(CONFIG_ARM) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) && !defined(CONFIG_SECURITY_COMPOSER_MAX) | |
299 | + if (function == probe_security_file_alloc) | |
300 | + return probe_security_ops_on_arm((unsigned int *) base); | |
301 | +#endif | |
302 | +#if defined(CONFIG_ARM) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
303 | + return probe_find_vfsmount_lock_on_arm(function, addr, | |
304 | + (unsigned int *) base); | |
305 | +#endif | |
306 | + /* First, assume absolute adressing mode is used. */ | |
307 | + for (i = 0; i < 128; i++) { | |
308 | + if (*(unsigned long *) cp == addr) | |
309 | + return base + i; | |
310 | + cp++; | |
311 | + } | |
312 | + /* Next, assume PC-relative addressing mode is used. */ | |
313 | + cp = function; | |
314 | + for (i = 0; i < 128; i++) { | |
315 | + if ((unsigned long) (cp + sizeof(int) + *(int *) cp) == addr) { | |
316 | + static void *cp4ret; | |
317 | + cp = base + i; | |
318 | + cp += sizeof(int) + *(int *) cp; | |
319 | + cp4ret = cp; | |
320 | + return &cp4ret; | |
321 | + } | |
322 | + cp++; | |
323 | + } | |
324 | + cp = function; | |
325 | + for (i = 0; i < 128; i++) { | |
326 | + if ((unsigned long) (long) (*(int *) cp) == addr) { | |
327 | + static void *cp4ret; | |
328 | + cp = base + i; | |
329 | + cp = (void *) (long) (*(int *) cp); | |
330 | + cp4ret = cp; | |
331 | + return &cp4ret; | |
332 | + } | |
333 | + cp++; | |
334 | + } | |
335 | + return NULL; | |
336 | +} | |
337 | + | |
338 | +#if defined(CONFIG_SECURITY_COMPOSER_MAX) | |
339 | + | |
340 | +static void * __init probe_find_variable(void *function, unsigned long addr, | |
341 | + const char *symbol); | |
342 | + | |
343 | +/** | |
344 | + * probe_lsm_hooks_list - Find address of "struct list_head lsm_hooks[LSM_MAX_HOOKS]". | |
345 | + * | |
346 | + * Returns pointer to "struct security_operations" on success, NULL otherwise. | |
347 | + */ | |
348 | +struct list_head * __init probe_lsm_hooks_list(void) | |
349 | +{ | |
350 | + void *cp; | |
351 | + /* Guess "struct list_head lsm_hooks[LSM_MAX_HOOKS];". */ | |
352 | + cp = probe_find_variable(probe_security_bprm_committed_creds, | |
353 | + (unsigned long) probe_lsm_hooks, | |
354 | + " security_bprm_committed_creds\n"); | |
355 | + if (!cp) { | |
356 | + printk(KERN_ERR | |
357 | + "Can't resolve security_bprm_committed_creds().\n"); | |
358 | + goto out; | |
359 | + } | |
360 | + /* This should be "struct list_head lsm_hooks[LSM_MAX_HOOKS];". */ | |
361 | + cp = (struct list_head *) (*(unsigned long *) cp); | |
362 | + if (!cp) { | |
363 | + printk(KERN_ERR "Can't resolve lsm_hooks array.\n"); | |
364 | + goto out; | |
365 | + } | |
366 | + printk(KERN_INFO "lsm_hooks=%p\n", cp); | |
367 | + return cp; | |
368 | +out: | |
369 | + return NULL; | |
370 | +} | |
371 | + | |
372 | +#else | |
373 | + | |
374 | +/** | |
375 | + * probe_security_ops - Find address of "struct security_operations *security_ops". | |
376 | + * | |
377 | + * Returns pointer to "struct security_operations" on success, NULL otherwise. | |
378 | + */ | |
379 | +struct security_operations * __init probe_security_ops(void) | |
380 | +{ | |
381 | + struct security_operations **ptr; | |
382 | + struct security_operations *ops; | |
383 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
384 | + void *cp; | |
385 | + /* Guess "struct security_operations *security_ops;". */ | |
386 | + cp = probe_find_variable(probe_security_file_alloc, (unsigned long) | |
387 | + &probe_dummy_security_ops, | |
388 | + " security_file_alloc\n"); | |
389 | + if (!cp) { | |
390 | + printk(KERN_ERR "Can't resolve security_file_alloc().\n"); | |
391 | + return NULL; | |
392 | + } | |
393 | + /* This should be "struct security_operations *security_ops;". */ | |
394 | + ptr = *(struct security_operations ***) cp; | |
395 | +#else | |
396 | + /* This is "struct security_operations *security_ops;". */ | |
397 | + ptr = (struct security_operations **) __symbol_get("security_ops"); | |
398 | +#endif | |
399 | + if (!ptr) { | |
400 | + printk(KERN_ERR "Can't resolve security_ops structure.\n"); | |
401 | + return NULL; | |
402 | + } | |
403 | + printk(KERN_INFO "security_ops=%p\n", ptr); | |
404 | + ops = *ptr; | |
405 | + if (!ops) { | |
406 | + printk(KERN_ERR "No security_operations registered.\n"); | |
407 | + return NULL; | |
408 | + } | |
409 | + return ops; | |
410 | +} | |
411 | + | |
412 | +#endif | |
413 | + | |
414 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
415 | + | |
416 | +/** | |
417 | + * probe_find_task_by_vpid - Find address of find_task_by_vpid(). | |
418 | + * | |
419 | + * Returns address of find_task_by_vpid() on success, NULL otherwise. | |
420 | + */ | |
421 | +void * __init probe_find_task_by_vpid(void) | |
422 | +{ | |
423 | + void *ptr; | |
424 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) | |
425 | + ptr = probe_find_symbol(" find_task_by_vpid\n"); | |
426 | +#else | |
427 | + ptr = __symbol_get("find_task_by_vpid"); | |
428 | +#endif | |
429 | + if (!ptr) { | |
430 | + printk(KERN_ERR "Can't resolve find_task_by_vpid().\n"); | |
431 | + return NULL; | |
432 | + } | |
433 | + printk(KERN_INFO "find_task_by_vpid=%p\n", ptr); | |
434 | + return ptr; | |
435 | +} | |
436 | + | |
437 | +/** | |
438 | + * probe_find_task_by_pid_ns - Find address of find_task_by_pid(). | |
439 | + * | |
440 | + * Returns address of find_task_by_pid_ns() on success, NULL otherwise. | |
441 | + */ | |
442 | +void * __init probe_find_task_by_pid_ns(void) | |
443 | +{ | |
444 | + void *ptr; | |
445 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) | |
446 | + ptr = probe_find_symbol(" find_task_by_pid_ns\n"); | |
447 | +#else | |
448 | + ptr = __symbol_get("find_task_by_pid_ns"); | |
449 | +#endif | |
450 | + if (!ptr) { | |
451 | + printk(KERN_ERR "Can't resolve find_task_by_pid_ns().\n"); | |
452 | + return NULL; | |
453 | + } | |
454 | + printk(KERN_INFO "find_task_by_pid_ns=%p\n", ptr); | |
455 | + return ptr; | |
456 | +} | |
457 | + | |
458 | +#endif | |
459 | + | |
460 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
461 | + | |
462 | +#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) | |
463 | + | |
464 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) | |
465 | + | |
466 | +/* Dummy variable for finding address of "spinlock_t vfsmount_lock". */ | |
467 | +static spinlock_t probe_vfsmount_lock __cacheline_aligned_in_smp = | |
468 | + SPIN_LOCK_UNLOCKED; | |
469 | + | |
470 | +static struct list_head *probe_mount_hashtable; | |
471 | +static int probe_hash_mask, probe_hash_bits; | |
472 | + | |
473 | +/** | |
474 | + * hash - Copy of hash() in fs/namespace.c. | |
475 | + * | |
476 | + * @mnt: Pointer to "struct vfsmount". | |
477 | + * @dentry: Pointer to "struct dentry". | |
478 | + * | |
479 | + * Returns hash value. | |
480 | + */ | |
481 | +static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) | |
482 | +{ | |
483 | + unsigned long tmp = ((unsigned long) mnt / L1_CACHE_BYTES); | |
484 | + tmp += ((unsigned long) dentry / L1_CACHE_BYTES); | |
485 | + tmp = tmp + (tmp >> probe_hash_bits); | |
486 | + return tmp & probe_hash_mask; | |
487 | +} | |
488 | + | |
489 | +/** | |
490 | + * probe_lookup_mnt - Dummy function which does identical to lookup_mnt() in fs/namespace.c. | |
491 | + * | |
492 | + * @mnt: Pointer to "struct vfsmount". | |
493 | + * @dentry: Pointer to "struct dentry". | |
494 | + * | |
495 | + * Returns pointer to "struct vfsmount". | |
496 | + */ | |
497 | +static struct vfsmount *probe_lookup_mnt(struct vfsmount *mnt, | |
498 | + struct dentry *dentry) | |
499 | +{ | |
500 | + struct list_head *head = probe_mount_hashtable + hash(mnt, dentry); | |
501 | + struct list_head *tmp = head; | |
502 | + struct vfsmount *p, *found = NULL; | |
503 | + | |
504 | + spin_lock(&probe_vfsmount_lock); | |
505 | + for (;;) { | |
506 | + tmp = tmp->next; | |
507 | + p = NULL; | |
508 | + if (tmp == head) | |
509 | + break; | |
510 | + p = list_entry(tmp, struct vfsmount, mnt_hash); | |
511 | + if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) { | |
512 | + found = mntget(p); | |
513 | + break; | |
514 | + } | |
515 | + } | |
516 | + spin_unlock(&probe_vfsmount_lock); | |
517 | + return found; | |
518 | +} | |
519 | + | |
520 | +/** | |
521 | + * probe_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". | |
522 | + * | |
523 | + * Returns address of vfsmount_lock on success, NULL otherwise. | |
524 | + */ | |
525 | +void * __init probe_vfsmount_lock(void) | |
526 | +{ | |
527 | + void *cp; | |
528 | + spinlock_t *ptr; | |
529 | + /* Guess "spinlock_t vfsmount_lock;". */ | |
530 | + cp = probe_find_variable(probe_lookup_mnt, (unsigned long) | |
531 | + &probe_vfsmount_lock, " lookup_mnt\n"); | |
532 | + if (!cp) { | |
533 | + printk(KERN_ERR "Can't resolve lookup_mnt().\n"); | |
534 | + return NULL; | |
535 | + } | |
536 | + /* This should be "spinlock_t *vfsmount_lock;". */ | |
537 | + ptr = *(spinlock_t **) cp; | |
538 | + if (!ptr) { | |
539 | + printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); | |
540 | + return NULL; | |
541 | + } | |
542 | + printk(KERN_INFO "vfsmount_lock=%p\n", ptr); | |
543 | + return ptr; | |
544 | +} | |
545 | + | |
546 | +#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) | |
547 | + | |
548 | +/* Dummy variable for finding address of "spinlock_t vfsmount_lock". */ | |
549 | +static spinlock_t probe_vfsmount_lock; | |
550 | + | |
551 | +/** | |
552 | + * probe_follow_up - Dummy function which does identical to follow_up() in fs/namei.c. | |
553 | + * | |
554 | + * @mnt: Pointer to "struct vfsmount *". | |
555 | + * @dentry: Pointer to "struct dentry *". | |
556 | + * | |
557 | + * Returns 1 if followed up, 0 otehrwise. | |
558 | + */ | |
559 | +static int probe_follow_up(struct vfsmount **mnt, struct dentry **dentry) | |
560 | +{ | |
561 | + struct vfsmount *parent; | |
562 | + struct dentry *mountpoint; | |
563 | + spin_lock(&probe_vfsmount_lock); | |
564 | + parent = (*mnt)->mnt_parent; | |
565 | + if (parent == *mnt) { | |
566 | + spin_unlock(&probe_vfsmount_lock); | |
567 | + return 0; | |
568 | + } | |
569 | + mntget(parent); | |
570 | + mountpoint = dget((*mnt)->mnt_mountpoint); | |
571 | + spin_unlock(&probe_vfsmount_lock); | |
572 | + dput(*dentry); | |
573 | + *dentry = mountpoint; | |
574 | + mntput(*mnt); | |
575 | + *mnt = parent; | |
576 | + return 1; | |
577 | +} | |
578 | + | |
579 | +/** | |
580 | + * probe_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". | |
581 | + * | |
582 | + * Returns address of vfsmount_lock on success, NULL otherwise. | |
583 | + */ | |
584 | +void * __init probe_vfsmount_lock(void) | |
585 | +{ | |
586 | + void *cp; | |
587 | + spinlock_t *ptr; | |
588 | + /* Guess "spinlock_t vfsmount_lock;". */ | |
589 | + cp = probe_find_variable(probe_follow_up, (unsigned long) | |
590 | + &probe_vfsmount_lock, "follow_up"); | |
591 | + if (!cp) { | |
592 | + printk(KERN_ERR "Can't resolve follow_up().\n"); | |
593 | + return NULL; | |
594 | + } | |
595 | + /* This should be "spinlock_t *vfsmount_lock;". */ | |
596 | + ptr = *(spinlock_t **) cp; | |
597 | + if (!ptr) { | |
598 | + printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); | |
599 | + return NULL; | |
600 | + } | |
601 | + printk(KERN_INFO "vfsmount_lock=%p\n", ptr); | |
602 | + return ptr; | |
603 | +} | |
604 | + | |
605 | +#else | |
606 | + | |
607 | +/* Dummy variable for finding address of "spinlock_t vfsmount_lock". */ | |
608 | +static spinlock_t probe_vfsmount_lock; | |
609 | + | |
610 | +/** | |
611 | + * probe_mnt_pin - Dummy function which does identical to mnt_pin() in fs/namespace.c. | |
612 | + * | |
613 | + * @mnt: Pointer to "struct vfsmount". | |
614 | + * | |
615 | + * Returns nothing. | |
616 | + */ | |
617 | +static void probe_mnt_pin(struct vfsmount *mnt) | |
618 | +{ | |
619 | + spin_lock(&probe_vfsmount_lock); | |
620 | + mnt->mnt_pinned++; | |
621 | + spin_unlock(&probe_vfsmount_lock); | |
622 | +} | |
623 | + | |
624 | +/** | |
625 | + * probe_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". | |
626 | + * | |
627 | + * Returns address of vfsmount_lock on success, NULL otherwise. | |
628 | + */ | |
629 | +void * __init probe_vfsmount_lock(void) | |
630 | +{ | |
631 | + void *cp; | |
632 | + spinlock_t *ptr; | |
633 | + /* Guess "spinlock_t vfsmount_lock;". */ | |
634 | + cp = probe_find_variable(probe_mnt_pin, (unsigned long) | |
635 | + &probe_vfsmount_lock, "mnt_pin"); | |
636 | + if (!cp) { | |
637 | + printk(KERN_ERR "Can't resolve mnt_pin().\n"); | |
638 | + return NULL; | |
639 | + } | |
640 | + /* This should be "spinlock_t *vfsmount_lock;". */ | |
641 | + ptr = *(spinlock_t **) cp; | |
642 | + if (!ptr) { | |
643 | + printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); | |
644 | + return NULL; | |
645 | + } | |
646 | + printk(KERN_INFO "vfsmount_lock=%p\n", ptr); | |
647 | + return ptr; | |
648 | +} | |
649 | + | |
650 | +#endif | |
651 | + | |
652 | +#else | |
653 | + | |
654 | +/* | |
655 | + * Never mark this variable as __initdata , for this variable might be accessed | |
656 | + * by caller of probe_find_vfsmount_lock(). | |
657 | + */ | |
658 | +static spinlock_t probe_vfsmount_lock; | |
659 | + | |
660 | +/** | |
661 | + * probe_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". | |
662 | + * | |
663 | + * Returns address of vfsmount_lock. | |
664 | + */ | |
665 | +void * __init probe_vfsmount_lock(void) | |
666 | +{ | |
667 | + return &probe_vfsmount_lock; | |
668 | +} | |
669 | + | |
670 | +#endif | |
671 | + | |
672 | +#endif | |
673 | + | |
674 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) && LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) | |
675 | + | |
676 | +/** | |
677 | + * probe___d_path - Find address of "__d_path()". | |
678 | + * | |
679 | + * Returns address of __d_path() on success, NULL otherwise. | |
680 | + */ | |
681 | +void * __init probe___d_path(void) | |
682 | +{ | |
683 | + void *ptr = probe_find_symbol(" __d_path\n"); | |
684 | + if (!ptr) { | |
685 | + printk(KERN_ERR "Can't resolve __d_path().\n"); | |
686 | + return NULL; | |
687 | + } | |
688 | + printk(KERN_INFO "__d_path=%p\n", ptr); | |
689 | + return ptr; | |
690 | +} | |
691 | + | |
692 | +#endif | |
693 | + | |
694 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0) | |
695 | + | |
696 | +/** | |
697 | + * probe_d_absolute_path - Find address of "d_absolute_path()". | |
698 | + * | |
699 | + * Returns address of d_absolute_path() on success, NULL otherwise. | |
700 | + */ | |
701 | +void * __init probe_d_absolute_path(void) | |
702 | +{ | |
703 | + void *ptr = probe_find_symbol(" d_absolute_path\n"); | |
704 | + if (!ptr) { | |
705 | + printk(KERN_ERR "Can't resolve d_absolute_path().\n"); | |
706 | + return NULL; | |
707 | + } | |
708 | + printk(KERN_INFO "d_absolute_path=%p\n", ptr); | |
709 | + return ptr; | |
710 | +} | |
711 | + | |
712 | +#endif |
@@ -1,12 +1,13 @@ | ||
1 | 1 | /* |
2 | 2 | * lsm.c |
3 | 3 | * |
4 | - * Copyright (C) 2010-2012 Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
4 | + * Copyright (C) 2010-2013 Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
5 | 5 | * |
6 | 6 | * Version: 0.1.8 2013/01/06 |
7 | 7 | */ |
8 | 8 | |
9 | 9 | #include "caitsith.h" |
10 | +#include "probe.h" | |
10 | 11 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0) |
11 | 12 | #define USE_UMODE_T |
12 | 13 | #else |
@@ -111,9 +112,7 @@ | ||
111 | 112 | if (!r) |
112 | 113 | return; |
113 | 114 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) |
114 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) | |
115 | - security->cs_flags &= ~CS_TASK_STARTED_EXECVE; | |
116 | -#else | |
115 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) | |
117 | 116 | /* |
118 | 117 | * Drop refcount on "struct cred" in "struct linux_binprm" and forget |
119 | 118 | * it. |
@@ -142,11 +141,11 @@ | ||
142 | 141 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) |
143 | 142 | /* |
144 | 143 | * If this security context was associated with "struct pid" and |
145 | - * ptr->cs_flags has CS_TASK_STARTED_EXECVE set, it indicates that a | |
144 | + * ptr->cs_flags has CS_TASK_IS_IN_EXECVE set, it indicates that a | |
146 | 145 | * "struct task_struct" associated with this security context exited |
147 | 146 | * immediately after do_execve() has failed. |
148 | 147 | */ |
149 | - if (ptr->pid && (ptr->cs_flags & CS_TASK_STARTED_EXECVE)) { | |
148 | + if (ptr->pid && (ptr->cs_flags & CS_TASK_IS_IN_EXECVE)) { | |
150 | 149 | #ifdef CONFIG_CAITSITH_DEBUG |
151 | 150 | static bool done; |
152 | 151 | if (!done) { |
@@ -203,7 +202,7 @@ | ||
203 | 202 | static bool done; |
204 | 203 | if (!done) { |
205 | 204 | printk(KERN_INFO "CAITSITH: Releasing memory in " |
206 | - "\"struct cs_execve\" because some " | |
205 | + "\"struct cs_request_info\" because some " | |
207 | 206 | "\"struct task_struct\" has exit()ed " |
208 | 207 | "immediately after do_execve() has failed.\n"); |
209 | 208 | done = true; |
@@ -771,9 +770,7 @@ | ||
771 | 770 | rc = cs_start_execve(bprm, &security->r); |
772 | 771 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) |
773 | 772 | if (security->r) { |
774 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) | |
775 | - security->cs_flags |= CS_TASK_STARTED_EXECVE; | |
776 | -#else | |
773 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 31) | |
777 | 774 | /* |
778 | 775 | * Get refcount on "struct cred" in |
779 | 776 | * "struct linux_binprm" and remember it. |
@@ -2488,608 +2485,6 @@ | ||
2488 | 2485 | |
2489 | 2486 | #endif |
2490 | 2487 | |
2491 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) | |
2492 | - | |
2493 | -#include <linux/mount.h> | |
2494 | -#include <linux/fs_struct.h> | |
2495 | - | |
2496 | -/** | |
2497 | - * cs_kernel_read - Wrapper for kernel_read(). | |
2498 | - * | |
2499 | - * @file: Pointer to "struct file". | |
2500 | - * @offset: Starting position. | |
2501 | - * @addr: Buffer. | |
2502 | - * @count: Size of @addr. | |
2503 | - * | |
2504 | - * Returns return value from kernel_read(). | |
2505 | - */ | |
2506 | -static int __init cs_kernel_read(struct file *file, unsigned long offset, | |
2507 | - char *addr, unsigned long count) | |
2508 | -{ | |
2509 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 8) | |
2510 | - /* | |
2511 | - * I can't use kernel_read() because seq_read() returns -EPIPE | |
2512 | - * if &pos != &file->f_pos . | |
2513 | - */ | |
2514 | - mm_segment_t old_fs; | |
2515 | - unsigned long pos = file->f_pos; | |
2516 | - int result; | |
2517 | - file->f_pos = offset; | |
2518 | - old_fs = get_fs(); | |
2519 | - set_fs(get_ds()); | |
2520 | - result = vfs_read(file, (void __user *)addr, count, &file->f_pos); | |
2521 | - set_fs(old_fs); | |
2522 | - file->f_pos = pos; | |
2523 | - return result; | |
2524 | -#else | |
2525 | - return kernel_read(file, offset, addr, count); | |
2526 | -#endif | |
2527 | -} | |
2528 | - | |
2529 | -/** | |
2530 | - * cs_find_symbol - Find function's address from /proc/kallsyms . | |
2531 | - * | |
2532 | - * @keyline: Function to find. | |
2533 | - * | |
2534 | - * Returns address of specified function on success, NULL otherwise. | |
2535 | - */ | |
2536 | -static void *__init cs_find_symbol(const char *keyline) | |
2537 | -{ | |
2538 | - struct file *file = NULL; | |
2539 | - char *buf; | |
2540 | - unsigned long entry = 0; | |
2541 | - { | |
2542 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 18) | |
2543 | - struct file_system_type *fstype = get_fs_type("proc"); | |
2544 | - struct vfsmount *mnt = vfs_kern_mount(fstype, 0, "proc", NULL); | |
2545 | -#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 8) | |
2546 | - struct file_system_type *fstype = NULL; | |
2547 | - struct vfsmount *mnt = do_kern_mount("proc", 0, "proc", NULL); | |
2548 | -#else | |
2549 | - struct file_system_type *fstype = get_fs_type("proc"); | |
2550 | - struct vfsmount *mnt = kern_mount(fstype); | |
2551 | -#endif | |
2552 | - struct dentry *root; | |
2553 | - struct dentry *dentry; | |
2554 | - /* | |
2555 | - * We embed put_filesystem() here because it is not exported. | |
2556 | - */ | |
2557 | - if (fstype) | |
2558 | - module_put(fstype->owner); | |
2559 | - if (IS_ERR(mnt)) | |
2560 | - goto out; | |
2561 | - root = dget(mnt->mnt_root); | |
2562 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 16) | |
2563 | - mutex_lock(&root->d_inode->i_mutex); | |
2564 | - dentry = lookup_one_len("kallsyms", root, 8); | |
2565 | - mutex_unlock(&root->d_inode->i_mutex); | |
2566 | -#else | |
2567 | - down(&root->d_inode->i_sem); | |
2568 | - dentry = lookup_one_len("kallsyms", root, 8); | |
2569 | - up(&root->d_inode->i_sem); | |
2570 | -#endif | |
2571 | - dput(root); | |
2572 | - if (IS_ERR(dentry)) | |
2573 | - mntput(mnt); | |
2574 | - else { | |
2575 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0) | |
2576 | - struct path path = { mnt, dentry }; | |
2577 | - file = dentry_open(&path, O_RDONLY, current_cred()); | |
2578 | -#else | |
2579 | - file = dentry_open(dentry, mnt, O_RDONLY | |
2580 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) | |
2581 | - , current_cred() | |
2582 | -#endif | |
2583 | - ); | |
2584 | -#endif | |
2585 | - } | |
2586 | - } | |
2587 | - if (IS_ERR(file) || !file) | |
2588 | - goto out; | |
2589 | - buf = kmalloc(PAGE_SIZE, GFP_KERNEL); | |
2590 | - if (buf) { | |
2591 | - int len; | |
2592 | - int offset = 0; | |
2593 | - while ((len = cs_kernel_read(file, offset, buf, | |
2594 | - PAGE_SIZE - 1)) > 0) { | |
2595 | - char *cp; | |
2596 | - buf[len] = '\0'; | |
2597 | - cp = strrchr(buf, '\n'); | |
2598 | - if (!cp) | |
2599 | - break; | |
2600 | - *(cp + 1) = '\0'; | |
2601 | - offset += strlen(buf); | |
2602 | - cp = strstr(buf, keyline); | |
2603 | - if (!cp) | |
2604 | - continue; | |
2605 | - *cp = '\0'; | |
2606 | - while (cp > buf && *(cp - 1) != '\n') | |
2607 | - cp--; | |
2608 | - entry = simple_strtoul(cp, NULL, 16); | |
2609 | - break; | |
2610 | - } | |
2611 | - kfree(buf); | |
2612 | - } | |
2613 | - filp_close(file, NULL); | |
2614 | -out: | |
2615 | - return (void *) entry; | |
2616 | -} | |
2617 | - | |
2618 | -#endif | |
2619 | - | |
2620 | -#if defined(CONFIG_ARM) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
2621 | -static int lsm_addr_calculator(struct file *file); | |
2622 | -static void * __init cs_find_security_ops_on_arm(unsigned int *base); | |
2623 | -#endif | |
2624 | - | |
2625 | -#if defined(CONFIG_ARM) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
2626 | -/** | |
2627 | - * cs_find_vfsmount_lock_on_arm - Find vfsmount_lock spinlock on ARM. | |
2628 | - * | |
2629 | - * @ip: Address of dummy function's entry point. | |
2630 | - * @addr: Address of the variable which is used within @function. | |
2631 | - * @base: Address of function's entry point. | |
2632 | - * | |
2633 | - * Returns address of vfsmount_lock on success, NULL otherwise. | |
2634 | - */ | |
2635 | -static void * __init cs_find_vfsmount_lock_on_arm(unsigned int *ip, | |
2636 | - unsigned long addr, | |
2637 | - unsigned int *base) | |
2638 | -{ | |
2639 | - int i; | |
2640 | - for (i = 0; i < 32; ip++, i++) { | |
2641 | - static unsigned int *ip4ret; | |
2642 | - if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) | |
2643 | - continue; | |
2644 | - ip = base + i; | |
2645 | - ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); | |
2646 | - return &ip4ret; | |
2647 | - } | |
2648 | - return NULL; | |
2649 | -} | |
2650 | -#endif | |
2651 | - | |
2652 | -/** | |
2653 | - * cs_find_variable - Find variable's address using dummy. | |
2654 | - * | |
2655 | - * @function: Pointer to dummy function's entry point. | |
2656 | - * @addr: Address of the variable which is used within @function. | |
2657 | - * @symbol: Name of symbol to resolve. | |
2658 | - * | |
2659 | - * This trick depends on below assumptions. | |
2660 | - * | |
2661 | - * (1) @addr is found within 128 bytes from @function, even if additional | |
2662 | - * code (e.g. debug symbols) is added. | |
2663 | - * (2) It is safe to read 128 bytes from @function. | |
2664 | - * (3) @addr != Byte code except @addr. | |
2665 | - */ | |
2666 | -static void * __init cs_find_variable(void *function, unsigned long addr, | |
2667 | - const char *symbol) | |
2668 | -{ | |
2669 | - int i; | |
2670 | - u8 *base; | |
2671 | - u8 *cp = function; | |
2672 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) | |
2673 | - if (*symbol == ' ') | |
2674 | - base = cs_find_symbol(symbol); | |
2675 | - else | |
2676 | -#endif | |
2677 | - base = __symbol_get(symbol); | |
2678 | - if (!base) | |
2679 | - return NULL; | |
2680 | -#if defined(CONFIG_ARM) && LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
2681 | - if (function == lsm_addr_calculator) | |
2682 | - return cs_find_security_ops_on_arm((unsigned int *) base); | |
2683 | -#endif | |
2684 | -#if defined(CONFIG_ARM) && LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
2685 | - return cs_find_vfsmount_lock_on_arm(function, addr, | |
2686 | - (unsigned int *) base); | |
2687 | -#endif | |
2688 | - /* First, assume absolute adressing mode is used. */ | |
2689 | - for (i = 0; i < 128; i++) { | |
2690 | - if (*(unsigned long *) cp == addr) | |
2691 | - return base + i; | |
2692 | - cp++; | |
2693 | - } | |
2694 | - /* Next, assume PC-relative addressing mode is used. */ | |
2695 | - cp = function; | |
2696 | - for (i = 0; i < 128; i++) { | |
2697 | - if ((unsigned long) (cp + sizeof(int) + *(int *) cp) == addr) { | |
2698 | - static void *cp4ret; | |
2699 | - cp = base + i; | |
2700 | - cp += sizeof(int) + *(int *) cp; | |
2701 | - cp4ret = cp; | |
2702 | - return &cp4ret; | |
2703 | - } | |
2704 | - cp++; | |
2705 | - } | |
2706 | - cp = function; | |
2707 | - for (i = 0; i < 128; i++) { | |
2708 | - if ((unsigned long) (long) (*(int *) cp) == addr) { | |
2709 | - static void *cp4ret; | |
2710 | - cp = base + i; | |
2711 | - cp = (void *) (long) (*(int *) cp); | |
2712 | - cp4ret = cp; | |
2713 | - return &cp4ret; | |
2714 | - } | |
2715 | - cp++; | |
2716 | - } | |
2717 | - return NULL; | |
2718 | -} | |
2719 | - | |
2720 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
2721 | - | |
2722 | -/* Never mark this variable as __initdata . */ | |
2723 | -static struct security_operations *cs_security_ops; | |
2724 | - | |
2725 | -/** | |
2726 | - * lsm_addr_calculator - Dummy function which does identical to security_file_alloc() in security/security.c. | |
2727 | - * | |
2728 | - * @file: Pointer to "struct file". | |
2729 | - * | |
2730 | - * Returns return value from security_file_alloc(). | |
2731 | - * | |
2732 | - * Never mark this function as __init in order to make sure that compiler | |
2733 | - * generates identical code for security_file_alloc() and this function. | |
2734 | - */ | |
2735 | -static int lsm_addr_calculator(struct file *file) | |
2736 | -{ | |
2737 | - return cs_security_ops->file_alloc_security(file); | |
2738 | -} | |
2739 | - | |
2740 | -#ifdef CONFIG_ARM | |
2741 | -/** | |
2742 | - * cs_find_security_ops_on_arm - Find security_ops on ARM. | |
2743 | - * | |
2744 | - * @base: Address of security_file_alloc(). | |
2745 | - * | |
2746 | - * Returns address of security_ops on success, NULL otherwise. | |
2747 | - */ | |
2748 | -static void * __init cs_find_security_ops_on_arm(unsigned int *base) | |
2749 | -{ | |
2750 | - static unsigned int *ip4ret; | |
2751 | - int i; | |
2752 | - const unsigned long addr = (unsigned long) &cs_security_ops; | |
2753 | - unsigned int *ip = (unsigned int *) lsm_addr_calculator; | |
2754 | - for (i = 0; i < 32; ip++, i++) { | |
2755 | - if (*(ip + 2 + ((*ip & 0xFFF) >> 2)) != addr) | |
2756 | - continue; | |
2757 | - ip = base + i; | |
2758 | - ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); | |
2759 | - return &ip4ret; | |
2760 | - } | |
2761 | - ip = (unsigned int *) lsm_addr_calculator; | |
2762 | - for (i = 0; i < 32; ip++, i++) { | |
2763 | - /* | |
2764 | - * Find | |
2765 | - * ldr r3, [pc, #offset1] | |
2766 | - * ldr r3, [r3, #offset2] | |
2767 | - * sequence. | |
2768 | - */ | |
2769 | - if ((*ip & 0xFFFFF000) != 0xE59F3000 || | |
2770 | - (*(ip + 1) & 0xFFFFF000) != 0xE5933000) | |
2771 | - continue; | |
2772 | - ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); | |
2773 | - ip4ret += (*(ip + 1) & 0xFFF) >> 2; | |
2774 | - if ((unsigned long) ip4ret != addr) | |
2775 | - continue; | |
2776 | - ip = base + i; | |
2777 | - ip4ret = (unsigned int *) (*(ip + 2 + ((*ip & 0xFFF) >> 2))); | |
2778 | - ip4ret += (*(ip + 1) & 0xFFF) >> 2; | |
2779 | - return &ip4ret; | |
2780 | - } | |
2781 | - return NULL; | |
2782 | -} | |
2783 | -#endif | |
2784 | -#endif | |
2785 | - | |
2786 | -/** | |
2787 | - * cs_find_security_ops - Find address of "struct security_operations *security_ops". | |
2788 | - * | |
2789 | - * Returns pointer to "struct security_operations" on success, NULL otherwise. | |
2790 | - */ | |
2791 | -static struct security_operations * __init cs_find_security_ops(void) | |
2792 | -{ | |
2793 | - struct security_operations **ptr; | |
2794 | - struct security_operations *ops; | |
2795 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
2796 | - void *cp; | |
2797 | - /* Guess "struct security_operations *security_ops;". */ | |
2798 | - cp = cs_find_variable(lsm_addr_calculator, (unsigned long) | |
2799 | - &cs_security_ops, " security_file_alloc\n"); | |
2800 | - if (!cp) { | |
2801 | - printk(KERN_ERR "Can't resolve security_file_alloc().\n"); | |
2802 | - goto out; | |
2803 | - } | |
2804 | - /* This should be "struct security_operations *security_ops;". */ | |
2805 | - ptr = *(struct security_operations ***) cp; | |
2806 | -#else | |
2807 | - /* This is "struct security_operations *security_ops;". */ | |
2808 | - ptr = (struct security_operations **) __symbol_get("security_ops"); | |
2809 | -#endif | |
2810 | - if (!ptr) { | |
2811 | - printk(KERN_ERR "Can't resolve security_ops structure.\n"); | |
2812 | - goto out; | |
2813 | - } | |
2814 | - printk(KERN_INFO "security_ops=%p\n", ptr); | |
2815 | - ops = *ptr; | |
2816 | - if (!ops) { | |
2817 | - printk(KERN_ERR "No security_operations registered.\n"); | |
2818 | - goto out; | |
2819 | - } | |
2820 | - return ops; | |
2821 | -out: | |
2822 | - return NULL; | |
2823 | -} | |
2824 | - | |
2825 | -/** | |
2826 | - * cs_find_find_task_by_pid - Find address of find_task_by_pid(). | |
2827 | - * | |
2828 | - * Returns true on success, false otherwise. | |
2829 | - */ | |
2830 | -static bool __init cs_find_find_task_by_pid(void) | |
2831 | -{ | |
2832 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
2833 | - void *ptr; | |
2834 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) | |
2835 | - ptr = cs_find_symbol(" find_task_by_vpid\n"); | |
2836 | -#else | |
2837 | - ptr = __symbol_get("find_task_by_vpid"); | |
2838 | -#endif | |
2839 | - if (!ptr) { | |
2840 | - printk(KERN_ERR "Can't resolve find_task_by_vpid().\n"); | |
2841 | - goto out; | |
2842 | - } | |
2843 | - caitsith_exports.find_task_by_vpid = ptr; | |
2844 | - printk(KERN_INFO "find_task_by_vpid=%p\n", ptr); | |
2845 | -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) | |
2846 | - ptr = cs_find_symbol(" find_task_by_pid_ns\n"); | |
2847 | -#else | |
2848 | - ptr = __symbol_get("find_task_by_pid_ns"); | |
2849 | -#endif | |
2850 | - if (!ptr) { | |
2851 | - printk(KERN_ERR "Can't resolve find_task_by_pid_ns().\n"); | |
2852 | - goto out; | |
2853 | - } | |
2854 | - caitsith_exports.find_task_by_pid_ns = ptr; | |
2855 | - printk(KERN_INFO "find_task_by_pid_ns=%p\n", ptr); | |
2856 | - return true; | |
2857 | -out: | |
2858 | - return false; | |
2859 | -#else | |
2860 | - return true; | |
2861 | -#endif | |
2862 | -} | |
2863 | - | |
2864 | -#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) || LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 36) | |
2865 | - | |
2866 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) | |
2867 | - | |
2868 | -/* Never mark this variable as __initdata . */ | |
2869 | -static spinlock_t cs_vfsmount_lock __cacheline_aligned_in_smp = | |
2870 | - SPIN_LOCK_UNLOCKED; | |
2871 | - | |
2872 | -static struct list_head *mount_hashtable; | |
2873 | -static int hash_mask, hash_bits; | |
2874 | - | |
2875 | -/** | |
2876 | - * hash - Copy of hash() in fs/namespace.c. | |
2877 | - * | |
2878 | - * @mnt: Pointer to "struct vfsmount". | |
2879 | - * @dentry: Pointer to "struct dentry". | |
2880 | - * | |
2881 | - * Returns hash value. | |
2882 | - */ | |
2883 | -static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry) | |
2884 | -{ | |
2885 | - unsigned long tmp = ((unsigned long) mnt / L1_CACHE_BYTES); | |
2886 | - tmp += ((unsigned long) dentry / L1_CACHE_BYTES); | |
2887 | - tmp = tmp + (tmp >> hash_bits); | |
2888 | - return tmp & hash_mask; | |
2889 | -} | |
2890 | - | |
2891 | -/** | |
2892 | - * lsm_lu_mnt - Dummy function which does identical to lookup_mnt() in fs/namespace.c. | |
2893 | - * | |
2894 | - * @mnt: Pointer to "struct vfsmount". | |
2895 | - * @dentry: Pointer to "struct dentry". | |
2896 | - * | |
2897 | - * Returns pointer to "struct vfsmount". | |
2898 | - * | |
2899 | - * Never mark this function as __init in order to make sure that compiler | |
2900 | - * generates identical code for lookup_mnt() and this function. | |
2901 | - */ | |
2902 | -static struct vfsmount *lsm_lu_mnt(struct vfsmount *mnt, struct dentry *dentry) | |
2903 | -{ | |
2904 | - struct list_head *head = mount_hashtable + hash(mnt, dentry); | |
2905 | - struct list_head *tmp = head; | |
2906 | - struct vfsmount *p, *found = NULL; | |
2907 | - | |
2908 | - spin_lock(&cs_vfsmount_lock); | |
2909 | - for (;;) { | |
2910 | - tmp = tmp->next; | |
2911 | - p = NULL; | |
2912 | - if (tmp == head) | |
2913 | - break; | |
2914 | - p = list_entry(tmp, struct vfsmount, mnt_hash); | |
2915 | - if (p->mnt_parent == mnt && p->mnt_mountpoint == dentry) { | |
2916 | - found = mntget(p); | |
2917 | - break; | |
2918 | - } | |
2919 | - } | |
2920 | - spin_unlock(&cs_vfsmount_lock); | |
2921 | - return found; | |
2922 | -} | |
2923 | - | |
2924 | -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) | |
2925 | - | |
2926 | -/* Never mark this variable as __initdata . */ | |
2927 | -static spinlock_t cs_vfsmount_lock; | |
2928 | - | |
2929 | -/** | |
2930 | - * lsm_flwup - Dummy function which does identical to follow_up() in fs/namei.c. | |
2931 | - * | |
2932 | - * @mnt: Pointer to "struct vfsmount *". | |
2933 | - * @dentry: Pointer to "struct dentry *". | |
2934 | - * | |
2935 | - * Returns 1 if followed up, 0 otehrwise. | |
2936 | - * | |
2937 | - * Never mark this function as __init in order to make sure that compiler | |
2938 | - * generates identical code for follow_up() and this function. | |
2939 | - */ | |
2940 | -static int lsm_flwup(struct vfsmount **mnt, struct dentry **dentry) | |
2941 | -{ | |
2942 | - struct vfsmount *parent; | |
2943 | - struct dentry *mountpoint; | |
2944 | - spin_lock(&cs_vfsmount_lock); | |
2945 | - parent = (*mnt)->mnt_parent; | |
2946 | - if (parent == *mnt) { | |
2947 | - spin_unlock(&cs_vfsmount_lock); | |
2948 | - return 0; | |
2949 | - } | |
2950 | - mntget(parent); | |
2951 | - mountpoint = dget((*mnt)->mnt_mountpoint); | |
2952 | - spin_unlock(&cs_vfsmount_lock); | |
2953 | - dput(*dentry); | |
2954 | - *dentry = mountpoint; | |
2955 | - mntput(*mnt); | |
2956 | - *mnt = parent; | |
2957 | - return 1; | |
2958 | -} | |
2959 | - | |
2960 | -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
2961 | - | |
2962 | -/* Never mark this variable as __initdata . */ | |
2963 | -static spinlock_t cs_vfsmount_lock; | |
2964 | - | |
2965 | -/** | |
2966 | - * lsm_pin - Dummy function which does identical to mnt_pin() in fs/namespace.c. | |
2967 | - * | |
2968 | - * @mnt: Pointer to "struct vfsmount". | |
2969 | - * | |
2970 | - * Returns nothing. | |
2971 | - * | |
2972 | - * Never mark this function as __init in order to make sure that compiler | |
2973 | - * generates identical code for mnt_pin() and this function. | |
2974 | - */ | |
2975 | -static void lsm_pin(struct vfsmount *mnt) | |
2976 | -{ | |
2977 | - spin_lock(&cs_vfsmount_lock); | |
2978 | - mnt->mnt_pinned++; | |
2979 | - spin_unlock(&cs_vfsmount_lock); | |
2980 | -} | |
2981 | - | |
2982 | -#endif | |
2983 | - | |
2984 | -/** | |
2985 | - * cs_find_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". | |
2986 | - * | |
2987 | - * Returns true on success, false otherwise. | |
2988 | - */ | |
2989 | -static bool __init cs_find_vfsmount_lock(void) | |
2990 | -{ | |
2991 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 3) | |
2992 | - void *cp; | |
2993 | - spinlock_t *ptr; | |
2994 | - /* Guess "spinlock_t vfsmount_lock;". */ | |
2995 | - cp = cs_find_variable(lsm_lu_mnt, (unsigned long) &cs_vfsmount_lock, | |
2996 | - " lookup_mnt\n"); | |
2997 | - if (!cp) { | |
2998 | - printk(KERN_ERR "Can't resolve lookup_mnt().\n"); | |
2999 | - goto out; | |
3000 | - } | |
3001 | - /* This should be "spinlock_t *vfsmount_lock;". */ | |
3002 | - ptr = *(spinlock_t **) cp; | |
3003 | - if (!ptr) { | |
3004 | - printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); | |
3005 | - goto out; | |
3006 | - } | |
3007 | - caitsith_exports.vfsmount_lock = ptr; | |
3008 | - printk(KERN_INFO "vfsmount_lock=%p\n", ptr); | |
3009 | - return true; | |
3010 | -out: | |
3011 | - return false; | |
3012 | -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 15) | |
3013 | - void *cp; | |
3014 | - spinlock_t *ptr; | |
3015 | - /* Guess "spinlock_t vfsmount_lock;". */ | |
3016 | - cp = cs_find_variable(lsm_flwup, (unsigned long) &cs_vfsmount_lock, | |
3017 | - "follow_up"); | |
3018 | - if (!cp) { | |
3019 | - printk(KERN_ERR "Can't resolve follow_up().\n"); | |
3020 | - goto out; | |
3021 | - } | |
3022 | - /* This should be "spinlock_t *vfsmount_lock;". */ | |
3023 | - ptr = *(spinlock_t **) cp; | |
3024 | - if (!ptr) { | |
3025 | - printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); | |
3026 | - goto out; | |
3027 | - } | |
3028 | - caitsith_exports.vfsmount_lock = ptr; | |
3029 | - printk(KERN_INFO "vfsmount_lock=%p\n", ptr); | |
3030 | - return true; | |
3031 | -out: | |
3032 | - return false; | |
3033 | -#elif LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
3034 | - void *cp; | |
3035 | - spinlock_t *ptr; | |
3036 | - /* Guess "spinlock_t vfsmount_lock;". */ | |
3037 | - cp = cs_find_variable(lsm_pin, (unsigned long) &cs_vfsmount_lock, | |
3038 | - "mnt_pin"); | |
3039 | - if (!cp) { | |
3040 | - printk(KERN_ERR "Can't resolve mnt_pin().\n"); | |
3041 | - goto out; | |
3042 | - } | |
3043 | - /* This should be "spinlock_t *vfsmount_lock;". */ | |
3044 | - ptr = *(spinlock_t **) cp; | |
3045 | - if (!ptr) { | |
3046 | - printk(KERN_ERR "Can't resolve vfsmount_lock .\n"); | |
3047 | - goto out; | |
3048 | - } | |
3049 | - caitsith_exports.vfsmount_lock = ptr; | |
3050 | - printk(KERN_INFO "vfsmount_lock=%p\n", ptr); | |
3051 | - return true; | |
3052 | -out: | |
3053 | - return false; | |
3054 | -#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) | |
3055 | - void *ptr = cs_find_symbol(" __d_path\n"); | |
3056 | - if (!ptr) { | |
3057 | - printk(KERN_ERR "Can't resolve __d_path().\n"); | |
3058 | - return false; | |
3059 | - } | |
3060 | - caitsith_exports.__d_path = ptr; | |
3061 | - printk(KERN_INFO "__d_path=%p\n", ptr); | |
3062 | - return true; | |
3063 | -#else | |
3064 | - void *ptr = cs_find_symbol(" d_absolute_path\n"); | |
3065 | - if (!ptr) { | |
3066 | - printk(KERN_ERR "Can't resolve d_absolute_path().\n"); | |
3067 | - return false; | |
3068 | - } | |
3069 | - caitsith_exports.d_absolute_path = ptr; | |
3070 | - printk(KERN_INFO "d_absolute_path=%p\n", ptr); | |
3071 | - return true; | |
3072 | -#endif | |
3073 | -} | |
3074 | - | |
3075 | -#else | |
3076 | - | |
3077 | -/* Never mark this variable as __initdata . */ | |
3078 | -static spinlock_t cs_vfsmount_lock; | |
3079 | - | |
3080 | -/** | |
3081 | - * cs_find_vfsmount_lock - Find address of "spinlock_t vfsmount_lock". | |
3082 | - * | |
3083 | - * Returns true on success, false otherwise. | |
3084 | - */ | |
3085 | -static bool __init cs_find_vfsmount_lock(void) | |
3086 | -{ | |
3087 | - caitsith_exports.vfsmount_lock = &cs_vfsmount_lock; | |
3088 | - return true; | |
3089 | -} | |
3090 | - | |
3091 | -#endif | |
3092 | - | |
3093 | 2488 | /* |
3094 | 2489 | * Why not to copy all operations by "original_security_ops = *ops" ? |
3095 | 2490 | * Because copying byte array is not atomic. Reader checks |
@@ -3199,10 +2594,30 @@ | ||
3199 | 2594 | */ |
3200 | 2595 | static int __init cs_init(void) |
3201 | 2596 | { |
3202 | - struct security_operations *ops = cs_find_security_ops(); | |
3203 | - if (!ops || !cs_find_find_task_by_pid() || | |
3204 | - !cs_find_vfsmount_lock()) | |
3205 | - return -EINVAL; | |
2597 | + struct security_operations *ops = probe_security_ops(); | |
2598 | + if (!ops) | |
2599 | + goto out; | |
2600 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
2601 | + caitsith_exports.find_task_by_vpid = probe_find_task_by_vpid(); | |
2602 | + if (!caitsith_exports.find_task_by_vpid) | |
2603 | + goto out; | |
2604 | + caitsith_exports.find_task_by_pid_ns = probe_find_task_by_pid_ns(); | |
2605 | + if (!caitsith_exports.find_task_by_pid_ns) | |
2606 | + goto out; | |
2607 | +#endif | |
2608 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
2609 | + caitsith_exports.vfsmount_lock = probe_vfsmount_lock(); | |
2610 | + if (!caitsith_exports.vfsmount_lock) | |
2611 | + goto out; | |
2612 | +#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) | |
2613 | + caitsith_exports.__d_path = probe___d_path(); | |
2614 | + if (!caitsith_exports.__d_path) | |
2615 | + goto out; | |
2616 | +#else | |
2617 | + caitsith_exports.d_absolute_path = probe_d_absolute_path(); | |
2618 | + if (!caitsith_exports.d_absolute_path) | |
2619 | + goto out; | |
2620 | +#endif | |
3206 | 2621 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29) |
3207 | 2622 | { |
3208 | 2623 | int idx; |
@@ -3221,6 +2636,8 @@ | ||
3221 | 2636 | cs_init_module(); |
3222 | 2637 | cs_update_security_ops(ops); |
3223 | 2638 | return 0; |
2639 | +out: | |
2640 | + return -EINVAL; | |
3224 | 2641 | } |
3225 | 2642 | |
3226 | 2643 | module_init(cs_init); |
@@ -3318,11 +2735,11 @@ | ||
3318 | 2735 | * security_prepare_creds(). |
3319 | 2736 | * |
3320 | 2737 | * If current->in_execve is not set but ptr->cs_flags has |
3321 | - * CS_TASK_STARTED_EXECVE set, it indicates that do_execve() | |
2738 | + * CS_TASK_IS_IN_EXECVE set, it indicates that do_execve() | |
3322 | 2739 | * has failed and reverting domain transition is needed. |
3323 | 2740 | */ |
3324 | 2741 | if (task == current && |
3325 | - (ptr->cs_flags & CS_TASK_STARTED_EXECVE) && | |
2742 | + (ptr->cs_flags & CS_TASK_IS_IN_EXECVE) && | |
3326 | 2743 | !current->in_execve) { |
3327 | 2744 | #ifdef CONFIG_CAITSITH_DEBUG |
3328 | 2745 | static bool done; |
@@ -3480,7 +2897,7 @@ | ||
3480 | 2897 | */ |
3481 | 2898 | static void cs_task_security_gc(void) |
3482 | 2899 | { |
3483 | - static DEFINE_MUTEX(lock); | |
2900 | + static DEFINE_SPINLOCK(lock); | |
3484 | 2901 | static atomic_t gc_counter = ATOMIC_INIT(0); |
3485 | 2902 | unsigned int idx; |
3486 | 2903 | /* |
@@ -3495,7 +2912,7 @@ | ||
3495 | 2912 | atomic_inc_return(&gc_counter) < 1024) |
3496 | 2913 | return; |
3497 | 2914 | atomic_set(&gc_counter, 0); |
3498 | - if (mutex_lock_interruptible(&lock)) | |
2915 | + if (!spin_trylock(&lock)) | |
3499 | 2916 | return; |
3500 | 2917 | rcu_read_lock(); |
3501 | 2918 | for (idx = 0; idx < CS_MAX_TASK_SECURITY_HASH; idx++) { |
@@ -3508,7 +2925,7 @@ | ||
3508 | 2925 | } |
3509 | 2926 | } |
3510 | 2927 | rcu_read_unlock(); |
3511 | - mutex_unlock(&lock); | |
2928 | + spin_unlock(&lock); | |
3512 | 2929 | } |
3513 | 2930 | |
3514 | 2931 | #endif |
@@ -0,0 +1,1571 @@ | ||
1 | +/* | |
2 | + * mclsm.c | |
3 | + * | |
4 | + * Copyright (C) 2010-2013 Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
5 | + * | |
6 | + * Version: 0.1.8 2013/01/06 | |
7 | + */ | |
8 | + | |
9 | +#include "caitsith.h" | |
10 | +#include "probe.h" | |
11 | + | |
12 | +/* Prototype definition. */ | |
13 | +static void cs_task_security_gc(void); | |
14 | +static int cs_copy_cred_security(const struct cred *new, | |
15 | + const struct cred *old, gfp_t gfp); | |
16 | +static struct cs_security *cs_find_cred_security(const struct cred *cred); | |
17 | +static DEFINE_SPINLOCK(cs_task_security_list_lock); | |
18 | +static atomic_t cs_in_execve_tasks = ATOMIC_INIT(0); | |
19 | +/* | |
20 | + * List of "struct cs_security" for "struct pid". | |
21 | + * | |
22 | + * All instances on this list is guaranteed that "struct cs_security"->pid != | |
23 | + * NULL. Also, instances on this list that are in execve() are guaranteed that | |
24 | + * "struct cs_security"->cred remembers "struct linux_binprm"->cred with a | |
25 | + * refcount on "struct linux_binprm"->cred. | |
26 | + */ | |
27 | +struct list_head cs_task_security_list[CS_MAX_TASK_SECURITY_HASH]; | |
28 | +/* | |
29 | + * List of "struct cs_security" for "struct cred". | |
30 | + * | |
31 | + * Since the number of "struct cred" is nearly equals to the number of | |
32 | + * "struct pid", we allocate hash tables like cs_task_security_list. | |
33 | + * | |
34 | + * All instances on this list are guaranteed that "struct cs_security"->pid == | |
35 | + * NULL and "struct cs_security"->cred != NULL. | |
36 | + */ | |
37 | +static struct list_head cs_cred_security_list[CS_MAX_TASK_SECURITY_HASH]; | |
38 | + | |
39 | +/* Dummy security context for avoiding NULL pointer dereference. */ | |
40 | +static struct cs_security cs_oom_security = { | |
41 | + .cs_domain_info = &cs_kernel_domain | |
42 | +}; | |
43 | + | |
44 | +/* Dummy security context for avoiding NULL pointer dereference. */ | |
45 | +static struct cs_security cs_default_security = { | |
46 | + .cs_domain_info = &cs_kernel_domain | |
47 | +}; | |
48 | + | |
49 | +/* For exporting variables and functions. */ | |
50 | +struct caitsith_exports caitsith_exports; | |
51 | + | |
52 | +/* Original hooks. */ | |
53 | +static struct security_operations original_security_ops; | |
54 | + | |
55 | +/** | |
56 | + * cs_clear_execve - Release memory used by do_execve(). | |
57 | + * | |
58 | + * @ret: 0 if do_execve() succeeded, negative value otherwise. | |
59 | + * @security: Pointer to "struct cs_security". | |
60 | + * | |
61 | + * Returns nothing. | |
62 | + */ | |
63 | +static void cs_clear_execve(int ret, struct cs_security *security) | |
64 | +{ | |
65 | + struct cs_request_info *r; | |
66 | + if (security == &cs_default_security || security == &cs_oom_security) | |
67 | + return; | |
68 | + r = security->r; | |
69 | + security->r = NULL; | |
70 | + if (!r) | |
71 | + return; | |
72 | + atomic_dec(&cs_in_execve_tasks); | |
73 | + cs_finish_execve(ret, r); | |
74 | +} | |
75 | + | |
76 | +/** | |
77 | + * cs_rcu_free - RCU callback for releasing "struct cs_security". | |
78 | + * | |
79 | + * @rcu: Pointer to "struct rcu_head". | |
80 | + * | |
81 | + * Returns nothing. | |
82 | + */ | |
83 | +static void cs_rcu_free(struct rcu_head *rcu) | |
84 | +{ | |
85 | + struct cs_security *ptr = container_of(rcu, typeof(*ptr), rcu); | |
86 | + struct cs_request_info *r = ptr->r; | |
87 | + /* | |
88 | + * If this security context was associated with "struct pid" and | |
89 | + * ptr->cs_flags has CS_TASK_IS_IN_EXECVE set, it indicates that a | |
90 | + * "struct task_struct" associated with this security context exited | |
91 | + * immediately after do_execve() has failed. | |
92 | + */ | |
93 | + if (ptr->pid && (ptr->cs_flags & CS_TASK_IS_IN_EXECVE)) { | |
94 | +#ifdef CONFIG_CAITSITH_DEBUG | |
95 | + static bool done; | |
96 | + if (!done) { | |
97 | + printk(KERN_INFO "CAITSITH: Decrementing " | |
98 | + "cs_in_execve_tasks counter " | |
99 | + "because some \"struct task_struct\" has " | |
100 | + "exit()ed immediately after do_execve() has " | |
101 | + "failed.\n"); | |
102 | + done = true; | |
103 | + } | |
104 | +#endif | |
105 | + atomic_dec(&cs_in_execve_tasks); | |
106 | + } | |
107 | + /* | |
108 | + * If this security context was associated with "struct pid", | |
109 | + * drop refcount obtained by get_pid() in cs_find_task_security(). | |
110 | + */ | |
111 | + if (ptr->pid) { | |
112 | +#ifdef CONFIG_CAITSITH_DEBUG | |
113 | + static bool done; | |
114 | + if (!done) { | |
115 | + printk(KERN_INFO "CAITSITH: Dropping refcount on " | |
116 | + "\"struct pid\".\n"); | |
117 | + done = true; | |
118 | + } | |
119 | +#endif | |
120 | + put_pid(ptr->pid); | |
121 | + } | |
122 | + if (r) { | |
123 | +#ifdef CONFIG_CAITSITH_DEBUG | |
124 | + static bool done; | |
125 | + if (!done) { | |
126 | + printk(KERN_INFO "CAITSITH: Releasing memory in " | |
127 | + "\"struct cs_execve\" because some " | |
128 | + "\"struct task_struct\" has exit()ed " | |
129 | + "immediately after do_execve() has failed.\n"); | |
130 | + done = true; | |
131 | + } | |
132 | +#endif | |
133 | + kfree(r->handler_path); | |
134 | + kfree(r); | |
135 | + } | |
136 | + kfree(ptr); | |
137 | +} | |
138 | + | |
139 | +/** | |
140 | + * cs_del_security - Release "struct cs_security". | |
141 | + * | |
142 | + * @ptr: Pointer to "struct cs_security". | |
143 | + * | |
144 | + * Returns nothing. | |
145 | + */ | |
146 | +static void cs_del_security(struct cs_security *ptr) | |
147 | +{ | |
148 | + unsigned long flags; | |
149 | + if (ptr == &cs_default_security || ptr == &cs_oom_security) | |
150 | + return; | |
151 | + spin_lock_irqsave(&cs_task_security_list_lock, flags); | |
152 | + list_del_rcu(&ptr->list); | |
153 | + spin_unlock_irqrestore(&cs_task_security_list_lock, flags); | |
154 | + call_rcu(&ptr->rcu, cs_rcu_free); | |
155 | +} | |
156 | + | |
157 | +/** | |
158 | + * cs_add_cred_security - Add "struct cs_security" to list. | |
159 | + * | |
160 | + * @ptr: Pointer to "struct cs_security". | |
161 | + * | |
162 | + * Returns nothing. | |
163 | + */ | |
164 | +static void cs_add_cred_security(struct cs_security *ptr) | |
165 | +{ | |
166 | + unsigned long flags; | |
167 | + struct list_head *list = &cs_cred_security_list | |
168 | + [hash_ptr((void *) ptr->cred, CS_TASK_SECURITY_HASH_BITS)]; | |
169 | +#ifdef CONFIG_CAITSITH_DEBUG | |
170 | + if (ptr->pid) | |
171 | + printk(KERN_INFO "CAITSITH: \"struct cs_security\"->pid != NULL" | |
172 | + "\n"); | |
173 | +#endif | |
174 | + ptr->pid = NULL; | |
175 | + spin_lock_irqsave(&cs_task_security_list_lock, flags); | |
176 | + list_add_rcu(&ptr->list, list); | |
177 | + spin_unlock_irqrestore(&cs_task_security_list_lock, flags); | |
178 | +} | |
179 | + | |
180 | +/** | |
181 | + * cs_task_create - Make snapshot of security context for new task. | |
182 | + * | |
183 | + * @clone_flags: Flags passed to clone(). | |
184 | + * | |
185 | + * Returns 0 on success, negative value otherwise. | |
186 | + */ | |
187 | +static int cs_task_create(unsigned long clone_flags) | |
188 | +{ | |
189 | + struct cs_security *old_security; | |
190 | + struct cs_security *new_security; | |
191 | + struct cred *cred = prepare_creds(); | |
192 | + if (!cred) | |
193 | + return -ENOMEM; | |
194 | + old_security = cs_find_task_security(current); | |
195 | + new_security = cs_find_cred_security(cred); | |
196 | + new_security->cs_domain_info = old_security->cs_domain_info; | |
197 | + new_security->cs_flags = old_security->cs_flags; | |
198 | + return commit_creds(cred); | |
199 | +} | |
200 | + | |
201 | +/** | |
202 | + * cs_cred_prepare - Allocate memory for new credentials. | |
203 | + * | |
204 | + * @new: Pointer to "struct cred". | |
205 | + * @old: Pointer to "struct cred". | |
206 | + * @gfp: Memory allocation flags. | |
207 | + * | |
208 | + * Returns 0 on success, negative value otherwise. | |
209 | + */ | |
210 | +static int cs_cred_prepare(struct cred *new, const struct cred *old, | |
211 | + gfp_t gfp) | |
212 | +{ | |
213 | + int rc1; | |
214 | + /* | |
215 | + * For checking whether reverting domain transition is needed or not. | |
216 | + * | |
217 | + * See cs_find_task_security() for reason. | |
218 | + */ | |
219 | + if (gfp == GFP_KERNEL) | |
220 | + cs_find_task_security(current); | |
221 | + rc1 = cs_copy_cred_security(new, old, gfp); | |
222 | + if (gfp == GFP_KERNEL) | |
223 | + cs_task_security_gc(); | |
224 | + if (original_security_ops.cred_prepare) { | |
225 | + const int rc2 = original_security_ops.cred_prepare(new, old, | |
226 | + gfp); | |
227 | + if (rc2) { | |
228 | + cs_del_security(cs_find_cred_security(new)); | |
229 | + return rc2; | |
230 | + } | |
231 | + } | |
232 | + return rc1; | |
233 | +} | |
234 | + | |
235 | +/** | |
236 | + * cs_cred_free - Release memory used by credentials. | |
237 | + * | |
238 | + * @cred: Pointer to "struct cred". | |
239 | + * | |
240 | + * Returns nothing. | |
241 | + */ | |
242 | +static void cs_cred_free(struct cred *cred) | |
243 | +{ | |
244 | + if (original_security_ops.cred_free) | |
245 | + original_security_ops.cred_free(cred); | |
246 | + cs_del_security(cs_find_cred_security(cred)); | |
247 | +} | |
248 | + | |
249 | +/** | |
250 | + * cs_alloc_cred_security - Allocate memory for new credentials. | |
251 | + * | |
252 | + * @cred: Pointer to "struct cred". | |
253 | + * @gfp: Memory allocation flags. | |
254 | + * | |
255 | + * Returns 0 on success, negative value otherwise. | |
256 | + */ | |
257 | +static int cs_alloc_cred_security(const struct cred *cred, gfp_t gfp) | |
258 | +{ | |
259 | + struct cs_security *new_security = kzalloc(sizeof(*new_security), | |
260 | + gfp); | |
261 | + if (!new_security) | |
262 | + return -ENOMEM; | |
263 | + new_security->cred = cred; | |
264 | + cs_add_cred_security(new_security); | |
265 | + return 0; | |
266 | +} | |
267 | + | |
268 | +/** | |
269 | + * cs_cred_alloc_blank - Allocate memory for new credentials. | |
270 | + * | |
271 | + * @new: Pointer to "struct cred". | |
272 | + * @gfp: Memory allocation flags. | |
273 | + * | |
274 | + * Returns 0 on success, negative value otherwise. | |
275 | + */ | |
276 | +static int cs_cred_alloc_blank(struct cred *new, gfp_t gfp) | |
277 | +{ | |
278 | + const int rc1 = cs_alloc_cred_security(new, gfp); | |
279 | + if (original_security_ops.cred_alloc_blank) { | |
280 | + const int rc2 = original_security_ops.cred_alloc_blank(new, | |
281 | + gfp); | |
282 | + if (rc2) { | |
283 | + cs_del_security(cs_find_cred_security(new)); | |
284 | + return rc2; | |
285 | + } | |
286 | + } | |
287 | + return rc1; | |
288 | +} | |
289 | + | |
290 | +/** | |
291 | + * cs_cred_transfer - Transfer "struct cs_security" between credentials. | |
292 | + * | |
293 | + * @new: Pointer to "struct cred". | |
294 | + * @old: Pointer to "struct cred". | |
295 | + * | |
296 | + * Returns nothing. | |
297 | + */ | |
298 | +static void cs_cred_transfer(struct cred *new, const struct cred *old) | |
299 | +{ | |
300 | + struct cs_security *new_security = cs_find_cred_security(new); | |
301 | + struct cs_security *old_security = cs_find_cred_security(old); | |
302 | + if (new_security == &cs_default_security || | |
303 | + new_security == &cs_oom_security || | |
304 | + old_security == &cs_default_security || | |
305 | + old_security == &cs_oom_security) | |
306 | + return; | |
307 | + new_security->cs_flags = old_security->cs_flags; | |
308 | + new_security->cs_domain_info = old_security->cs_domain_info; | |
309 | +} | |
310 | + | |
311 | +/** | |
312 | + * cs_bprm_committing_creds - A hook which is called when do_execve() succeeded. | |
313 | + * | |
314 | + * @bprm: Pointer to "struct linux_binprm". | |
315 | + * | |
316 | + * Returns nothing. | |
317 | + */ | |
318 | +static void cs_bprm_committing_creds(struct linux_binprm *bprm) | |
319 | +{ | |
320 | + struct cs_security *old_security = cs_current_security(); | |
321 | + struct cs_security *new_security; | |
322 | + if (old_security == &cs_default_security || | |
323 | + old_security == &cs_oom_security) | |
324 | + return; | |
325 | + cs_clear_execve(0, old_security); | |
326 | + /* Update current task's cred's domain for future fork(). */ | |
327 | + new_security = cs_find_cred_security(bprm->cred); | |
328 | + new_security->cs_flags = old_security->cs_flags; | |
329 | + new_security->cs_domain_info = old_security->cs_domain_info; | |
330 | +} | |
331 | + | |
332 | +#ifndef CONFIG_CAITSITH_OMIT_USERSPACE_LOADER | |
333 | + | |
334 | +/** | |
335 | + * cs_policy_loader_exists - Check whether /sbin/caitsith-init exists. | |
336 | + * | |
337 | + * Returns true if /sbin/caitsith-init exists, false otherwise. | |
338 | + */ | |
339 | +static _Bool cs_policy_loader_exists(void) | |
340 | +{ | |
341 | + struct path path; | |
342 | + if (kern_path(CONFIG_CAITSITH_POLICY_LOADER, LOOKUP_FOLLOW, &path) | |
343 | + == 0) { | |
344 | + path_put(&path); | |
345 | + return 1; | |
346 | + } | |
347 | + printk(KERN_INFO "Not activating CaitSith as %s does not exist.\n", | |
348 | + CONFIG_CAITSITH_POLICY_LOADER); | |
349 | + return 0; | |
350 | +} | |
351 | + | |
352 | +/** | |
353 | + * cs_load_policy - Run external policy loader to load policy. | |
354 | + * | |
355 | + * @filename: The program about to start. | |
356 | + * | |
357 | + * Returns nothing. | |
358 | + * | |
359 | + * This function checks whether @filename is /sbin/init, and if so | |
360 | + * invoke /sbin/caitsith-init and wait for the termination of | |
361 | + * /sbin/caitsith-init and then continues invocation of /sbin/init. | |
362 | + * /sbin/caitsith-init reads policy files in /etc/caitsith/ directory and | |
363 | + * writes to /proc/caitsith/ interfaces. | |
364 | + */ | |
365 | +static void cs_load_policy(const char *filename) | |
366 | +{ | |
367 | + static _Bool done; | |
368 | + if (done) | |
369 | + return; | |
370 | + if (strcmp(filename, CONFIG_CAITSITH_ACTIVATION_TRIGGER)) | |
371 | + return; | |
372 | + if (!cs_policy_loader_exists()) | |
373 | + return; | |
374 | + done = 1; | |
375 | + { | |
376 | + char *argv[2]; | |
377 | + char *envp[3]; | |
378 | + printk(KERN_INFO "Calling %s to load policy. Please wait.\n", | |
379 | + CONFIG_CAITSITH_POLICY_LOADER); | |
380 | + argv[0] = (char *) CONFIG_CAITSITH_POLICY_LOADER; | |
381 | + argv[1] = NULL; | |
382 | + envp[0] = "HOME=/"; | |
383 | + envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | |
384 | + envp[2] = NULL; | |
385 | + call_usermodehelper(argv[0], argv, envp, UMH_WAIT_PROC); | |
386 | + } | |
387 | + cs_check_profile(); | |
388 | +} | |
389 | + | |
390 | +#endif | |
391 | + | |
392 | +/** | |
393 | + * cs_bprm_check_security - Check permission for execve(). | |
394 | + * | |
395 | + * @bprm: Pointer to "struct linux_binprm". | |
396 | + * | |
397 | + * Returns 0 on success, negative value otherwise. | |
398 | + */ | |
399 | +static int cs_bprm_check_security(struct linux_binprm *bprm) | |
400 | +{ | |
401 | + struct cs_security *security = cs_current_security(); | |
402 | + int rc; | |
403 | + if (security == &cs_default_security || security == &cs_oom_security) | |
404 | + return -ENOMEM; | |
405 | + if (security->r) | |
406 | + return 0; | |
407 | +#ifndef CONFIG_CAITSITH_OMIT_USERSPACE_LOADER | |
408 | + if (!cs_policy_loaded) | |
409 | + cs_load_policy(bprm->filename); | |
410 | +#endif | |
411 | + rc = cs_start_execve(bprm, &security->r); | |
412 | + if (security->r) | |
413 | + atomic_inc(&cs_in_execve_tasks); | |
414 | + return rc; | |
415 | +} | |
416 | + | |
417 | +/** | |
418 | + * cs_file_open - Check permission for open(). | |
419 | + * | |
420 | + * @f: Pointer to "struct file". | |
421 | + * @cred: Pointer to "struct cred". | |
422 | + * | |
423 | + * Returns 0 on success, negative value otherwise. | |
424 | + */ | |
425 | +static int cs_file_open(struct file *f, const struct cred *cred) | |
426 | +{ | |
427 | + return cs_open_permission(f); | |
428 | +} | |
429 | + | |
430 | +#ifdef CONFIG_SECURITY_PATH | |
431 | + | |
432 | +/** | |
433 | + * cs_path_chown - Check permission for chown()/chgrp(). | |
434 | + * | |
435 | + * @path: Pointer to "struct path". | |
436 | + * @user: User ID. | |
437 | + * @group: Group ID. | |
438 | + * | |
439 | + * Returns 0 on success, negative value otherwise. | |
440 | + */ | |
441 | +static int cs_path_chown(struct path *path, kuid_t user, kgid_t group) | |
442 | +{ | |
443 | + return cs_chown_permission(path->dentry, path->mnt, user, group); | |
444 | +} | |
445 | + | |
446 | +/** | |
447 | + * cs_path_chmod - Check permission for chmod(). | |
448 | + * | |
449 | + * @path: Pointer to "struct path". | |
450 | + * @mode: Mode. | |
451 | + * | |
452 | + * Returns 0 on success, negative value otherwise. | |
453 | + */ | |
454 | +static int cs_path_chmod(struct path *path, umode_t mode) | |
455 | +{ | |
456 | + return cs_chmod_permission(path->dentry, path->mnt, mode); | |
457 | +} | |
458 | + | |
459 | +/** | |
460 | + * cs_path_chroot - Check permission for chroot(). | |
461 | + * | |
462 | + * @path: Pointer to "struct path". | |
463 | + * | |
464 | + * Returns 0 on success, negative value otherwise. | |
465 | + */ | |
466 | +static int cs_path_chroot(struct path *path) | |
467 | +{ | |
468 | + return cs_chroot_permission(path); | |
469 | +} | |
470 | + | |
471 | +/** | |
472 | + * cs_path_truncate - Check permission for truncate(). | |
473 | + * | |
474 | + * @path: Pointer to "struct path". | |
475 | + * | |
476 | + * Returns 0 on success, negative value otherwise. | |
477 | + */ | |
478 | +static int cs_path_truncate(struct path *path) | |
479 | +{ | |
480 | + return cs_truncate_permission(path->dentry, path->mnt); | |
481 | +} | |
482 | + | |
483 | +#else | |
484 | + | |
485 | +/** | |
486 | + * cs_inode_setattr - Check permission for chown()/chgrp()/chmod()/truncate(). | |
487 | + * | |
488 | + * @dentry: Pointer to "struct dentry". | |
489 | + * @attr: Pointer to "struct iattr". | |
490 | + * | |
491 | + * Returns 0 on success, negative value otherwise. | |
492 | + */ | |
493 | +static int cs_inode_setattr(struct dentry *dentry, struct iattr *attr) | |
494 | +{ | |
495 | + const int rc1 = (attr->ia_valid & ATTR_UID) ? | |
496 | + cs_chown_permission(dentry, NULL, attr->ia_uid, INVALID_GID) : | |
497 | + 0; | |
498 | + const int rc2 = (attr->ia_valid & ATTR_GID) ? | |
499 | + cs_chown_permission(dentry, NULL, INVALID_UID, attr->ia_gid) : | |
500 | + 0; | |
501 | + const int rc3 = (attr->ia_valid & ATTR_MODE) ? | |
502 | + cs_chmod_permission(dentry, NULL, attr->ia_mode) : 0; | |
503 | + const int rc4 = (attr->ia_valid & ATTR_SIZE) ? | |
504 | + cs_truncate_permission(dentry, NULL) : 0; | |
505 | + if (rc4) | |
506 | + return rc4; | |
507 | + if (rc3) | |
508 | + return rc3; | |
509 | + if (rc2) | |
510 | + return rc2; | |
511 | + return rc1; | |
512 | +} | |
513 | + | |
514 | +#endif | |
515 | + | |
516 | +/** | |
517 | + * cs_inode_getattr - Check permission for stat(). | |
518 | + * | |
519 | + * @mnt: Pointer to "struct vfsmount". | |
520 | + * @dentry: Pointer to "struct dentry". | |
521 | + * | |
522 | + * Returns 0 on success, negative value otherwise. | |
523 | + */ | |
524 | +static int cs_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) | |
525 | +{ | |
526 | + return cs_getattr_permission(mnt, dentry); | |
527 | +} | |
528 | + | |
529 | +#ifdef CONFIG_SECURITY_PATH | |
530 | + | |
531 | +/** | |
532 | + * cs_path_mknod - Check permission for mknod(). | |
533 | + * | |
534 | + * @dir: Pointer to "struct path". | |
535 | + * @dentry: Pointer to "struct dentry". | |
536 | + * @mode: Create mode. | |
537 | + * @dev: Device major/minor number. | |
538 | + * | |
539 | + * Returns 0 on success, negative value otherwise. | |
540 | + */ | |
541 | +static int cs_path_mknod(struct path *dir, struct dentry *dentry, | |
542 | + umode_t mode, unsigned int dev) | |
543 | +{ | |
544 | + return cs_mknod_permission(dentry, dir->mnt, mode, dev); | |
545 | +} | |
546 | + | |
547 | +/** | |
548 | + * cs_path_mkdir - Check permission for mkdir(). | |
549 | + * | |
550 | + * @dir: Pointer to "struct path". | |
551 | + * @dentry: Pointer to "struct dentry". | |
552 | + * @mode: Create mode. | |
553 | + * | |
554 | + * Returns 0 on success, negative value otherwise. | |
555 | + */ | |
556 | +static int cs_path_mkdir(struct path *dir, struct dentry *dentry, | |
557 | + umode_t mode) | |
558 | +{ | |
559 | + return cs_mkdir_permission(dentry, dir->mnt, mode); | |
560 | +} | |
561 | + | |
562 | +/** | |
563 | + * cs_path_rmdir - Check permission for rmdir(). | |
564 | + * | |
565 | + * @dir: Pointer to "struct path". | |
566 | + * @dentry: Pointer to "struct dentry". | |
567 | + * | |
568 | + * Returns 0 on success, negative value otherwise. | |
569 | + */ | |
570 | +static int cs_path_rmdir(struct path *dir, struct dentry *dentry) | |
571 | +{ | |
572 | + return cs_rmdir_permission(dentry, dir->mnt); | |
573 | +} | |
574 | + | |
575 | +/** | |
576 | + * cs_path_unlink - Check permission for unlink(). | |
577 | + * | |
578 | + * @dir: Pointer to "struct path". | |
579 | + * @dentry: Pointer to "struct dentry". | |
580 | + * | |
581 | + * Returns 0 on success, negative value otherwise. | |
582 | + */ | |
583 | +static int cs_path_unlink(struct path *dir, struct dentry *dentry) | |
584 | +{ | |
585 | + return cs_unlink_permission(dentry, dir->mnt); | |
586 | +} | |
587 | + | |
588 | +/** | |
589 | + * cs_path_symlink - Check permission for symlink(). | |
590 | + * | |
591 | + * @dir: Pointer to "struct path". | |
592 | + * @dentry: Pointer to "struct dentry". | |
593 | + * @old_name: Content of symbolic link. | |
594 | + * | |
595 | + * Returns 0 on success, negative value otherwise. | |
596 | + */ | |
597 | +static int cs_path_symlink(struct path *dir, struct dentry *dentry, | |
598 | + const char *old_name) | |
599 | +{ | |
600 | + return cs_symlink_permission(dentry, dir->mnt, old_name); | |
601 | +} | |
602 | + | |
603 | +/** | |
604 | + * cs_path_rename - Check permission for rename(). | |
605 | + * | |
606 | + * @old_dir: Pointer to "struct path". | |
607 | + * @old_dentry: Pointer to "struct dentry". | |
608 | + * @new_dir: Pointer to "struct path". | |
609 | + * @new_dentry: Pointer to "struct dentry". | |
610 | + * | |
611 | + * Returns 0 on success, negative value otherwise. | |
612 | + */ | |
613 | +static int cs_path_rename(struct path *old_dir, struct dentry *old_dentry, | |
614 | + struct path *new_dir, struct dentry *new_dentry) | |
615 | +{ | |
616 | + return cs_rename_permission(old_dentry, new_dentry, old_dir->mnt); | |
617 | +} | |
618 | + | |
619 | +/** | |
620 | + * cs_path_link - Check permission for link(). | |
621 | + * | |
622 | + * @old_dentry: Pointer to "struct dentry". | |
623 | + * @new_dir: Pointer to "struct path". | |
624 | + * @new_dentry: Pointer to "struct dentry". | |
625 | + * | |
626 | + * Returns 0 on success, negative value otherwise. | |
627 | + */ | |
628 | +static int cs_path_link(struct dentry *old_dentry, struct path *new_dir, | |
629 | + struct dentry *new_dentry) | |
630 | +{ | |
631 | + return cs_link_permission(old_dentry, new_dentry, new_dir->mnt); | |
632 | +} | |
633 | + | |
634 | +#else | |
635 | + | |
636 | +/** | |
637 | + * cs_inode_mknod - Check permission for mknod(). | |
638 | + * | |
639 | + * @dir: Pointer to "struct inode". | |
640 | + * @dentry: Pointer to "struct dentry". | |
641 | + * @mode: Create mode. | |
642 | + * @dev: Device major/minor number. | |
643 | + * | |
644 | + * Returns 0 on success, negative value otherwise. | |
645 | + */ | |
646 | +static int cs_inode_mknod(struct inode *dir, struct dentry *dentry, | |
647 | + umode_t mode, dev_t dev) | |
648 | +{ | |
649 | + return cs_mknod_permission(dentry, NULL, mode, dev); | |
650 | +} | |
651 | + | |
652 | +/** | |
653 | + * cs_inode_mkdir - Check permission for mkdir(). | |
654 | + * | |
655 | + * @dir: Pointer to "struct inode". | |
656 | + * @dentry: Pointer to "struct dentry". | |
657 | + * @mode: Create mode. | |
658 | + * | |
659 | + * Returns 0 on success, negative value otherwise. | |
660 | + */ | |
661 | +static int cs_inode_mkdir(struct inode *dir, struct dentry *dentry, | |
662 | + umode_t mode) | |
663 | +{ | |
664 | + return cs_mkdir_permission(dentry, NULL, mode); | |
665 | +} | |
666 | + | |
667 | +/** | |
668 | + * cs_inode_rmdir - Check permission for rmdir(). | |
669 | + * | |
670 | + * @dir: Pointer to "struct inode". | |
671 | + * @dentry: Pointer to "struct dentry". | |
672 | + * | |
673 | + * Returns 0 on success, negative value otherwise. | |
674 | + */ | |
675 | +static int cs_inode_rmdir(struct inode *dir, struct dentry *dentry) | |
676 | +{ | |
677 | + return cs_rmdir_permission(dentry, NULL); | |
678 | +} | |
679 | + | |
680 | +/** | |
681 | + * cs_inode_unlink - Check permission for unlink(). | |
682 | + * | |
683 | + * @dir: Pointer to "struct inode". | |
684 | + * @dentry: Pointer to "struct dentry". | |
685 | + * | |
686 | + * Returns 0 on success, negative value otherwise. | |
687 | + */ | |
688 | +static int cs_inode_unlink(struct inode *dir, struct dentry *dentry) | |
689 | +{ | |
690 | + return cs_unlink_permission(dentry, NULL); | |
691 | +} | |
692 | + | |
693 | +/** | |
694 | + * cs_inode_symlink - Check permission for symlink(). | |
695 | + * | |
696 | + * @dir: Pointer to "struct inode". | |
697 | + * @dentry: Pointer to "struct dentry". | |
698 | + * @old_name: Content of symbolic link. | |
699 | + * | |
700 | + * Returns 0 on success, negative value otherwise. | |
701 | + */ | |
702 | +static int cs_inode_symlink(struct inode *dir, struct dentry *dentry, | |
703 | + const char *old_name) | |
704 | +{ | |
705 | + return cs_symlink_permission(dentry, NULL, old_name); | |
706 | +} | |
707 | + | |
708 | +/** | |
709 | + * cs_inode_rename - Check permission for rename(). | |
710 | + * | |
711 | + * @old_dir: Pointer to "struct inode". | |
712 | + * @old_dentry: Pointer to "struct dentry". | |
713 | + * @new_dir: Pointer to "struct inode". | |
714 | + * @new_dentry: Pointer to "struct dentry". | |
715 | + * | |
716 | + * Returns 0 on success, negative value otherwise. | |
717 | + */ | |
718 | +static int cs_inode_rename(struct inode *old_dir, struct dentry *old_dentry, | |
719 | + struct inode *new_dir, struct dentry *new_dentry) | |
720 | +{ | |
721 | + return cs_rename_permission(old_dentry, new_dentry, NULL); | |
722 | +} | |
723 | + | |
724 | +/** | |
725 | + * cs_inode_link - Check permission for link(). | |
726 | + * | |
727 | + * @old_dentry: Pointer to "struct dentry". | |
728 | + * @dir: Pointer to "struct inode". | |
729 | + * @new_dentry: Pointer to "struct dentry". | |
730 | + * | |
731 | + * Returns 0 on success, negative value otherwise. | |
732 | + */ | |
733 | +static int cs_inode_link(struct dentry *old_dentry, struct inode *dir, | |
734 | + struct dentry *new_dentry) | |
735 | +{ | |
736 | + return cs_link_permission(old_dentry, new_dentry, NULL); | |
737 | +} | |
738 | + | |
739 | +/** | |
740 | + * cs_inode_create - Check permission for creat(). | |
741 | + * | |
742 | + * @dir: Pointer to "struct inode". | |
743 | + * @dentry: Pointer to "struct dentry". | |
744 | + * @mode: Create mode. | |
745 | + * | |
746 | + * Returns 0 on success, negative value otherwise. | |
747 | + */ | |
748 | +static int cs_inode_create(struct inode *dir, struct dentry *dentry, | |
749 | + umode_t mode) | |
750 | +{ | |
751 | + return cs_mknod_permission(dentry, NULL, mode, 0); | |
752 | +} | |
753 | + | |
754 | +#endif | |
755 | + | |
756 | +#ifdef CONFIG_SECURITY_NETWORK | |
757 | + | |
758 | +#include <net/sock.h> | |
759 | + | |
760 | +/* Structure for remembering an accept()ed socket's status. */ | |
761 | +struct cs_socket_tag { | |
762 | + struct list_head list; | |
763 | + struct inode *inode; | |
764 | + int status; | |
765 | + struct rcu_head rcu; | |
766 | +}; | |
767 | + | |
768 | +/* | |
769 | + * List for managing accept()ed sockets. | |
770 | + * Since we don't need to keep an accept()ed socket into this list after | |
771 | + * once the permission was granted, the number of entries in this list is | |
772 | + * likely small. Therefore, we don't use hash tables. | |
773 | + */ | |
774 | +static LIST_HEAD(cs_accepted_socket_list); | |
775 | +/* Lock for protecting cs_accepted_socket_list . */ | |
776 | +static DEFINE_SPINLOCK(cs_accepted_socket_list_lock); | |
777 | + | |
778 | +/** | |
779 | + * cs_socket_rcu_free - RCU callback for releasing "struct cs_socket_tag". | |
780 | + * | |
781 | + * @rcu: Pointer to "struct rcu_head". | |
782 | + * | |
783 | + * Returns nothing. | |
784 | + */ | |
785 | +static void cs_socket_rcu_free(struct rcu_head *rcu) | |
786 | +{ | |
787 | + struct cs_socket_tag *ptr = container_of(rcu, typeof(*ptr), rcu); | |
788 | + kfree(ptr); | |
789 | +} | |
790 | + | |
791 | +/** | |
792 | + * cs_update_socket_tag - Update tag associated with accept()ed sockets. | |
793 | + * | |
794 | + * @inode: Pointer to "struct inode". | |
795 | + * @status: New status. | |
796 | + * | |
797 | + * Returns nothing. | |
798 | + * | |
799 | + * If @status == 0, memory for that socket will be released after RCU grace | |
800 | + * period. | |
801 | + */ | |
802 | +static void cs_update_socket_tag(struct inode *inode, int status) | |
803 | +{ | |
804 | + struct cs_socket_tag *ptr; | |
805 | + /* | |
806 | + * Protect whole section because multiple threads may call this | |
807 | + * function with same "sock" via cs_validate_socket(). | |
808 | + */ | |
809 | + spin_lock(&cs_accepted_socket_list_lock); | |
810 | + rcu_read_lock(); | |
811 | + list_for_each_entry_rcu(ptr, &cs_accepted_socket_list, list) { | |
812 | + if (ptr->inode != inode) | |
813 | + continue; | |
814 | + ptr->status = status; | |
815 | + if (status) | |
816 | + break; | |
817 | + list_del_rcu(&ptr->list); | |
818 | + call_rcu(&ptr->rcu, cs_socket_rcu_free); | |
819 | + break; | |
820 | + } | |
821 | + rcu_read_unlock(); | |
822 | + spin_unlock(&cs_accepted_socket_list_lock); | |
823 | +} | |
824 | + | |
825 | +/** | |
826 | + * cs_validate_socket - Check post accept() permission if needed. | |
827 | + * | |
828 | + * @sock: Pointer to "struct socket". | |
829 | + * | |
830 | + * Returns 0 on success, negative value otherwise. | |
831 | + */ | |
832 | +static int cs_validate_socket(struct socket *sock) | |
833 | +{ | |
834 | + struct inode *inode = SOCK_INODE(sock); | |
835 | + struct cs_socket_tag *ptr; | |
836 | + int ret = 0; | |
837 | + rcu_read_lock(); | |
838 | + list_for_each_entry_rcu(ptr, &cs_accepted_socket_list, list) { | |
839 | + if (ptr->inode != inode) | |
840 | + continue; | |
841 | + ret = ptr->status; | |
842 | + break; | |
843 | + } | |
844 | + rcu_read_unlock(); | |
845 | + if (ret <= 0) | |
846 | + /* | |
847 | + * This socket is not an accept()ed socket or this socket is | |
848 | + * an accept()ed socket and post accept() permission is done. | |
849 | + */ | |
850 | + return ret; | |
851 | + /* | |
852 | + * Check post accept() permission now. | |
853 | + * | |
854 | + * Strictly speaking, we need to pass both listen()ing socket and | |
855 | + * accept()ed socket to __cs_socket_post_accept_permission(). | |
856 | + * But since socket's family and type are same for both sockets, | |
857 | + * passing the accept()ed socket in place for the listen()ing socket | |
858 | + * will work. | |
859 | + */ | |
860 | + ret = cs_socket_post_accept_permission(sock, sock); | |
861 | + /* | |
862 | + * If permission was granted, we forget that this is an accept()ed | |
863 | + * socket. Otherwise, we remember that this socket needs to return | |
864 | + * error for subsequent socketcalls. | |
865 | + */ | |
866 | + cs_update_socket_tag(inode, ret); | |
867 | + return ret; | |
868 | +} | |
869 | + | |
870 | +/** | |
871 | + * cs_socket_accept - Check permission for accept(). | |
872 | + * | |
873 | + * @sock: Pointer to "struct socket". | |
874 | + * @newsock: Pointer to "struct socket". | |
875 | + * | |
876 | + * Returns 0 on success, negative value otherwise. | |
877 | + * | |
878 | + * This hook is used for setting up environment for doing post accept() | |
879 | + * permission check. If dereferencing sock->ops->something() were ordered by | |
880 | + * rcu_dereference(), we could replace sock->ops with "a copy of original | |
881 | + * sock->ops with modified sock->ops->accept()" using rcu_assign_pointer() | |
882 | + * in order to do post accept() permission check before returning to userspace. | |
883 | + * If we make the copy in security_socket_post_create(), it would be possible | |
884 | + * to safely replace sock->ops here, but we don't do so because we don't want | |
885 | + * to allocate memory for sockets which do not call sock->ops->accept(). | |
886 | + * Therefore, we do post accept() permission check upon next socket syscalls | |
887 | + * rather than between sock->ops->accept() and returning to userspace. | |
888 | + * This means that if a socket was close()d before calling some socket | |
889 | + * syscalls, post accept() permission check will not be done. | |
890 | + */ | |
891 | +static int cs_socket_accept(struct socket *sock, struct socket *newsock) | |
892 | +{ | |
893 | + struct cs_socket_tag *ptr; | |
894 | + const int rc = cs_validate_socket(sock); | |
895 | + if (rc < 0) | |
896 | + return rc; | |
897 | + ptr = kzalloc(sizeof(*ptr), GFP_KERNEL); | |
898 | + if (!ptr) | |
899 | + return -ENOMEM; | |
900 | + /* | |
901 | + * Subsequent LSM hooks will receive "newsock". Therefore, I mark | |
902 | + * "newsock" as "an accept()ed socket but post accept() permission | |
903 | + * check is not done yet" by allocating memory using inode of the | |
904 | + * "newsock" as a search key. | |
905 | + */ | |
906 | + ptr->inode = SOCK_INODE(newsock); | |
907 | + ptr->status = 1; /* Check post accept() permission later. */ | |
908 | + spin_lock(&cs_accepted_socket_list_lock); | |
909 | + list_add_tail_rcu(&ptr->list, &cs_accepted_socket_list); | |
910 | + spin_unlock(&cs_accepted_socket_list_lock); | |
911 | + return 0; | |
912 | +} | |
913 | + | |
914 | +/** | |
915 | + * cs_socket_listen - Check permission for listen(). | |
916 | + * | |
917 | + * @sock: Pointer to "struct socket". | |
918 | + * @backlog: Backlog parameter. | |
919 | + * | |
920 | + * Returns 0 on success, negative value otherwise. | |
921 | + */ | |
922 | +static int cs_socket_listen(struct socket *sock, int backlog) | |
923 | +{ | |
924 | + const int rc = cs_validate_socket(sock); | |
925 | + if (rc < 0) | |
926 | + return rc; | |
927 | + return cs_socket_listen_permission(sock); | |
928 | +} | |
929 | + | |
930 | +/** | |
931 | + * cs_socket_connect - Check permission for connect(). | |
932 | + * | |
933 | + * @sock: Pointer to "struct socket". | |
934 | + * @addr: Pointer to "struct sockaddr". | |
935 | + * @addr_len: Size of @addr. | |
936 | + * | |
937 | + * Returns 0 on success, negative value otherwise. | |
938 | + */ | |
939 | +static int cs_socket_connect(struct socket *sock, struct sockaddr *addr, | |
940 | + int addr_len) | |
941 | +{ | |
942 | + const int rc = cs_validate_socket(sock); | |
943 | + if (rc < 0) | |
944 | + return rc; | |
945 | + return cs_socket_connect_permission(sock, addr, addr_len); | |
946 | +} | |
947 | + | |
948 | +/** | |
949 | + * cs_socket_bind - Check permission for bind(). | |
950 | + * | |
951 | + * @sock: Pointer to "struct socket". | |
952 | + * @addr: Pointer to "struct sockaddr". | |
953 | + * @addr_len: Size of @addr. | |
954 | + * | |
955 | + * Returns 0 on success, negative value otherwise. | |
956 | + */ | |
957 | +static int cs_socket_bind(struct socket *sock, struct sockaddr *addr, | |
958 | + int addr_len) | |
959 | +{ | |
960 | + const int rc = cs_validate_socket(sock); | |
961 | + if (rc < 0) | |
962 | + return rc; | |
963 | + return cs_socket_bind_permission(sock, addr, addr_len); | |
964 | +} | |
965 | + | |
966 | +/** | |
967 | + * cs_socket_sendmsg - Check permission for sendmsg(). | |
968 | + * | |
969 | + * @sock: Pointer to "struct socket". | |
970 | + * @msg: Pointer to "struct msghdr". | |
971 | + * @size: Size of message. | |
972 | + * | |
973 | + * Returns 0 on success, negative value otherwise. | |
974 | + */ | |
975 | +static int cs_socket_sendmsg(struct socket *sock, struct msghdr *msg, | |
976 | + int size) | |
977 | +{ | |
978 | + const int rc = cs_validate_socket(sock); | |
979 | + if (rc < 0) | |
980 | + return rc; | |
981 | + return cs_socket_sendmsg_permission(sock, msg, size); | |
982 | +} | |
983 | + | |
984 | +/** | |
985 | + * cs_socket_recvmsg - Check permission for recvmsg(). | |
986 | + * | |
987 | + * @sock: Pointer to "struct socket". | |
988 | + * @msg: Pointer to "struct msghdr". | |
989 | + * @size: Size of message. | |
990 | + * @flags: Flags. | |
991 | + * | |
992 | + * Returns 0 on success, negative value otherwise. | |
993 | + */ | |
994 | +static int cs_socket_recvmsg(struct socket *sock, struct msghdr *msg, | |
995 | + int size, int flags) | |
996 | +{ | |
997 | + return cs_validate_socket(sock); | |
998 | +} | |
999 | + | |
1000 | +/** | |
1001 | + * cs_socket_getsockname - Check permission for getsockname(). | |
1002 | + * | |
1003 | + * @sock: Pointer to "struct socket". | |
1004 | + * | |
1005 | + * Returns 0 on success, negative value otherwise. | |
1006 | + */ | |
1007 | +static int cs_socket_getsockname(struct socket *sock) | |
1008 | +{ | |
1009 | + return cs_validate_socket(sock); | |
1010 | +} | |
1011 | + | |
1012 | +/** | |
1013 | + * cs_socket_getpeername - Check permission for getpeername(). | |
1014 | + * | |
1015 | + * @sock: Pointer to "struct socket". | |
1016 | + * | |
1017 | + * Returns 0 on success, negative value otherwise. | |
1018 | + */ | |
1019 | +static int cs_socket_getpeername(struct socket *sock) | |
1020 | +{ | |
1021 | + return cs_validate_socket(sock); | |
1022 | +} | |
1023 | + | |
1024 | +/** | |
1025 | + * cs_socket_getsockopt - Check permission for getsockopt(). | |
1026 | + * | |
1027 | + * @sock: Pointer to "struct socket". | |
1028 | + * @level: Level. | |
1029 | + * @optname: Option's name, | |
1030 | + * | |
1031 | + * Returns 0 on success, negative value otherwise. | |
1032 | + */ | |
1033 | +static int cs_socket_getsockopt(struct socket *sock, int level, int optname) | |
1034 | +{ | |
1035 | + return cs_validate_socket(sock); | |
1036 | +} | |
1037 | + | |
1038 | +/** | |
1039 | + * cs_socket_setsockopt - Check permission for setsockopt(). | |
1040 | + * | |
1041 | + * @sock: Pointer to "struct socket". | |
1042 | + * @level: Level. | |
1043 | + * @optname: Option's name, | |
1044 | + * | |
1045 | + * Returns 0 on success, negative value otherwise. | |
1046 | + */ | |
1047 | +static int cs_socket_setsockopt(struct socket *sock, int level, int optname) | |
1048 | +{ | |
1049 | + return cs_validate_socket(sock); | |
1050 | +} | |
1051 | + | |
1052 | +/** | |
1053 | + * cs_socket_shutdown - Check permission for shutdown(). | |
1054 | + * | |
1055 | + * @sock: Pointer to "struct socket". | |
1056 | + * @how: Shutdown mode. | |
1057 | + * | |
1058 | + * Returns 0 on success, negative value otherwise. | |
1059 | + */ | |
1060 | +static int cs_socket_shutdown(struct socket *sock, int how) | |
1061 | +{ | |
1062 | + return cs_validate_socket(sock); | |
1063 | +} | |
1064 | + | |
1065 | +#define SOCKFS_MAGIC 0x534F434B | |
1066 | + | |
1067 | +/** | |
1068 | + * cs_inode_free_security - Release memory associated with an inode. | |
1069 | + * | |
1070 | + * @inode: Pointer to "struct inode". | |
1071 | + * | |
1072 | + * Returns nothing. | |
1073 | + * | |
1074 | + * We use this hook for releasing memory associated with an accept()ed socket. | |
1075 | + */ | |
1076 | +static void cs_inode_free_security(struct inode *inode) | |
1077 | +{ | |
1078 | + if (inode->i_sb && inode->i_sb->s_magic == SOCKFS_MAGIC) | |
1079 | + cs_update_socket_tag(inode, 0); | |
1080 | +} | |
1081 | + | |
1082 | +#endif | |
1083 | + | |
1084 | +/** | |
1085 | + * cs_sb_pivotroot - Check permission for pivot_root(). | |
1086 | + * | |
1087 | + * @old_path: Pointer to "struct path". | |
1088 | + * @new_path: Pointer to "struct path". | |
1089 | + * | |
1090 | + * Returns 0 on success, negative value otherwise. | |
1091 | + */ | |
1092 | +static int cs_sb_pivotroot(struct path *old_path, struct path *new_path) | |
1093 | +{ | |
1094 | + return cs_pivot_root_permission(old_path, new_path); | |
1095 | +} | |
1096 | + | |
1097 | +/** | |
1098 | + * cs_sb_mount - Check permission for mount(). | |
1099 | + * | |
1100 | + * @dev_name: Name of device file. | |
1101 | + * @path: Pointer to "struct path". | |
1102 | + * @type: Name of filesystem type. Maybe NULL. | |
1103 | + * @flags: Mount options. | |
1104 | + * @data_page: Optional data. Maybe NULL. | |
1105 | + * | |
1106 | + * Returns 0 on success, negative value otherwise. | |
1107 | + */ | |
1108 | +static int cs_sb_mount(const char *dev_name, struct path *path, | |
1109 | + const char *type, unsigned long flags, void *data_page) | |
1110 | +{ | |
1111 | + return cs_mount_permission(dev_name, path, type, flags, data_page); | |
1112 | +} | |
1113 | + | |
1114 | +/** | |
1115 | + * cs_sb_umount - Check permission for umount(). | |
1116 | + * | |
1117 | + * @mnt: Pointer to "struct vfsmount". | |
1118 | + * @flags: Unmount flags. | |
1119 | + * | |
1120 | + * Returns 0 on success, negative value otherwise. | |
1121 | + */ | |
1122 | +static int cs_sb_umount(struct vfsmount *mnt, int flags) | |
1123 | +{ | |
1124 | + return cs_umount_permission(mnt, flags); | |
1125 | +} | |
1126 | + | |
1127 | +/** | |
1128 | + * cs_file_fcntl - Check permission for fcntl(). | |
1129 | + * | |
1130 | + * @file: Pointer to "struct file". | |
1131 | + * @cmd: Command number. | |
1132 | + * @arg: Value for @cmd. | |
1133 | + * | |
1134 | + * Returns 0 on success, negative value otherwise. | |
1135 | + */ | |
1136 | +static int cs_file_fcntl(struct file *file, unsigned int cmd, | |
1137 | + unsigned long arg) | |
1138 | +{ | |
1139 | + return cs_fcntl_permission(file, cmd, arg); | |
1140 | +} | |
1141 | + | |
1142 | +/** | |
1143 | + * cs_file_ioctl - Check permission for ioctl(). | |
1144 | + * | |
1145 | + * @filp: Pointer to "struct file". | |
1146 | + * @cmd: Command number. | |
1147 | + * @arg: Value for @cmd. | |
1148 | + * | |
1149 | + * Returns 0 on success, negative value otherwise. | |
1150 | + */ | |
1151 | +static int cs_file_ioctl(struct file *filp, unsigned int cmd, | |
1152 | + unsigned long arg) | |
1153 | +{ | |
1154 | + return cs_ioctl_permission(filp, cmd, arg); | |
1155 | +} | |
1156 | + | |
1157 | +static struct security_operations caitsith_security_ops = { | |
1158 | + .name = "caitsith", | |
1159 | + .task_create = cs_task_create, | |
1160 | + .cred_prepare = cs_cred_prepare, | |
1161 | + .cred_free = cs_cred_free, | |
1162 | + .cred_alloc_blank = cs_cred_alloc_blank, | |
1163 | + .cred_transfer = cs_cred_transfer, | |
1164 | + .bprm_check_security = cs_bprm_check_security, | |
1165 | + .bprm_committing_creds = cs_bprm_committing_creds, | |
1166 | + .file_open = cs_file_open, | |
1167 | + .file_fcntl = cs_file_fcntl, | |
1168 | + .file_ioctl = cs_file_ioctl, | |
1169 | + .sb_pivotroot = cs_sb_pivotroot, | |
1170 | + .sb_mount = cs_sb_mount, | |
1171 | + .sb_umount = cs_sb_umount, | |
1172 | +#ifdef CONFIG_SECURITY_PATH | |
1173 | + .path_mknod = cs_path_mknod, | |
1174 | + .path_mkdir = cs_path_mkdir, | |
1175 | + .path_rmdir = cs_path_rmdir, | |
1176 | + .path_unlink = cs_path_unlink, | |
1177 | + .path_symlink = cs_path_symlink, | |
1178 | + .path_rename = cs_path_rename, | |
1179 | + .path_link = cs_path_link, | |
1180 | + .path_truncate = cs_path_truncate, | |
1181 | + .path_chmod = cs_path_chmod, | |
1182 | + .path_chown = cs_path_chown, | |
1183 | + .path_chroot = cs_path_chroot, | |
1184 | +#else | |
1185 | + .inode_mknod = cs_inode_mknod, | |
1186 | + .inode_mkdir = cs_inode_mkdir, | |
1187 | + .inode_rmdir = cs_inode_rmdir, | |
1188 | + .inode_unlink = cs_inode_unlink, | |
1189 | + .inode_symlink = cs_inode_symlink, | |
1190 | + .inode_rename = cs_inode_rename, | |
1191 | + .inode_link = cs_inode_link, | |
1192 | + .inode_create = cs_inode_create, | |
1193 | + .inode_setattr = cs_inode_setattr, | |
1194 | +#endif | |
1195 | + .inode_getattr = cs_inode_getattr, | |
1196 | +#ifdef CONFIG_SECURITY_NETWORK | |
1197 | + .socket_bind = cs_socket_bind, | |
1198 | + .socket_connect = cs_socket_connect, | |
1199 | + .socket_listen = cs_socket_listen, | |
1200 | + .socket_sendmsg = cs_socket_sendmsg, | |
1201 | + .socket_recvmsg = cs_socket_recvmsg, | |
1202 | + .socket_getsockname = cs_socket_getsockname, | |
1203 | + .socket_getpeername = cs_socket_getpeername, | |
1204 | + .socket_getsockopt = cs_socket_getsockopt, | |
1205 | + .socket_setsockopt = cs_socket_setsockopt, | |
1206 | + .socket_shutdown = cs_socket_shutdown, | |
1207 | + .socket_accept = cs_socket_accept, | |
1208 | + .inode_free_security = cs_inode_free_security, | |
1209 | +#endif | |
1210 | +}; | |
1211 | + | |
1212 | +#define swap_security_ops(op, lsm_list) \ | |
1213 | + do { \ | |
1214 | + if (list_empty(&lsm_list[lsm_##op])) \ | |
1215 | + add_security_ops(op, lsm_list); \ | |
1216 | + else { \ | |
1217 | + struct security_operations *ops = \ | |
1218 | + container_of(lsm_list, \ | |
1219 | + struct security_operations, \ | |
1220 | + list[0]); \ | |
1221 | + original_security_ops.op = ops->op; \ | |
1222 | + smp_wmb(); \ | |
1223 | + ops->op = cs_##op; \ | |
1224 | + } \ | |
1225 | + } while (0) | |
1226 | + | |
1227 | +#define add_security_ops(op, lsm_list) \ | |
1228 | + do { \ | |
1229 | + list_add_tail_rcu(&caitsith_security_ops.list[lsm_##op], \ | |
1230 | + &lsm_list[lsm_##op]); \ | |
1231 | + } while (0) | |
1232 | + | |
1233 | +/** | |
1234 | + * cs_update_security_ops - Overwrite original "struct security_operations". | |
1235 | + * | |
1236 | + * @lsm_list: Pointer to "struct list_head lsm_hooks[LSM_MAX_HOOKS]". | |
1237 | + * | |
1238 | + * Returns nothing. | |
1239 | + */ | |
1240 | +static void __init cs_update_security_ops(struct list_head *lsm_list) | |
1241 | +{ | |
1242 | + /* Security context allocator. */ | |
1243 | + swap_security_ops(cred_free, lsm_list); | |
1244 | + swap_security_ops(cred_prepare, lsm_list); | |
1245 | + swap_security_ops(cred_alloc_blank, lsm_list); | |
1246 | + add_security_ops(cred_transfer, lsm_list); | |
1247 | + add_security_ops(task_create, lsm_list); | |
1248 | + /* Security context updater for successful execve(). */ | |
1249 | + add_security_ops(bprm_check_security, lsm_list); | |
1250 | + add_security_ops(bprm_committing_creds, lsm_list); | |
1251 | + /* Various permission checker. */ | |
1252 | + add_security_ops(file_open, lsm_list); | |
1253 | + add_security_ops(file_fcntl, lsm_list); | |
1254 | + add_security_ops(file_ioctl, lsm_list); | |
1255 | + add_security_ops(sb_pivotroot, lsm_list); | |
1256 | + add_security_ops(sb_mount, lsm_list); | |
1257 | + add_security_ops(sb_umount, lsm_list); | |
1258 | +#ifdef CONFIG_SECURITY_PATH | |
1259 | + add_security_ops(path_mknod, lsm_list); | |
1260 | + add_security_ops(path_mkdir, lsm_list); | |
1261 | + add_security_ops(path_rmdir, lsm_list); | |
1262 | + add_security_ops(path_unlink, lsm_list); | |
1263 | + add_security_ops(path_symlink, lsm_list); | |
1264 | + add_security_ops(path_rename, lsm_list); | |
1265 | + add_security_ops(path_link, lsm_list); | |
1266 | + add_security_ops(path_truncate, lsm_list); | |
1267 | + add_security_ops(path_chmod, lsm_list); | |
1268 | + add_security_ops(path_chown, lsm_list); | |
1269 | + add_security_ops(path_chroot, lsm_list); | |
1270 | +#else | |
1271 | + add_security_ops(inode_mknod, lsm_list); | |
1272 | + add_security_ops(inode_mkdir, lsm_list); | |
1273 | + add_security_ops(inode_rmdir, lsm_list); | |
1274 | + add_security_ops(inode_unlink, lsm_list); | |
1275 | + add_security_ops(inode_symlink, lsm_list); | |
1276 | + add_security_ops(inode_rename, lsm_list); | |
1277 | + add_security_ops(inode_link, lsm_list); | |
1278 | + add_security_ops(inode_create, lsm_list); | |
1279 | + add_security_ops(inode_setattr, lsm_list); | |
1280 | +#endif | |
1281 | + add_security_ops(inode_getattr, lsm_list); | |
1282 | +#ifdef CONFIG_SECURITY_NETWORK | |
1283 | + add_security_ops(inode_free_security, lsm_list); | |
1284 | + add_security_ops(socket_bind, lsm_list); | |
1285 | + add_security_ops(socket_connect, lsm_list); | |
1286 | + add_security_ops(socket_listen, lsm_list); | |
1287 | + add_security_ops(socket_sendmsg, lsm_list); | |
1288 | + add_security_ops(socket_recvmsg, lsm_list); | |
1289 | + add_security_ops(socket_getsockname, lsm_list); | |
1290 | + add_security_ops(socket_getpeername, lsm_list); | |
1291 | + add_security_ops(socket_getsockopt, lsm_list); | |
1292 | + add_security_ops(socket_setsockopt, lsm_list); | |
1293 | + add_security_ops(socket_shutdown, lsm_list); | |
1294 | + add_security_ops(socket_accept, lsm_list); | |
1295 | +#endif | |
1296 | +} | |
1297 | + | |
1298 | +#undef swap_security_ops | |
1299 | +#undef add_security_ops | |
1300 | + | |
1301 | +/** | |
1302 | + * cs_init - Initialize this module. | |
1303 | + * | |
1304 | + * Returns 0 on success, negative value otherwise. | |
1305 | + */ | |
1306 | +static int __init cs_init(void) | |
1307 | +{ | |
1308 | + int idx; | |
1309 | + struct list_head *hooks = probe_lsm_hooks_list(); | |
1310 | + if (!hooks) | |
1311 | + goto out; | |
1312 | + caitsith_exports.find_task_by_vpid = probe_find_task_by_vpid(); | |
1313 | + if (!caitsith_exports.find_task_by_vpid) | |
1314 | + goto out; | |
1315 | + caitsith_exports.find_task_by_pid_ns = probe_find_task_by_pid_ns(); | |
1316 | + if (!caitsith_exports.find_task_by_pid_ns) | |
1317 | + goto out; | |
1318 | + caitsith_exports.d_absolute_path = probe_d_absolute_path(); | |
1319 | + if (!caitsith_exports.d_absolute_path) | |
1320 | + goto out; | |
1321 | + for (idx = 0; idx < CS_MAX_TASK_SECURITY_HASH; idx++) { | |
1322 | + INIT_LIST_HEAD(&cs_cred_security_list[idx]); | |
1323 | + INIT_LIST_HEAD(&cs_task_security_list[idx]); | |
1324 | + } | |
1325 | + cs_init_module(); | |
1326 | + cs_update_security_ops(hooks); | |
1327 | + printk(KERN_INFO "CAITSITH: 1.0.29 2012/11/04\n"); | |
1328 | + printk(KERN_INFO | |
1329 | + "Access Keeping And Regulating Instrument registered.\n"); | |
1330 | + return 0; | |
1331 | +out: | |
1332 | + return -EINVAL; | |
1333 | +} | |
1334 | + | |
1335 | +module_init(cs_init); | |
1336 | +MODULE_LICENSE("GPL"); | |
1337 | + | |
1338 | +/** | |
1339 | + * cs_used_by_cred - Check whether the given domain is in use or not. | |
1340 | + * | |
1341 | + * @domain: Pointer to "struct cs_domain_info". | |
1342 | + * | |
1343 | + * Returns true if @domain is in use, false otherwise. | |
1344 | + * | |
1345 | + * Caller holds rcu_read_lock(). | |
1346 | + */ | |
1347 | +bool cs_used_by_cred(const struct cs_domain_info *domain) | |
1348 | +{ | |
1349 | + int idx; | |
1350 | + struct cs_security *ptr; | |
1351 | + for (idx = 0; idx < CS_MAX_TASK_SECURITY_HASH; idx++) { | |
1352 | + struct list_head *list = &cs_cred_security_list[idx]; | |
1353 | + list_for_each_entry_rcu(ptr, list, list) { | |
1354 | + struct cs_request_info *r = ptr->r; | |
1355 | + if (ptr->cs_domain_info == domain || | |
1356 | + (r && r->previous_domain == domain)) { | |
1357 | + return true; | |
1358 | + } | |
1359 | + } | |
1360 | + } | |
1361 | + return false; | |
1362 | +} | |
1363 | + | |
1364 | +/** | |
1365 | + * cs_add_task_security - Add "struct cs_security" to list. | |
1366 | + * | |
1367 | + * @ptr: Pointer to "struct cs_security". | |
1368 | + * @list: Pointer to "struct list_head". | |
1369 | + * | |
1370 | + * Returns nothing. | |
1371 | + */ | |
1372 | +static void cs_add_task_security(struct cs_security *ptr, | |
1373 | + struct list_head *list) | |
1374 | +{ | |
1375 | + unsigned long flags; | |
1376 | + spin_lock_irqsave(&cs_task_security_list_lock, flags); | |
1377 | + list_add_rcu(&ptr->list, list); | |
1378 | + spin_unlock_irqrestore(&cs_task_security_list_lock, flags); | |
1379 | +} | |
1380 | + | |
1381 | +/** | |
1382 | + * cs_find_task_security - Find "struct cs_security" for given task. | |
1383 | + * | |
1384 | + * @task: Pointer to "struct task_struct". | |
1385 | + * | |
1386 | + * Returns pointer to "struct cs_security" on success, &cs_oom_security on | |
1387 | + * out of memory, &cs_default_security otherwise. | |
1388 | + * | |
1389 | + * If @task is current thread and "struct cs_security" for current thread was | |
1390 | + * not found, I try to allocate it. But if allocation failed, current thread | |
1391 | + * will be killed by SIGKILL. Note that if current->pid == 1, sending SIGKILL | |
1392 | + * won't work. | |
1393 | + */ | |
1394 | +struct cs_security *cs_find_task_security(const struct task_struct *task) | |
1395 | +{ | |
1396 | + struct cs_security *ptr; | |
1397 | + struct list_head *list = &cs_task_security_list | |
1398 | + [hash_ptr((void *) task, CS_TASK_SECURITY_HASH_BITS)]; | |
1399 | + /* Make sure INIT_LIST_HEAD() in cs_mm_init() takes effect. */ | |
1400 | + while (!list->next); | |
1401 | + rcu_read_lock(); | |
1402 | + list_for_each_entry_rcu(ptr, list, list) { | |
1403 | + if (ptr->pid != task->pids[PIDTYPE_PID].pid) | |
1404 | + continue; | |
1405 | + rcu_read_unlock(); | |
1406 | + /* | |
1407 | + * Current thread needs to transit from old domain to new | |
1408 | + * domain before do_execve() succeeds in order to check | |
1409 | + * permission for interpreters and environment variables using | |
1410 | + * new domain's ACL rules. The domain transition has to be | |
1411 | + * visible from other CPU in order to allow interactive | |
1412 | + * enforcing mode. Also, the domain transition has to be | |
1413 | + * reverted if do_execve() failed. However, an LSM hook for | |
1414 | + * reverting domain transition is missing. | |
1415 | + * | |
1416 | + * security_prepare_creds() is called from prepare_creds() from | |
1417 | + * prepare_bprm_creds() from do_execve() before setting | |
1418 | + * current->in_execve flag, and current->in_execve flag is | |
1419 | + * cleared by the time next do_execve() request starts. | |
1420 | + * This means that we can emulate the missing LSM hook for | |
1421 | + * reverting domain transition, by calling this function from | |
1422 | + * security_prepare_creds(). | |
1423 | + * | |
1424 | + * If current->in_execve is not set but ptr->cs_flags has | |
1425 | + * CS_TASK_IS_IN_EXECVE set, it indicates that do_execve() | |
1426 | + * has failed and reverting domain transition is needed. | |
1427 | + */ | |
1428 | + if (task == current && | |
1429 | + (ptr->cs_flags & CS_TASK_IS_IN_EXECVE) && | |
1430 | + !current->in_execve) { | |
1431 | +#ifdef CONFIG_CAITSITH_DEBUG | |
1432 | + static bool done; | |
1433 | + if (!done) { | |
1434 | + printk(KERN_INFO "CAITSITH: Reverting domain " | |
1435 | + "transition because do_execve() has " | |
1436 | + "failed.\n"); | |
1437 | + done = true; | |
1438 | + } | |
1439 | +#endif | |
1440 | + cs_clear_execve(-1, ptr); | |
1441 | + } | |
1442 | + return ptr; | |
1443 | + } | |
1444 | + rcu_read_unlock(); | |
1445 | + if (task != current) { | |
1446 | + /* | |
1447 | + * If a thread does nothing after fork(), caller will reach | |
1448 | + * here because "struct cs_security" for that thread is not | |
1449 | + * yet allocated. But that thread is keeping a snapshot of | |
1450 | + * "struct cs_security" taken as of cs_task_create() | |
1451 | + * associated with that thread's "struct cred". | |
1452 | + * | |
1453 | + * Since that snapshot will be used as initial data when that | |
1454 | + * thread allocates "struct cs_security" for that thread, we | |
1455 | + * can return that snapshot rather than &cs_default_security. | |
1456 | + * | |
1457 | + * Since this function is called by only cs_select_one() and | |
1458 | + * cs_read_pid() (via cs_task_domain() and cs_task_flags()), | |
1459 | + * it is guaranteed that caller has called rcu_read_lock() | |
1460 | + * (via cs_tasklist_lock()) before finding this thread and | |
1461 | + * this thread is valid. Therefore, we can do __task_cred(task) | |
1462 | + * like get_robust_list() does. | |
1463 | + */ | |
1464 | + return cs_find_cred_security(__task_cred(task)); | |
1465 | + } | |
1466 | + /* Use GFP_ATOMIC because caller may have called rcu_read_lock(). */ | |
1467 | + ptr = kzalloc(sizeof(*ptr), GFP_ATOMIC); | |
1468 | + if (!ptr) { | |
1469 | + printk(KERN_WARNING "Unable to allocate memory for pid=%u\n", | |
1470 | + task->pid); | |
1471 | + send_sig(SIGKILL, current, 0); | |
1472 | + return &cs_oom_security; | |
1473 | + } | |
1474 | + *ptr = *cs_find_cred_security(task->cred); | |
1475 | + /* We can shortcut because task == current. */ | |
1476 | + ptr->pid = get_pid(((struct task_struct *) task)-> | |
1477 | + pids[PIDTYPE_PID].pid); | |
1478 | + ptr->cred = NULL; | |
1479 | + cs_add_task_security(ptr, list); | |
1480 | + return ptr; | |
1481 | +} | |
1482 | + | |
1483 | +/** | |
1484 | + * cs_copy_cred_security - Allocate memory for new credentials. | |
1485 | + * | |
1486 | + * @new: Pointer to "struct cred". | |
1487 | + * @old: Pointer to "struct cred". | |
1488 | + * @gfp: Memory allocation flags. | |
1489 | + * | |
1490 | + * Returns 0 on success, negative value otherwise. | |
1491 | + */ | |
1492 | +static int cs_copy_cred_security(const struct cred *new, | |
1493 | + const struct cred *old, gfp_t gfp) | |
1494 | +{ | |
1495 | + struct cs_security *old_security = cs_find_cred_security(old); | |
1496 | + struct cs_security *new_security = | |
1497 | + kzalloc(sizeof(*new_security), gfp); | |
1498 | + if (!new_security) | |
1499 | + return -ENOMEM; | |
1500 | + *new_security = *old_security; | |
1501 | + new_security->cred = new; | |
1502 | + cs_add_cred_security(new_security); | |
1503 | + return 0; | |
1504 | +} | |
1505 | + | |
1506 | +/** | |
1507 | + * cs_find_cred_security - Find "struct cs_security" for given credential. | |
1508 | + * | |
1509 | + * @cred: Pointer to "struct cred". | |
1510 | + * | |
1511 | + * Returns pointer to "struct cs_security" on success, &cs_default_security | |
1512 | + * otherwise. | |
1513 | + */ | |
1514 | +static struct cs_security *cs_find_cred_security(const struct cred *cred) | |
1515 | +{ | |
1516 | + struct cs_security *ptr; | |
1517 | + struct list_head *list = &cs_cred_security_list | |
1518 | + [hash_ptr((void *) cred, CS_TASK_SECURITY_HASH_BITS)]; | |
1519 | + rcu_read_lock(); | |
1520 | + list_for_each_entry_rcu(ptr, list, list) { | |
1521 | + if (ptr->cred != cred) | |
1522 | + continue; | |
1523 | + rcu_read_unlock(); | |
1524 | + return ptr; | |
1525 | + } | |
1526 | + rcu_read_unlock(); | |
1527 | + return &cs_default_security; | |
1528 | +} | |
1529 | + | |
1530 | +/** | |
1531 | + * cs_task_security_gc - Do garbage collection for "struct task_struct". | |
1532 | + * | |
1533 | + * Returns nothing. | |
1534 | + * | |
1535 | + * Since security_task_free_security() is missing, I can't release memory | |
1536 | + * associated with "struct task_struct" when a task dies. Therefore, I hold | |
1537 | + * a reference on "struct pid" and runs garbage collection when associated | |
1538 | + * "struct task_struct" has gone. | |
1539 | + */ | |
1540 | +static void cs_task_security_gc(void) | |
1541 | +{ | |
1542 | + static DEFINE_SPINLOCK(lock); | |
1543 | + static atomic_t gc_counter = ATOMIC_INIT(0); | |
1544 | + unsigned int idx; | |
1545 | + /* | |
1546 | + * If some process is doing execve(), try to garbage collection now. | |
1547 | + * We should kfree() memory associated with "struct cs_security"->r | |
1548 | + * as soon as execve() has completed in order to compensate for lack of | |
1549 | + * security_bprm_free() and security_task_free() hooks. | |
1550 | + * | |
1551 | + * Otherwise, reduce frequency for performance reason. | |
1552 | + */ | |
1553 | + if (!atomic_read(&cs_in_execve_tasks) && | |
1554 | + atomic_inc_return(&gc_counter) < 1024) | |
1555 | + return; | |
1556 | + atomic_set(&gc_counter, 0); | |
1557 | + if (!spin_trylock(&lock)) | |
1558 | + return; | |
1559 | + rcu_read_lock(); | |
1560 | + for (idx = 0; idx < CS_MAX_TASK_SECURITY_HASH; idx++) { | |
1561 | + struct cs_security *ptr; | |
1562 | + struct list_head *list = &cs_task_security_list[idx]; | |
1563 | + list_for_each_entry_rcu(ptr, list, list) { | |
1564 | + if (pid_task(ptr->pid, PIDTYPE_PID)) | |
1565 | + continue; | |
1566 | + cs_del_security(ptr); | |
1567 | + } | |
1568 | + } | |
1569 | + rcu_read_unlock(); | |
1570 | + spin_unlock(&lock); | |
1571 | +} |
@@ -1,4 +1,10 @@ | ||
1 | -caitsith-objs := permission.o gc.o policy_io.o realpath.o lsm.o | |
1 | +caitsith-objs := permission.o gc.o policy_io.o realpath.o probe.o | |
2 | +ifdef CONFIG_SECURITY_COMPOSER_MAX | |
3 | +caitsith-objs += mclsm.o | |
4 | +else | |
5 | +caitsith-objs += lsm.o | |
6 | +endif | |
7 | +caitsith_test-objs := test.o probe.o | |
2 | 8 | obj-m += caitsith_test.o caitsith.o |
3 | 9 | |
4 | 10 | $(obj)/policy/policy.conf: |
@@ -0,0 +1,45 @@ | ||
1 | +#include <linux/version.h> | |
2 | +#include <linux/module.h> | |
3 | +#include <linux/sched.h> | |
4 | +#include <linux/dcache.h> | |
5 | +#include <linux/mount.h> | |
6 | +#include <linux/namei.h> | |
7 | +#include <linux/fs.h> | |
8 | +#include <linux/security.h> | |
9 | + | |
10 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 0) | |
11 | +#error This module supports only 2.6.0 and later kernels. | |
12 | +#endif | |
13 | +#ifndef CONFIG_SECURITY | |
14 | +#error You must choose CONFIG_SECURITY=y for building this module. | |
15 | +#endif | |
16 | +#ifndef CONFIG_KALLSYMS | |
17 | +#error You must choose CONFIG_KALLSYMS=y for building this module. | |
18 | +#endif | |
19 | +#ifndef CONFIG_PROC_FS | |
20 | +#error You must choose CONFIG_PROC_FS=y for building this module. | |
21 | +#endif | |
22 | +#ifndef CONFIG_MODULES | |
23 | +#error You must choose CONFIG_MODULES=y for building this module. | |
24 | +#endif | |
25 | + | |
26 | +#if defined(CONFIG_SECURITY_COMPOSER_MAX) | |
27 | +struct list_head; | |
28 | +struct list_head * __init probe_lsm_hooks_list(void); | |
29 | +#else | |
30 | +struct security_operations; | |
31 | +struct security_operations * __init probe_security_ops(void); | |
32 | +#endif | |
33 | + | |
34 | +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) | |
35 | +void * __init probe_find_task_by_vpid(void); | |
36 | +void * __init probe_find_task_by_pid_ns(void); | |
37 | +#endif | |
38 | + | |
39 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 36) | |
40 | +void * __init probe_vfsmount_lock(void); | |
41 | +#elif LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0) | |
42 | +void * __init probe___d_path(void); | |
43 | +#else | |
44 | +void * __init probe_d_absolute_path(void); | |
45 | +#endif |