lkml-11
@@ -0,0 +1,54 @@ | ||
1 | +Subject: Kconfig and Makefile | |
2 | + | |
3 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
4 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
5 | +--- | |
6 | + security/Kconfig | 1 + | |
7 | + security/Makefile | 2 ++ | |
8 | + security/tomoyo/Kconfig | 11 +++++++++++ | |
9 | + security/tomoyo/Makefile | 1 + | |
10 | + 4 files changed, 15 insertions(+) | |
11 | + | |
12 | +--- linux-next.orig/security/Kconfig | |
13 | ++++ linux-next/security/Kconfig | |
14 | +@@ -134,6 +134,7 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR | |
15 | + | |
16 | + source security/selinux/Kconfig | |
17 | + source security/smack/Kconfig | |
18 | ++source security/tomoyo/Kconfig | |
19 | + | |
20 | + endmenu | |
21 | + | |
22 | +--- linux-next.orig/security/Makefile | |
23 | ++++ linux-next/security/Makefile | |
24 | +@@ -5,6 +5,7 @@ | |
25 | + obj-$(CONFIG_KEYS) += keys/ | |
26 | + subdir-$(CONFIG_SECURITY_SELINUX) += selinux | |
27 | + subdir-$(CONFIG_SECURITY_SMACK) += smack | |
28 | ++subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo | |
29 | + | |
30 | + # always enable default capabilities | |
31 | + obj-y += commoncap.o | |
32 | +@@ -17,3 +18,4 @@ obj-$(CONFIG_SECURITY_SELINUX) += selin | |
33 | + obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o | |
34 | + obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o | |
35 | + obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o | |
36 | ++obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o | |
37 | +--- /dev/null | |
38 | ++++ linux-next/security/tomoyo/Kconfig | |
39 | +@@ -0,0 +1,11 @@ | |
40 | ++config SECURITY_TOMOYO | |
41 | ++ bool "TOMOYO Linux Support" | |
42 | ++ depends on SECURITY | |
43 | ++ select SECURITYFS | |
44 | ++ select SECURITY_PATH | |
45 | ++ default n | |
46 | ++ help | |
47 | ++ This selects TOMOYO Linux, pathname-based access control. | |
48 | ++ Required userspace tools and further information may be | |
49 | ++ found at <http://tomoyo.sourceforge.jp/>. | |
50 | ++ If you are unsure how to answer this question, answer N. | |
51 | +--- /dev/null | |
52 | ++++ linux-next/security/tomoyo/Makefile | |
53 | +@@ -0,0 +1 @@ | |
54 | ++obj-y = common.o realpath.o tomoyo.o domain.o file.o |
@@ -0,0 +1,627 @@ | ||
1 | +Subject: Memory and pathname management functions. | |
2 | + | |
3 | +TOMOYO Linux performs pathname based access control. | |
4 | +To remove factors that make pathname based access control difficult | |
5 | +(e.g. symbolic links, "..", "//" etc.), TOMOYO Linux derives realpath | |
6 | +of requested pathname from "struct dentry" and "struct vfsmount". | |
7 | + | |
8 | +The maximum length of string data is limited to 4000 including trailing '\0'. | |
9 | +Since TOMOYO Linux uses '\ooo' style representation for non ASCII printable | |
10 | +characters, may be TOMOYO Linux should be able to support 16336 (which means | |
11 | +(NAME_MAX * (PATH_MAX / (NAME_MAX + 1)) * 4 + (PATH_MAX / (NAME_MAX + 1))) | |
12 | +including trailing '\0'), but I think 4000 is enough for practical use. | |
13 | + | |
14 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
15 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
16 | +Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp> | |
17 | +--- | |
18 | + security/tomoyo/realpath.c | 540 +++++++++++++++++++++++++++++++++++++++++++++ | |
19 | + security/tomoyo/realpath.h | 60 +++++ | |
20 | + 2 files changed, 600 insertions(+) | |
21 | + | |
22 | +--- /dev/null | |
23 | ++++ linux-next/security/tomoyo/realpath.c | |
24 | +@@ -0,0 +1,540 @@ | |
25 | ++/* | |
26 | ++ * security/tomoyo/realpath.c | |
27 | ++ * | |
28 | ++ * Get the canonicalized absolute pathnames. The basis for TOMOYO. | |
29 | ++ * | |
30 | ++ * Copyright (C) 2005-2008 NTT DATA CORPORATION | |
31 | ++ * | |
32 | ++ * Version: 2.2.0-pre 2008/10/10 | |
33 | ++ * | |
34 | ++ */ | |
35 | ++ | |
36 | ++#include <linux/types.h> | |
37 | ++#include <linux/mount.h> | |
38 | ++#include <linux/magic.h> | |
39 | ++#include <linux/sysctl.h> | |
40 | ++#include "common.h" | |
41 | ++#include "realpath.h" | |
42 | ++ | |
43 | ++/** | |
44 | ++ * tmy_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root. | |
45 | ++ * | |
46 | ++ * @path: Pointer to "struct path". | |
47 | ++ * @newname: Pointer to buffer to return value in. | |
48 | ++ * @newname_len: Size of @newname. | |
49 | ++ * | |
50 | ++ * Returns 0 on success, negative value otherwise. | |
51 | ++ * | |
52 | ++ * If dentry is a directory, trailing '/' is appended. | |
53 | ++ * Characters out of 0x20 < c < 0x7F range are converted to | |
54 | ++ * \ooo style octal string. | |
55 | ++ * Character \ is converted to \\ string. | |
56 | ++ */ | |
57 | ++int tmy_realpath_from_path2(struct path *path, char *newname, int newname_len) | |
58 | ++{ | |
59 | ++ int error = -ENOMEM; | |
60 | ++ struct dentry *dentry = path->dentry; | |
61 | ++ char *sp; | |
62 | ++ | |
63 | ++ if (!dentry || !path->mnt || !newname || newname_len <= 2048) | |
64 | ++ return -EINVAL; | |
65 | ++ if (dentry->d_op && dentry->d_op->d_dname) { | |
66 | ++ /* For "socket:[\$]" and "pipe:[\$]". */ | |
67 | ++ static const int offset = 1536; | |
68 | ++ sp = dentry->d_op->d_dname(dentry, newname + offset, | |
69 | ++ newname_len - offset); | |
70 | ++ } else { | |
71 | ++ path_get(path); | |
72 | ++ sp = d_realpath(path, newname, newname_len); | |
73 | ++ path_put(path); | |
74 | ++ } | |
75 | ++ if (IS_ERR(sp)) { | |
76 | ++ error = PTR_ERR(sp); | |
77 | ++ } else { | |
78 | ++ char *dp = newname; | |
79 | ++ newname += newname_len - 5; | |
80 | ++ while (dp <= newname) { | |
81 | ++ const unsigned char c = *(unsigned char *) sp++; | |
82 | ++ *dp++ = c; | |
83 | ++ if (c == '\\') { | |
84 | ++ *dp++ = '\\'; | |
85 | ++ } else if (c > ' ' && c < 127) { | |
86 | ++ continue; | |
87 | ++ } else if (!c) { | |
88 | ++ error = 0; | |
89 | ++ break; | |
90 | ++ } else { | |
91 | ++ *dp++ = '\\'; | |
92 | ++ *dp++ = (c >> 6) + '0'; | |
93 | ++ *dp++ = ((c >> 3) & 7) + '0'; | |
94 | ++ *dp++ = (c & 7) + '0'; | |
95 | ++ } | |
96 | ++ } | |
97 | ++ } | |
98 | ++ if (error) | |
99 | ++ printk(KERN_WARNING "tmy_realpath: Pathname too long.\n"); | |
100 | ++ return error; | |
101 | ++} | |
102 | ++ | |
103 | ++/** | |
104 | ++ * tmy_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. | |
105 | ++ * | |
106 | ++ * @path: Pointer to "struct path". | |
107 | ++ * | |
108 | ++ * Returns the realpath of the given @path on success, NULL otherwise. | |
109 | ++ * | |
110 | ++ * These functions use tmy_alloc(), so the caller must call tmy_free() | |
111 | ++ * if these functions didn't return NULL. | |
112 | ++ */ | |
113 | ++char *tmy_realpath_from_path(struct path *path) | |
114 | ++{ | |
115 | ++ char *buf = tmy_alloc(sizeof(struct tmy_page_buffer)); | |
116 | ++ | |
117 | ++ if (buf && tmy_realpath_from_path2(path, buf, | |
118 | ++ TMY_MAX_PATHNAME_LEN - 1) == 0) | |
119 | ++ return buf; | |
120 | ++ tmy_free(buf); | |
121 | ++ return NULL; | |
122 | ++} | |
123 | ++ | |
124 | ++/** | |
125 | ++ * tmy_realpath - Get realpath of a pathname. | |
126 | ++ * | |
127 | ++ * @pathname: The pathname to solve. | |
128 | ++ * | |
129 | ++ * Returns the realpath of @pathname on success, NULL otherwise. | |
130 | ++ */ | |
131 | ++char *tmy_realpath(const char *pathname) | |
132 | ++{ | |
133 | ++ struct nameidata nd; | |
134 | ++ | |
135 | ++ if (pathname && path_lookup(pathname, LOOKUP_FOLLOW, &nd) == 0) { | |
136 | ++ char *buf = tmy_realpath_from_path(&nd.path); | |
137 | ++ path_put(&nd.path); | |
138 | ++ return buf; | |
139 | ++ } | |
140 | ++ return NULL; | |
141 | ++} | |
142 | ++ | |
143 | ++/** | |
144 | ++ * tmy_realpath_nofollow - Get realpath of a pathname. | |
145 | ++ * | |
146 | ++ * @pathname: The pathname to solve. | |
147 | ++ * | |
148 | ++ * Returns the realpath of @pathname on success, NULL otherwise. | |
149 | ++ */ | |
150 | ++char *tmy_realpath_nofollow(const char *pathname) | |
151 | ++{ | |
152 | ++ struct nameidata nd; | |
153 | ++ | |
154 | ++ if (pathname && path_lookup(pathname, 0, &nd) == 0) { | |
155 | ++ char *buf = tmy_realpath_from_path(&nd.path); | |
156 | ++ path_put(&nd.path); | |
157 | ++ return buf; | |
158 | ++ } | |
159 | ++ return NULL; | |
160 | ++} | |
161 | ++ | |
162 | ++/** | |
163 | ++ * round_up - Round up an integer so that the returned pointers are appropriately aligned. | |
164 | ++ * | |
165 | ++ * @size: Size in bytes. | |
166 | ++ * | |
167 | ++ * Returns rounded value of @size. | |
168 | ++ * | |
169 | ++ * FIXME: Are there more requirements that is needed for assigning value | |
170 | ++ * atomically? | |
171 | ++ */ | |
172 | ++static inline unsigned int round_up(const unsigned int size) | |
173 | ++{ | |
174 | ++ if (sizeof(void *) >= sizeof(long)) | |
175 | ++ return ((size + sizeof(void *) - 1) | |
176 | ++ / sizeof(void *)) * sizeof(void *); | |
177 | ++ else | |
178 | ++ return ((size + sizeof(long) - 1) | |
179 | ++ / sizeof(long)) * sizeof(long); | |
180 | ++} | |
181 | ++ | |
182 | ++/* Memory allocated for non-string data. */ | |
183 | ++static unsigned int allocated_memory_for_elements; | |
184 | ++/* Quota for holding non-string data. */ | |
185 | ++static unsigned int quota_for_elements; | |
186 | ++ | |
187 | ++/** | |
188 | ++ * tmy_alloc_element - Allocate permanent memory for structures. | |
189 | ++ * | |
190 | ++ * @size: Size in bytes. | |
191 | ++ * | |
192 | ++ * Returns pointer to allocated memory on success, NULL otherwise. | |
193 | ++ * | |
194 | ++ * The RAM is chunked, so NEVER try to kfree() the returned pointer. | |
195 | ++ */ | |
196 | ++void *tmy_alloc_element(const unsigned int size) | |
197 | ++{ | |
198 | ++ static char *buf; | |
199 | ++ static DEFINE_MUTEX(lock); | |
200 | ++ static unsigned int buf_used_len = PAGE_SIZE; | |
201 | ++ char *ptr = NULL; | |
202 | ++ const unsigned int word_aligned_size = round_up(size); | |
203 | ++ | |
204 | ++ if (word_aligned_size > PAGE_SIZE) | |
205 | ++ return NULL; | |
206 | ++ /***** EXCLUSIVE SECTION START *****/ | |
207 | ++ mutex_lock(&lock); | |
208 | ++ if (buf_used_len + word_aligned_size > PAGE_SIZE) { | |
209 | ++ if (!quota_for_elements || allocated_memory_for_elements | |
210 | ++ + PAGE_SIZE <= quota_for_elements) | |
211 | ++ ptr = kzalloc(PAGE_SIZE, GFP_KERNEL); | |
212 | ++ if (!ptr) { | |
213 | ++ printk(KERN_WARNING "ERROR: Out of memory " | |
214 | ++ "for tmy_alloc_element().\n"); | |
215 | ++ if (!sbin_init_started) | |
216 | ++ panic("MAC Initialization failed.\n"); | |
217 | ++ } else { | |
218 | ++ buf = ptr; | |
219 | ++ allocated_memory_for_elements += PAGE_SIZE; | |
220 | ++ buf_used_len = word_aligned_size; | |
221 | ++ ptr = buf; | |
222 | ++ } | |
223 | ++ } else if (word_aligned_size) { | |
224 | ++ int i; | |
225 | ++ ptr = buf + buf_used_len; | |
226 | ++ buf_used_len += word_aligned_size; | |
227 | ++ for (i = 0; i < word_aligned_size; i++) { | |
228 | ++ if (!ptr[i]) | |
229 | ++ continue; | |
230 | ++ printk(KERN_ERR "WARNING: Reserved memory was tainted! " | |
231 | ++ "The system might go wrong.\n"); | |
232 | ++ ptr[i] = '\0'; | |
233 | ++ } | |
234 | ++ } | |
235 | ++ mutex_unlock(&lock); | |
236 | ++ /***** EXCLUSIVE SECTION END *****/ | |
237 | ++ return ptr; | |
238 | ++} | |
239 | ++ | |
240 | ++/* Memory allocated for string data. */ | |
241 | ++static unsigned int allocated_memory_for_savename; | |
242 | ++/* Quota for holding string data. */ | |
243 | ++static unsigned int quota_for_savename; | |
244 | ++ | |
245 | ++/* | |
246 | ++ * TOMOYO uses this hash only when appending a string into the string | |
247 | ++ * table. Frequency of appending strings is very low. So we don't need | |
248 | ++ * large (e.g. 64k) hash size. 256 will be sufficient. | |
249 | ++ */ | |
250 | ++#define MAX_HASH 256 | |
251 | ++ | |
252 | ++/* Structure for string data. */ | |
253 | ++struct name_entry { | |
254 | ++ struct list1_head list; | |
255 | ++ struct path_info entry; | |
256 | ++}; | |
257 | ++ | |
258 | ++/* Structure for available memory region. */ | |
259 | ++struct free_memory_block_list { | |
260 | ++ struct list_head list; | |
261 | ++ char *ptr; /* Pointer to a free area. */ | |
262 | ++ int len; /* Length of the area. */ | |
263 | ++}; | |
264 | ++ | |
265 | ++/* | |
266 | ++ * The list for "struct name_entry". | |
267 | ++ * | |
268 | ++ * This list is updated only inside tmy_save_name(), thus | |
269 | ++ * no global mutex exists. | |
270 | ++ */ | |
271 | ++static struct list1_head name_list[MAX_HASH]; | |
272 | ++ | |
273 | ++/** | |
274 | ++ * tmy_save_name - Allocate permanent memory for string data. | |
275 | ++ * | |
276 | ++ * @name: The string to store into the permernent memory. | |
277 | ++ * | |
278 | ++ * Returns pointer to "struct path_info" on success, NULL otherwise. | |
279 | ++ * | |
280 | ++ * The RAM is shared, so NEVER try to modify or kfree() the returned name. | |
281 | ++ */ | |
282 | ++const struct path_info *tmy_save_name(const char *name) | |
283 | ++{ | |
284 | ++ static LIST_HEAD(fmb_list); | |
285 | ++ static DEFINE_MUTEX(lock); | |
286 | ++ struct name_entry *ptr; | |
287 | ++ unsigned int hash; | |
288 | ++ struct free_memory_block_list *fmb; | |
289 | ++ int len; | |
290 | ++ char *cp; | |
291 | ++ | |
292 | ++ if (!name) | |
293 | ++ return NULL; | |
294 | ++ len = strlen(name) + 1; | |
295 | ++ if (len > TMY_MAX_PATHNAME_LEN) { | |
296 | ++ printk(KERN_WARNING "ERROR: Name too long " | |
297 | ++ "for tmy_save_name().\n"); | |
298 | ++ return NULL; | |
299 | ++ } | |
300 | ++ hash = full_name_hash((const unsigned char *) name, len - 1); | |
301 | ++ /***** EXCLUSIVE SECTION START *****/ | |
302 | ++ mutex_lock(&lock); | |
303 | ++ list1_for_each_entry(ptr, &name_list[hash % MAX_HASH], list) { | |
304 | ++ if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name)) | |
305 | ++ goto out; | |
306 | ++ } | |
307 | ++ list_for_each_entry(fmb, &fmb_list, list) { | |
308 | ++ if (len <= fmb->len) | |
309 | ++ goto ready; | |
310 | ++ } | |
311 | ++ if (!quota_for_savename || allocated_memory_for_savename + PAGE_SIZE | |
312 | ++ <= quota_for_savename) | |
313 | ++ cp = kzalloc(PAGE_SIZE, GFP_KERNEL); | |
314 | ++ else | |
315 | ++ cp = NULL; | |
316 | ++ fmb = kzalloc(sizeof(*fmb), GFP_KERNEL); | |
317 | ++ if (!cp || !fmb) { | |
318 | ++ kfree(cp); | |
319 | ++ kfree(fmb); | |
320 | ++ printk(KERN_WARNING "ERROR: Out of memory " | |
321 | ++ "for tmy_save_name().\n"); | |
322 | ++ if (!sbin_init_started) | |
323 | ++ panic("MAC Initialization failed.\n"); | |
324 | ++ ptr = NULL; | |
325 | ++ goto out; | |
326 | ++ } | |
327 | ++ allocated_memory_for_savename += PAGE_SIZE; | |
328 | ++ list_add(&fmb->list, &fmb_list); | |
329 | ++ fmb->ptr = cp; | |
330 | ++ fmb->len = PAGE_SIZE; | |
331 | ++ready: | |
332 | ++ ptr = tmy_alloc_element(sizeof(*ptr)); | |
333 | ++ if (!ptr) | |
334 | ++ goto out; | |
335 | ++ ptr->entry.name = fmb->ptr; | |
336 | ++ memmove(fmb->ptr, name, len); | |
337 | ++ tmy_fill_path_info(&ptr->entry); | |
338 | ++ fmb->ptr += len; | |
339 | ++ fmb->len -= len; | |
340 | ++ list1_add_tail(&ptr->list, &name_list[hash % MAX_HASH]); | |
341 | ++ if (fmb->len == 0) { | |
342 | ++ list_del(&fmb->list); | |
343 | ++ kfree(fmb); | |
344 | ++ } | |
345 | ++out: | |
346 | ++ mutex_unlock(&lock); | |
347 | ++ /***** EXCLUSIVE SECTION END *****/ | |
348 | ++ return ptr ? &ptr->entry : NULL; | |
349 | ++} | |
350 | ++ | |
351 | ++/** | |
352 | ++ * tmy_realpath_init - Initialize realpath related code. | |
353 | ++ * | |
354 | ++ * Returns 0. | |
355 | ++ */ | |
356 | ++static int __init tmy_realpath_init(void) | |
357 | ++{ | |
358 | ++ int i; | |
359 | ++ | |
360 | ++ if (TMY_MAX_PATHNAME_LEN > PAGE_SIZE) | |
361 | ++ panic("Bad size."); | |
362 | ++ for (i = 0; i < MAX_HASH; i++) | |
363 | ++ INIT_LIST1_HEAD(&name_list[i]); | |
364 | ++ INIT_LIST1_HEAD(&KERNEL_DOMAIN.acl_info_list); | |
365 | ++ KERNEL_DOMAIN.domainname = tmy_save_name(ROOT_NAME); | |
366 | ++ list1_add_tail(&KERNEL_DOMAIN.list, &domain_list); | |
367 | ++ if (tmy_find_domain(ROOT_NAME) != &KERNEL_DOMAIN) | |
368 | ++ panic("Can't register KERNEL_DOMAIN"); | |
369 | ++ return 0; | |
370 | ++} | |
371 | ++ | |
372 | ++security_initcall(tmy_realpath_init); | |
373 | ++ | |
374 | ++/* Memory allocated for temporal purpose. */ | |
375 | ++static atomic_t dynamic_memory_size; | |
376 | ++ | |
377 | ++/** | |
378 | ++ * tmy_alloc - Allocate memory for temporal purpose. | |
379 | ++ * | |
380 | ++ * @size: Size in bytes. | |
381 | ++ * | |
382 | ++ * Returns pointer to allocated memory on success, NULL otherwise. | |
383 | ++ */ | |
384 | ++void *tmy_alloc(const size_t size) | |
385 | ++{ | |
386 | ++ void *p = kzalloc(size, GFP_KERNEL); | |
387 | ++ if (p) | |
388 | ++ atomic_add(ksize(p), &dynamic_memory_size); | |
389 | ++ return p; | |
390 | ++} | |
391 | ++ | |
392 | ++/** | |
393 | ++ * tmy_free - Release memory allocated by tmy_alloc(). | |
394 | ++ * | |
395 | ++ * @p: Pointer returned by tmy_alloc(). May be NULL. | |
396 | ++ * | |
397 | ++ * Returns nothing. | |
398 | ++ */ | |
399 | ++void tmy_free(const void *p) | |
400 | ++{ | |
401 | ++ if (p) | |
402 | ++ atomic_sub(ksize(p), &dynamic_memory_size); | |
403 | ++ kfree(p); | |
404 | ++} | |
405 | ++ | |
406 | ++static int tmy_print_ascii(const char *sp, const char *cp, | |
407 | ++ int *buflen0, char **end0) | |
408 | ++{ | |
409 | ++ int error = -ENOMEM; | |
410 | ++ int buflen = *buflen0; | |
411 | ++ char *end = *end0; | |
412 | ++ | |
413 | ++ while (sp <= cp) { | |
414 | ++ unsigned char c; | |
415 | ++ | |
416 | ++ c = *(unsigned char *) cp; | |
417 | ++ if (c == '\\') { | |
418 | ++ buflen -= 2; | |
419 | ++ if (buflen < 0) | |
420 | ++ goto out; | |
421 | ++ *--end = '\\'; | |
422 | ++ *--end = '\\'; | |
423 | ++ } else if (c > ' ' && c < 127) { | |
424 | ++ if (--buflen < 0) | |
425 | ++ goto out; | |
426 | ++ *--end = (char) c; | |
427 | ++ } else { | |
428 | ++ buflen -= 4; | |
429 | ++ if (buflen < 0) | |
430 | ++ goto out; | |
431 | ++ *--end = (c & 7) + '0'; | |
432 | ++ *--end = ((c >> 3) & 7) + '0'; | |
433 | ++ *--end = (c >> 6) + '0'; | |
434 | ++ *--end = '\\'; | |
435 | ++ } | |
436 | ++ cp--; | |
437 | ++ } | |
438 | ++ | |
439 | ++ *buflen0 = buflen; | |
440 | ++ *end0 = end; | |
441 | ++ error = 0; | |
442 | ++out: | |
443 | ++ return error; | |
444 | ++} | |
445 | ++ | |
446 | ++ | |
447 | ++/* tmy_realpath_from_path2() for "struct ctl_table". */ | |
448 | ++static int tmy_sysctl_path(struct ctl_table *table, char *buffer, int buflen) | |
449 | ++{ | |
450 | ++ int error = -ENOMEM; | |
451 | ++ char *end = buffer + buflen; | |
452 | ++ | |
453 | ++ if (buflen < 256) | |
454 | ++ goto out; | |
455 | ++ | |
456 | ++ *--end = '\0'; | |
457 | ++ buflen--; | |
458 | ++ | |
459 | ++ buflen -= 9; /* for "/proc/sys" prefix */ | |
460 | ++ | |
461 | ++ while (table) { | |
462 | ++ char buf[32]; | |
463 | ++ const char *sp = table->procname; | |
464 | ++ const char *cp; | |
465 | ++ | |
466 | ++ if (!sp) { | |
467 | ++ memset(buf, 0, sizeof(buf)); | |
468 | ++ snprintf(buf, sizeof(buf) - 1, "=%d=", table->ctl_name); | |
469 | ++ sp = buf; | |
470 | ++ } | |
471 | ++ cp = strchr(sp, '\0') - 1; | |
472 | ++ | |
473 | ++ if (tmy_print_ascii(sp, cp, &buflen, &end)) | |
474 | ++ goto out; | |
475 | ++ | |
476 | ++ if (--buflen < 0) | |
477 | ++ goto out; | |
478 | ++ | |
479 | ++ *--end = '/'; | |
480 | ++ table = table->parent; | |
481 | ++ } | |
482 | ++ | |
483 | ++ /* Move the pathname to the top of the buffer. */ | |
484 | ++ memmove(buffer, "/proc/sys", 9); | |
485 | ++ memmove(buffer + 9, end, strlen(end) + 1); | |
486 | ++ error = 0; | |
487 | ++out: | |
488 | ++ return error; | |
489 | ++} | |
490 | ++ | |
491 | ++/** | |
492 | ++ * sysctlpath_from_table - return the realpath of a ctl_table. | |
493 | ++ * @table: pointer to "struct ctl_table". | |
494 | ++ * | |
495 | ++ * Returns realpath(3) of the @table on success. | |
496 | ++ * Returns NULL on failure. | |
497 | ++ * | |
498 | ++ * This function uses tmy_alloc(), so the caller must call tmy_free() | |
499 | ++ * if this function didn't return NULL. | |
500 | ++ */ | |
501 | ++char *sysctlpath_from_table(struct ctl_table *table) | |
502 | ++{ | |
503 | ++ char *buf = tmy_alloc(TMY_MAX_PATHNAME_LEN); | |
504 | ++ | |
505 | ++ if (buf && tmy_sysctl_path(table, buf, TMY_MAX_PATHNAME_LEN - 1) == 0) | |
506 | ++ return buf; | |
507 | ++ tmy_free(buf); | |
508 | ++ return NULL; | |
509 | ++} | |
510 | ++ | |
511 | ++/** | |
512 | ++ * tmy_read_memory_counter - Check for memory usage. | |
513 | ++ * | |
514 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
515 | ++ * | |
516 | ++ * Returns memory usage. | |
517 | ++ */ | |
518 | ++int tmy_read_memory_counter(struct tmy_io_buffer *head) | |
519 | ++{ | |
520 | ++ if (!head->read_eof) { | |
521 | ++ const unsigned int shared = allocated_memory_for_savename; | |
522 | ++ const unsigned int private = allocated_memory_for_elements; | |
523 | ++ const unsigned int dynamic = atomic_read(&dynamic_memory_size); | |
524 | ++ char buffer[64]; | |
525 | ++ | |
526 | ++ memset(buffer, 0, sizeof(buffer)); | |
527 | ++ if (quota_for_savename) | |
528 | ++ snprintf(buffer, sizeof(buffer) - 1, | |
529 | ++ " (Quota: %10u)", quota_for_savename); | |
530 | ++ else | |
531 | ++ buffer[0] = '\0'; | |
532 | ++ tmy_io_printf(head, "Shared: %10u%s\n", shared, buffer); | |
533 | ++ if (quota_for_elements) | |
534 | ++ snprintf(buffer, sizeof(buffer) - 1, | |
535 | ++ " (Quota: %10u)", quota_for_elements); | |
536 | ++ else | |
537 | ++ buffer[0] = '\0'; | |
538 | ++ tmy_io_printf(head, "Private: %10u%s\n", private, buffer); | |
539 | ++ tmy_io_printf(head, "Dynamic: %10u\n", dynamic); | |
540 | ++ tmy_io_printf(head, "Total: %10u\n", | |
541 | ++ shared + private + dynamic); | |
542 | ++ head->read_eof = true; | |
543 | ++ } | |
544 | ++ return 0; | |
545 | ++} | |
546 | ++ | |
547 | ++/** | |
548 | ++ * tmy_write_memory_quota - Set memory quota. | |
549 | ++ * | |
550 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
551 | ++ * | |
552 | ++ * Returns 0. | |
553 | ++ */ | |
554 | ++int tmy_write_memory_quota(struct tmy_io_buffer *head) | |
555 | ++{ | |
556 | ++ char *data = head->write_buf; | |
557 | ++ unsigned int size; | |
558 | ++ | |
559 | ++ if (sscanf(data, "Shared: %u", &size) == 1) | |
560 | ++ quota_for_savename = size; | |
561 | ++ else if (sscanf(data, "Private: %u", &size) == 1) | |
562 | ++ quota_for_elements = size; | |
563 | ++ return 0; | |
564 | ++} | |
565 | +--- /dev/null | |
566 | ++++ linux-next/security/tomoyo/realpath.h | |
567 | +@@ -0,0 +1,60 @@ | |
568 | ++/* | |
569 | ++ * security/tomoyo/realpath.h | |
570 | ++ * | |
571 | ++ * Get the canonicalized absolute pathnames. The basis for TOMOYO. | |
572 | ++ * | |
573 | ++ * Copyright (C) 2005-2008 NTT DATA CORPORATION | |
574 | ++ * | |
575 | ++ * Version: 2.2.0-pre 2008/10/10 | |
576 | ++ * | |
577 | ++ */ | |
578 | ++ | |
579 | ++#ifndef _SECURITY_TOMOYO_REALPATH_H | |
580 | ++#define _SECURITY_TOMOYO_REALPATH_H | |
581 | ++ | |
582 | ++struct path; | |
583 | ++struct condition_list; | |
584 | ++struct path_info; | |
585 | ++struct tmy_io_buffer; | |
586 | ++ | |
587 | ++/* Returns realpath(3) of the given pathname but ignores chroot'ed root. */ | |
588 | ++int tmy_realpath_from_path2(struct path *path, char *newname, int newname_len); | |
589 | ++ | |
590 | ++/* | |
591 | ++ * Returns realpath(3) of the given pathname but ignores chroot'ed root. | |
592 | ++ * These functions use tmy_alloc(), so the caller must call tmy_free() | |
593 | ++ * if these functions didn't return NULL. | |
594 | ++ */ | |
595 | ++char *tmy_realpath(const char *pathname); | |
596 | ++/* Same with tmy_realpath() except that it doesn't follow the final symlink. */ | |
597 | ++char *tmy_realpath_nofollow(const char *pathname); | |
598 | ++/* Same with tmy_realpath() except that the pathname is already solved. */ | |
599 | ++char *tmy_realpath_from_path(struct path *path); | |
600 | ++/* Same with tmy_realpath() except that it uses struct ctl_table. */ | |
601 | ++char *sysctlpath_from_table(struct ctl_table *table); | |
602 | ++ | |
603 | ++/* | |
604 | ++ * Allocate memory for ACL entry. | |
605 | ++ * The RAM is chunked, so NEVER try to kfree() the returned pointer. | |
606 | ++ */ | |
607 | ++void *tmy_alloc_element(const unsigned int size); | |
608 | ++ | |
609 | ++/* | |
610 | ++ * Keep the given name on the RAM. | |
611 | ++ * The RAM is shared, so NEVER try to modify or kfree() the returned name. | |
612 | ++ */ | |
613 | ++const struct path_info *tmy_save_name(const char *name); | |
614 | ++ | |
615 | ++/* Allocate memory for temporary use (e.g. permission checks). */ | |
616 | ++void *tmy_alloc(const size_t size); | |
617 | ++ | |
618 | ++/* Free memory allocated by tmy_alloc(). */ | |
619 | ++void tmy_free(const void *p); | |
620 | ++ | |
621 | ++/* Check for memory usage. */ | |
622 | ++int tmy_read_memory_counter(struct tmy_io_buffer *head); | |
623 | ++ | |
624 | ++/* Set memory quota. */ | |
625 | ++int tmy_write_memory_quota(struct tmy_io_buffer *head); | |
626 | ++ | |
627 | ++#endif /* !defined(_SECURITY_TOMOYO_REALPATH_H) */ |
@@ -0,0 +1,885 @@ | ||
1 | +Subject: Domain transition handler. | |
2 | + | |
3 | +This file controls domain creation/deletion/transition. | |
4 | + | |
5 | +Every process belongs to a domain in TOMOYO Linux. | |
6 | +Domain transition occurs when execve(2) is called | |
7 | +and the domain is expressed as 'process invocation history', | |
8 | +such as '<kernel> /sbin/init /etc/init.d/rc'. | |
9 | +Domain information is stored in task_struct->cred->security field. | |
10 | + | |
11 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
12 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
13 | +Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp> | |
14 | +--- | |
15 | + security/tomoyo/domain.c | 865 +++++++++++++++++++++++++++++++++++++++++++++++ | |
16 | + 1 file changed, 865 insertions(+) | |
17 | + | |
18 | +--- /dev/null | |
19 | ++++ linux-next/security/tomoyo/domain.c | |
20 | +@@ -0,0 +1,865 @@ | |
21 | ++/* | |
22 | ++ * security/tomoyo/domain.c | |
23 | ++ * | |
24 | ++ * Implementation of the Domain-Based Mandatory Access Control. | |
25 | ++ * | |
26 | ++ * Copyright (C) 2005-2008 NTT DATA CORPORATION | |
27 | ++ * | |
28 | ++ * Version: 2.2.0-pre 2008/10/10 | |
29 | ++ * | |
30 | ++ */ | |
31 | ++ | |
32 | ++#include "common.h" | |
33 | ++#include "tomoyo.h" | |
34 | ++#include "realpath.h" | |
35 | ++#include <linux/binfmts.h> | |
36 | ++ | |
37 | ++/* Variables definitions.*/ | |
38 | ++ | |
39 | ++/* The initial domain. */ | |
40 | ++struct domain_info KERNEL_DOMAIN; | |
41 | ++ | |
42 | ++/* | |
43 | ++ * The list for "struct domain_info". | |
44 | ++ * | |
45 | ++ * The domain_list_lock mutex protects the domain_list list. | |
46 | ++ */ | |
47 | ++LIST1_HEAD(domain_list); | |
48 | ++static DEFINE_MUTEX(domain_list_lock); | |
49 | ++ | |
50 | ++/* Structure for "initialize_domain" and "no_initialize_domain" keyword. */ | |
51 | ++struct domain_initializer_entry { | |
52 | ++ struct list1_head list; | |
53 | ++ const struct path_info *domainname; /* This may be NULL */ | |
54 | ++ const struct path_info *program; | |
55 | ++ bool is_deleted; | |
56 | ++ bool is_not; /* True if this entry is "no_initialize_domain". */ | |
57 | ++ bool is_last_name; /* True if the domainname is tmy_get_last_name(). */ | |
58 | ++}; | |
59 | ++ | |
60 | ++/* Structure for "keep_domain" and "no_keep_domain" keyword. */ | |
61 | ++struct domain_keeper_entry { | |
62 | ++ struct list1_head list; | |
63 | ++ const struct path_info *domainname; | |
64 | ++ const struct path_info *program; /* This may be NULL */ | |
65 | ++ bool is_deleted; | |
66 | ++ bool is_not; /* True if this entry is "no_keep_domain". */ | |
67 | ++ bool is_last_name; /* True if the domainname is tmy_get_last_name(). */ | |
68 | ++}; | |
69 | ++ | |
70 | ++/* Structure for "alias" keyword. */ | |
71 | ++struct alias_entry { | |
72 | ++ struct list1_head list; | |
73 | ++ const struct path_info *original_name; | |
74 | ++ const struct path_info *aliased_name; | |
75 | ++ bool is_deleted; | |
76 | ++}; | |
77 | ++ | |
78 | ++/** | |
79 | ++ * tmy_set_domain_flag - Set or clear domain's attribute flags. | |
80 | ++ * | |
81 | ++ * @domain: Pointer to "struct domain_info". | |
82 | ++ * @is_delete: True if it is a delete request. | |
83 | ++ * @flags: Flags to set or clear. | |
84 | ++ * | |
85 | ++ * Returns nothing. | |
86 | ++ */ | |
87 | ++void tmy_set_domain_flag(struct domain_info *domain, const bool is_delete, | |
88 | ++ const u8 flags) | |
89 | ++{ | |
90 | ++ /* We need to serialize because this is bitfield operation. */ | |
91 | ++ static DEFINE_SPINLOCK(lock); | |
92 | ++ /***** CRITICAL SECTION START *****/ | |
93 | ++ spin_lock(&lock); | |
94 | ++ if (!is_delete) | |
95 | ++ domain->flags |= flags; | |
96 | ++ else | |
97 | ++ domain->flags &= ~flags; | |
98 | ++ spin_unlock(&lock); | |
99 | ++ /***** CRITICAL SECTION END *****/ | |
100 | ++} | |
101 | ++ | |
102 | ++/** | |
103 | ++ * tmy_get_last_name - Get last component of a domainname. | |
104 | ++ * | |
105 | ++ * @domain: Pointer to "struct domain_info". | |
106 | ++ * | |
107 | ++ * Returns the last component of the domainname. | |
108 | ++ */ | |
109 | ++const char *tmy_get_last_name(const struct domain_info *domain) | |
110 | ++{ | |
111 | ++ const char *cp0 = domain->domainname->name; | |
112 | ++ const char *cp1 = strrchr(cp0, ' '); | |
113 | ++ | |
114 | ++ if (cp1) | |
115 | ++ return cp1 + 1; | |
116 | ++ return cp0; | |
117 | ++} | |
118 | ++ | |
119 | ++/* | |
120 | ++ * The list for "struct domain_initializer_entry". | |
121 | ++ * | |
122 | ++ * This list is updated only inside update_domain_initializer_entry(), thus | |
123 | ++ * no global mutex exists. | |
124 | ++ */ | |
125 | ++static LIST1_HEAD(domain_initializer_list); | |
126 | ++ | |
127 | ++/** | |
128 | ++ * update_domain_initializer_entry - Update "struct domain_initializer_entry" list. | |
129 | ++ * | |
130 | ++ * @domainname: The name of domain. May be NULL. | |
131 | ++ * @program: The name of program. | |
132 | ++ * @is_not: True if it is "no_initialize_domain" entry. | |
133 | ++ * @is_delete: True if it is a delete request. | |
134 | ++ * | |
135 | ++ * Returns 0 on success, negative value otherwise. | |
136 | ++ */ | |
137 | ++static int update_domain_initializer_entry(const char *domainname, | |
138 | ++ const char *program, | |
139 | ++ const bool is_not, | |
140 | ++ const bool is_delete) | |
141 | ++{ | |
142 | ++ struct domain_initializer_entry *new_entry; | |
143 | ++ struct domain_initializer_entry *ptr; | |
144 | ++ static DEFINE_MUTEX(lock); | |
145 | ++ const struct path_info *saved_program; | |
146 | ++ const struct path_info *saved_domainname = NULL; | |
147 | ++ int error = -ENOMEM; | |
148 | ++ bool is_last_name = false; | |
149 | ++ | |
150 | ++ if (!tmy_is_correct_path(program, 1, -1, -1, __func__)) | |
151 | ++ return -EINVAL; /* No patterns allowed. */ | |
152 | ++ if (domainname) { | |
153 | ++ if (!tmy_is_domain_def(domainname) && | |
154 | ++ tmy_is_correct_path(domainname, 1, -1, -1, __func__)) | |
155 | ++ is_last_name = true; | |
156 | ++ else if (!tmy_is_correct_domain(domainname, __func__)) | |
157 | ++ return -EINVAL; | |
158 | ++ saved_domainname = tmy_save_name(domainname); | |
159 | ++ if (!saved_domainname) | |
160 | ++ return -ENOMEM; | |
161 | ++ } | |
162 | ++ saved_program = tmy_save_name(program); | |
163 | ++ if (!saved_program) | |
164 | ++ return -ENOMEM; | |
165 | ++ /***** EXCLUSIVE SECTION START *****/ | |
166 | ++ mutex_lock(&lock); | |
167 | ++ list1_for_each_entry(ptr, &domain_initializer_list, list) { | |
168 | ++ if (ptr->is_not != is_not || | |
169 | ++ ptr->domainname != saved_domainname || | |
170 | ++ ptr->program != saved_program) | |
171 | ++ continue; | |
172 | ++ ptr->is_deleted = is_delete; | |
173 | ++ error = 0; | |
174 | ++ goto out; | |
175 | ++ } | |
176 | ++ if (is_delete) { | |
177 | ++ error = -ENOENT; | |
178 | ++ goto out; | |
179 | ++ } | |
180 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
181 | ++ if (!new_entry) | |
182 | ++ goto out; | |
183 | ++ new_entry->domainname = saved_domainname; | |
184 | ++ new_entry->program = saved_program; | |
185 | ++ new_entry->is_not = is_not; | |
186 | ++ new_entry->is_last_name = is_last_name; | |
187 | ++ list1_add_tail(&new_entry->list, &domain_initializer_list); | |
188 | ++ error = 0; | |
189 | ++out: | |
190 | ++ mutex_unlock(&lock); | |
191 | ++ /***** EXCLUSIVE SECTION END *****/ | |
192 | ++ tmy_update_counter(TMY_UPDATES_COUNTER_EXCEPTION_POLICY); | |
193 | ++ return error; | |
194 | ++} | |
195 | ++ | |
196 | ++/** | |
197 | ++ * tmy_read_domain_initializer_policy - Read "struct domain_initializer_entry" list. | |
198 | ++ * | |
199 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
200 | ++ * | |
201 | ++ * Returns true on success, false otherwise. | |
202 | ++ */ | |
203 | ++bool tmy_read_domain_initializer_policy(struct tmy_io_buffer *head) | |
204 | ++{ | |
205 | ++ struct list1_head *pos; | |
206 | ++ | |
207 | ++ list1_for_each_cookie(pos, head->read_var2, &domain_initializer_list) { | |
208 | ++ const char *no; | |
209 | ++ const char *from = ""; | |
210 | ++ const char *domain = ""; | |
211 | ++ struct domain_initializer_entry *ptr; | |
212 | ++ ptr = list1_entry(pos, struct domain_initializer_entry, list); | |
213 | ++ if (ptr->is_deleted) | |
214 | ++ continue; | |
215 | ++ no = ptr->is_not ? "no_" : ""; | |
216 | ++ if (ptr->domainname) { | |
217 | ++ from = " from "; | |
218 | ++ domain = ptr->domainname->name; | |
219 | ++ } | |
220 | ++ if (!tmy_io_printf(head, | |
221 | ++ "%s" KEYWORD_INITIALIZE_DOMAIN "%s%s%s\n", | |
222 | ++ no, ptr->program->name, from, domain)) | |
223 | ++ goto out; | |
224 | ++ } | |
225 | ++ return true; | |
226 | ++out: | |
227 | ++ return false; | |
228 | ++} | |
229 | ++ | |
230 | ++/** | |
231 | ++ * tmy_write_domain_initializer_policy - Write "struct domain_initializer_entry" list. | |
232 | ++ * | |
233 | ++ * @data: String to parse. | |
234 | ++ * @is_not: True if it is "no_initialize_domain" entry. | |
235 | ++ * @is_delete: True if it is a delete request. | |
236 | ++ * | |
237 | ++ * Returns 0 on success, negative value otherwise. | |
238 | ++ */ | |
239 | ++int tmy_write_domain_initializer_policy(char *data, const bool is_not, | |
240 | ++ const bool is_delete) | |
241 | ++{ | |
242 | ++ char *cp = strstr(data, " from "); | |
243 | ++ | |
244 | ++ if (cp) { | |
245 | ++ *cp = '\0'; | |
246 | ++ return update_domain_initializer_entry(cp + 6, data, is_not, | |
247 | ++ is_delete); | |
248 | ++ } | |
249 | ++ return update_domain_initializer_entry(NULL, data, is_not, is_delete); | |
250 | ++} | |
251 | ++ | |
252 | ++/** | |
253 | ++ * is_domain_initializer - Check whether the given program causes domainname reinitialization. | |
254 | ++ * | |
255 | ++ * @domainname: The name of domain. | |
256 | ++ * @program: The name of program. | |
257 | ++ * @last_name: The last component of @domainname. | |
258 | ++ * | |
259 | ++ * Returns true if executing @program reinitializes domain transition, | |
260 | ++ * false otherwise. | |
261 | ++ */ | |
262 | ++static bool is_domain_initializer(const struct path_info *domainname, | |
263 | ++ const struct path_info *program, | |
264 | ++ const struct path_info *last_name) | |
265 | ++{ | |
266 | ++ struct domain_initializer_entry *ptr; | |
267 | ++ bool flag = false; | |
268 | ++ | |
269 | ++ list1_for_each_entry(ptr, &domain_initializer_list, list) { | |
270 | ++ if (ptr->is_deleted) | |
271 | ++ continue; | |
272 | ++ if (ptr->domainname) { | |
273 | ++ if (!ptr->is_last_name) { | |
274 | ++ if (ptr->domainname != domainname) | |
275 | ++ continue; | |
276 | ++ } else { | |
277 | ++ if (tmy_pathcmp(ptr->domainname, last_name)) | |
278 | ++ continue; | |
279 | ++ } | |
280 | ++ } | |
281 | ++ if (tmy_pathcmp(ptr->program, program)) | |
282 | ++ continue; | |
283 | ++ if (ptr->is_not) | |
284 | ++ return false; | |
285 | ++ flag = true; | |
286 | ++ } | |
287 | ++ return flag; | |
288 | ++} | |
289 | ++ | |
290 | ++/* | |
291 | ++ * The list for "struct domain_keeper_entry". | |
292 | ++ * | |
293 | ++ * This list is updated only inside update_domain_keeper_entry(), thus | |
294 | ++ * no global mutex exists. | |
295 | ++ */ | |
296 | ++static LIST1_HEAD(domain_keeper_list); | |
297 | ++ | |
298 | ++/** | |
299 | ++ * update_domain_keeper_entry - Update "struct domain_keeper_entry" list. | |
300 | ++ * | |
301 | ++ * @domainname: The name of domain. | |
302 | ++ * @program: The name of program. May be NULL. | |
303 | ++ * @is_not: True if it is "no_keep_domain" entry. | |
304 | ++ * @is_delete: True if it is a delete request. | |
305 | ++ * | |
306 | ++ * Returns 0 on success, negative value otherwise. | |
307 | ++ */ | |
308 | ++static int update_domain_keeper_entry(const char *domainname, | |
309 | ++ const char *program, | |
310 | ++ const bool is_not, const bool is_delete) | |
311 | ++{ | |
312 | ++ struct domain_keeper_entry *new_entry; | |
313 | ++ struct domain_keeper_entry *ptr; | |
314 | ++ const struct path_info *saved_domainname; | |
315 | ++ const struct path_info *saved_program = NULL; | |
316 | ++ static DEFINE_MUTEX(lock); | |
317 | ++ int error = -ENOMEM; | |
318 | ++ bool is_last_name = false; | |
319 | ++ | |
320 | ++ if (!tmy_is_domain_def(domainname) && | |
321 | ++ tmy_is_correct_path(domainname, 1, -1, -1, __func__)) | |
322 | ++ is_last_name = true; | |
323 | ++ else if (!tmy_is_correct_domain(domainname, __func__)) | |
324 | ++ return -EINVAL; | |
325 | ++ if (program) { | |
326 | ++ if (!tmy_is_correct_path(program, 1, -1, -1, __func__)) | |
327 | ++ return -EINVAL; | |
328 | ++ saved_program = tmy_save_name(program); | |
329 | ++ if (!saved_program) | |
330 | ++ return -ENOMEM; | |
331 | ++ } | |
332 | ++ saved_domainname = tmy_save_name(domainname); | |
333 | ++ if (!saved_domainname) | |
334 | ++ return -ENOMEM; | |
335 | ++ /***** EXCLUSIVE SECTION START *****/ | |
336 | ++ mutex_lock(&lock); | |
337 | ++ list1_for_each_entry(ptr, &domain_keeper_list, list) { | |
338 | ++ if (ptr->is_not != is_not || | |
339 | ++ ptr->domainname != saved_domainname || | |
340 | ++ ptr->program != saved_program) | |
341 | ++ continue; | |
342 | ++ ptr->is_deleted = is_delete; | |
343 | ++ error = 0; | |
344 | ++ goto out; | |
345 | ++ } | |
346 | ++ if (is_delete) { | |
347 | ++ error = -ENOENT; | |
348 | ++ goto out; | |
349 | ++ } | |
350 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
351 | ++ if (!new_entry) | |
352 | ++ goto out; | |
353 | ++ new_entry->domainname = saved_domainname; | |
354 | ++ new_entry->program = saved_program; | |
355 | ++ new_entry->is_not = is_not; | |
356 | ++ new_entry->is_last_name = is_last_name; | |
357 | ++ list1_add_tail(&new_entry->list, &domain_keeper_list); | |
358 | ++ error = 0; | |
359 | ++out: | |
360 | ++ mutex_unlock(&lock); | |
361 | ++ /***** EXCLUSIVE SECTION END *****/ | |
362 | ++ tmy_update_counter(TMY_UPDATES_COUNTER_EXCEPTION_POLICY); | |
363 | ++ return error; | |
364 | ++} | |
365 | ++ | |
366 | ++/** | |
367 | ++ * tmy_write_domain_keeper_policy - Write "struct domain_keeper_entry" list. | |
368 | ++ * | |
369 | ++ * @data: String to parse. | |
370 | ++ * @is_not: True if it is "no_keep_domain" entry. | |
371 | ++ * @is_delete: True if it is a delete request. | |
372 | ++ * | |
373 | ++ */ | |
374 | ++int tmy_write_domain_keeper_policy(char *data, const bool is_not, | |
375 | ++ const bool is_delete) | |
376 | ++{ | |
377 | ++ char *cp = strstr(data, " from "); | |
378 | ++ | |
379 | ++ if (cp) { | |
380 | ++ *cp = '\0'; | |
381 | ++ return update_domain_keeper_entry(cp + 6, data, | |
382 | ++ is_not, is_delete); | |
383 | ++ } | |
384 | ++ return update_domain_keeper_entry(data, NULL, is_not, is_delete); | |
385 | ++} | |
386 | ++ | |
387 | ++/** | |
388 | ++ * tmy_read_domain_keeper_policy - Read "struct domain_keeper_entry" list. | |
389 | ++ * | |
390 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
391 | ++ * | |
392 | ++ * Returns true on success, false otherwise. | |
393 | ++ */ | |
394 | ++bool tmy_read_domain_keeper_policy(struct tmy_io_buffer *head) | |
395 | ++{ | |
396 | ++ struct list1_head *pos; | |
397 | ++ | |
398 | ++ list1_for_each_cookie(pos, head->read_var2, &domain_keeper_list) { | |
399 | ++ struct domain_keeper_entry *ptr; | |
400 | ++ const char *no; | |
401 | ++ const char *from = ""; | |
402 | ++ const char *program = ""; | |
403 | ++ | |
404 | ++ ptr = list1_entry(pos, struct domain_keeper_entry, list); | |
405 | ++ if (ptr->is_deleted) | |
406 | ++ continue; | |
407 | ++ no = ptr->is_not ? "no_" : ""; | |
408 | ++ if (ptr->program) { | |
409 | ++ from = " from "; | |
410 | ++ program = ptr->program->name; | |
411 | ++ } | |
412 | ++ if (!tmy_io_printf(head, | |
413 | ++ "%s" KEYWORD_KEEP_DOMAIN "%s%s%s\n", no, | |
414 | ++ program, from, ptr->domainname->name)) | |
415 | ++ goto out; | |
416 | ++ } | |
417 | ++ return true; | |
418 | ++out: | |
419 | ++ return false; | |
420 | ++} | |
421 | ++ | |
422 | ++/** | |
423 | ++ * is_domain_keeper - Check whether the given program causes domain transition suppression. | |
424 | ++ * | |
425 | ++ * @domainname: The name of domain. | |
426 | ++ * @program: The name of program. | |
427 | ++ * @last_name: The last component of @domainname. | |
428 | ++ * | |
429 | ++ * Returns true if executing @program supresses domain transition, | |
430 | ++ * false otherwise. | |
431 | ++ */ | |
432 | ++static bool is_domain_keeper(const struct path_info *domainname, | |
433 | ++ const struct path_info *program, | |
434 | ++ const struct path_info *last_name) | |
435 | ++{ | |
436 | ++ struct domain_keeper_entry *ptr; | |
437 | ++ bool flag = false; | |
438 | ++ | |
439 | ++ list1_for_each_entry(ptr, &domain_keeper_list, list) { | |
440 | ++ if (ptr->is_deleted) | |
441 | ++ continue; | |
442 | ++ if (!ptr->is_last_name) { | |
443 | ++ if (ptr->domainname != domainname) | |
444 | ++ continue; | |
445 | ++ } else { | |
446 | ++ if (tmy_pathcmp(ptr->domainname, last_name)) | |
447 | ++ continue; | |
448 | ++ } | |
449 | ++ if (ptr->program && tmy_pathcmp(ptr->program, program)) | |
450 | ++ continue; | |
451 | ++ if (ptr->is_not) | |
452 | ++ return false; | |
453 | ++ flag = true; | |
454 | ++ } | |
455 | ++ return flag; | |
456 | ++} | |
457 | ++ | |
458 | ++/* | |
459 | ++ * The list for "struct alias_entry". | |
460 | ++ * | |
461 | ++ * This list is updated only inside update_alias_entry(), thus | |
462 | ++ * no global mutex exists. | |
463 | ++ */ | |
464 | ++static LIST1_HEAD(alias_list); | |
465 | ++ | |
466 | ++/** | |
467 | ++ * update_alias_entry - Update "struct alias_entry" list. | |
468 | ++ * | |
469 | ++ * @original_name: The original program's real name. | |
470 | ++ * @aliased_name: The symbolic program's symbolic link's name. | |
471 | ++ * @is_delete: True if it is a delete request. | |
472 | ++ * | |
473 | ++ * Returns 0 on success, negative value otherwise. | |
474 | ++ */ | |
475 | ++static int update_alias_entry(const char *original_name, | |
476 | ++ const char *aliased_name, | |
477 | ++ const bool is_delete) | |
478 | ++{ | |
479 | ++ struct alias_entry *new_entry; | |
480 | ++ struct alias_entry *ptr; | |
481 | ++ static DEFINE_MUTEX(lock); | |
482 | ++ const struct path_info *saved_original_name; | |
483 | ++ const struct path_info *saved_aliased_name; | |
484 | ++ int error = -ENOMEM; | |
485 | ++ | |
486 | ++ if (!tmy_is_correct_path(original_name, 1, -1, -1, __func__) || | |
487 | ++ !tmy_is_correct_path(aliased_name, 1, -1, -1, __func__)) | |
488 | ++ return -EINVAL; /* No patterns allowed. */ | |
489 | ++ saved_original_name = tmy_save_name(original_name); | |
490 | ++ saved_aliased_name = tmy_save_name(aliased_name); | |
491 | ++ if (!saved_original_name || !saved_aliased_name) | |
492 | ++ return -ENOMEM; | |
493 | ++ /***** EXCLUSIVE SECTION START *****/ | |
494 | ++ mutex_lock(&lock); | |
495 | ++ list1_for_each_entry(ptr, &alias_list, list) { | |
496 | ++ if (ptr->original_name != saved_original_name || | |
497 | ++ ptr->aliased_name != saved_aliased_name) | |
498 | ++ continue; | |
499 | ++ ptr->is_deleted = is_delete; | |
500 | ++ error = 0; | |
501 | ++ goto out; | |
502 | ++ } | |
503 | ++ if (is_delete) { | |
504 | ++ error = -ENOENT; | |
505 | ++ goto out; | |
506 | ++ } | |
507 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
508 | ++ if (!new_entry) | |
509 | ++ goto out; | |
510 | ++ new_entry->original_name = saved_original_name; | |
511 | ++ new_entry->aliased_name = saved_aliased_name; | |
512 | ++ list1_add_tail(&new_entry->list, &alias_list); | |
513 | ++ error = 0; | |
514 | ++out: | |
515 | ++ mutex_unlock(&lock); | |
516 | ++ /***** EXCLUSIVE SECTION END *****/ | |
517 | ++ tmy_update_counter(TMY_UPDATES_COUNTER_EXCEPTION_POLICY); | |
518 | ++ return error; | |
519 | ++} | |
520 | ++ | |
521 | ++/** | |
522 | ++ * tmy_read_alias_policy - Read "struct alias_entry" list. | |
523 | ++ * | |
524 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
525 | ++ * | |
526 | ++ * Returns true on success, false otherwise. | |
527 | ++ */ | |
528 | ++bool tmy_read_alias_policy(struct tmy_io_buffer *head) | |
529 | ++{ | |
530 | ++ struct list1_head *pos; | |
531 | ++ | |
532 | ++ list1_for_each_cookie(pos, head->read_var2, &alias_list) { | |
533 | ++ struct alias_entry *ptr; | |
534 | ++ | |
535 | ++ ptr = list1_entry(pos, struct alias_entry, list); | |
536 | ++ if (ptr->is_deleted) | |
537 | ++ continue; | |
538 | ++ if (!tmy_io_printf(head, KEYWORD_ALIAS "%s %s\n", | |
539 | ++ ptr->original_name->name, | |
540 | ++ ptr->aliased_name->name)) | |
541 | ++ goto out; | |
542 | ++ } | |
543 | ++ return true; | |
544 | ++out: | |
545 | ++ return false; | |
546 | ++} | |
547 | ++ | |
548 | ++/** | |
549 | ++ * tmy_write_alias_policy - Write "struct alias_entry" list. | |
550 | ++ * | |
551 | ++ * @data: String to parse. | |
552 | ++ * @is_delete: True if it is a delete request. | |
553 | ++ * | |
554 | ++ * Returns 0 on success, negative value otherwise. | |
555 | ++ */ | |
556 | ++int tmy_write_alias_policy(char *data, const bool is_delete) | |
557 | ++{ | |
558 | ++ char *cp = strchr(data, ' '); | |
559 | ++ | |
560 | ++ if (!cp) | |
561 | ++ return -EINVAL; | |
562 | ++ *cp++ = '\0'; | |
563 | ++ return update_alias_entry(data, cp, is_delete); | |
564 | ++} | |
565 | ++ | |
566 | ++/* Domain create/delete/undelete handler. */ | |
567 | ++ | |
568 | ++/* #define DEBUG_DOMAIN_UNDELETE */ | |
569 | ++ | |
570 | ++/** | |
571 | ++ * tmy_delete_domain - Delete a domain. | |
572 | ++ * | |
573 | ++ * @domainname: The name of domain. | |
574 | ++ * | |
575 | ++ * Returns 0. | |
576 | ++ */ | |
577 | ++int tmy_delete_domain(char *domainname) | |
578 | ++{ | |
579 | ++ struct domain_info *domain; | |
580 | ++ struct path_info name; | |
581 | ++ | |
582 | ++ name.name = domainname; | |
583 | ++ tmy_fill_path_info(&name); | |
584 | ++ /***** EXCLUSIVE SECTION START *****/ | |
585 | ++ mutex_lock(&domain_list_lock); | |
586 | ++#ifdef DEBUG_DOMAIN_UNDELETE | |
587 | ++ printk(KERN_DEBUG "tmy_delete_domain %s\n", domainname); | |
588 | ++ list1_for_each_entry(domain, &domain_list, list) { | |
589 | ++ if (tmy_pathcmp(domain->domainname, &name)) | |
590 | ++ continue; | |
591 | ++ printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted); | |
592 | ++ } | |
593 | ++#endif | |
594 | ++ /* Is there an active domain? */ | |
595 | ++ list1_for_each_entry(domain, &domain_list, list) { | |
596 | ++ struct domain_info *domain2; | |
597 | ++ /* Never delete KERNEL_DOMAIN */ | |
598 | ++ if (domain == &KERNEL_DOMAIN) | |
599 | ++ continue; | |
600 | ++ if (domain->is_deleted || | |
601 | ++ tmy_pathcmp(domain->domainname, &name)) | |
602 | ++ continue; | |
603 | ++ /* Mark already deleted domains as non undeletable. */ | |
604 | ++ list1_for_each_entry(domain2, &domain_list, list) { | |
605 | ++ if (!domain2->is_deleted || | |
606 | ++ tmy_pathcmp(domain2->domainname, &name)) | |
607 | ++ continue; | |
608 | ++#ifdef DEBUG_DOMAIN_UNDELETE | |
609 | ++ if (domain2->is_deleted != 255) | |
610 | ++ printk(KERN_DEBUG | |
611 | ++ "Marked %p as non undeletable\n", | |
612 | ++ domain2); | |
613 | ++#endif | |
614 | ++ domain2->is_deleted = 255; | |
615 | ++ } | |
616 | ++ /* Delete and mark active domain as undeletable. */ | |
617 | ++ domain->is_deleted = 1; | |
618 | ++#ifdef DEBUG_DOMAIN_UNDELETE | |
619 | ++ printk(KERN_DEBUG "Marked %p as undeletable\n", domain); | |
620 | ++#endif | |
621 | ++ break; | |
622 | ++ } | |
623 | ++ mutex_unlock(&domain_list_lock); | |
624 | ++ /***** EXCLUSIVE SECTION END *****/ | |
625 | ++ return 0; | |
626 | ++} | |
627 | ++ | |
628 | ++/** | |
629 | ++ * tmy_undelete_domain - Undelete a domain. | |
630 | ++ * | |
631 | ++ * @domainname: The name of domain. | |
632 | ++ * | |
633 | ++ * Returns pointer to "struct domain_info" on success, NULL otherwise. | |
634 | ++ */ | |
635 | ++struct domain_info *tmy_undelete_domain(const char *domainname) | |
636 | ++{ | |
637 | ++ struct domain_info *domain; | |
638 | ++ struct domain_info *candidate_domain = NULL; | |
639 | ++ struct path_info name; | |
640 | ++ | |
641 | ++ name.name = domainname; | |
642 | ++ tmy_fill_path_info(&name); | |
643 | ++ /***** EXCLUSIVE SECTION START *****/ | |
644 | ++ mutex_lock(&domain_list_lock); | |
645 | ++#ifdef DEBUG_DOMAIN_UNDELETE | |
646 | ++ printk(KERN_DEBUG "tmy_undelete_domain %s\n", domainname); | |
647 | ++ list1_for_each_entry(domain, &domain_list, list) { | |
648 | ++ if (tmy_pathcmp(domain->domainname, &name)) | |
649 | ++ continue; | |
650 | ++ printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted); | |
651 | ++ } | |
652 | ++#endif | |
653 | ++ list1_for_each_entry(domain, &domain_list, list) { | |
654 | ++ if (tmy_pathcmp(&name, domain->domainname)) | |
655 | ++ continue; | |
656 | ++ if (!domain->is_deleted) { | |
657 | ++ /* This domain is active. I can't undelete. */ | |
658 | ++ candidate_domain = NULL; | |
659 | ++#ifdef DEBUG_DOMAIN_UNDELETE | |
660 | ++ printk(KERN_DEBUG "%p is active. I can't undelete.\n", | |
661 | ++ domain); | |
662 | ++#endif | |
663 | ++ break; | |
664 | ++ } | |
665 | ++ /* Is this domain undeletable? */ | |
666 | ++ if (domain->is_deleted == 1) | |
667 | ++ candidate_domain = domain; | |
668 | ++ } | |
669 | ++ if (candidate_domain) { | |
670 | ++ candidate_domain->is_deleted = 0; | |
671 | ++#ifdef DEBUG_DOMAIN_UNDELETE | |
672 | ++ printk(KERN_DEBUG "%p was undeleted.\n", candidate_domain); | |
673 | ++#endif | |
674 | ++ } | |
675 | ++ mutex_unlock(&domain_list_lock); | |
676 | ++ /***** EXCLUSIVE SECTION END *****/ | |
677 | ++ return candidate_domain; | |
678 | ++} | |
679 | ++ | |
680 | ++/** | |
681 | ++ * tmy_find_or_assign_new_domain - Create a domain. | |
682 | ++ * | |
683 | ++ * @domainname: The name of domain. | |
684 | ++ * @profile: Profile number to assign if the domain was newly created. | |
685 | ++ * | |
686 | ++ * Returns pointer to "struct domain_info" on success, NULL otherwise. | |
687 | ++ */ | |
688 | ++struct domain_info *tmy_find_or_assign_new_domain(const char *domainname, | |
689 | ++ const u8 profile) | |
690 | ++{ | |
691 | ++ struct domain_info *domain = NULL; | |
692 | ++ const struct path_info *saved_domainname; | |
693 | ++ | |
694 | ++ /***** EXCLUSIVE SECTION START *****/ | |
695 | ++ mutex_lock(&domain_list_lock); | |
696 | ++ domain = tmy_find_domain(domainname); | |
697 | ++ if (domain) | |
698 | ++ goto out; | |
699 | ++ if (!tmy_is_correct_domain(domainname, __func__)) | |
700 | ++ goto out; | |
701 | ++ saved_domainname = tmy_save_name(domainname); | |
702 | ++ if (!saved_domainname) | |
703 | ++ goto out; | |
704 | ++ /* Can I reuse memory of deleted domain? */ | |
705 | ++ list1_for_each_entry(domain, &domain_list, list) { | |
706 | ++ struct task_struct *p; | |
707 | ++ struct acl_info *ptr; | |
708 | ++ bool flag; | |
709 | ++ if (!domain->is_deleted || | |
710 | ++ domain->domainname != saved_domainname) | |
711 | ++ continue; | |
712 | ++ flag = false; | |
713 | ++ /***** CRITICAL SECTION START *****/ | |
714 | ++ read_lock(&tasklist_lock); | |
715 | ++ for_each_process(p) { | |
716 | ++ if (tmy_real_domain(p) != domain) | |
717 | ++ continue; | |
718 | ++ flag = true; | |
719 | ++ break; | |
720 | ++ } | |
721 | ++ read_unlock(&tasklist_lock); | |
722 | ++ /***** CRITICAL SECTION END *****/ | |
723 | ++ if (flag) | |
724 | ++ continue; | |
725 | ++#ifdef DEBUG_DOMAIN_UNDELETE | |
726 | ++ printk(KERN_DEBUG "Reusing %p %s\n", domain, | |
727 | ++ domain->domainname->name); | |
728 | ++#endif | |
729 | ++ list1_for_each_entry(ptr, &domain->acl_info_list, list) { | |
730 | ++ ptr->type |= ACL_DELETED; | |
731 | ++ } | |
732 | ++ tmy_set_domain_flag(domain, true, domain->flags); | |
733 | ++ domain->profile = profile; | |
734 | ++ domain->quota_warned = false; | |
735 | ++ mb(); /* Avoid out-of-order execution. */ | |
736 | ++ domain->is_deleted = 0; | |
737 | ++ goto out; | |
738 | ++ } | |
739 | ++ /* No memory reusable. Create using new memory. */ | |
740 | ++ domain = tmy_alloc_element(sizeof(*domain)); | |
741 | ++ if (domain) { | |
742 | ++ INIT_LIST1_HEAD(&domain->acl_info_list); | |
743 | ++ domain->domainname = saved_domainname; | |
744 | ++ domain->profile = profile; | |
745 | ++ list1_add_tail(&domain->list, &domain_list); | |
746 | ++ } | |
747 | ++out: | |
748 | ++ mutex_unlock(&domain_list_lock); | |
749 | ++ /***** EXCLUSIVE SECTION END *****/ | |
750 | ++ return domain; | |
751 | ++} | |
752 | ++ | |
753 | ++/** | |
754 | ++ * tmy_find_next_domain - Find a domain. | |
755 | ++ * | |
756 | ++ * @bprm: Pointer to "struct linux_binprm". | |
757 | ++ * @next_domain: Pointer to pointer to "struct domain_info". | |
758 | ++ * | |
759 | ++ * Returns 0 on success, negative value otherwise. | |
760 | ++ */ | |
761 | ++int tmy_find_next_domain(struct linux_binprm *bprm, | |
762 | ++ struct domain_info **next_domain) | |
763 | ++{ | |
764 | ++ /* | |
765 | ++ * This function assumes that the size of buffer returned by | |
766 | ++ * tmy_realpath() = TMY_MAX_PATHNAME_LEN. | |
767 | ++ */ | |
768 | ++ struct tmy_page_buffer *tmp = tmy_alloc(sizeof(*tmp)); | |
769 | ++ struct domain_info *old_domain = tmy_domain(); | |
770 | ++ struct domain_info *domain = NULL; | |
771 | ++ const char *old_domain_name = old_domain->domainname->name; | |
772 | ++ const char *original_name = bprm->filename; | |
773 | ++ char *new_domain_name = NULL; | |
774 | ++ char *real_program_name = NULL; | |
775 | ++ char *symlink_program_name = NULL; | |
776 | ++ const u8 mode = tmy_check_flags(old_domain, TMY_TOMOYO_MAC_FOR_FILE); | |
777 | ++ const bool is_enforce = (mode == 3); | |
778 | ++ int retval = -ENOMEM; | |
779 | ++ struct path_info r; /* real name */ | |
780 | ++ struct path_info s; /* symlink name */ | |
781 | ++ struct path_info l; /* last name */ | |
782 | ++ | |
783 | ++ if (!tmp) | |
784 | ++ goto out; | |
785 | ++ | |
786 | ++ { | |
787 | ++ /* | |
788 | ++ * Built-in initializers. This is needed because policies are | |
789 | ++ * not loaded until starting /sbin/init. | |
790 | ++ */ | |
791 | ++ static bool first = true; | |
792 | ++ if (first) { | |
793 | ++ update_domain_initializer_entry(NULL, "/sbin/hotplug", | |
794 | ++ false, false); | |
795 | ++ update_domain_initializer_entry(NULL, "/sbin/modprobe", | |
796 | ++ false, false); | |
797 | ++ first = false; | |
798 | ++ } | |
799 | ++ } | |
800 | ++ | |
801 | ++ /* Get tmy_realpath of program. */ | |
802 | ++ retval = -ENOENT; /* I hope tmy_realpath() won't fail with -ENOMEM. */ | |
803 | ++ real_program_name = tmy_realpath(original_name); | |
804 | ++ if (!real_program_name) | |
805 | ++ goto out; | |
806 | ++ /* Get tmy_realpath of symbolic link. */ | |
807 | ++ symlink_program_name = tmy_realpath_nofollow(original_name); | |
808 | ++ if (!symlink_program_name) | |
809 | ++ goto out; | |
810 | ++ | |
811 | ++ r.name = real_program_name; | |
812 | ++ tmy_fill_path_info(&r); | |
813 | ++ s.name = symlink_program_name; | |
814 | ++ tmy_fill_path_info(&s); | |
815 | ++ l.name = tmy_get_last_name(old_domain); | |
816 | ++ tmy_fill_path_info(&l); | |
817 | ++ | |
818 | ++ /* Check 'alias' directive. */ | |
819 | ++ if (tmy_pathcmp(&r, &s)) { | |
820 | ++ struct alias_entry *ptr; | |
821 | ++ /* Is this program allowed to be called via symbolic links? */ | |
822 | ++ list1_for_each_entry(ptr, &alias_list, list) { | |
823 | ++ if (ptr->is_deleted || | |
824 | ++ tmy_pathcmp(&r, ptr->original_name) || | |
825 | ++ tmy_pathcmp(&s, ptr->aliased_name)) | |
826 | ++ continue; | |
827 | ++ memset(real_program_name, 0, TMY_MAX_PATHNAME_LEN); | |
828 | ++ strncpy(real_program_name, ptr->aliased_name->name, | |
829 | ++ TMY_MAX_PATHNAME_LEN - 1); | |
830 | ++ tmy_fill_path_info(&r); | |
831 | ++ break; | |
832 | ++ } | |
833 | ++ } | |
834 | ++ | |
835 | ++ /* Check execute permission. */ | |
836 | ++ retval = tmy_check_exec_perm(old_domain, &r, tmp); | |
837 | ++ if (retval < 0) | |
838 | ++ goto out; | |
839 | ++ | |
840 | ++ new_domain_name = tmp->buffer; | |
841 | ++ if (is_domain_initializer(old_domain->domainname, &r, &l)) { | |
842 | ++ /* Transit to the child of KERNEL_DOMAIN domain. */ | |
843 | ++ snprintf(new_domain_name, TMY_MAX_PATHNAME_LEN + 1, | |
844 | ++ ROOT_NAME " " "%s", real_program_name); | |
845 | ++ } else if (old_domain == &KERNEL_DOMAIN && !sbin_init_started) { | |
846 | ++ /* | |
847 | ++ * Needn't to transit from kernel domain before starting | |
848 | ++ * /sbin/init. But transit from kernel domain if executing | |
849 | ++ * initializers because they might start before /sbin/init. | |
850 | ++ */ | |
851 | ++ domain = old_domain; | |
852 | ++ } else if (is_domain_keeper(old_domain->domainname, &r, &l)) { | |
853 | ++ /* Keep current domain. */ | |
854 | ++ domain = old_domain; | |
855 | ++ } else { | |
856 | ++ /* Normal domain transition. */ | |
857 | ++ snprintf(new_domain_name, TMY_MAX_PATHNAME_LEN + 1, | |
858 | ++ "%s %s", old_domain_name, real_program_name); | |
859 | ++ } | |
860 | ++ if (domain || strlen(new_domain_name) >= TMY_MAX_PATHNAME_LEN) | |
861 | ++ goto done; | |
862 | ++ domain = tmy_find_domain(new_domain_name); | |
863 | ++ if (domain) | |
864 | ++ goto done; | |
865 | ++ if (is_enforce) | |
866 | ++ goto done; | |
867 | ++ domain = tmy_find_or_assign_new_domain(new_domain_name, | |
868 | ++ old_domain->profile); | |
869 | ++done: | |
870 | ++ if (domain) | |
871 | ++ goto out; | |
872 | ++ printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", | |
873 | ++ new_domain_name); | |
874 | ++ if (is_enforce) | |
875 | ++ retval = -EPERM; | |
876 | ++ else | |
877 | ++ tmy_set_domain_flag(old_domain, false, | |
878 | ++ DOMAIN_FLAGS_TRANSITION_FAILED); | |
879 | ++out: | |
880 | ++ tmy_free(real_program_name); | |
881 | ++ tmy_free(symlink_program_name); | |
882 | ++ *next_domain = domain ? domain : old_domain; | |
883 | ++ tmy_free(tmp); | |
884 | ++ return retval; | |
885 | ++} |
@@ -0,0 +1,497 @@ | ||
1 | +Subject: LSM adapter functions. | |
2 | + | |
3 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
4 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
5 | +Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp> | |
6 | +--- | |
7 | + security/tomoyo/tomoyo.c | 376 +++++++++++++++++++++++++++++++++++++++++++++++ | |
8 | + security/tomoyo/tomoyo.h | 105 +++++++++++++ | |
9 | + 2 files changed, 481 insertions(+) | |
10 | + | |
11 | +--- /dev/null | |
12 | ++++ linux-next/security/tomoyo/tomoyo.c | |
13 | +@@ -0,0 +1,376 @@ | |
14 | ++/* | |
15 | ++ * security/tomoyo/tomoyo.c | |
16 | ++ * | |
17 | ++ * LSM hooks for TOMOYO Linux. | |
18 | ++ * | |
19 | ++ * Copyright (C) 2005-2008 NTT DATA CORPORATION | |
20 | ++ * | |
21 | ++ * Version: 2.2.0-pre 2008/10/10 | |
22 | ++ * | |
23 | ++ */ | |
24 | ++ | |
25 | ++#include <linux/security.h> | |
26 | ++#include "common.h" | |
27 | ++#include "tomoyo.h" | |
28 | ++#include "realpath.h" | |
29 | ++ | |
30 | ++static int tmy_cred_prepare(struct cred *new, const struct cred *old, gfp_t gfp) | |
31 | ++{ | |
32 | ++ /* | |
33 | ++ * Since "struct domain_info *" is a sharable pointer, we don't need | |
34 | ++ * to duplicate. | |
35 | ++ */ | |
36 | ++ new->security = old->security; | |
37 | ++ return 0; | |
38 | ++} | |
39 | ++ | |
40 | ++static int tmy_bprm_set_creds(struct linux_binprm *bprm) | |
41 | ++{ | |
42 | ++ /* | |
43 | ++ * Do only if this function is called for the first time of an execve | |
44 | ++ * operation. | |
45 | ++ */ | |
46 | ++ if (bprm->cred_prepared) | |
47 | ++ return 0; | |
48 | ++ /* | |
49 | ++ * Load policy if /sbin/tomoyo-init exists and /sbin/init is requested | |
50 | ++ * for the first time. | |
51 | ++ */ | |
52 | ++ if (!sbin_init_started) | |
53 | ++ tmy_load_policy(bprm->filename); | |
54 | ++ /* | |
55 | ++ * Tell tmy_bprm_check_security() is called for the first time of an | |
56 | ++ * execve operation. | |
57 | ++ */ | |
58 | ++ bprm->cred->security = NULL; | |
59 | ++ return 0; | |
60 | ++} | |
61 | ++ | |
62 | ++static int tmy_bprm_check_security(struct linux_binprm *bprm) | |
63 | ++{ | |
64 | ++ struct domain_info *domain = bprm->cred->security; | |
65 | ++ | |
66 | ++ /* | |
67 | ++ * Execute permission is checked against pathname passed to do_execve() | |
68 | ++ * using current domain. | |
69 | ++ */ | |
70 | ++ if (!domain) { | |
71 | ++ struct domain_info *next_domain = NULL; | |
72 | ++ int retval = tmy_find_next_domain(bprm, &next_domain); | |
73 | ++ | |
74 | ++ if (!retval) | |
75 | ++ bprm->cred->security = next_domain; | |
76 | ++ return retval; | |
77 | ++ } | |
78 | ++ /* | |
79 | ++ * Read permission is checked against interpreters using next domain. | |
80 | ++ * '1' is the result of open_to_namei_flags(O_RDONLY). | |
81 | ++ */ | |
82 | ++ return tmy_check_open_permission(domain, &bprm->file->f_path, 1); | |
83 | ++} | |
84 | ++ | |
85 | ++static int tmy_sysctl(struct ctl_table *table, int op) | |
86 | ++{ | |
87 | ++ int error; | |
88 | ++ char *name; | |
89 | ++ | |
90 | ++ op &= MAY_READ | MAY_WRITE; | |
91 | ++ if (!op) | |
92 | ++ return 0; | |
93 | ++ name = sysctlpath_from_table(table); | |
94 | ++ if (!name) | |
95 | ++ return -ENOMEM; | |
96 | ++ error = tmy_check_file_perm(tmy_domain(), name, op); | |
97 | ++ tmy_free(name); | |
98 | ++ return error; | |
99 | ++} | |
100 | ++ | |
101 | ++/** | |
102 | ++ * tmy_update_result - Update error code. | |
103 | ++ * | |
104 | ++ * @error: Return code from security_path_*(). | |
105 | ++ * | |
106 | ++ * To be able to return DAC's error (if any) to the caller instead of | |
107 | ++ * MAC's error, we don't return MAC's error at security_path_*(). | |
108 | ++ * | |
109 | ++ * We remember MAC's error only if security_path_*() returned an error. | |
110 | ++ * | |
111 | ++ * Returns 0 on success, -ENOMEM otherwise if @error != 0. | |
112 | ++ * Returns previously saved error code and clears it if @error == 0. | |
113 | ++ */ | |
114 | ++static int tmy_update_result(int error) | |
115 | ++{ | |
116 | ++ /* Structure for holding the result of security_path_*(). */ | |
117 | ++ struct check_result_entry { | |
118 | ++ struct list_head list; | |
119 | ++ struct task_struct *task; /* = current */ | |
120 | ++ int error; /* != 0 */ | |
121 | ++ }; | |
122 | ++ static LIST_HEAD(list); | |
123 | ++ static DEFINE_SPINLOCK(lock); | |
124 | ++ struct task_struct *task = current; | |
125 | ++ struct check_result_entry *entry; | |
126 | ++ if (!error) { | |
127 | ++ if (!list_empty(&list)) { | |
128 | ++ struct check_result_entry *p; | |
129 | ++ entry = NULL; | |
130 | ++ /***** CRITICAL SECTION START *****/ | |
131 | ++ spin_lock(&lock); | |
132 | ++ list_for_each_entry(p, &list, list) { | |
133 | ++ if (p->task != task) | |
134 | ++ continue; | |
135 | ++ list_del(&p->list); | |
136 | ++ entry = p; | |
137 | ++ break; | |
138 | ++ } | |
139 | ++ spin_unlock(&lock); | |
140 | ++ /***** CRITICAL SECTION END *****/ | |
141 | ++ if (entry) { | |
142 | ++ error = entry->error; | |
143 | ++ kfree(entry); | |
144 | ++ } | |
145 | ++ } | |
146 | ++ return error; | |
147 | ++ } | |
148 | ++ entry = kmalloc(sizeof(*entry), GFP_KERNEL); | |
149 | ++ if (!entry) | |
150 | ++ return -ENOMEM; | |
151 | ++ entry->task = task; | |
152 | ++ entry->error = error; | |
153 | ++ /***** CRITICAL SECTION START *****/ | |
154 | ++ spin_lock(&lock); | |
155 | ++ list_add(&entry->list, &list); | |
156 | ++ spin_unlock(&lock); | |
157 | ++ /***** CRITICAL SECTION END *****/ | |
158 | ++ return 0; | |
159 | ++} | |
160 | ++ | |
161 | ++/** | |
162 | ++ * tmy_save_result - Remember error code for security_inode_*() if any. | |
163 | ++ * | |
164 | ++ * @error: Return code from security_path_*(). | |
165 | ++ * | |
166 | ++ * Returns 0 on success, -ENOMEM otherwise. | |
167 | ++ * | |
168 | ++ * We don't save if @error == 0. | |
169 | ++ */ | |
170 | ++static int tmy_save_result(const int error) | |
171 | ++{ | |
172 | ++ return error ? tmy_update_result(error) : 0; | |
173 | ++} | |
174 | ++ | |
175 | ++/** | |
176 | ++ * tmy_load_result - Fetch error code for security_inode_*(). | |
177 | ++ * | |
178 | ++ * Returns error code saved by security_path_*(). | |
179 | ++ */ | |
180 | ++static int tmy_load_result(void) | |
181 | ++{ | |
182 | ++ return tmy_update_result(0); | |
183 | ++} | |
184 | ++ | |
185 | ++/* Clear error code in case security_inode_*() was not called. */ | |
186 | ++static void tmy_path_clear(void) | |
187 | ++{ | |
188 | ++ tmy_load_result(); | |
189 | ++} | |
190 | ++ | |
191 | ++static int tmy_path_truncate(struct path *path, loff_t length, | |
192 | ++ unsigned int time_attrs, struct file *filp) | |
193 | ++{ | |
194 | ++ return tmy_save_result(tmy_check_1path_perm(tmy_domain(), | |
195 | ++ TMY_TYPE_TRUNCATE_ACL, | |
196 | ++ path)); | |
197 | ++} | |
198 | ++ | |
199 | ++static int tmy_path_unlink(struct path *parent, struct dentry *dentry) | |
200 | ++{ | |
201 | ++ struct path path = { parent->mnt, dentry }; | |
202 | ++ return tmy_save_result(tmy_check_1path_perm(tmy_domain(), | |
203 | ++ TMY_TYPE_UNLINK_ACL, | |
204 | ++ &path)); | |
205 | ++} | |
206 | ++ | |
207 | ++static int tmy_path_mkdir(struct path *parent, struct dentry *dentry, int mode) | |
208 | ++{ | |
209 | ++ struct path path = { parent->mnt, dentry }; | |
210 | ++ return tmy_save_result(tmy_check_1path_perm(tmy_domain(), | |
211 | ++ TMY_TYPE_MKDIR_ACL, | |
212 | ++ &path)); | |
213 | ++} | |
214 | ++ | |
215 | ++static int tmy_path_rmdir(struct path *parent, struct dentry *dentry) | |
216 | ++{ | |
217 | ++ struct path path = { parent->mnt, dentry }; | |
218 | ++ return tmy_save_result(tmy_check_1path_perm(tmy_domain(), | |
219 | ++ TMY_TYPE_RMDIR_ACL, | |
220 | ++ &path)); | |
221 | ++} | |
222 | ++ | |
223 | ++static int tmy_path_symlink(struct path *parent, struct dentry *dentry, | |
224 | ++ const char *old_name) | |
225 | ++{ | |
226 | ++ struct path path = { parent->mnt, dentry }; | |
227 | ++ return tmy_save_result(tmy_check_1path_perm(tmy_domain(), | |
228 | ++ TMY_TYPE_SYMLINK_ACL, | |
229 | ++ &path)); | |
230 | ++} | |
231 | ++ | |
232 | ++static int tmy_path_mknod(struct path *parent, struct dentry *dentry, int mode, | |
233 | ++ unsigned int dev) | |
234 | ++{ | |
235 | ++ struct path path = { parent->mnt, dentry }; | |
236 | ++ int type = TMY_TYPE_CREATE_ACL; | |
237 | ++ | |
238 | ++ switch (mode & S_IFMT) { | |
239 | ++ case S_IFCHR: | |
240 | ++ type = TMY_TYPE_MKCHAR_ACL; | |
241 | ++ break; | |
242 | ++ case S_IFBLK: | |
243 | ++ type = TMY_TYPE_MKBLOCK_ACL; | |
244 | ++ break; | |
245 | ++ case S_IFIFO: | |
246 | ++ type = TMY_TYPE_MKFIFO_ACL; | |
247 | ++ break; | |
248 | ++ case S_IFSOCK: | |
249 | ++ type = TMY_TYPE_MKSOCK_ACL; | |
250 | ++ break; | |
251 | ++ } | |
252 | ++ return tmy_save_result(tmy_check_1path_perm(tmy_domain(), | |
253 | ++ type, &path)); | |
254 | ++} | |
255 | ++ | |
256 | ++static int tmy_path_link(struct dentry *old_dentry, struct path *new_dir, | |
257 | ++ struct dentry *new_dentry) | |
258 | ++{ | |
259 | ++ struct path path1 = { new_dir->mnt, old_dentry }; | |
260 | ++ struct path path2 = { new_dir->mnt, new_dentry }; | |
261 | ++ return tmy_save_result(tmy_check_2path_perm(tmy_domain(), | |
262 | ++ TMY_TYPE_LINK_ACL, | |
263 | ++ &path1, &path2)); | |
264 | ++} | |
265 | ++ | |
266 | ++static int tmy_path_rename(struct path *old_parent, struct dentry *old_dentry, | |
267 | ++ struct path *new_parent, struct dentry *new_dentry) | |
268 | ++{ | |
269 | ++ struct path path1 = { old_parent->mnt, old_dentry }; | |
270 | ++ struct path path2 = { new_parent->mnt, new_dentry }; | |
271 | ++ return tmy_save_result(tmy_check_2path_perm(tmy_domain(), | |
272 | ++ TMY_TYPE_RENAME_ACL, | |
273 | ++ &path1, &path2)); | |
274 | ++} | |
275 | ++ | |
276 | ++static int tmy_inode_link(struct dentry *old_dentry, struct inode *inode, | |
277 | ++ struct dentry *new_dentry) | |
278 | ++{ | |
279 | ++ return tmy_load_result(); | |
280 | ++} | |
281 | ++ | |
282 | ++static int tmy_inode_unlink(struct inode *inode, struct dentry *dentry) | |
283 | ++{ | |
284 | ++ return tmy_load_result(); | |
285 | ++} | |
286 | ++ | |
287 | ++static int tmy_inode_symlink(struct inode *inode, struct dentry *dentry, | |
288 | ++ const char *name) | |
289 | ++{ | |
290 | ++ return tmy_load_result(); | |
291 | ++} | |
292 | ++ | |
293 | ++static int tmy_inode_mkdir(struct inode *inode, struct dentry *dentry, | |
294 | ++ int mask) | |
295 | ++{ | |
296 | ++ return tmy_load_result(); | |
297 | ++} | |
298 | ++ | |
299 | ++static int tmy_inode_rmdir(struct inode *inode, struct dentry *dentry) | |
300 | ++{ | |
301 | ++ return tmy_load_result(); | |
302 | ++} | |
303 | ++ | |
304 | ++static int tmy_inode_create(struct inode *dir, struct dentry *dentry, int mode) | |
305 | ++{ | |
306 | ++ return tmy_load_result(); | |
307 | ++} | |
308 | ++ | |
309 | ++static int tmy_inode_mknod(struct inode *inode, struct dentry *dentry, | |
310 | ++ int mode, dev_t dev) | |
311 | ++{ | |
312 | ++ return tmy_load_result(); | |
313 | ++} | |
314 | ++ | |
315 | ++static int tmy_inode_rename(struct inode *old_inode, struct dentry *old_dentry, | |
316 | ++ struct inode *new_inode, struct dentry *new_dentry) | |
317 | ++{ | |
318 | ++ return tmy_load_result(); | |
319 | ++} | |
320 | ++ | |
321 | ++static int tmy_inode_setattr(struct dentry *dentry, struct iattr *iattr) | |
322 | ++{ | |
323 | ++ return tmy_load_result(); | |
324 | ++} | |
325 | ++ | |
326 | ++static int tmy_file_fcntl(struct file *file, unsigned int cmd, | |
327 | ++ unsigned long arg) | |
328 | ++{ | |
329 | ++ if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND)) | |
330 | ++ return tmy_check_rewrite_permission(tmy_domain(), file); | |
331 | ++ return 0; | |
332 | ++} | |
333 | ++ | |
334 | ++static int tmy_dentry_open(struct file *f, const struct cred *cred) | |
335 | ++{ | |
336 | ++ int flags = f->f_flags; | |
337 | ++ | |
338 | ++ if ((flags + 1) & O_ACCMODE) | |
339 | ++ flags++; | |
340 | ++ flags |= f->f_flags & (O_APPEND | O_TRUNC); | |
341 | ++ /* Don't check read permission here if called from do_execve(). */ | |
342 | ++ if (current->in_execve) | |
343 | ++ return 0; | |
344 | ++ return tmy_check_open_permission(tmy_domain(), &f->f_path, flags); | |
345 | ++} | |
346 | ++ | |
347 | ++static struct security_operations tomoyo_security_ops = { | |
348 | ++ .name = "tomoyo", | |
349 | ++ .cred_prepare = tmy_cred_prepare, | |
350 | ++ .bprm_set_creds = tmy_bprm_set_creds, | |
351 | ++ .bprm_check_security = tmy_bprm_check_security, | |
352 | ++ .sysctl = tmy_sysctl, | |
353 | ++ .file_fcntl = tmy_file_fcntl, | |
354 | ++ .dentry_open = tmy_dentry_open, | |
355 | ++ .path_truncate = tmy_path_truncate, | |
356 | ++ .path_unlink = tmy_path_unlink, | |
357 | ++ .path_mkdir = tmy_path_mkdir, | |
358 | ++ .path_rmdir = tmy_path_rmdir, | |
359 | ++ .path_symlink = tmy_path_symlink, | |
360 | ++ .path_mknod = tmy_path_mknod, | |
361 | ++ .path_link = tmy_path_link, | |
362 | ++ .path_rename = tmy_path_rename, | |
363 | ++ .inode_create = tmy_inode_create, | |
364 | ++ .inode_setattr = tmy_inode_setattr, | |
365 | ++ .inode_unlink = tmy_inode_unlink, | |
366 | ++ .inode_mkdir = tmy_inode_mkdir, | |
367 | ++ .inode_rmdir = tmy_inode_rmdir, | |
368 | ++ .inode_symlink = tmy_inode_symlink, | |
369 | ++ .inode_mknod = tmy_inode_mknod, | |
370 | ++ .inode_link = tmy_inode_link, | |
371 | ++ .inode_rename = tmy_inode_rename, | |
372 | ++ .path_clear = tmy_path_clear, | |
373 | ++}; | |
374 | ++ | |
375 | ++static int __init tmy_init(void) | |
376 | ++{ | |
377 | ++ struct cred *cred = (struct cred *) current_cred(); | |
378 | ++ | |
379 | ++ if (!security_module_enable(&tomoyo_security_ops)) | |
380 | ++ return 0; | |
381 | ++ /* register ourselves with the security framework */ | |
382 | ++ if (register_security(&tomoyo_security_ops)) | |
383 | ++ panic("Failure registering TOMOYO Linux"); | |
384 | ++ printk(KERN_INFO "TOMOYO Linux initialized\n"); | |
385 | ++ cred->security = &KERNEL_DOMAIN; | |
386 | ++ return 0; | |
387 | ++} | |
388 | ++ | |
389 | ++security_initcall(tmy_init); | |
390 | +--- /dev/null | |
391 | ++++ linux-next/security/tomoyo/tomoyo.h | |
392 | +@@ -0,0 +1,105 @@ | |
393 | ++/* | |
394 | ++ * security/tomoyo/tomoyo.h | |
395 | ++ * | |
396 | ++ * Implementation of the Domain-Based Mandatory Access Control. | |
397 | ++ * | |
398 | ++ * Copyright (C) 2005-2008 NTT DATA CORPORATION | |
399 | ++ * | |
400 | ++ * Version: 2.2.0-pre 2008/10/10 | |
401 | ++ * | |
402 | ++ */ | |
403 | ++ | |
404 | ++#ifndef _SECURITY_TOMOYO_TOMOYO_H | |
405 | ++#define _SECURITY_TOMOYO_TOMOYO_H | |
406 | ++ | |
407 | ++struct path_info; | |
408 | ++struct path; | |
409 | ++struct inode; | |
410 | ++struct linux_binprm; | |
411 | ++struct pt_regs; | |
412 | ++struct tmy_page_buffer; | |
413 | ++ | |
414 | ++int tmy_check_file_perm(struct domain_info *domain, const char *filename, | |
415 | ++ const u8 perm); | |
416 | ++int tmy_check_exec_perm(struct domain_info *domain, | |
417 | ++ const struct path_info *filename, | |
418 | ++ struct tmy_page_buffer *buf); | |
419 | ++int tmy_check_open_permission(struct domain_info *domain, struct path *path, | |
420 | ++ const int flag); | |
421 | ++int tmy_check_1path_perm(struct domain_info *domain, const u8 operation, | |
422 | ++ struct path *path); | |
423 | ++int tmy_check_2path_perm(struct domain_info *domain, const u8 operation, | |
424 | ++ struct path *path1, struct path *path2); | |
425 | ++int tmy_check_rewrite_permission(struct domain_info *domain, | |
426 | ++ struct file *filp); | |
427 | ++int tmy_find_next_domain(struct linux_binprm *bprm, | |
428 | ++ struct domain_info **next_domain); | |
429 | ++ | |
430 | ++/* Index numbers for Access Controls. */ | |
431 | ++ | |
432 | ++#define TYPE_SINGLE_PATH_ACL 0 | |
433 | ++#define TYPE_DOUBLE_PATH_ACL 1 | |
434 | ++ | |
435 | ++/* Index numbers for File Controls. */ | |
436 | ++ | |
437 | ++/* | |
438 | ++ * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set | |
439 | ++ * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and | |
440 | ++ * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set. | |
441 | ++ * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or | |
442 | ++ * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are | |
443 | ++ * automatically cleared if TYPE_READ_WRITE_ACL is cleared. | |
444 | ++ */ | |
445 | ++ | |
446 | ++#define TMY_TYPE_READ_WRITE_ACL 0 | |
447 | ++#define TMY_TYPE_EXECUTE_ACL 1 | |
448 | ++#define TMY_TYPE_READ_ACL 2 | |
449 | ++#define TMY_TYPE_WRITE_ACL 3 | |
450 | ++#define TMY_TYPE_CREATE_ACL 4 | |
451 | ++#define TMY_TYPE_UNLINK_ACL 5 | |
452 | ++#define TMY_TYPE_MKDIR_ACL 6 | |
453 | ++#define TMY_TYPE_RMDIR_ACL 7 | |
454 | ++#define TMY_TYPE_MKFIFO_ACL 8 | |
455 | ++#define TMY_TYPE_MKSOCK_ACL 9 | |
456 | ++#define TMY_TYPE_MKBLOCK_ACL 10 | |
457 | ++#define TMY_TYPE_MKCHAR_ACL 11 | |
458 | ++#define TMY_TYPE_TRUNCATE_ACL 12 | |
459 | ++#define TMY_TYPE_SYMLINK_ACL 13 | |
460 | ++#define TMY_TYPE_REWRITE_ACL 14 | |
461 | ++#define MAX_SINGLE_PATH_OPERATION 15 | |
462 | ++ | |
463 | ++#define TMY_TYPE_LINK_ACL 0 | |
464 | ++#define TMY_TYPE_RENAME_ACL 1 | |
465 | ++#define MAX_DOUBLE_PATH_OPERATION 2 | |
466 | ++ | |
467 | ++#define TMY_DOMAINPOLICY 0 | |
468 | ++#define TMY_EXCEPTIONPOLICY 1 | |
469 | ++#define TMY_DOMAIN_STATUS 2 | |
470 | ++#define TMY_PROCESS_STATUS 3 | |
471 | ++#define TMY_MEMINFO 4 | |
472 | ++#define TMY_SELFDOMAIN 5 | |
473 | ++#define TMY_VERSION 6 | |
474 | ++#define TMY_PROFILE 7 | |
475 | ++#define TMY_MANAGER 8 | |
476 | ++#define TMY_UPDATESCOUNTER 9 | |
477 | ++ | |
478 | ++extern struct domain_info KERNEL_DOMAIN; | |
479 | ++ | |
480 | ++static inline struct domain_info *tmy_domain(void) | |
481 | ++{ | |
482 | ++ return current_cred()->security; | |
483 | ++} | |
484 | ++ | |
485 | ++/* Caller holds tasklist_lock spinlock. */ | |
486 | ++static inline struct domain_info *tmy_real_domain(struct task_struct *task) | |
487 | ++{ | |
488 | ++ /***** CRITICAL SECTION START *****/ | |
489 | ++ const struct cred *cred = get_task_cred(task); | |
490 | ++ struct domain_info *domain = cred->security; | |
491 | ++ | |
492 | ++ put_cred(cred); | |
493 | ++ return domain; | |
494 | ++ /***** CRITICAL SECTION END *****/ | |
495 | ++} | |
496 | ++ | |
497 | ++#endif /* !defined(_SECURITY_TOMOYO_TOMOYO_H) */ |
@@ -0,0 +1,1246 @@ | ||
1 | +Subject: File operation restriction part. | |
2 | + | |
3 | +This file controls file related operations. | |
4 | + | |
5 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
6 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
7 | +Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp> | |
8 | +--- | |
9 | + security/tomoyo/file.c | 1232 +++++++++++++++++++++++++++++++++++++++++++++++++ | |
10 | + 1 file changed, 1232 insertions(+) | |
11 | + | |
12 | +--- /dev/null | |
13 | ++++ linux-next/security/tomoyo/file.c | |
14 | +@@ -0,0 +1,1232 @@ | |
15 | ++/* | |
16 | ++ * security/tomoyo/file.c | |
17 | ++ * | |
18 | ++ * Implementation of the Domain-Based Mandatory Access Control. | |
19 | ++ * | |
20 | ++ * Copyright (C) 2005-2008 NTT DATA CORPORATION | |
21 | ++ * | |
22 | ++ * Version: 2.2.0-pre 2008/10/10 | |
23 | ++ * | |
24 | ++ */ | |
25 | ++ | |
26 | ++#include "common.h" | |
27 | ++#include "tomoyo.h" | |
28 | ++#include "realpath.h" | |
29 | ++#define ACC_MODE(x) ("\000\004\002\006"[(x)&O_ACCMODE]) | |
30 | ++ | |
31 | ++/* Structure for "allow_read" keyword. */ | |
32 | ++struct globally_readable_file_entry { | |
33 | ++ struct list1_head list; | |
34 | ++ const struct path_info *filename; | |
35 | ++ bool is_deleted; | |
36 | ++}; | |
37 | ++ | |
38 | ++/* Structure for "file_pattern" keyword. */ | |
39 | ++struct pattern_entry { | |
40 | ++ struct list1_head list; | |
41 | ++ const struct path_info *pattern; | |
42 | ++ bool is_deleted; | |
43 | ++}; | |
44 | ++ | |
45 | ++/* Structure for "deny_rewrite" keyword. */ | |
46 | ++struct no_rewrite_entry { | |
47 | ++ struct list1_head list; | |
48 | ++ const struct path_info *pattern; | |
49 | ++ bool is_deleted; | |
50 | ++}; | |
51 | ++ | |
52 | ++/* Keyword array for single path operations. */ | |
53 | ++static const char *sp_keyword[MAX_SINGLE_PATH_OPERATION] = { | |
54 | ++ [TMY_TYPE_READ_WRITE_ACL] = "read/write", | |
55 | ++ [TMY_TYPE_EXECUTE_ACL] = "execute", | |
56 | ++ [TMY_TYPE_READ_ACL] = "read", | |
57 | ++ [TMY_TYPE_WRITE_ACL] = "write", | |
58 | ++ [TMY_TYPE_CREATE_ACL] = "create", | |
59 | ++ [TMY_TYPE_UNLINK_ACL] = "unlink", | |
60 | ++ [TMY_TYPE_MKDIR_ACL] = "mkdir", | |
61 | ++ [TMY_TYPE_RMDIR_ACL] = "rmdir", | |
62 | ++ [TMY_TYPE_MKFIFO_ACL] = "mkfifo", | |
63 | ++ [TMY_TYPE_MKSOCK_ACL] = "mksock", | |
64 | ++ [TMY_TYPE_MKBLOCK_ACL] = "mkblock", | |
65 | ++ [TMY_TYPE_MKCHAR_ACL] = "mkchar", | |
66 | ++ [TMY_TYPE_TRUNCATE_ACL] = "truncate", | |
67 | ++ [TMY_TYPE_SYMLINK_ACL] = "symlink", | |
68 | ++ [TMY_TYPE_REWRITE_ACL] = "rewrite", | |
69 | ++}; | |
70 | ++ | |
71 | ++/* Keyword array for double path operations. */ | |
72 | ++static const char *dp_keyword[MAX_DOUBLE_PATH_OPERATION] = { | |
73 | ++ [TMY_TYPE_LINK_ACL] = "link", | |
74 | ++ [TMY_TYPE_RENAME_ACL] = "rename", | |
75 | ++}; | |
76 | ++ | |
77 | ++/** | |
78 | ++ * tmy_sp2keyword - Get the name of single path operation. | |
79 | ++ * | |
80 | ++ * @operation: Type of operation. | |
81 | ++ * | |
82 | ++ * Returns the name of single path operation. | |
83 | ++ */ | |
84 | ++const char *tmy_sp2keyword(const u8 operation) | |
85 | ++{ | |
86 | ++ return (operation < MAX_SINGLE_PATH_OPERATION) | |
87 | ++ ? sp_keyword[operation] : NULL; | |
88 | ++} | |
89 | ++ | |
90 | ++/** | |
91 | ++ * tmy_dp2keyword - Get the name of double path operation. | |
92 | ++ * | |
93 | ++ * @operation: Type of operation. | |
94 | ++ * | |
95 | ++ * Returns the name of double path operation. | |
96 | ++ */ | |
97 | ++const char *tmy_dp2keyword(const u8 operation) | |
98 | ++{ | |
99 | ++ return (operation < MAX_DOUBLE_PATH_OPERATION) | |
100 | ++ ? dp_keyword[operation] : NULL; | |
101 | ++} | |
102 | ++ | |
103 | ++/** | |
104 | ++ * strendswith - Check whether the token ends with the given token. | |
105 | ++ * | |
106 | ++ * @name: The token to check. | |
107 | ++ * @tail: The token to find. | |
108 | ++ * | |
109 | ++ * Returns true if @name ends with @tail, false otherwise. | |
110 | ++ */ | |
111 | ++static bool strendswith(const char *name, const char *tail) | |
112 | ++{ | |
113 | ++ int len; | |
114 | ++ | |
115 | ++ if (!name || !tail) | |
116 | ++ return false; | |
117 | ++ len = strlen(name) - strlen(tail); | |
118 | ++ return len >= 0 && !strcmp(name + len, tail); | |
119 | ++} | |
120 | ++ | |
121 | ++/** | |
122 | ++ * tmy_get_path - Get realpath. | |
123 | ++ * | |
124 | ++ * @path: Pointer to "struct path". | |
125 | ++ * | |
126 | ++ * Returns pointer to "struct path_info" on success, NULL otherwise. | |
127 | ++ */ | |
128 | ++static struct path_info *tmy_get_path(struct path *path) | |
129 | ++{ | |
130 | ++ int error; | |
131 | ++ struct path_info_with_data *buf = tmy_alloc(sizeof(*buf)); | |
132 | ++ | |
133 | ++ if (!buf) | |
134 | ++ return NULL; | |
135 | ++ /* Preserve one byte for appending "/". */ | |
136 | ++ error = tmy_realpath_from_path2(path, buf->body, | |
137 | ++ sizeof(buf->body) - 2); | |
138 | ++ if (!error) { | |
139 | ++ buf->head.name = buf->body; | |
140 | ++ tmy_fill_path_info(&buf->head); | |
141 | ++ return &buf->head; | |
142 | ++ } | |
143 | ++ tmy_free(buf); | |
144 | ++ return NULL; | |
145 | ++} | |
146 | ++ | |
147 | ++static int update_double_path_acl(const u8 type, const char *filename1, | |
148 | ++ const char *filename2, | |
149 | ++ struct domain_info * const domain, | |
150 | ++ const bool is_delete); | |
151 | ++static int update_single_path_acl(const u8 type, const char *filename, | |
152 | ++ struct domain_info * const domain, | |
153 | ++ const bool is_delete); | |
154 | ++ | |
155 | ++/** | |
156 | ++ * tmy_add_domain_acl - Add the given ACL to the given domain. | |
157 | ++ * | |
158 | ++ * @domain: Pointer to "struct domain_info". May be NULL. | |
159 | ++ * @acl: Pointer to "struct acl_info". | |
160 | ++ * | |
161 | ++ * Returns 0. | |
162 | ++ */ | |
163 | ++static int tmy_add_domain_acl(struct domain_info *domain, struct acl_info *acl) | |
164 | ++{ | |
165 | ++ if (domain) { | |
166 | ++ /* | |
167 | ++ * We need to serialize because this function is called by | |
168 | ++ * update_single_path_acl() and update_double_path_acl(). | |
169 | ++ */ | |
170 | ++ static DEFINE_SPINLOCK(lock); | |
171 | ++ /***** CRITICAL SECTION START *****/ | |
172 | ++ spin_lock(&lock); | |
173 | ++ list1_add_tail(&acl->list, &domain->acl_info_list); | |
174 | ++ spin_unlock(&lock); | |
175 | ++ /***** CRITICAL SECTION END *****/ | |
176 | ++ } else { | |
177 | ++ acl->type &= ~ACL_DELETED; | |
178 | ++ } | |
179 | ++ tmy_update_counter(TMY_UPDATES_COUNTER_DOMAIN_POLICY); | |
180 | ++ return 0; | |
181 | ++} | |
182 | ++ | |
183 | ++/** | |
184 | ++ * tmy_del_domain_acl - Delete the given ACL from the domain. | |
185 | ++ * | |
186 | ++ * @acl: Pointer to "struct acl_info". May be NULL. | |
187 | ++ * | |
188 | ++ * Returns 0. | |
189 | ++ */ | |
190 | ++static int tmy_del_domain_acl(struct acl_info *acl) | |
191 | ++{ | |
192 | ++ if (acl) | |
193 | ++ acl->type |= ACL_DELETED; | |
194 | ++ tmy_update_counter(TMY_UPDATES_COUNTER_DOMAIN_POLICY); | |
195 | ++ return 0; | |
196 | ++} | |
197 | ++ | |
198 | ++/* | |
199 | ++ * The list for "struct globally_readable_file_entry". | |
200 | ++ * | |
201 | ++ * This list is updated only inside update_globally_readable_entry(), thus | |
202 | ++ * no global mutex exists. | |
203 | ++ */ | |
204 | ++static LIST1_HEAD(globally_readable_list); | |
205 | ++ | |
206 | ++/** | |
207 | ++ * update_globally_readable_entry - Update "struct globally_readable_file_entry" list. | |
208 | ++ * | |
209 | ++ * @filename: Filename unconditionally permitted to open() for reading. | |
210 | ++ * @is_delete: True if it is a delete request. | |
211 | ++ * | |
212 | ++ * Returns 0 on success, negative value otherwise. | |
213 | ++ */ | |
214 | ++static int update_globally_readable_entry(const char *filename, | |
215 | ++ const bool is_delete) | |
216 | ++{ | |
217 | ++ struct globally_readable_file_entry *new_entry; | |
218 | ++ struct globally_readable_file_entry *ptr; | |
219 | ++ static DEFINE_MUTEX(lock); | |
220 | ++ const struct path_info *saved_filename; | |
221 | ++ int error = -ENOMEM; | |
222 | ++ | |
223 | ++ if (!tmy_is_correct_path(filename, 1, 0, -1, __func__)) | |
224 | ++ return -EINVAL; | |
225 | ++ saved_filename = tmy_save_name(filename); | |
226 | ++ if (!saved_filename) | |
227 | ++ return -ENOMEM; | |
228 | ++ /***** EXCLUSIVE SECTION START *****/ | |
229 | ++ mutex_lock(&lock); | |
230 | ++ list1_for_each_entry(ptr, &globally_readable_list, list) { | |
231 | ++ if (ptr->filename != saved_filename) | |
232 | ++ continue; | |
233 | ++ ptr->is_deleted = is_delete; | |
234 | ++ error = 0; | |
235 | ++ goto out; | |
236 | ++ } | |
237 | ++ if (is_delete) { | |
238 | ++ error = -ENOENT; | |
239 | ++ goto out; | |
240 | ++ } | |
241 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
242 | ++ if (!new_entry) | |
243 | ++ goto out; | |
244 | ++ new_entry->filename = saved_filename; | |
245 | ++ list1_add_tail(&new_entry->list, &globally_readable_list); | |
246 | ++ error = 0; | |
247 | ++out: | |
248 | ++ mutex_unlock(&lock); | |
249 | ++ /***** EXCLUSIVE SECTION END *****/ | |
250 | ++ tmy_update_counter(TMY_UPDATES_COUNTER_EXCEPTION_POLICY); | |
251 | ++ return error; | |
252 | ++} | |
253 | ++ | |
254 | ++/** | |
255 | ++ * is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading. | |
256 | ++ * | |
257 | ++ * @filename: The filename to check. | |
258 | ++ * | |
259 | ++ * Returns true if any domain can open @filename for reading, false otherwise. | |
260 | ++ */ | |
261 | ++static bool is_globally_readable_file(const struct path_info *filename) | |
262 | ++{ | |
263 | ++ struct globally_readable_file_entry *ptr; | |
264 | ++ | |
265 | ++ list1_for_each_entry(ptr, &globally_readable_list, list) { | |
266 | ++ if (!ptr->is_deleted && | |
267 | ++ tmy_path_matches_pattern(filename, ptr->filename)) | |
268 | ++ return true; | |
269 | ++ } | |
270 | ++ return false; | |
271 | ++} | |
272 | ++ | |
273 | ++/** | |
274 | ++ * tmy_write_globally_readable_policy - Write "struct globally_readable_file_entry" list. | |
275 | ++ * | |
276 | ++ * @data: String to parse. | |
277 | ++ * @is_delete: True if it is a delete request. | |
278 | ++ * | |
279 | ++ * Returns 0 on success, negative value otherwise. | |
280 | ++ */ | |
281 | ++int tmy_write_globally_readable_policy(char *data, const bool is_delete) | |
282 | ++{ | |
283 | ++ return update_globally_readable_entry(data, is_delete); | |
284 | ++} | |
285 | ++ | |
286 | ++/** | |
287 | ++ * tmy_read_globally_readable_policy - Read "struct globally_readable_file_entry" list. | |
288 | ++ * | |
289 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
290 | ++ * | |
291 | ++ * Returns true on success, false otherwise. | |
292 | ++ */ | |
293 | ++bool tmy_read_globally_readable_policy(struct tmy_io_buffer *head) | |
294 | ++{ | |
295 | ++ struct list1_head *pos; | |
296 | ++ | |
297 | ++ list1_for_each_cookie(pos, head->read_var2, &globally_readable_list) { | |
298 | ++ struct globally_readable_file_entry *ptr; | |
299 | ++ ptr = list1_entry(pos, struct globally_readable_file_entry, | |
300 | ++ list); | |
301 | ++ if (ptr->is_deleted) | |
302 | ++ continue; | |
303 | ++ if (!tmy_io_printf(head, KEYWORD_ALLOW_READ "%s\n", | |
304 | ++ ptr->filename->name)) | |
305 | ++ goto out; | |
306 | ++ } | |
307 | ++ return true; | |
308 | ++out: | |
309 | ++ return false; | |
310 | ++} | |
311 | ++ | |
312 | ++/* | |
313 | ++ * The list for "struct pattern_entry". | |
314 | ++ * | |
315 | ++ * This list is updated only inside update_file_pattern_entry(), thus | |
316 | ++ * no global mutex exists. | |
317 | ++ */ | |
318 | ++static LIST1_HEAD(pattern_list); | |
319 | ++ | |
320 | ++/** | |
321 | ++ * update_file_pattern_entry - Update "struct pattern_entry" list. | |
322 | ++ * | |
323 | ++ * @pattern: Pathname pattern. | |
324 | ++ * @is_delete: True if it is a delete request. | |
325 | ++ * | |
326 | ++ * Returns 0 on success, negative value otherwise. | |
327 | ++ */ | |
328 | ++static int update_file_pattern_entry(const char *pattern, const bool is_delete) | |
329 | ++{ | |
330 | ++ struct pattern_entry *new_entry; | |
331 | ++ struct pattern_entry *ptr; | |
332 | ++ static DEFINE_MUTEX(lock); | |
333 | ++ const struct path_info *saved_pattern; | |
334 | ++ int error = -ENOMEM; | |
335 | ++ | |
336 | ++ if (!tmy_is_correct_path(pattern, 0, 1, 0, __func__)) | |
337 | ++ return -EINVAL; | |
338 | ++ saved_pattern = tmy_save_name(pattern); | |
339 | ++ if (!saved_pattern) | |
340 | ++ return -ENOMEM; | |
341 | ++ /***** EXCLUSIVE SECTION START *****/ | |
342 | ++ mutex_lock(&lock); | |
343 | ++ list1_for_each_entry(ptr, &pattern_list, list) { | |
344 | ++ if (saved_pattern != ptr->pattern) | |
345 | ++ continue; | |
346 | ++ ptr->is_deleted = is_delete; | |
347 | ++ error = 0; | |
348 | ++ goto out; | |
349 | ++ } | |
350 | ++ if (is_delete) { | |
351 | ++ error = -ENOENT; | |
352 | ++ goto out; | |
353 | ++ } | |
354 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
355 | ++ if (!new_entry) | |
356 | ++ goto out; | |
357 | ++ new_entry->pattern = saved_pattern; | |
358 | ++ list1_add_tail(&new_entry->list, &pattern_list); | |
359 | ++ error = 0; | |
360 | ++out: | |
361 | ++ mutex_unlock(&lock); | |
362 | ++ /***** EXCLUSIVE SECTION END *****/ | |
363 | ++ tmy_update_counter(TMY_UPDATES_COUNTER_EXCEPTION_POLICY); | |
364 | ++ return error; | |
365 | ++} | |
366 | ++ | |
367 | ++/** | |
368 | ++ * get_file_pattern - Get patterned pathname. | |
369 | ++ * | |
370 | ++ * @filename: The filename to find patterned pathname. | |
371 | ++ * | |
372 | ++ * Returns pointer to pathname pattern if matched, @filename otherwise. | |
373 | ++ */ | |
374 | ++static const struct path_info * | |
375 | ++get_file_pattern(const struct path_info *filename) | |
376 | ++{ | |
377 | ++ struct pattern_entry *ptr; | |
378 | ++ const struct path_info *pattern = NULL; | |
379 | ++ | |
380 | ++ list1_for_each_entry(ptr, &pattern_list, list) { | |
381 | ++ if (ptr->is_deleted) | |
382 | ++ continue; | |
383 | ++ if (!tmy_path_matches_pattern(filename, ptr->pattern)) | |
384 | ++ continue; | |
385 | ++ pattern = ptr->pattern; | |
386 | ++ if (strendswith(pattern->name, "/\\*")) { | |
387 | ++ /* Do nothing. Try to find the better match. */ | |
388 | ++ } else { | |
389 | ++ /* This would be the better match. Use this. */ | |
390 | ++ break; | |
391 | ++ } | |
392 | ++ } | |
393 | ++ if (pattern) | |
394 | ++ filename = pattern; | |
395 | ++ return filename; | |
396 | ++} | |
397 | ++ | |
398 | ++/** | |
399 | ++ * tmy_write_pattern_policy - Write "struct pattern_entry" list. | |
400 | ++ * | |
401 | ++ * @data: String to parse. | |
402 | ++ * @is_delete: True if it is a delete request. | |
403 | ++ * | |
404 | ++ * Returns 0 on success, negative value otherwise. | |
405 | ++ */ | |
406 | ++int tmy_write_pattern_policy(char *data, const bool is_delete) | |
407 | ++{ | |
408 | ++ return update_file_pattern_entry(data, is_delete); | |
409 | ++} | |
410 | ++ | |
411 | ++/** | |
412 | ++ * tmy_read_file_pattern - Read "struct pattern_entry" list. | |
413 | ++ * | |
414 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
415 | ++ * | |
416 | ++ * Returns true on success, false otherwise. | |
417 | ++ */ | |
418 | ++bool tmy_read_file_pattern(struct tmy_io_buffer *head) | |
419 | ++{ | |
420 | ++ struct list1_head *pos; | |
421 | ++ | |
422 | ++ list1_for_each_cookie(pos, head->read_var2, &pattern_list) { | |
423 | ++ struct pattern_entry *ptr; | |
424 | ++ ptr = list1_entry(pos, struct pattern_entry, list); | |
425 | ++ if (ptr->is_deleted) | |
426 | ++ continue; | |
427 | ++ if (!tmy_io_printf(head, KEYWORD_FILE_PATTERN "%s\n", | |
428 | ++ ptr->pattern->name)) | |
429 | ++ goto out; | |
430 | ++ } | |
431 | ++ return true; | |
432 | ++out: | |
433 | ++ return false; | |
434 | ++} | |
435 | ++ | |
436 | ++/* | |
437 | ++ * The list for "struct no_rewrite_entry". | |
438 | ++ * | |
439 | ++ * This list is updated only inside update_no_rewrite_entry(), thus | |
440 | ++ * no global mutex exists. | |
441 | ++ */ | |
442 | ++static LIST1_HEAD(no_rewrite_list); | |
443 | ++ | |
444 | ++/** | |
445 | ++ * update_no_rewrite_entry - Update "struct no_rewrite_entry" list. | |
446 | ++ * | |
447 | ++ * @pattern: Pathname pattern that are not rewritable by default. | |
448 | ++ * @is_delete: True if it is a delete request. | |
449 | ++ * | |
450 | ++ * Returns 0 on success, negative value otherwise. | |
451 | ++ */ | |
452 | ++static int update_no_rewrite_entry(const char *pattern, const bool is_delete) | |
453 | ++{ | |
454 | ++ struct no_rewrite_entry *new_entry, *ptr; | |
455 | ++ static DEFINE_MUTEX(lock); | |
456 | ++ const struct path_info *saved_pattern; | |
457 | ++ int error = -ENOMEM; | |
458 | ++ | |
459 | ++ if (!tmy_is_correct_path(pattern, 0, 0, 0, __func__)) | |
460 | ++ return -EINVAL; | |
461 | ++ saved_pattern = tmy_save_name(pattern); | |
462 | ++ if (!saved_pattern) | |
463 | ++ return -ENOMEM; | |
464 | ++ /***** EXCLUSIVE SECTION START *****/ | |
465 | ++ mutex_lock(&lock); | |
466 | ++ list1_for_each_entry(ptr, &no_rewrite_list, list) { | |
467 | ++ if (ptr->pattern != saved_pattern) | |
468 | ++ continue; | |
469 | ++ ptr->is_deleted = is_delete; | |
470 | ++ error = 0; | |
471 | ++ goto out; | |
472 | ++ } | |
473 | ++ if (is_delete) { | |
474 | ++ error = -ENOENT; | |
475 | ++ goto out; | |
476 | ++ } | |
477 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
478 | ++ if (!new_entry) | |
479 | ++ goto out; | |
480 | ++ new_entry->pattern = saved_pattern; | |
481 | ++ list1_add_tail(&new_entry->list, &no_rewrite_list); | |
482 | ++ error = 0; | |
483 | ++out: | |
484 | ++ mutex_unlock(&lock); | |
485 | ++ /***** EXCLUSIVE SECTION END *****/ | |
486 | ++ tmy_update_counter(TMY_UPDATES_COUNTER_EXCEPTION_POLICY); | |
487 | ++ return error; | |
488 | ++} | |
489 | ++ | |
490 | ++/** | |
491 | ++ * is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited. | |
492 | ++ * | |
493 | ++ * @filename: Filename to check. | |
494 | ++ * | |
495 | ++ * Returns true if @filename is specified by "deny_rewrite" directive, | |
496 | ++ * false otherwise. | |
497 | ++ */ | |
498 | ++static bool is_no_rewrite_file(const struct path_info *filename) | |
499 | ++{ | |
500 | ++ struct no_rewrite_entry *ptr; | |
501 | ++ | |
502 | ++ list1_for_each_entry(ptr, &no_rewrite_list, list) { | |
503 | ++ if (ptr->is_deleted) | |
504 | ++ continue; | |
505 | ++ if (!tmy_path_matches_pattern(filename, ptr->pattern)) | |
506 | ++ continue; | |
507 | ++ return true; | |
508 | ++ } | |
509 | ++ return false; | |
510 | ++} | |
511 | ++ | |
512 | ++/** | |
513 | ++ * tmy_write_no_rewrite_policy - Write "struct no_rewrite_entry" list. | |
514 | ++ * | |
515 | ++ * @data: String to parse. | |
516 | ++ * @is_delete: True if it is a delete request. | |
517 | ++ * | |
518 | ++ * Returns 0 on success, negative value otherwise. | |
519 | ++ */ | |
520 | ++int tmy_write_no_rewrite_policy(char *data, const bool is_delete) | |
521 | ++{ | |
522 | ++ return update_no_rewrite_entry(data, is_delete); | |
523 | ++} | |
524 | ++ | |
525 | ++/** | |
526 | ++ * tmy_read_no_rewrite_policy - Read "struct no_rewrite_entry" list. | |
527 | ++ * | |
528 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
529 | ++ * | |
530 | ++ * Returns true on success, false otherwise. | |
531 | ++ */ | |
532 | ++bool tmy_read_no_rewrite_policy(struct tmy_io_buffer *head) | |
533 | ++{ | |
534 | ++ struct list1_head *pos; | |
535 | ++ | |
536 | ++ list1_for_each_cookie(pos, head->read_var2, &no_rewrite_list) { | |
537 | ++ struct no_rewrite_entry *ptr; | |
538 | ++ ptr = list1_entry(pos, struct no_rewrite_entry, list); | |
539 | ++ if (ptr->is_deleted) | |
540 | ++ continue; | |
541 | ++ if (!tmy_io_printf(head, KEYWORD_DENY_REWRITE "%s\n", | |
542 | ++ ptr->pattern->name)) | |
543 | ++ goto out; | |
544 | ++ } | |
545 | ++ return true; | |
546 | ++out: | |
547 | ++ return false; | |
548 | ++} | |
549 | ++ | |
550 | ++/** | |
551 | ++ * update_file_acl - Update file's read/write/execute ACL. | |
552 | ++ * | |
553 | ++ * @filename: Filename. | |
554 | ++ * @perm: Permission (between 1 to 7). | |
555 | ++ * @domain: Pointer to "struct domain_info". | |
556 | ++ * @is_delete: True if it is a delete request. | |
557 | ++ * | |
558 | ++ * Returns 0 on success, negative value otherwise. | |
559 | ++ * | |
560 | ++ * This is legacy support interface for older policy syntax. | |
561 | ++ * Current policy syntax uses "allow_read/write" instead of "6", | |
562 | ++ * "allow_read" instead of "4", "allow_write" instead of "2", | |
563 | ++ * "allow_execute" instead of "1". | |
564 | ++ */ | |
565 | ++static int update_file_acl(const char *filename, u8 perm, | |
566 | ++ struct domain_info * const domain, | |
567 | ++ const bool is_delete) | |
568 | ++{ | |
569 | ++ if (perm > 7 || !perm) { | |
570 | ++ printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n", | |
571 | ++ __func__, perm, filename); | |
572 | ++ return -EINVAL; | |
573 | ++ } | |
574 | ++ if (filename[0] != '@' && strendswith(filename, "/")) | |
575 | ++ /* | |
576 | ++ * Only 'allow_mkdir' and 'allow_rmdir' are valid for | |
577 | ++ * directory permissions. | |
578 | ++ */ | |
579 | ++ return 0; | |
580 | ++ if (perm & 4) | |
581 | ++ update_single_path_acl(TMY_TYPE_READ_ACL, filename, domain, | |
582 | ++ is_delete); | |
583 | ++ if (perm & 2) | |
584 | ++ update_single_path_acl(TMY_TYPE_WRITE_ACL, filename, domain, | |
585 | ++ is_delete); | |
586 | ++ if (perm & 1) | |
587 | ++ update_single_path_acl(TMY_TYPE_EXECUTE_ACL, filename, domain, | |
588 | ++ is_delete); | |
589 | ++ return 0; | |
590 | ++} | |
591 | ++ | |
592 | ++/** | |
593 | ++ * check_single_path_acl2 - Check permission for single path operation. | |
594 | ++ * | |
595 | ++ * @domain: Pointer to "struct domain_info". | |
596 | ++ * @filename: Filename to check. | |
597 | ++ * @perm: Permission. | |
598 | ++ * @may_use_pattern: True if patterned ACL is permitted. | |
599 | ++ * | |
600 | ++ * Returns 0 on success, -EPERM otherwise. | |
601 | ++ */ | |
602 | ++static int check_single_path_acl2(const struct domain_info *domain, | |
603 | ++ const struct path_info *filename, | |
604 | ++ const u16 perm, const bool may_use_pattern) | |
605 | ++{ | |
606 | ++ struct acl_info *ptr; | |
607 | ++ | |
608 | ++ list1_for_each_entry(ptr, &domain->acl_info_list, list) { | |
609 | ++ struct single_path_acl_record *acl; | |
610 | ++ if (tmy_acl_type2(ptr) != TYPE_SINGLE_PATH_ACL) | |
611 | ++ continue; | |
612 | ++ acl = container_of(ptr, struct single_path_acl_record, head); | |
613 | ++ if (!(acl->perm & perm)) | |
614 | ++ continue; | |
615 | ++ if (may_use_pattern || !acl->filename->is_patterned) { | |
616 | ++ if (!tmy_path_matches_pattern(filename, | |
617 | ++ acl->filename)) | |
618 | ++ continue; | |
619 | ++ } else { | |
620 | ++ continue; | |
621 | ++ } | |
622 | ++ return 0; | |
623 | ++ } | |
624 | ++ return -EPERM; | |
625 | ++} | |
626 | ++ | |
627 | ++/** | |
628 | ++ * check_file_acl - Check permission for opening files. | |
629 | ++ * | |
630 | ++ * @domain: Pointer to "struct domain_info". | |
631 | ++ * @filename: Filename to check. | |
632 | ++ * @operation: Mode ("read" or "write" or "read/write" or "execute"). | |
633 | ++ * | |
634 | ++ * Returns 0 on success, -EPERM otherwise. | |
635 | ++ */ | |
636 | ++static int check_file_acl(const struct domain_info *domain, | |
637 | ++ const struct path_info *filename, const u8 operation) | |
638 | ++{ | |
639 | ++ u16 perm = 0; | |
640 | ++ | |
641 | ++ if (!tmy_check_flags(domain, TMY_TOMOYO_MAC_FOR_FILE)) | |
642 | ++ return 0; | |
643 | ++ if (operation == 6) | |
644 | ++ perm = 1 << TMY_TYPE_READ_WRITE_ACL; | |
645 | ++ else if (operation == 4) | |
646 | ++ perm = 1 << TMY_TYPE_READ_ACL; | |
647 | ++ else if (operation == 2) | |
648 | ++ perm = 1 << TMY_TYPE_WRITE_ACL; | |
649 | ++ else if (operation == 1) | |
650 | ++ perm = 1 << TMY_TYPE_EXECUTE_ACL; | |
651 | ++ else | |
652 | ++ BUG(); | |
653 | ++ return check_single_path_acl2(domain, filename, perm, operation != 1); | |
654 | ++} | |
655 | ++ | |
656 | ++/** | |
657 | ++ * check_file_perm2 - Check permission for opening files. | |
658 | ++ * | |
659 | ++ * @domain: Pointer to "struct domain_info". | |
660 | ++ * @filename: Filename to check. | |
661 | ++ * @perm: Mode ("read" or "write" or "read/write" or "execute"). | |
662 | ++ * @operation: Operation name passed used for verbose mode. | |
663 | ++ * @mode: Access control mode. | |
664 | ++ * | |
665 | ++ * Returns 0 on success, negative value otherwise. | |
666 | ++ */ | |
667 | ++static int check_file_perm2(struct domain_info * const domain, | |
668 | ++ const struct path_info *filename, const u8 perm, | |
669 | ++ const char *operation, const u8 mode) | |
670 | ++{ | |
671 | ++ const bool is_enforce = (mode == 3); | |
672 | ++ const char *msg = "<unknown>"; | |
673 | ++ int error = 0; | |
674 | ++ | |
675 | ++ if (!filename) | |
676 | ++ return 0; | |
677 | ++ error = check_file_acl(domain, filename, perm); | |
678 | ++ if (error && perm == 4 && | |
679 | ++ (domain->flags & DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ) == 0 && | |
680 | ++ is_globally_readable_file(filename)) | |
681 | ++ error = 0; | |
682 | ++ if (perm == 6) | |
683 | ++ msg = tmy_sp2keyword(TMY_TYPE_READ_WRITE_ACL); | |
684 | ++ else if (perm == 4) | |
685 | ++ msg = tmy_sp2keyword(TMY_TYPE_READ_ACL); | |
686 | ++ else if (perm == 2) | |
687 | ++ msg = tmy_sp2keyword(TMY_TYPE_WRITE_ACL); | |
688 | ++ else if (perm == 1) | |
689 | ++ msg = tmy_sp2keyword(TMY_TYPE_EXECUTE_ACL); | |
690 | ++ else | |
691 | ++ BUG(); | |
692 | ++ if (!error) | |
693 | ++ return 0; | |
694 | ++ if (tmy_verbose_mode(domain)) | |
695 | ++ printk(KERN_WARNING "TOMOYO-%s: Access '%s(%s) %s' denied " | |
696 | ++ "for %s\n", tmy_get_msg(is_enforce), msg, operation, | |
697 | ++ filename->name, tmy_get_last_name(domain)); | |
698 | ++ if (is_enforce) | |
699 | ++ return error; | |
700 | ++ if (mode == 1 && tmy_check_domain_quota(domain)) { | |
701 | ++ /* Don't use patterns for execute permission. */ | |
702 | ++ const struct path_info *patterned_file = (perm != 1) ? | |
703 | ++ get_file_pattern(filename) : filename; | |
704 | ++ update_file_acl(patterned_file->name, perm, | |
705 | ++ domain, false); | |
706 | ++ } | |
707 | ++ return 0; | |
708 | ++} | |
709 | ++ | |
710 | ++/** | |
711 | ++ * tmy_write_file_policy - Update file related list. | |
712 | ++ * | |
713 | ++ * @data: String to parse. | |
714 | ++ * @domain: Pointer to "struct domain_info". | |
715 | ++ * @is_delete: True if it is a delete request. | |
716 | ++ * | |
717 | ++ * Returns 0 on success, negative value otherwise. | |
718 | ++ */ | |
719 | ++int tmy_write_file_policy(char *data, struct domain_info *domain, | |
720 | ++ const bool is_delete) | |
721 | ++{ | |
722 | ++ char *filename = strchr(data, ' '); | |
723 | ++ char *filename2; | |
724 | ++ unsigned int perm; | |
725 | ++ u8 type; | |
726 | ++ | |
727 | ++ if (!filename) | |
728 | ++ return -EINVAL; | |
729 | ++ *filename++ = '\0'; | |
730 | ++ if (sscanf(data, "%u", &perm) == 1) | |
731 | ++ return update_file_acl(filename, (u8) perm, domain, is_delete); | |
732 | ++ if (strncmp(data, "allow_", 6)) | |
733 | ++ goto out; | |
734 | ++ data += 6; | |
735 | ++ for (type = 0; type < MAX_SINGLE_PATH_OPERATION; type++) { | |
736 | ++ if (strcmp(data, sp_keyword[type])) | |
737 | ++ continue; | |
738 | ++ return update_single_path_acl(type, filename, | |
739 | ++ domain, is_delete); | |
740 | ++ } | |
741 | ++ filename2 = strchr(filename, ' '); | |
742 | ++ if (!filename2) | |
743 | ++ goto out; | |
744 | ++ *filename2++ = '\0'; | |
745 | ++ for (type = 0; type < MAX_DOUBLE_PATH_OPERATION; type++) { | |
746 | ++ if (strcmp(data, dp_keyword[type])) | |
747 | ++ continue; | |
748 | ++ return update_double_path_acl(type, filename, filename2, domain, | |
749 | ++ is_delete); | |
750 | ++ } | |
751 | ++out: | |
752 | ++ return -EINVAL; | |
753 | ++} | |
754 | ++ | |
755 | ++/** | |
756 | ++ * update_single_path_acl - Update "struct single_path_acl_record" list. | |
757 | ++ * | |
758 | ++ * @type: Type of operation. | |
759 | ++ * @filename: Filename. | |
760 | ++ * @domain: Pointer to "struct domain_info". | |
761 | ++ * @is_delete: True if it is a delete request. | |
762 | ++ * | |
763 | ++ * Returns 0 on success, negative value otherwise. | |
764 | ++ */ | |
765 | ++static int update_single_path_acl(const u8 type, const char *filename, | |
766 | ++ struct domain_info * const domain, | |
767 | ++ const bool is_delete) | |
768 | ++{ | |
769 | ++ static DEFINE_MUTEX(lock); | |
770 | ++ static const u16 rw_mask = | |
771 | ++ (1 << TMY_TYPE_READ_ACL) | (1 << TMY_TYPE_WRITE_ACL); | |
772 | ++ const struct path_info *saved_filename; | |
773 | ++ struct acl_info *ptr; | |
774 | ++ struct single_path_acl_record *acl; | |
775 | ++ int error = -ENOMEM; | |
776 | ++ const u16 perm = 1 << type; | |
777 | ++ | |
778 | ++ if (!domain) | |
779 | ++ return -EINVAL; | |
780 | ++ if (!tmy_is_correct_path(filename, 0, 0, 0, __func__)) | |
781 | ++ return -EINVAL; | |
782 | ++ saved_filename = tmy_save_name(filename); | |
783 | ++ if (!saved_filename) | |
784 | ++ return -ENOMEM; | |
785 | ++ /***** EXCLUSIVE SECTION START *****/ | |
786 | ++ mutex_lock(&lock); | |
787 | ++ if (is_delete) | |
788 | ++ goto delete; | |
789 | ++ list1_for_each_entry(ptr, &domain->acl_info_list, list) { | |
790 | ++ if (tmy_acl_type1(ptr) != TYPE_SINGLE_PATH_ACL) | |
791 | ++ continue; | |
792 | ++ acl = container_of(ptr, struct single_path_acl_record, head); | |
793 | ++ if (acl->filename != saved_filename) | |
794 | ++ continue; | |
795 | ++ /* Special case. Clear all bits if marked as deleted. */ | |
796 | ++ if (ptr->type & ACL_DELETED) | |
797 | ++ acl->perm = 0; | |
798 | ++ acl->perm |= perm; | |
799 | ++ if ((acl->perm & rw_mask) == rw_mask) | |
800 | ++ acl->perm |= 1 << TMY_TYPE_READ_WRITE_ACL; | |
801 | ++ else if (acl->perm & (1 << TMY_TYPE_READ_WRITE_ACL)) | |
802 | ++ acl->perm |= rw_mask; | |
803 | ++ error = tmy_add_domain_acl(NULL, ptr); | |
804 | ++ goto out; | |
805 | ++ } | |
806 | ++ /* Not found. Append it to the tail. */ | |
807 | ++ acl = tmy_alloc_acl_element(TYPE_SINGLE_PATH_ACL); | |
808 | ++ if (!acl) | |
809 | ++ goto out; | |
810 | ++ acl->perm = perm; | |
811 | ++ acl->filename = saved_filename; | |
812 | ++ error = tmy_add_domain_acl(domain, &acl->head); | |
813 | ++ goto out; | |
814 | ++delete: | |
815 | ++ error = -ENOENT; | |
816 | ++ list1_for_each_entry(ptr, &domain->acl_info_list, list) { | |
817 | ++ if (tmy_acl_type2(ptr) != TYPE_SINGLE_PATH_ACL) | |
818 | ++ continue; | |
819 | ++ acl = container_of(ptr, struct single_path_acl_record, head); | |
820 | ++ if (acl->filename != saved_filename) | |
821 | ++ continue; | |
822 | ++ acl->perm &= ~perm; | |
823 | ++ if ((acl->perm & rw_mask) != rw_mask) | |
824 | ++ acl->perm &= ~(1 << TMY_TYPE_READ_WRITE_ACL); | |
825 | ++ else if (!(acl->perm & (1 << TMY_TYPE_READ_WRITE_ACL))) | |
826 | ++ acl->perm &= ~rw_mask; | |
827 | ++ error = tmy_del_domain_acl(acl->perm ? NULL : ptr); | |
828 | ++ break; | |
829 | ++ } | |
830 | ++out: | |
831 | ++ mutex_unlock(&lock); | |
832 | ++ /***** EXCLUSIVE SECTION END *****/ | |
833 | ++ return error; | |
834 | ++} | |
835 | ++ | |
836 | ++/** | |
837 | ++ * update_double_path_acl - Update "struct double_path_acl_record" list. | |
838 | ++ * | |
839 | ++ * @type: Type of operation. | |
840 | ++ * @filename1: First filename. | |
841 | ++ * @filename2: Second filename. | |
842 | ++ * @domain: Pointer to "struct domain_info". | |
843 | ++ * @is_delete: True if it is a delete request. | |
844 | ++ * | |
845 | ++ * Returns 0 on success, negative value otherwise. | |
846 | ++ */ | |
847 | ++static int update_double_path_acl(const u8 type, const char *filename1, | |
848 | ++ const char *filename2, | |
849 | ++ struct domain_info * const domain, | |
850 | ++ const bool is_delete) | |
851 | ++{ | |
852 | ++ static DEFINE_MUTEX(lock); | |
853 | ++ const struct path_info *saved_filename1; | |
854 | ++ const struct path_info *saved_filename2; | |
855 | ++ struct acl_info *ptr; | |
856 | ++ struct double_path_acl_record *acl; | |
857 | ++ int error = -ENOMEM; | |
858 | ++ const u8 perm = 1 << type; | |
859 | ++ | |
860 | ++ if (!domain) | |
861 | ++ return -EINVAL; | |
862 | ++ if (!tmy_is_correct_path(filename1, 0, 0, 0, __func__) || | |
863 | ++ !tmy_is_correct_path(filename2, 0, 0, 0, __func__)) | |
864 | ++ return -EINVAL; | |
865 | ++ saved_filename1 = tmy_save_name(filename1); | |
866 | ++ saved_filename2 = tmy_save_name(filename2); | |
867 | ++ if (!saved_filename1 || !saved_filename2) | |
868 | ++ return -ENOMEM; | |
869 | ++ /***** EXCLUSIVE SECTION START *****/ | |
870 | ++ mutex_lock(&lock); | |
871 | ++ if (is_delete) | |
872 | ++ goto delete; | |
873 | ++ list1_for_each_entry(ptr, &domain->acl_info_list, list) { | |
874 | ++ if (tmy_acl_type1(ptr) != TYPE_DOUBLE_PATH_ACL) | |
875 | ++ continue; | |
876 | ++ acl = container_of(ptr, struct double_path_acl_record, head); | |
877 | ++ if (acl->filename1 != saved_filename1 || | |
878 | ++ acl->filename2 != saved_filename2) | |
879 | ++ continue; | |
880 | ++ /* Special case. Clear all bits if marked as deleted. */ | |
881 | ++ if (ptr->type & ACL_DELETED) | |
882 | ++ acl->perm = 0; | |
883 | ++ acl->perm |= perm; | |
884 | ++ error = tmy_add_domain_acl(NULL, ptr); | |
885 | ++ goto out; | |
886 | ++ } | |
887 | ++ /* Not found. Append it to the tail. */ | |
888 | ++ acl = tmy_alloc_acl_element(TYPE_DOUBLE_PATH_ACL); | |
889 | ++ if (!acl) | |
890 | ++ goto out; | |
891 | ++ acl->perm = perm; | |
892 | ++ acl->filename1 = saved_filename1; | |
893 | ++ acl->filename2 = saved_filename2; | |
894 | ++ error = tmy_add_domain_acl(domain, &acl->head); | |
895 | ++ goto out; | |
896 | ++delete: | |
897 | ++ error = -ENOENT; | |
898 | ++ list1_for_each_entry(ptr, &domain->acl_info_list, list) { | |
899 | ++ if (tmy_acl_type2(ptr) != TYPE_DOUBLE_PATH_ACL) | |
900 | ++ continue; | |
901 | ++ acl = container_of(ptr, struct double_path_acl_record, head); | |
902 | ++ if (acl->filename1 != saved_filename1 || | |
903 | ++ acl->filename2 != saved_filename2) | |
904 | ++ continue; | |
905 | ++ acl->perm &= ~perm; | |
906 | ++ error = tmy_del_domain_acl(acl->perm ? NULL : ptr); | |
907 | ++ break; | |
908 | ++ } | |
909 | ++out: | |
910 | ++ mutex_unlock(&lock); | |
911 | ++ /***** EXCLUSIVE SECTION END *****/ | |
912 | ++ return error; | |
913 | ++} | |
914 | ++ | |
915 | ++/** | |
916 | ++ * check_single_path_acl - Check permission for single path operation. | |
917 | ++ * | |
918 | ++ * @domain: Pointer to "struct domain_info". | |
919 | ++ * @type: Type of operation. | |
920 | ++ * @filename: Filename to check. | |
921 | ++ * | |
922 | ++ * Returns 0 on success, negative value otherwise. | |
923 | ++ */ | |
924 | ++static int check_single_path_acl(struct domain_info *domain, const u8 type, | |
925 | ++ const struct path_info *filename) | |
926 | ++{ | |
927 | ++ if (!tmy_check_flags(domain, TMY_TOMOYO_MAC_FOR_FILE)) | |
928 | ++ return 0; | |
929 | ++ return check_single_path_acl2(domain, filename, 1 << type, 1); | |
930 | ++} | |
931 | ++ | |
932 | ++/** | |
933 | ++ * check_double_path_acl - Check permission for double path operation. | |
934 | ++ * | |
935 | ++ * @domain: Pointer to "struct domain_info". | |
936 | ++ * @type: Type of operation. | |
937 | ++ * @filename1: First filename to check. | |
938 | ++ * @filename2: Second filename to check. | |
939 | ++ * | |
940 | ++ * Returns 0 on success, -EPERM otherwise. | |
941 | ++ */ | |
942 | ++static int check_double_path_acl(const struct domain_info *domain, | |
943 | ++ const u8 type, | |
944 | ++ const struct path_info *filename1, | |
945 | ++ const struct path_info *filename2) | |
946 | ++{ | |
947 | ++ struct acl_info *ptr; | |
948 | ++ const u8 perm = 1 << type; | |
949 | ++ | |
950 | ++ if (!tmy_check_flags(domain, TMY_TOMOYO_MAC_FOR_FILE)) | |
951 | ++ return 0; | |
952 | ++ list1_for_each_entry(ptr, &domain->acl_info_list, list) { | |
953 | ++ struct double_path_acl_record *acl; | |
954 | ++ if (tmy_acl_type2(ptr) != TYPE_DOUBLE_PATH_ACL) | |
955 | ++ continue; | |
956 | ++ acl = container_of(ptr, struct double_path_acl_record, head); | |
957 | ++ if (!(acl->perm & perm)) | |
958 | ++ continue; | |
959 | ++ if (!tmy_path_matches_pattern(filename1, acl->filename1)) | |
960 | ++ continue; | |
961 | ++ if (!tmy_path_matches_pattern(filename2, acl->filename2)) | |
962 | ++ continue; | |
963 | ++ return 0; | |
964 | ++ } | |
965 | ++ return -EPERM; | |
966 | ++} | |
967 | ++ | |
968 | ++/** | |
969 | ++ * check_single_path_permission2 - Check permission for single path operation. | |
970 | ++ * | |
971 | ++ * @domain: Pointer to "struct domain_info". | |
972 | ++ * @operation: Type of operation. | |
973 | ++ * @filename: Filename to check. | |
974 | ++ * @mode: Access control mode. | |
975 | ++ * | |
976 | ++ * Returns 0 on success, negative value otherwise. | |
977 | ++ */ | |
978 | ++static int check_single_path_permission2(struct domain_info * const domain, | |
979 | ++ u8 operation, | |
980 | ++ const struct path_info *filename, | |
981 | ++ const u8 mode) | |
982 | ++{ | |
983 | ++ const char *msg; | |
984 | ++ int error; | |
985 | ++ const bool is_enforce = (mode == 3); | |
986 | ++ | |
987 | ++ if (!mode) | |
988 | ++ return 0; | |
989 | ++next: | |
990 | ++ error = check_single_path_acl(domain, operation, filename); | |
991 | ++ msg = tmy_sp2keyword(operation); | |
992 | ++ if (!error) | |
993 | ++ goto ok; | |
994 | ++ if (tmy_verbose_mode(domain)) | |
995 | ++ printk(KERN_WARNING "TOMOYO-%s: Access '%s %s' denied for %s\n", | |
996 | ++ tmy_get_msg(is_enforce), msg, filename->name, | |
997 | ++ tmy_get_last_name(domain)); | |
998 | ++ if (mode == 1 && tmy_check_domain_quota(domain)) | |
999 | ++ update_single_path_acl(operation, | |
1000 | ++ get_file_pattern(filename)->name, | |
1001 | ++ domain, false); | |
1002 | ++ if (!is_enforce) | |
1003 | ++ error = 0; | |
1004 | ++ok: | |
1005 | ++ /* | |
1006 | ++ * Since "allow_truncate" doesn't imply "allow_rewrite" permission, | |
1007 | ++ * we need to check "allow_rewrite" permission if the filename is | |
1008 | ++ * specified by "deny_rewrite" keyword. | |
1009 | ++ */ | |
1010 | ++ if (!error && operation == TMY_TYPE_TRUNCATE_ACL && | |
1011 | ++ is_no_rewrite_file(filename)) { | |
1012 | ++ operation = TMY_TYPE_REWRITE_ACL; | |
1013 | ++ goto next; | |
1014 | ++ } | |
1015 | ++ return error; | |
1016 | ++} | |
1017 | ++ | |
1018 | ++/** | |
1019 | ++ * tmy_check_file_perm - Check permission for sysctl()'s "read" and "write". | |
1020 | ++ * | |
1021 | ++ * @domain: Pointer to "struct domain_info". | |
1022 | ++ * @filename: Filename to check. | |
1023 | ++ * @perm: Mode ("read" or "write" or "read/write"). | |
1024 | ++ * Returns 0 on success, negative value otherwise. | |
1025 | ++ */ | |
1026 | ++int tmy_check_file_perm(struct domain_info *domain, const char *filename, | |
1027 | ++ const u8 perm) | |
1028 | ++{ | |
1029 | ++ struct path_info name; | |
1030 | ++ const u8 mode = tmy_check_flags(domain, TMY_TOMOYO_MAC_FOR_FILE); | |
1031 | ++ | |
1032 | ++ if (!mode) | |
1033 | ++ return 0; | |
1034 | ++ name.name = filename; | |
1035 | ++ tmy_fill_path_info(&name); | |
1036 | ++ return check_file_perm2(domain, &name, perm, "sysctl", mode); | |
1037 | ++} | |
1038 | ++ | |
1039 | ++/** | |
1040 | ++ * tmy_check_exec_perm - Check permission for "execute". | |
1041 | ++ * | |
1042 | ++ * @domain: Pointer to "struct domain_info". | |
1043 | ++ * @filename: Check permission for "execute". | |
1044 | ++ * @tmp: Buffer for temporal use. | |
1045 | ++ * | |
1046 | ++ * Returns 0 on success, negativevalue otherwise. | |
1047 | ++ */ | |
1048 | ++int tmy_check_exec_perm(struct domain_info *domain, | |
1049 | ++ const struct path_info *filename, | |
1050 | ++ struct tmy_page_buffer *tmp) | |
1051 | ++{ | |
1052 | ++ const u8 mode = tmy_check_flags(domain, TMY_TOMOYO_MAC_FOR_FILE); | |
1053 | ++ | |
1054 | ++ if (!mode) | |
1055 | ++ return 0; | |
1056 | ++ return check_file_perm2(domain, filename, 1, "do_execve", mode); | |
1057 | ++} | |
1058 | ++ | |
1059 | ++/** | |
1060 | ++ * tmy_check_open_permission - Check permission for "read" and "write". | |
1061 | ++ * | |
1062 | ++ * @domain: Pointer to "struct domain_info". | |
1063 | ++ * @path: Pointer to "struct path". | |
1064 | ++ * @flag: Flags for open(). | |
1065 | ++ * | |
1066 | ++ * Returns 0 on success, negative value otherwise. | |
1067 | ++ */ | |
1068 | ++int tmy_check_open_permission(struct domain_info *domain, | |
1069 | ++ struct path *path, const int flag) | |
1070 | ++{ | |
1071 | ++ const u8 acc_mode = ACC_MODE(flag); | |
1072 | ++ int error = -ENOMEM; | |
1073 | ++ struct path_info *buf; | |
1074 | ++ const u8 mode = tmy_check_flags(domain, TMY_TOMOYO_MAC_FOR_FILE); | |
1075 | ++ const bool is_enforce = (mode == 3); | |
1076 | ++ | |
1077 | ++ if (!mode || !path->mnt) | |
1078 | ++ return 0; | |
1079 | ++ if (acc_mode == 0) | |
1080 | ++ return 0; | |
1081 | ++ if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)) | |
1082 | ++ /* | |
1083 | ++ * I don't check directories here because mkdir() and rmdir() | |
1084 | ++ * don't call me. | |
1085 | ++ */ | |
1086 | ++ return 0; | |
1087 | ++ buf = tmy_get_path(path); | |
1088 | ++ if (!buf) | |
1089 | ++ goto out; | |
1090 | ++ error = 0; | |
1091 | ++ /* | |
1092 | ++ * If the filename is specified by "deny_rewrite" keyword, | |
1093 | ++ * we need to check "allow_rewrite" permission when the filename is not | |
1094 | ++ * opened for append mode or the filename is truncated at open time. | |
1095 | ++ */ | |
1096 | ++ if ((acc_mode & MAY_WRITE) && | |
1097 | ++ ((flag & O_TRUNC) || !(flag & O_APPEND)) && | |
1098 | ++ (is_no_rewrite_file(buf))) { | |
1099 | ++ error = check_single_path_permission2(domain, | |
1100 | ++ TMY_TYPE_REWRITE_ACL, | |
1101 | ++ buf, mode); | |
1102 | ++ } | |
1103 | ++ if (!error) | |
1104 | ++ error = check_file_perm2(domain, buf, acc_mode, "open", mode); | |
1105 | ++ if (!error && (flag & O_TRUNC)) | |
1106 | ++ error = check_single_path_permission2(domain, | |
1107 | ++ TMY_TYPE_TRUNCATE_ACL, | |
1108 | ++ buf, mode); | |
1109 | ++out: | |
1110 | ++ tmy_free(buf); | |
1111 | ++ if (!is_enforce) | |
1112 | ++ error = 0; | |
1113 | ++ return error; | |
1114 | ++} | |
1115 | ++ | |
1116 | ++/** | |
1117 | ++ * tmy_check_1path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate" and "symlink". | |
1118 | ++ * | |
1119 | ++ * @domain: Pointer to "struct domain_info". | |
1120 | ++ * @operation: Type of operation. | |
1121 | ++ * @path: Pointer to "struct path". | |
1122 | ++ * | |
1123 | ++ * Returns 0 on success, negative value otherwise. | |
1124 | ++ */ | |
1125 | ++int tmy_check_1path_perm(struct domain_info *domain, const u8 operation, | |
1126 | ++ struct path *path) | |
1127 | ++{ | |
1128 | ++ int error = -ENOMEM; | |
1129 | ++ struct path_info *buf; | |
1130 | ++ const u8 mode = tmy_check_flags(domain, TMY_TOMOYO_MAC_FOR_FILE); | |
1131 | ++ const bool is_enforce = (mode == 3); | |
1132 | ++ | |
1133 | ++ if (!mode || !path->mnt) | |
1134 | ++ return 0; | |
1135 | ++ buf = tmy_get_path(path); | |
1136 | ++ if (!buf) | |
1137 | ++ goto out; | |
1138 | ++ switch (operation) { | |
1139 | ++ case TMY_TYPE_MKDIR_ACL: | |
1140 | ++ case TMY_TYPE_RMDIR_ACL: | |
1141 | ++ if (!buf->is_dir) { | |
1142 | ++ /* tmy_get_path() preserves space for appending "/." */ | |
1143 | ++ strcat((char *) buf->name, "/"); | |
1144 | ++ tmy_fill_path_info(buf); | |
1145 | ++ } | |
1146 | ++ } | |
1147 | ++ error = check_single_path_permission2(domain, operation, buf, mode); | |
1148 | ++out: | |
1149 | ++ tmy_free(buf); | |
1150 | ++ if (!is_enforce) | |
1151 | ++ error = 0; | |
1152 | ++ return error; | |
1153 | ++} | |
1154 | ++ | |
1155 | ++/** | |
1156 | ++ * tmy_check_rewrite_permission - Check permission for "rewrite". | |
1157 | ++ * | |
1158 | ++ * @domain: Pointer to "struct domain_info". | |
1159 | ++ * @filp: Pointer to "struct file". | |
1160 | ++ * | |
1161 | ++ * Returns 0 on success, negative value otherwise. | |
1162 | ++ */ | |
1163 | ++int tmy_check_rewrite_permission(struct domain_info *domain, struct file *filp) | |
1164 | ++{ | |
1165 | ++ int error = -ENOMEM; | |
1166 | ++ const u8 mode = tmy_check_flags(domain, TMY_TOMOYO_MAC_FOR_FILE); | |
1167 | ++ const bool is_enforce = (mode == 3); | |
1168 | ++ struct path_info *buf; | |
1169 | ++ | |
1170 | ++ if (!mode || !filp->f_path.mnt) | |
1171 | ++ return 0; | |
1172 | ++ buf = tmy_get_path(&filp->f_path); | |
1173 | ++ if (!buf) | |
1174 | ++ goto out; | |
1175 | ++ if (!is_no_rewrite_file(buf)) { | |
1176 | ++ error = 0; | |
1177 | ++ goto out; | |
1178 | ++ } | |
1179 | ++ error = check_single_path_permission2(domain, TMY_TYPE_REWRITE_ACL, | |
1180 | ++ buf, mode); | |
1181 | ++out: | |
1182 | ++ tmy_free(buf); | |
1183 | ++ if (!is_enforce) | |
1184 | ++ error = 0; | |
1185 | ++ return error; | |
1186 | ++} | |
1187 | ++ | |
1188 | ++/** | |
1189 | ++ * tmy_check_2path_perm - Check permission for "rename" and "link". | |
1190 | ++ * | |
1191 | ++ * @domain: Pointer to "struct domain_info". | |
1192 | ++ * @operation: Type of operation. | |
1193 | ++ * @path1: Pointer to "struct path". | |
1194 | ++ * @path2: Pointer to "struct path". | |
1195 | ++ * | |
1196 | ++ * Returns 0 on success, negative value otherwise. | |
1197 | ++ */ | |
1198 | ++int tmy_check_2path_perm(struct domain_info * const domain, const u8 operation, | |
1199 | ++ struct path *path1, struct path *path2) | |
1200 | ++{ | |
1201 | ++ int error = -ENOMEM; | |
1202 | ++ struct path_info *buf1, *buf2; | |
1203 | ++ const u8 mode = tmy_check_flags(domain, TMY_TOMOYO_MAC_FOR_FILE); | |
1204 | ++ const bool is_enforce = (mode == 3); | |
1205 | ++ const char *msg; | |
1206 | ++ | |
1207 | ++ if (!mode || !path1->mnt || !path2->mnt) | |
1208 | ++ return 0; | |
1209 | ++ buf1 = tmy_get_path(path1); | |
1210 | ++ buf2 = tmy_get_path(path2); | |
1211 | ++ if (!buf1 || !buf2) | |
1212 | ++ goto out; | |
1213 | ++ { | |
1214 | ++ struct dentry *dentry = path1->dentry; | |
1215 | ++ if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) { | |
1216 | ++ /* tmy_get_path() preserves space for appending "/." */ | |
1217 | ++ if (!buf1->is_dir) { | |
1218 | ++ strcat((char *) buf1->name, "/"); | |
1219 | ++ tmy_fill_path_info(buf1); | |
1220 | ++ } | |
1221 | ++ if (!buf2->is_dir) { | |
1222 | ++ strcat((char *) buf2->name, "/"); | |
1223 | ++ tmy_fill_path_info(buf2); | |
1224 | ++ } | |
1225 | ++ } | |
1226 | ++ } | |
1227 | ++ error = check_double_path_acl(domain, operation, buf1, buf2); | |
1228 | ++ msg = tmy_dp2keyword(operation); | |
1229 | ++ if (!error) | |
1230 | ++ goto out; | |
1231 | ++ if (tmy_verbose_mode(domain)) | |
1232 | ++ printk(KERN_WARNING "TOMOYO-%s: Access '%s %s %s' " | |
1233 | ++ "denied for %s\n", tmy_get_msg(is_enforce), | |
1234 | ++ msg, buf1->name, buf2->name, tmy_get_last_name(domain)); | |
1235 | ++ if (mode == 1 && tmy_check_domain_quota(domain)) | |
1236 | ++ update_double_path_acl(operation, | |
1237 | ++ get_file_pattern(buf1)->name, | |
1238 | ++ get_file_pattern(buf2)->name, | |
1239 | ++ domain, false); | |
1240 | ++out: | |
1241 | ++ tmy_free(buf1); | |
1242 | ++ tmy_free(buf2); | |
1243 | ++ if (!is_enforce) | |
1244 | ++ error = 0; | |
1245 | ++ return error; | |
1246 | ++} |
@@ -0,0 +1,692 @@ | ||
1 | +Subject: Introduce new LSM hooks where vfsmount is available. | |
2 | + | |
3 | +----- What is this patch for? ----- | |
4 | + | |
5 | +There are security_inode_*() LSM hooks for attribute-based MAC, but they are not | |
6 | +suitable for pathname-based MAC because they don't receive "struct vfsmount" | |
7 | +information. | |
8 | + | |
9 | +----- How this patch was developed? ----- | |
10 | + | |
11 | +Two pathname-based MACs, AppArmor and TOMOYO Linux, are trying to merge | |
12 | +upstream. But because of "struct vfsmount" problem, they have been unable to | |
13 | +merge upstream. | |
14 | + | |
15 | +Here are the list of approaches and the reasons of denial. | |
16 | + | |
17 | +(1) Not using LSM | |
18 | + http://lwn.net/Articles/277833/ | |
19 | + | |
20 | + This approach was rejected because security modules should use LSM because the | |
21 | + whole idea behind LSM was to have a single set of hooks for all security | |
22 | + modules; if every module now adds its own set of hooks, that purpose will have | |
23 | + been defeated and the kernel will turn into a big mess of security hooks. | |
24 | + | |
25 | +(2) Retrieving "struct vfsmount" from "struct task_struct". | |
26 | + http://lkml.org/lkml/2007/11/5/388 | |
27 | + | |
28 | + Since "struct task_struct" contains list of "struct vfsmount", | |
29 | + "struct vfsmount" which corresponds to "struct dentry" can be retrieved from | |
30 | + the list unless "mount --bind" is used. | |
31 | + | |
32 | + This approach turned out to cause a critical problem that getting namespace_sem | |
33 | + lock from security_inode_*() triggers AB-BA deadlock. | |
34 | + | |
35 | +(3) Adding "struct vfsmount" parameter to VFS helper functions. | |
36 | + http://lkml.org/lkml/2008/5/29/207 | |
37 | + | |
38 | + This approach adds "struct vfsmount" to VFS helper functions (e.g. vfs_mkdir() | |
39 | + and vfs_symlink()) and LSM hooks inside VFS helper functions. This approach is | |
40 | + helpful for not only AppArmor and TOMOYO Linux 2.x but also SELinux and | |
41 | + auditing purpose, for this approach allows existent LSM users to use pathnames | |
42 | + in their access control and audit logs. | |
43 | + | |
44 | + This approach was rejected by Al Viro, the VFS maintainer, because he thinks | |
45 | + individual filesystem should remain "struct vfsmount"-unaware and VFS helper | |
46 | + functions should not receive "struct vfsmount". | |
47 | + | |
48 | + Al Viro also suggested to move existing security_inode_*() to out of VFS | |
49 | + helper functions so that security_inode_*() can receive "struct vfsmount" | |
50 | + without modifying VFS helper functions, but this suggestion was opposed by | |
51 | + Stephen Smalley because changing the order of permission checks (i.e. | |
52 | + MAC checks before DAC checks) is not acceptable. | |
53 | + | |
54 | +(4) Passing "struct vfsmount" via "struct task_struct". | |
55 | + http://lkml.org/lkml/2007/11/16/157 | |
56 | + | |
57 | + Since we didn't understand the reason why accessing "struct vfsmount" from | |
58 | + LSM hooks inside VFS helper functions is not acceptable, we thought the reason | |
59 | + why VFS helper functions don't receive "struct vfsmount" is the amount of | |
60 | + modifications needed to do so. Thus, we proposed to pass "struct vfsmount" via | |
61 | + "struct task_struct" so that modifications remain minimal. | |
62 | + | |
63 | + This approach was rejected because this is an abuse of "struct task_struct". | |
64 | + | |
65 | +(5) Remembering pathname of "struct vfsmount" via "struct task_struct". | |
66 | + http://lkml.org/lkml/2008/8/19/16 | |
67 | + | |
68 | + Since pathname of a "struct dentry" up to the mount point can be calculated | |
69 | + without "struct vfsmount", absolute pathname of a "struct dentry" can be | |
70 | + calculated if "struct task_struct" can remember absolute pathname of a | |
71 | + "struct vfsmount" which corresponds to "struct dentry". | |
72 | + As we now understand that Al Viro is opposing to access "struct vfsmount" from | |
73 | + LSM hooks inside VFS helper functions, we gave up delivering "struct vfsmount" | |
74 | + to LSM hooks inside VFS helper functions. | |
75 | + Kernel 2.6.26 introduced read-only bind mount feature, and hooks for that | |
76 | + feature (i.e. mnt_want_write() and mnt_drop_write()) were inserted around | |
77 | + VFS helper functions call. Since mnt_want_write() receives "struct vfsmount" | |
78 | + which corresponds to "struct dentry" that will be passed to subsequent VFS | |
79 | + helper functions call, we associated pathname of "struct vfsmount" with | |
80 | + "struct task_struct" instead of associating "struct vfsmount" itself. | |
81 | + | |
82 | + This approach was not explicitly rejected, but there seems to be performance | |
83 | + problem. | |
84 | + | |
85 | +(6) Introducing new LSM hooks. | |
86 | + (this patch) | |
87 | + | |
88 | + We understand that adding new LSM hooks which receive "struct vfsmount" outside | |
89 | + VFS helper functions is the most straightforward approach. This approach has | |
90 | + less impact to existing LSM module and no impact to VFS helper functions. | |
91 | + | |
92 | +(6.1) Introducing security_path_clear() hook. | |
93 | + (this patch) | |
94 | + | |
95 | + To perform DAC performed in vfs_foo() before MAC, we let security_path_foo() | |
96 | + save a result into our own hash table and return 0, and let security_inode_foo() | |
97 | + return the saved result. Since security_inode_foo() is not always called after | |
98 | + security_path_foo(), we need security_path_clear() to clear the hash table. | |
99 | + | |
100 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
101 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
102 | +Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp> | |
103 | +Cc: Al Viro <viro@zeniv.linux.org.uk> | |
104 | +Cc: Christoph Hellwig <hch@lst.de> | |
105 | +Cc: Crispin Cowan <crispin@crispincowan.com> | |
106 | +Cc: Stephen Smalley <sds@tycho.nsa.gov> | |
107 | +Cc: Casey Schaufler <casey@schaufler-ca.com> | |
108 | +Cc: James Morris <jmorris@namei.org> | |
109 | +Signed-off-by: Andrew Morton <akpm@linux-foundation.org> | |
110 | +--- | |
111 | + | |
112 | + fs/namei.c | 46 ++++++++++++++ | |
113 | + fs/open.c | 5 + | |
114 | + include/linux/security.h | 151 +++++++++++++++++++++++++++++++++++++++++++++++ | |
115 | + net/unix/af_unix.c | 5 + | |
116 | + security/Kconfig | 9 ++ | |
117 | + security/capability.c | 63 +++++++++++++++++++ | |
118 | + security/security.c | 73 ++++++++++++++++++++++ | |
119 | + 7 files changed, 352 insertions(+) | |
120 | + | |
121 | +--- linux-next.orig/fs/namei.c | |
122 | ++++ linux-next/fs/namei.c | |
123 | +@@ -1571,12 +1571,17 @@ int may_open(struct nameidata *nd, int a | |
124 | + * Refuse to truncate files with mandatory locks held on them. | |
125 | + */ | |
126 | + error = locks_verify_locked(inode); | |
127 | ++ if (!error) | |
128 | ++ error = security_path_truncate(&nd->path, 0, | |
129 | ++ ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, | |
130 | ++ NULL); | |
131 | + if (!error) { | |
132 | + DQUOT_INIT(inode); | |
133 | + | |
134 | + error = do_truncate(dentry, 0, | |
135 | + ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, | |
136 | + NULL); | |
137 | ++ security_path_clear(); | |
138 | + } | |
139 | + put_write_access(inode); | |
140 | + if (error) | |
141 | +@@ -1601,7 +1606,12 @@ static int __open_namei_create(struct na | |
142 | + | |
143 | + if (!IS_POSIXACL(dir->d_inode)) | |
144 | + mode &= ~current->fs->umask; | |
145 | ++ error = security_path_mknod(&nd->path, path->dentry, mode, 0); | |
146 | ++ if (error) | |
147 | ++ goto out_unlock; | |
148 | + error = vfs_create(dir->d_inode, path->dentry, mode, nd); | |
149 | ++ security_path_clear(); | |
150 | ++out_unlock: | |
151 | + mutex_unlock(&dir->d_inode->i_mutex); | |
152 | + dput(nd->path.dentry); | |
153 | + nd->path.dentry = path->dentry; | |
154 | +@@ -2014,6 +2024,9 @@ asmlinkage long sys_mknodat(int dfd, con | |
155 | + error = mnt_want_write(nd.path.mnt); | |
156 | + if (error) | |
157 | + goto out_dput; | |
158 | ++ error = security_path_mknod(&nd.path, dentry, mode, dev); | |
159 | ++ if (error) | |
160 | ++ goto out_drop_write; | |
161 | + switch (mode & S_IFMT) { | |
162 | + case 0: case S_IFREG: | |
163 | + error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd); | |
164 | +@@ -2026,6 +2039,8 @@ asmlinkage long sys_mknodat(int dfd, con | |
165 | + error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0); | |
166 | + break; | |
167 | + } | |
168 | ++ security_path_clear(); | |
169 | ++out_drop_write: | |
170 | + mnt_drop_write(nd.path.mnt); | |
171 | + out_dput: | |
172 | + dput(dentry); | |
173 | +@@ -2085,7 +2100,12 @@ asmlinkage long sys_mkdirat(int dfd, con | |
174 | + error = mnt_want_write(nd.path.mnt); | |
175 | + if (error) | |
176 | + goto out_dput; | |
177 | ++ error = security_path_mkdir(&nd.path, dentry, mode); | |
178 | ++ if (error) | |
179 | ++ goto out_drop_write; | |
180 | + error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode); | |
181 | ++ security_path_clear(); | |
182 | ++out_drop_write: | |
183 | + mnt_drop_write(nd.path.mnt); | |
184 | + out_dput: | |
185 | + dput(dentry); | |
186 | +@@ -2192,7 +2212,12 @@ static long do_rmdir(int dfd, const char | |
187 | + error = mnt_want_write(nd.path.mnt); | |
188 | + if (error) | |
189 | + goto exit3; | |
190 | ++ error = security_path_rmdir(&nd.path, dentry); | |
191 | ++ if (error) | |
192 | ++ goto exit4; | |
193 | + error = vfs_rmdir(nd.path.dentry->d_inode, dentry); | |
194 | ++ security_path_clear(); | |
195 | ++exit4: | |
196 | + mnt_drop_write(nd.path.mnt); | |
197 | + exit3: | |
198 | + dput(dentry); | |
199 | +@@ -2274,7 +2299,12 @@ static long do_unlinkat(int dfd, const c | |
200 | + error = mnt_want_write(nd.path.mnt); | |
201 | + if (error) | |
202 | + goto exit2; | |
203 | ++ error = security_path_unlink(&nd.path, dentry); | |
204 | ++ if (error) | |
205 | ++ goto exit3; | |
206 | + error = vfs_unlink(nd.path.dentry->d_inode, dentry); | |
207 | ++ security_path_clear(); | |
208 | ++exit3: | |
209 | + mnt_drop_write(nd.path.mnt); | |
210 | + exit2: | |
211 | + dput(dentry); | |
212 | +@@ -2355,7 +2385,12 @@ asmlinkage long sys_symlinkat(const char | |
213 | + error = mnt_want_write(nd.path.mnt); | |
214 | + if (error) | |
215 | + goto out_dput; | |
216 | ++ error = security_path_symlink(&nd.path, dentry, from); | |
217 | ++ if (error) | |
218 | ++ goto out_drop_write; | |
219 | + error = vfs_symlink(nd.path.dentry->d_inode, dentry, from); | |
220 | ++ security_path_clear(); | |
221 | ++out_drop_write: | |
222 | + mnt_drop_write(nd.path.mnt); | |
223 | + out_dput: | |
224 | + dput(dentry); | |
225 | +@@ -2452,7 +2487,12 @@ asmlinkage long sys_linkat(int olddfd, c | |
226 | + error = mnt_want_write(nd.path.mnt); | |
227 | + if (error) | |
228 | + goto out_dput; | |
229 | ++ error = security_path_link(old_path.dentry, &nd.path, new_dentry); | |
230 | ++ if (error) | |
231 | ++ goto out_drop_write; | |
232 | + error = vfs_link(old_path.dentry, nd.path.dentry->d_inode, new_dentry); | |
233 | ++ security_path_clear(); | |
234 | ++out_drop_write: | |
235 | + mnt_drop_write(nd.path.mnt); | |
236 | + out_dput: | |
237 | + dput(new_dentry); | |
238 | +@@ -2684,8 +2724,14 @@ asmlinkage long sys_renameat(int olddfd, | |
239 | + error = mnt_want_write(oldnd.path.mnt); | |
240 | + if (error) | |
241 | + goto exit5; | |
242 | ++ error = security_path_rename(&oldnd.path, old_dentry, | |
243 | ++ &newnd.path, new_dentry); | |
244 | ++ if (error) | |
245 | ++ goto exit6; | |
246 | + error = vfs_rename(old_dir->d_inode, old_dentry, | |
247 | + new_dir->d_inode, new_dentry); | |
248 | ++ security_path_clear(); | |
249 | ++exit6: | |
250 | + mnt_drop_write(oldnd.path.mnt); | |
251 | + exit5: | |
252 | + dput(new_dentry); | |
253 | +--- linux-next.orig/fs/open.c | |
254 | ++++ linux-next/fs/open.c | |
255 | +@@ -272,6 +272,8 @@ static long do_sys_truncate(const char _ | |
256 | + goto put_write_and_out; | |
257 | + | |
258 | + error = locks_verify_truncate(inode, NULL, length); | |
259 | ++ if (!error) | |
260 | ++ error = security_path_truncate(&path, length, 0, NULL); | |
261 | + if (!error) { | |
262 | + DQUOT_INIT(inode); | |
263 | + error = do_truncate(path.dentry, length, 0, NULL); | |
264 | +@@ -329,6 +331,9 @@ static long do_sys_ftruncate(unsigned in | |
265 | + | |
266 | + error = locks_verify_truncate(inode, file, length); | |
267 | + if (!error) | |
268 | ++ error = security_path_truncate(&file->f_path, length, | |
269 | ++ ATTR_MTIME|ATTR_CTIME, file); | |
270 | ++ if (!error) | |
271 | + error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file); | |
272 | + out_putf: | |
273 | + fput(file); | |
274 | +--- linux-next.orig/include/linux/security.h | |
275 | ++++ linux-next/include/linux/security.h | |
276 | +@@ -331,17 +331,37 @@ static inline void security_free_mnt_opt | |
277 | + * @dir contains the inode structure of the parent directory of the new link. | |
278 | + * @new_dentry contains the dentry structure for the new link. | |
279 | + * Return 0 if permission is granted. | |
280 | ++ * @path_link: | |
281 | ++ * Check permission before creating a new hard link to a file. | |
282 | ++ * @old_dentry contains the dentry structure for an existing link | |
283 | ++ * to the file. | |
284 | ++ * @new_dir contains the path structure of the parent directory of | |
285 | ++ * the new link. | |
286 | ++ * @new_dentry contains the dentry structure for the new link. | |
287 | ++ * Return 0 if permission is granted. | |
288 | + * @inode_unlink: | |
289 | + * Check the permission to remove a hard link to a file. | |
290 | + * @dir contains the inode structure of parent directory of the file. | |
291 | + * @dentry contains the dentry structure for file to be unlinked. | |
292 | + * Return 0 if permission is granted. | |
293 | ++ * @path_unlink: | |
294 | ++ * Check the permission to remove a hard link to a file. | |
295 | ++ * @dir contains the path structure of parent directory of the file. | |
296 | ++ * @dentry contains the dentry structure for file to be unlinked. | |
297 | ++ * Return 0 if permission is granted. | |
298 | + * @inode_symlink: | |
299 | + * Check the permission to create a symbolic link to a file. | |
300 | + * @dir contains the inode structure of parent directory of the symbolic link. | |
301 | + * @dentry contains the dentry structure of the symbolic link. | |
302 | + * @old_name contains the pathname of file. | |
303 | + * Return 0 if permission is granted. | |
304 | ++ * @path_symlink: | |
305 | ++ * Check the permission to create a symbolic link to a file. | |
306 | ++ * @dir contains the path structure of parent directory of | |
307 | ++ * the symbolic link. | |
308 | ++ * @dentry contains the dentry structure of the symbolic link. | |
309 | ++ * @old_name contains the pathname of file. | |
310 | ++ * Return 0 if permission is granted. | |
311 | + * @inode_mkdir: | |
312 | + * Check permissions to create a new directory in the existing directory | |
313 | + * associated with inode strcture @dir. | |
314 | +@@ -349,11 +369,25 @@ static inline void security_free_mnt_opt | |
315 | + * @dentry contains the dentry structure of new directory. | |
316 | + * @mode contains the mode of new directory. | |
317 | + * Return 0 if permission is granted. | |
318 | ++ * @path_mkdir: | |
319 | ++ * Check permissions to create a new directory in the existing directory | |
320 | ++ * associated with path strcture @path. | |
321 | ++ * @dir containst the path structure of parent of the directory | |
322 | ++ * to be created. | |
323 | ++ * @dentry contains the dentry structure of new directory. | |
324 | ++ * @mode contains the mode of new directory. | |
325 | ++ * Return 0 if permission is granted. | |
326 | + * @inode_rmdir: | |
327 | + * Check the permission to remove a directory. | |
328 | + * @dir contains the inode structure of parent of the directory to be removed. | |
329 | + * @dentry contains the dentry structure of directory to be removed. | |
330 | + * Return 0 if permission is granted. | |
331 | ++ * @path_rmdir: | |
332 | ++ * Check the permission to remove a directory. | |
333 | ++ * @dir contains the path structure of parent of the directory to be | |
334 | ++ * removed. | |
335 | ++ * @dentry contains the dentry structure of directory to be removed. | |
336 | ++ * Return 0 if permission is granted. | |
337 | + * @inode_mknod: | |
338 | + * Check permissions when creating a special file (or a socket or a fifo | |
339 | + * file created via the mknod system call). Note that if mknod operation | |
340 | +@@ -364,6 +398,15 @@ static inline void security_free_mnt_opt | |
341 | + * @mode contains the mode of the new file. | |
342 | + * @dev contains the device number. | |
343 | + * Return 0 if permission is granted. | |
344 | ++ * @path_mknod: | |
345 | ++ * Check permissions when creating a file. Note that this hook is called | |
346 | ++ * even if mknod operation is being done for a regular file. | |
347 | ++ * @dir contains the path structure of parent of the new file. | |
348 | ++ * @dentry contains the dentry structure of the new file. | |
349 | ++ * @mode contains the mode of the new file. | |
350 | ++ * @dev contains the undecoded device number. Use new_decode_dev() to get | |
351 | ++ * the decoded device number. | |
352 | ++ * Return 0 if permission is granted. | |
353 | + * @inode_rename: | |
354 | + * Check for permission to rename a file or directory. | |
355 | + * @old_dir contains the inode structure for parent of the old link. | |
356 | +@@ -371,6 +414,13 @@ static inline void security_free_mnt_opt | |
357 | + * @new_dir contains the inode structure for parent of the new link. | |
358 | + * @new_dentry contains the dentry structure of the new link. | |
359 | + * Return 0 if permission is granted. | |
360 | ++ * @path_rename: | |
361 | ++ * Check for permission to rename a file or directory. | |
362 | ++ * @old_dir contains the path structure for parent of the old link. | |
363 | ++ * @old_dentry contains the dentry structure of the old link. | |
364 | ++ * @new_dir contains the path structure for parent of the new link. | |
365 | ++ * @new_dentry contains the dentry structure of the new link. | |
366 | ++ * Return 0 if permission is granted. | |
367 | + * @inode_readlink: | |
368 | + * Check the permission to read the symbolic link. | |
369 | + * @dentry contains the dentry structure for the file link. | |
370 | +@@ -399,6 +449,13 @@ static inline void security_free_mnt_opt | |
371 | + * @dentry contains the dentry structure for the file. | |
372 | + * @attr is the iattr structure containing the new file attributes. | |
373 | + * Return 0 if permission is granted. | |
374 | ++ * @path_truncate: | |
375 | ++ * Check permission before truncating a file. | |
376 | ++ * @path contains the path structure for the file. | |
377 | ++ * @length is the new length of the file. | |
378 | ++ * @time_attrs is the flags passed to do_truncate(). | |
379 | ++ * @filp is the file structure (may be NULL). | |
380 | ++ * Return 0 if permission is granted. | |
381 | + * @inode_getattr: | |
382 | + * Check permission before obtaining file attributes. | |
383 | + * @mnt is the vfsmount where the dentry was looked up | |
384 | +@@ -466,6 +523,12 @@ static inline void security_free_mnt_opt | |
385 | + * @inode contains a pointer to the inode. | |
386 | + * @secid contains a pointer to the location where result will be saved. | |
387 | + * In case of failure, @secid will be set to zero. | |
388 | ++ * @path_clear: | |
389 | ++ * Clear error code stored by security_path_*() in case | |
390 | ++ * security_inode_*() was not called when DAC returned an error. | |
391 | ++ * This hook allows LSM modules which use security_path_*() defer | |
392 | ++ * returning LSM's error code till security_inode_*() is called so that | |
393 | ++ * DAC's error (if any) is returned to the caller instead of LSM's error. | |
394 | + * | |
395 | + * Security hooks for file operations | |
396 | + * | |
397 | +@@ -1327,6 +1390,23 @@ struct security_operations { | |
398 | + struct super_block *newsb); | |
399 | + int (*sb_parse_opts_str) (char *options, struct security_mnt_opts *opts); | |
400 | + | |
401 | ++#ifdef CONFIG_SECURITY_PATH | |
402 | ++ int (*path_unlink) (struct path *dir, struct dentry *dentry); | |
403 | ++ int (*path_mkdir) (struct path *dir, struct dentry *dentry, int mode); | |
404 | ++ int (*path_rmdir) (struct path *dir, struct dentry *dentry); | |
405 | ++ int (*path_mknod) (struct path *dir, struct dentry *dentry, int mode, | |
406 | ++ unsigned int dev); | |
407 | ++ int (*path_truncate) (struct path *path, loff_t length, | |
408 | ++ unsigned int time_attrs, struct file *filp); | |
409 | ++ int (*path_symlink) (struct path *dir, struct dentry *dentry, | |
410 | ++ const char *old_name); | |
411 | ++ int (*path_link) (struct dentry *old_dentry, struct path *new_dir, | |
412 | ++ struct dentry *new_dentry); | |
413 | ++ int (*path_rename) (struct path *old_dir, struct dentry *old_dentry, | |
414 | ++ struct path *new_dir, struct dentry *new_dentry); | |
415 | ++ void (*path_clear) (void); | |
416 | ++#endif | |
417 | ++ | |
418 | + int (*inode_alloc_security) (struct inode *inode); | |
419 | + void (*inode_free_security) (struct inode *inode); | |
420 | + int (*inode_init_security) (struct inode *inode, struct inode *dir, | |
421 | +@@ -2685,6 +2765,77 @@ static inline void security_skb_classify | |
422 | + | |
423 | + #endif /* CONFIG_SECURITY_NETWORK_XFRM */ | |
424 | + | |
425 | ++#ifdef CONFIG_SECURITY_PATH | |
426 | ++int security_path_unlink(struct path *dir, struct dentry *dentry); | |
427 | ++int security_path_mkdir(struct path *dir, struct dentry *dentry, int mode); | |
428 | ++int security_path_rmdir(struct path *dir, struct dentry *dentry); | |
429 | ++int security_path_mknod(struct path *dir, struct dentry *dentry, int mode, | |
430 | ++ unsigned int dev); | |
431 | ++int security_path_truncate(struct path *path, loff_t length, | |
432 | ++ unsigned int time_attrs, struct file *filp); | |
433 | ++int security_path_symlink(struct path *dir, struct dentry *dentry, | |
434 | ++ const char *old_name); | |
435 | ++int security_path_link(struct dentry *old_dentry, struct path *new_dir, | |
436 | ++ struct dentry *new_dentry); | |
437 | ++int security_path_rename(struct path *old_dir, struct dentry *old_dentry, | |
438 | ++ struct path *new_dir, struct dentry *new_dentry); | |
439 | ++void security_path_clear(void); | |
440 | ++#else /* CONFIG_SECURITY_PATH */ | |
441 | ++static inline int security_path_unlink(struct path *dir, struct dentry *dentry) | |
442 | ++{ | |
443 | ++ return 0; | |
444 | ++} | |
445 | ++ | |
446 | ++static inline int security_path_mkdir(struct path *dir, struct dentry *dentry, | |
447 | ++ int mode) | |
448 | ++{ | |
449 | ++ return 0; | |
450 | ++} | |
451 | ++ | |
452 | ++static inline int security_path_rmdir(struct path *dir, struct dentry *dentry) | |
453 | ++{ | |
454 | ++ return 0; | |
455 | ++} | |
456 | ++ | |
457 | ++static inline int security_path_mknod(struct path *dir, struct dentry *dentry, | |
458 | ++ int mode, unsigned int dev) | |
459 | ++{ | |
460 | ++ return 0; | |
461 | ++} | |
462 | ++ | |
463 | ++static inline int security_path_truncate(struct path *path, loff_t length, | |
464 | ++ unsigned int time_attrs, | |
465 | ++ struct file *filp) | |
466 | ++{ | |
467 | ++ return 0; | |
468 | ++} | |
469 | ++ | |
470 | ++static inline int security_path_symlink(struct path *dir, struct dentry *dentry, | |
471 | ++ const char *old_name) | |
472 | ++{ | |
473 | ++ return 0; | |
474 | ++} | |
475 | ++ | |
476 | ++static inline int security_path_link(struct dentry *old_dentry, | |
477 | ++ struct path *new_dir, | |
478 | ++ struct dentry *new_dentry) | |
479 | ++{ | |
480 | ++ return 0; | |
481 | ++} | |
482 | ++ | |
483 | ++static inline int security_path_rename(struct path *old_dir, | |
484 | ++ struct dentry *old_dentry, | |
485 | ++ struct path *new_dir, | |
486 | ++ struct dentry *new_dentry) | |
487 | ++{ | |
488 | ++ return 0; | |
489 | ++} | |
490 | ++ | |
491 | ++static inline void security_path_clear(void) | |
492 | ++{ | |
493 | ++} | |
494 | ++#endif /* CONFIG_SECURITY_PATH */ | |
495 | ++ | |
496 | + #ifdef CONFIG_KEYS | |
497 | + #ifdef CONFIG_SECURITY | |
498 | + | |
499 | +--- linux-next.orig/net/unix/af_unix.c | |
500 | ++++ linux-next/net/unix/af_unix.c | |
501 | +@@ -828,7 +828,12 @@ static int unix_bind(struct socket *sock | |
502 | + err = mnt_want_write(nd.path.mnt); | |
503 | + if (err) | |
504 | + goto out_mknod_dput; | |
505 | ++ err = security_path_mknod(&nd.path, dentry, mode, 0); | |
506 | ++ if (err) | |
507 | ++ goto out_mknod_drop_write; | |
508 | + err = vfs_mknod(nd.path.dentry->d_inode, dentry, mode, 0); | |
509 | ++ security_path_clear(); | |
510 | ++out_mknod_drop_write: | |
511 | + mnt_drop_write(nd.path.mnt); | |
512 | + if (err) | |
513 | + goto out_mknod_dput; | |
514 | +--- linux-next.orig/security/Kconfig | |
515 | ++++ linux-next/security/Kconfig | |
516 | +@@ -81,6 +81,15 @@ config SECURITY_NETWORK_XFRM | |
517 | + IPSec. | |
518 | + If you are unsure how to answer this question, answer N. | |
519 | + | |
520 | ++config SECURITY_PATH | |
521 | ++ bool "Security hooks for pathname based access control" | |
522 | ++ depends on SECURITY | |
523 | ++ help | |
524 | ++ This enables the security hooks for pathname based access control. | |
525 | ++ If enabled, a security module can use these hooks to | |
526 | ++ implement pathname based access controls. | |
527 | ++ If you are unsure how to answer this question, answer N. | |
528 | ++ | |
529 | + config SECURITY_FILE_CAPABILITIES | |
530 | + bool "File POSIX Capabilities" | |
531 | + default n | |
532 | +--- linux-next.orig/security/capability.c | |
533 | ++++ linux-next/security/capability.c | |
534 | +@@ -263,6 +263,58 @@ static void cap_inode_getsecid(const str | |
535 | + *secid = 0; | |
536 | + } | |
537 | + | |
538 | ++#ifdef CONFIG_SECURITY_PATH | |
539 | ++static int cap_path_mknod(struct path *dir, struct dentry *dentry, int mode, | |
540 | ++ unsigned int dev) | |
541 | ++{ | |
542 | ++ return 0; | |
543 | ++} | |
544 | ++ | |
545 | ++static int cap_path_mkdir(struct path *dir, struct dentry *dentry, int mode) | |
546 | ++{ | |
547 | ++ return 0; | |
548 | ++} | |
549 | ++ | |
550 | ++static int cap_path_rmdir(struct path *dir, struct dentry *dentry) | |
551 | ++{ | |
552 | ++ return 0; | |
553 | ++} | |
554 | ++ | |
555 | ++static int cap_path_unlink(struct path *dir, struct dentry *dentry) | |
556 | ++{ | |
557 | ++ return 0; | |
558 | ++} | |
559 | ++ | |
560 | ++static int cap_path_symlink(struct path *dir, struct dentry *dentry, | |
561 | ++ const char *old_name) | |
562 | ++{ | |
563 | ++ return 0; | |
564 | ++} | |
565 | ++ | |
566 | ++static int cap_path_link(struct dentry *old_dentry, struct path *new_dir, | |
567 | ++ struct dentry *new_dentry) | |
568 | ++{ | |
569 | ++ return 0; | |
570 | ++} | |
571 | ++ | |
572 | ++static int cap_path_rename(struct path *old_path, struct dentry *old_dentry, | |
573 | ++ struct path *new_path, struct dentry *new_dentry) | |
574 | ++{ | |
575 | ++ return 0; | |
576 | ++} | |
577 | ++ | |
578 | ++static int cap_path_truncate(struct path *path, loff_t length, | |
579 | ++ unsigned int time_attrs, struct file *filp) | |
580 | ++{ | |
581 | ++ return 0; | |
582 | ++} | |
583 | ++ | |
584 | ++static void cap_path_clear(void) | |
585 | ++{ | |
586 | ++} | |
587 | ++ | |
588 | ++#endif | |
589 | ++ | |
590 | + static int cap_file_permission(struct file *file, int mask) | |
591 | + { | |
592 | + return 0; | |
593 | +@@ -883,6 +935,17 @@ void security_fixup_ops(struct security_ | |
594 | + set_to_cap_if_null(ops, inode_setsecurity); | |
595 | + set_to_cap_if_null(ops, inode_listsecurity); | |
596 | + set_to_cap_if_null(ops, inode_getsecid); | |
597 | ++#ifdef CONFIG_SECURITY_PATH | |
598 | ++ set_to_cap_if_null(ops, path_mknod); | |
599 | ++ set_to_cap_if_null(ops, path_mkdir); | |
600 | ++ set_to_cap_if_null(ops, path_rmdir); | |
601 | ++ set_to_cap_if_null(ops, path_unlink); | |
602 | ++ set_to_cap_if_null(ops, path_symlink); | |
603 | ++ set_to_cap_if_null(ops, path_link); | |
604 | ++ set_to_cap_if_null(ops, path_rename); | |
605 | ++ set_to_cap_if_null(ops, path_truncate); | |
606 | ++ set_to_cap_if_null(ops, path_clear); | |
607 | ++#endif | |
608 | + set_to_cap_if_null(ops, file_permission); | |
609 | + set_to_cap_if_null(ops, file_alloc_security); | |
610 | + set_to_cap_if_null(ops, file_free_security); | |
611 | +--- linux-next.orig/security/security.c | |
612 | ++++ linux-next/security/security.c | |
613 | +@@ -341,6 +341,79 @@ int security_inode_init_security(struct | |
614 | + } | |
615 | + EXPORT_SYMBOL(security_inode_init_security); | |
616 | + | |
617 | ++#ifdef CONFIG_SECURITY_PATH | |
618 | ++int security_path_mknod(struct path *path, struct dentry *dentry, int mode, | |
619 | ++ unsigned int dev) | |
620 | ++{ | |
621 | ++ if (unlikely(IS_PRIVATE(path->dentry->d_inode))) | |
622 | ++ return 0; | |
623 | ++ return security_ops->path_mknod(path, dentry, mode, dev); | |
624 | ++} | |
625 | ++EXPORT_SYMBOL(security_path_mknod); | |
626 | ++ | |
627 | ++int security_path_mkdir(struct path *path, struct dentry *dentry, int mode) | |
628 | ++{ | |
629 | ++ if (unlikely(IS_PRIVATE(path->dentry->d_inode))) | |
630 | ++ return 0; | |
631 | ++ return security_ops->path_mkdir(path, dentry, mode); | |
632 | ++} | |
633 | ++ | |
634 | ++int security_path_rmdir(struct path *path, struct dentry *dentry) | |
635 | ++{ | |
636 | ++ if (unlikely(IS_PRIVATE(path->dentry->d_inode))) | |
637 | ++ return 0; | |
638 | ++ return security_ops->path_rmdir(path, dentry); | |
639 | ++} | |
640 | ++ | |
641 | ++int security_path_unlink(struct path *path, struct dentry *dentry) | |
642 | ++{ | |
643 | ++ if (unlikely(IS_PRIVATE(path->dentry->d_inode))) | |
644 | ++ return 0; | |
645 | ++ return security_ops->path_unlink(path, dentry); | |
646 | ++} | |
647 | ++ | |
648 | ++int security_path_symlink(struct path *path, struct dentry *dentry, | |
649 | ++ const char *old_name) | |
650 | ++{ | |
651 | ++ if (unlikely(IS_PRIVATE(path->dentry->d_inode))) | |
652 | ++ return 0; | |
653 | ++ return security_ops->path_symlink(path, dentry, old_name); | |
654 | ++} | |
655 | ++ | |
656 | ++int security_path_link(struct dentry *old_dentry, struct path *new_dir, | |
657 | ++ struct dentry *new_dentry) | |
658 | ++{ | |
659 | ++ if (unlikely(IS_PRIVATE(old_dentry->d_inode))) | |
660 | ++ return 0; | |
661 | ++ return security_ops->path_link(old_dentry, new_dir, new_dentry); | |
662 | ++} | |
663 | ++ | |
664 | ++int security_path_rename(struct path *old_dir, struct dentry *old_dentry, | |
665 | ++ struct path *new_dir, struct dentry *new_dentry) | |
666 | ++{ | |
667 | ++ if (unlikely(IS_PRIVATE(old_dentry->d_inode) || | |
668 | ++ (new_dentry->d_inode && IS_PRIVATE(new_dentry->d_inode)))) | |
669 | ++ return 0; | |
670 | ++ return security_ops->path_rename(old_dir, old_dentry, new_dir, | |
671 | ++ new_dentry); | |
672 | ++} | |
673 | ++ | |
674 | ++int security_path_truncate(struct path *path, loff_t length, | |
675 | ++ unsigned int time_attrs, struct file *filp) | |
676 | ++{ | |
677 | ++ if (unlikely(IS_PRIVATE(path->dentry->d_inode))) | |
678 | ++ return 0; | |
679 | ++ return security_ops->path_truncate(path, length, time_attrs, filp); | |
680 | ++} | |
681 | ++ | |
682 | ++void security_path_clear(void) | |
683 | ++{ | |
684 | ++ return security_ops->path_clear(); | |
685 | ++} | |
686 | ++EXPORT_SYMBOL(security_path_clear); | |
687 | ++ | |
688 | ++#endif | |
689 | ++ | |
690 | + int security_inode_create(struct inode *dir, struct dentry *dentry, int mode) | |
691 | + { | |
692 | + if (unlikely(IS_PRIVATE(dir))) |
@@ -0,0 +1,2547 @@ | ||
1 | +Subject: Common functions for TOMOYO Linux. | |
2 | + | |
3 | +This file contains common functions (e.g. policy I/O, pattern matching). | |
4 | + | |
5 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
6 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
7 | +Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp> | |
8 | +--- | |
9 | + security/tomoyo/common.c | 2209 +++++++++++++++++++++++++++++++++++++++++++++++ | |
10 | + security/tomoyo/common.h | 320 ++++++ | |
11 | + 2 files changed, 2529 insertions(+) | |
12 | + | |
13 | +--- /dev/null | |
14 | ++++ linux-next/security/tomoyo/common.c | |
15 | +@@ -0,0 +1,2209 @@ | |
16 | ++/* | |
17 | ++ * security/tomoyo/common.c | |
18 | ++ * | |
19 | ++ * Common functions for TOMOYO. | |
20 | ++ * | |
21 | ++ * Copyright (C) 2005-2008 NTT DATA CORPORATION | |
22 | ++ * | |
23 | ++ * Version: 2.2.0-pre 2008/10/10 | |
24 | ++ * | |
25 | ++ */ | |
26 | ++ | |
27 | ++#include <linux/uaccess.h> | |
28 | ++#include <linux/security.h> | |
29 | ++#include <linux/hardirq.h> | |
30 | ++#include "realpath.h" | |
31 | ++#include "common.h" | |
32 | ++#include "tomoyo.h" | |
33 | ++ | |
34 | ++/* Set default specified by the kernel config. */ | |
35 | ++#define MAX_ACCEPT_ENTRY 2048 | |
36 | ++ | |
37 | ++/* Has /sbin/init started? */ | |
38 | ++bool sbin_init_started; | |
39 | ++ | |
40 | ++/* String table for functionality that takes 4 modes. */ | |
41 | ++static const char *mode_4[4] = { | |
42 | ++ "disabled", "learning", "permissive", "enforcing" | |
43 | ++}; | |
44 | ++/* String table for functionality that takes 2 modes. */ | |
45 | ++static const char *mode_2[4] = { | |
46 | ++ "disabled", "enabled", "enabled", "enabled" | |
47 | ++}; | |
48 | ++ | |
49 | ++/* Table for profile. */ | |
50 | ++static struct { | |
51 | ++ const char *keyword; | |
52 | ++ unsigned int current_value; | |
53 | ++ const unsigned int max_value; | |
54 | ++} tmy_control_array[TMY_MAX_CONTROL_INDEX] = { | |
55 | ++ [TMY_TOMOYO_MAC_FOR_FILE] = { "MAC_FOR_FILE", 0, 3 }, | |
56 | ++ [TMY_TOMOYO_MAX_ACCEPT_ENTRY] | |
57 | ++ = { "MAX_ACCEPT_ENTRY", MAX_ACCEPT_ENTRY, INT_MAX }, | |
58 | ++ [TMY_TOMOYO_VERBOSE] = { "TOMOYO_VERBOSE", 1, 1 }, | |
59 | ++}; | |
60 | ++ | |
61 | ++/* Profile table. Memory is allocated as needed. */ | |
62 | ++static struct profile { | |
63 | ++ unsigned int value[TMY_MAX_CONTROL_INDEX]; | |
64 | ++ const struct path_info *comment; | |
65 | ++} *profile_ptr[MAX_PROFILES]; | |
66 | ++ | |
67 | ++/* Permit policy management by non-root user? */ | |
68 | ++static bool manage_by_non_root; | |
69 | ++ | |
70 | ++/* Utility functions. */ | |
71 | ++ | |
72 | ++/* Open operation for /sys/kernel/security/tomoyo/ interface. */ | |
73 | ++static int tmy_open_control(const u8 type, struct file *file); | |
74 | ++/* Close /sys/kernel/security/tomoyo/ interface. */ | |
75 | ++static int tmy_close_control(struct file *file); | |
76 | ++/* Read operation for /sys/kernel/security/tomoyo/ interface. */ | |
77 | ++static int tmy_read_control(struct file *file, char __user *buffer, | |
78 | ++ const int buffer_len); | |
79 | ++/* Write operation for /sys/kernel/security/tomoyo/ interface. */ | |
80 | ++static int tmy_write_control(struct file *file, const char __user *buffer, | |
81 | ++ const int buffer_len); | |
82 | ++ | |
83 | ++/** | |
84 | ++ * is_byte_range - Check whether the string isa \ooo style octal value. | |
85 | ++ * | |
86 | ++ * @str: Pointer to the string. | |
87 | ++ * | |
88 | ++ * Returns true if @str is a \ooo style octal value, false otherwise. | |
89 | ++ */ | |
90 | ++static bool is_byte_range(const char *str) | |
91 | ++{ | |
92 | ++ return *str >= '0' && *str++ <= '3' && | |
93 | ++ *str >= '0' && *str++ <= '7' && | |
94 | ++ *str >= '0' && *str <= '7'; | |
95 | ++} | |
96 | ++ | |
97 | ++/** | |
98 | ++ * is_decimal - Check whether the character is a decimal character. | |
99 | ++ * | |
100 | ++ * @c: The character to check. | |
101 | ++ * | |
102 | ++ * Returns true if @c is a decimal character, false otherwise. | |
103 | ++ */ | |
104 | ++static bool is_decimal(const char c) | |
105 | ++{ | |
106 | ++ return c >= '0' && c <= '9'; | |
107 | ++} | |
108 | ++ | |
109 | ++/** | |
110 | ++ * is_hexadecimal - Check whether the character is a hexadecimal character. | |
111 | ++ * | |
112 | ++ * @c: The character to check. | |
113 | ++ * | |
114 | ++ * Returns true if @c is a hexadecimal character, false otherwise. | |
115 | ++ */ | |
116 | ++static bool is_hexadecimal(const char c) | |
117 | ++{ | |
118 | ++ return (c >= '0' && c <= '9') || | |
119 | ++ (c >= 'A' && c <= 'F') || | |
120 | ++ (c >= 'a' && c <= 'f'); | |
121 | ++} | |
122 | ++ | |
123 | ++/** | |
124 | ++ * is_alphabet_char - Check whether the character is an alphabet. | |
125 | ++ * | |
126 | ++ * @c: The character to check. | |
127 | ++ * | |
128 | ++ * Returns true if @c is an alphabet character, false otherwise. | |
129 | ++ */ | |
130 | ++static bool is_alphabet_char(const char c) | |
131 | ++{ | |
132 | ++ return (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f'); | |
133 | ++} | |
134 | ++ | |
135 | ++/** | |
136 | ++ * make_byte - Make byte value from three octal characters. | |
137 | ++ * | |
138 | ++ * @c1: The first character. | |
139 | ++ * @c2: The second character. | |
140 | ++ * @c3: The third character. | |
141 | ++ * | |
142 | ++ * Returns byte value. | |
143 | ++ */ | |
144 | ++static u8 make_byte(const u8 c1, const u8 c2, const u8 c3) | |
145 | ++{ | |
146 | ++ return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); | |
147 | ++} | |
148 | ++ | |
149 | ++/** | |
150 | ++ * str_starts - Check whether the given string starts with the given keyword. | |
151 | ++ * | |
152 | ++ * @src: Pointer to pointer to the string. | |
153 | ++ * @find: Pointer to the keyword. | |
154 | ++ * | |
155 | ++ * Returns true if @src starts with @find, false otherwise. | |
156 | ++ * | |
157 | ++ * The @src is updated to point the first character after the @find | |
158 | ++ * if @src starts with @find. | |
159 | ++ */ | |
160 | ++static bool str_starts(char **src, const char *find) | |
161 | ++{ | |
162 | ++ const int len = strlen(find); | |
163 | ++ char *tmp = *src; | |
164 | ++ | |
165 | ++ if (strncmp(tmp, find, len)) | |
166 | ++ return false; | |
167 | ++ tmp += len; | |
168 | ++ *src = tmp; | |
169 | ++ return true; | |
170 | ++} | |
171 | ++ | |
172 | ++/** | |
173 | ++ * normalize_line - Format string. | |
174 | ++ * | |
175 | ++ * @buffer: The line to normalize. | |
176 | ++ * | |
177 | ++ * Leading and trailing whitespaces are removed. | |
178 | ++ * Multiple whitespaces are packed into single space. | |
179 | ++ * | |
180 | ++ * Returns nothing. | |
181 | ++ */ | |
182 | ++static void normalize_line(unsigned char *buffer) | |
183 | ++{ | |
184 | ++ unsigned char *sp = buffer; | |
185 | ++ unsigned char *dp = buffer; | |
186 | ++ bool first = true; | |
187 | ++ | |
188 | ++ while (*sp && (*sp <= ' ' || *sp >= 127)) | |
189 | ++ sp++; | |
190 | ++ while (*sp) { | |
191 | ++ if (!first) | |
192 | ++ *dp++ = ' '; | |
193 | ++ first = false; | |
194 | ++ while (*sp > ' ' && *sp < 127) | |
195 | ++ *dp++ = *sp++; | |
196 | ++ while (*sp && (*sp <= ' ' || *sp >= 127)) | |
197 | ++ sp++; | |
198 | ++ } | |
199 | ++ *dp = '\0'; | |
200 | ++} | |
201 | ++ | |
202 | ++/** | |
203 | ++ * tmy_is_correct_path - Validate a pathname. | |
204 | ++ * @filename: The pathname to check. | |
205 | ++ * @start_type: Should the pathname start with '/'? | |
206 | ++ * 1 = must / -1 = must not / 0 = don't care | |
207 | ++ * @pattern_type: Can the pathname contain a wildcard? | |
208 | ++ * 1 = must / -1 = must not / 0 = don't care | |
209 | ++ * @end_type: Should the pathname end with '/'? | |
210 | ++ * 1 = must / -1 = must not / 0 = don't care | |
211 | ++ * @function: The name of function calling me. | |
212 | ++ * | |
213 | ++ * Check whether the given filename follows the naming rules. | |
214 | ++ * Returns true if @filename follows the naming rules, false otherwise. | |
215 | ++ */ | |
216 | ++bool tmy_is_correct_path(const char *filename, const s8 start_type, | |
217 | ++ const s8 pattern_type, const s8 end_type, | |
218 | ++ const char *function) | |
219 | ++{ | |
220 | ++ bool contains_pattern = false; | |
221 | ++ unsigned char c; | |
222 | ++ unsigned char d; | |
223 | ++ unsigned char e; | |
224 | ++ const char *original_filename = filename; | |
225 | ++ | |
226 | ++ if (!filename) | |
227 | ++ goto out; | |
228 | ++ c = *filename; | |
229 | ++ if (start_type == 1) { /* Must start with '/' */ | |
230 | ++ if (c != '/') | |
231 | ++ goto out; | |
232 | ++ } else if (start_type == -1) { /* Must not start with '/' */ | |
233 | ++ if (c == '/') | |
234 | ++ goto out; | |
235 | ++ } | |
236 | ++ if (c) | |
237 | ++ c = *(strchr(filename, '\0') - 1); | |
238 | ++ if (end_type == 1) { /* Must end with '/' */ | |
239 | ++ if (c != '/') | |
240 | ++ goto out; | |
241 | ++ } else if (end_type == -1) { /* Must not end with '/' */ | |
242 | ++ if (c == '/') | |
243 | ++ goto out; | |
244 | ++ } | |
245 | ++ while ((c = *filename++) != '\0') { | |
246 | ++ if (c == '\\') { | |
247 | ++ switch ((c = *filename++)) { | |
248 | ++ case '\\': /* "\\" */ | |
249 | ++ continue; | |
250 | ++ case '$': /* "\$" */ | |
251 | ++ case '+': /* "\+" */ | |
252 | ++ case '?': /* "\?" */ | |
253 | ++ case '*': /* "\*" */ | |
254 | ++ case '@': /* "\@" */ | |
255 | ++ case 'x': /* "\x" */ | |
256 | ++ case 'X': /* "\X" */ | |
257 | ++ case 'a': /* "\a" */ | |
258 | ++ case 'A': /* "\A" */ | |
259 | ++ case '-': /* "\-" */ | |
260 | ++ if (pattern_type == -1) | |
261 | ++ break; /* Must not contain pattern */ | |
262 | ++ contains_pattern = true; | |
263 | ++ continue; | |
264 | ++ case '0': /* "\ooo" */ | |
265 | ++ case '1': | |
266 | ++ case '2': | |
267 | ++ case '3': | |
268 | ++ d = *filename++; | |
269 | ++ if (d < '0' || d > '7') | |
270 | ++ break; | |
271 | ++ e = *filename++; | |
272 | ++ if (e < '0' || e > '7') | |
273 | ++ break; | |
274 | ++ c = make_byte(c, d, e); | |
275 | ++ if (c && (c <= ' ' || c >= 127)) | |
276 | ++ continue; /* pattern is not \000 */ | |
277 | ++ } | |
278 | ++ goto out; | |
279 | ++ } else if (c <= ' ' || c >= 127) { | |
280 | ++ goto out; | |
281 | ++ } | |
282 | ++ } | |
283 | ++ if (pattern_type == 1) { /* Must contain pattern */ | |
284 | ++ if (!contains_pattern) | |
285 | ++ goto out; | |
286 | ++ } | |
287 | ++ return true; | |
288 | ++out: | |
289 | ++ printk(KERN_DEBUG "%s: Invalid pathname '%s'\n", function, | |
290 | ++ original_filename); | |
291 | ++ return false; | |
292 | ++} | |
293 | ++ | |
294 | ++/** | |
295 | ++ * tmy_is_correct_domain - Check whether the given domainname follows the naming rules. | |
296 | ++ * @domainname: The domainname to check. | |
297 | ++ * @function: The name of function calling me. | |
298 | ++ * | |
299 | ++ * Returns true if @domainname follows the naming rules, false otherwise. | |
300 | ++ */ | |
301 | ++bool tmy_is_correct_domain(const unsigned char *domainname, | |
302 | ++ const char *function) | |
303 | ++{ | |
304 | ++ unsigned char c; | |
305 | ++ unsigned char d; | |
306 | ++ unsigned char e; | |
307 | ++ const char *org_domainname = domainname; | |
308 | ++ | |
309 | ++ if (!domainname || strncmp(domainname, ROOT_NAME, ROOT_NAME_LEN)) | |
310 | ++ goto out; | |
311 | ++ domainname += ROOT_NAME_LEN; | |
312 | ++ if (!*domainname) | |
313 | ++ return true; | |
314 | ++ do { | |
315 | ++ if (*domainname++ != ' ') | |
316 | ++ goto out; | |
317 | ++ if (*domainname++ != '/') | |
318 | ++ goto out; | |
319 | ++ while ((c = *domainname) != '\0' && c != ' ') { | |
320 | ++ domainname++; | |
321 | ++ if (c == '\\') { | |
322 | ++ c = *domainname++; | |
323 | ++ switch ((c)) { | |
324 | ++ case '\\': /* "\\" */ | |
325 | ++ continue; | |
326 | ++ case '0': /* "\ooo" */ | |
327 | ++ case '1': | |
328 | ++ case '2': | |
329 | ++ case '3': | |
330 | ++ d = *domainname++; | |
331 | ++ if (d < '0' || d > '7') | |
332 | ++ break; | |
333 | ++ e = *domainname++; | |
334 | ++ if (e < '0' || e > '7') | |
335 | ++ break; | |
336 | ++ c = make_byte(c, d, e); | |
337 | ++ if (c && (c <= ' ' || c >= 127)) | |
338 | ++ /* pattern is not \000 */ | |
339 | ++ continue; | |
340 | ++ } | |
341 | ++ goto out; | |
342 | ++ } else if (c < ' ' || c >= 127) { | |
343 | ++ goto out; | |
344 | ++ } | |
345 | ++ } | |
346 | ++ } while (*domainname); | |
347 | ++ return true; | |
348 | ++out: | |
349 | ++ printk(KERN_DEBUG "%s: Invalid domainname '%s'\n", function, | |
350 | ++ org_domainname); | |
351 | ++ return false; | |
352 | ++} | |
353 | ++ | |
354 | ++/** | |
355 | ++ * tmy_is_domain_def - Check whether the given token can be a domainname. | |
356 | ++ * | |
357 | ++ * @buffer: The token to check. | |
358 | ++ * | |
359 | ++ * Returns true if @buffer possibly be a domainname, false otherwise. | |
360 | ++ */ | |
361 | ++bool tmy_is_domain_def(const unsigned char *buffer) | |
362 | ++{ | |
363 | ++ return !strncmp(buffer, ROOT_NAME, ROOT_NAME_LEN); | |
364 | ++} | |
365 | ++ | |
366 | ++/** | |
367 | ++ * tmy_find_domain - Find a domain by the given name. | |
368 | ++ * | |
369 | ++ * @domainname: The domainname to find. | |
370 | ++ * | |
371 | ++ * Returns pointer to "struct domain_info" if found, NULL otherwise. | |
372 | ++ */ | |
373 | ++struct domain_info *tmy_find_domain(const char *domainname) | |
374 | ++{ | |
375 | ++ struct domain_info *domain; | |
376 | ++ struct path_info name; | |
377 | ++ | |
378 | ++ name.name = domainname; | |
379 | ++ tmy_fill_path_info(&name); | |
380 | ++ list1_for_each_entry(domain, &domain_list, list) { | |
381 | ++ if (!domain->is_deleted && | |
382 | ++ !tmy_pathcmp(&name, domain->domainname)) | |
383 | ++ return domain; | |
384 | ++ } | |
385 | ++ return NULL; | |
386 | ++} | |
387 | ++ | |
388 | ++/** | |
389 | ++ * path_depth - Evaluate the number of '/' in a string. | |
390 | ++ * | |
391 | ++ * @pathname: The string to evaluate. | |
392 | ++ * | |
393 | ++ * Returns path depth of the string. | |
394 | ++ * | |
395 | ++ * I score 2 for each of the '/' in the @pathname | |
396 | ++ * and score 1 if the @pathname ends with '/'. | |
397 | ++ */ | |
398 | ++static int path_depth(const char *pathname) | |
399 | ++{ | |
400 | ++ int i = 0; | |
401 | ++ | |
402 | ++ if (pathname) { | |
403 | ++ char *ep = strchr(pathname, '\0'); | |
404 | ++ if (pathname < ep--) { | |
405 | ++ if (*ep != '/') | |
406 | ++ i++; | |
407 | ++ while (pathname <= ep) | |
408 | ++ if (*ep-- == '/') | |
409 | ++ i += 2; | |
410 | ++ } | |
411 | ++ } | |
412 | ++ return i; | |
413 | ++} | |
414 | ++ | |
415 | ++/** | |
416 | ++ * const_part_length - Evaluate the initial length without a pattern in a token. | |
417 | ++ * | |
418 | ++ * @filename: The string to evaluate. | |
419 | ++ * | |
420 | ++ * Returns the initial length without a pattern in @filename. | |
421 | ++ */ | |
422 | ++static int const_part_length(const char *filename) | |
423 | ++{ | |
424 | ++ char c; | |
425 | ++ int len = 0; | |
426 | ++ | |
427 | ++ if (!filename) | |
428 | ++ return 0; | |
429 | ++ while ((c = *filename++) != '\0') { | |
430 | ++ if (c != '\\') { | |
431 | ++ len++; | |
432 | ++ continue; | |
433 | ++ } | |
434 | ++ c = *filename++; | |
435 | ++ switch (c) { | |
436 | ++ case '\\': /* "\\" */ | |
437 | ++ len += 2; | |
438 | ++ continue; | |
439 | ++ case '0': /* "\ooo" */ | |
440 | ++ case '1': | |
441 | ++ case '2': | |
442 | ++ case '3': | |
443 | ++ c = *filename++; | |
444 | ++ if (c < '0' || c > '7') | |
445 | ++ break; | |
446 | ++ c = *filename++; | |
447 | ++ if (c < '0' || c > '7') | |
448 | ++ break; | |
449 | ++ len += 4; | |
450 | ++ continue; | |
451 | ++ } | |
452 | ++ break; | |
453 | ++ } | |
454 | ++ return len; | |
455 | ++} | |
456 | ++ | |
457 | ++/** | |
458 | ++ * tmy_fill_path_info - Fill in "struct path_info" members. | |
459 | ++ * | |
460 | ++ * @ptr: Pointer to "struct path_info" to fill in. | |
461 | ++ * | |
462 | ++ * The caller sets "struct path_info"->name. | |
463 | ++ */ | |
464 | ++void tmy_fill_path_info(struct path_info *ptr) | |
465 | ++{ | |
466 | ++ const char *name = ptr->name; | |
467 | ++ const int len = strlen(name); | |
468 | ++ | |
469 | ++ ptr->total_len = len; | |
470 | ++ ptr->const_len = const_part_length(name); | |
471 | ++ ptr->is_dir = len && (name[len - 1] == '/'); | |
472 | ++ ptr->is_patterned = (ptr->const_len < len); | |
473 | ++ ptr->hash = full_name_hash(name, len); | |
474 | ++ ptr->depth = path_depth(name); | |
475 | ++} | |
476 | ++ | |
477 | ++/** | |
478 | ++ * file_matches_to_pattern2 - Pattern matching without '/' character | |
479 | ++ * and "\-" pattern. | |
480 | ++ * | |
481 | ++ * @filename: The start of string to check. | |
482 | ++ * @filename_end: The end of string to check. | |
483 | ++ * @pattern: The start of pattern to compare. | |
484 | ++ * @pattern_end: The end of pattern to compare. | |
485 | ++ * | |
486 | ++ * Returns true if @filename matches @pattern, false otherwise. | |
487 | ++ */ | |
488 | ++static bool file_matches_to_pattern2(const char *filename, | |
489 | ++ const char *filename_end, | |
490 | ++ const char *pattern, | |
491 | ++ const char *pattern_end) | |
492 | ++{ | |
493 | ++ while (filename < filename_end && pattern < pattern_end) { | |
494 | ++ char c; | |
495 | ++ if (*pattern != '\\') { | |
496 | ++ if (*filename++ != *pattern++) | |
497 | ++ return false; | |
498 | ++ continue; | |
499 | ++ } | |
500 | ++ c = *filename; | |
501 | ++ pattern++; | |
502 | ++ switch (*pattern) { | |
503 | ++ int i; | |
504 | ++ int j; | |
505 | ++ case '?': | |
506 | ++ if (c == '/') { | |
507 | ++ return false; | |
508 | ++ } else if (c == '\\') { | |
509 | ++ if (filename[1] == '\\') | |
510 | ++ filename++; | |
511 | ++ else if (is_byte_range(filename + 1)) | |
512 | ++ filename += 3; | |
513 | ++ else | |
514 | ++ return false; | |
515 | ++ } | |
516 | ++ break; | |
517 | ++ case '\\': | |
518 | ++ if (c != '\\') | |
519 | ++ return false; | |
520 | ++ if (*++filename != '\\') | |
521 | ++ return false; | |
522 | ++ break; | |
523 | ++ case '+': | |
524 | ++ if (!is_decimal(c)) | |
525 | ++ return false; | |
526 | ++ break; | |
527 | ++ case 'x': | |
528 | ++ if (!is_hexadecimal(c)) | |
529 | ++ return false; | |
530 | ++ break; | |
531 | ++ case 'a': | |
532 | ++ if (!is_alphabet_char(c)) | |
533 | ++ return false; | |
534 | ++ break; | |
535 | ++ case '0': | |
536 | ++ case '1': | |
537 | ++ case '2': | |
538 | ++ case '3': | |
539 | ++ if (c == '\\' && is_byte_range(filename + 1) | |
540 | ++ && strncmp(filename + 1, pattern, 3) == 0) { | |
541 | ++ filename += 3; | |
542 | ++ pattern += 2; | |
543 | ++ break; | |
544 | ++ } | |
545 | ++ return false; /* Not matched. */ | |
546 | ++ case '*': | |
547 | ++ case '@': | |
548 | ++ for (i = 0; i <= filename_end - filename; i++) { | |
549 | ++ if (file_matches_to_pattern2(filename + i, | |
550 | ++ filename_end, | |
551 | ++ pattern + 1, | |
552 | ++ pattern_end)) | |
553 | ++ return true; | |
554 | ++ c = filename[i]; | |
555 | ++ if (c == '.' && *pattern == '@') | |
556 | ++ break; | |
557 | ++ if (c != '\\') | |
558 | ++ continue; | |
559 | ++ if (filename[i + 1] == '\\') | |
560 | ++ i++; | |
561 | ++ else if (is_byte_range(filename + i + 1)) | |
562 | ++ i += 3; | |
563 | ++ else | |
564 | ++ break; /* Bad pattern. */ | |
565 | ++ } | |
566 | ++ return false; /* Not matched. */ | |
567 | ++ default: | |
568 | ++ j = 0; | |
569 | ++ c = *pattern; | |
570 | ++ if (c == '$') { | |
571 | ++ while (is_decimal(filename[j])) | |
572 | ++ j++; | |
573 | ++ } else if (c == 'X') { | |
574 | ++ while (is_hexadecimal(filename[j])) | |
575 | ++ j++; | |
576 | ++ } else if (c == 'A') { | |
577 | ++ while (is_alphabet_char(filename[j])) | |
578 | ++ j++; | |
579 | ++ } | |
580 | ++ for (i = 1; i <= j; i++) { | |
581 | ++ if (file_matches_to_pattern2(filename + i, | |
582 | ++ filename_end, | |
583 | ++ pattern + 1, | |
584 | ++ pattern_end)) | |
585 | ++ return true; | |
586 | ++ } | |
587 | ++ return false; /* Not matched or bad pattern. */ | |
588 | ++ } | |
589 | ++ filename++; | |
590 | ++ pattern++; | |
591 | ++ } | |
592 | ++ while (*pattern == '\\' && | |
593 | ++ (*(pattern + 1) == '*' || *(pattern + 1) == '@')) | |
594 | ++ pattern += 2; | |
595 | ++ return filename == filename_end && pattern == pattern_end; | |
596 | ++} | |
597 | ++ | |
598 | ++/** | |
599 | ++ * file_matches_to_pattern - Pattern matching without without '/' character. | |
600 | ++ * | |
601 | ++ * @filename: The start of string to check. | |
602 | ++ * @filename_end: The end of string to check. | |
603 | ++ * @pattern: The start of pattern to compare. | |
604 | ++ * @pattern_end: The end of pattern to compare. | |
605 | ++ * | |
606 | ++ * Returns true if @filename matches @pattern, false otherwise. | |
607 | ++ */ | |
608 | ++static bool file_matches_to_pattern(const char *filename, | |
609 | ++ const char *filename_end, | |
610 | ++ const char *pattern, | |
611 | ++ const char *pattern_end) | |
612 | ++{ | |
613 | ++ const char *pattern_start = pattern; | |
614 | ++ bool first = true; | |
615 | ++ bool result; | |
616 | ++ | |
617 | ++ while (pattern < pattern_end - 1) { | |
618 | ++ /* Split at "\-" pattern. */ | |
619 | ++ if (*pattern++ != '\\' || *pattern++ != '-') | |
620 | ++ continue; | |
621 | ++ result = file_matches_to_pattern2(filename, filename_end, | |
622 | ++ pattern_start, pattern - 2); | |
623 | ++ if (first) | |
624 | ++ result = !result; | |
625 | ++ if (result) | |
626 | ++ return false; | |
627 | ++ first = false; | |
628 | ++ pattern_start = pattern; | |
629 | ++ } | |
630 | ++ result = file_matches_to_pattern2(filename, filename_end, | |
631 | ++ pattern_start, pattern_end); | |
632 | ++ return first ? result : !result; | |
633 | ++} | |
634 | ++ | |
635 | ++/** | |
636 | ++ * tmy_path_matches_pattern - Check whether the given filename matches the given pattern. | |
637 | ++ * @filename: The filename to check. | |
638 | ++ * @pattern: The pattern to compare. | |
639 | ++ * | |
640 | ++ * Returns true if matches, false otherwise. | |
641 | ++ * | |
642 | ++ * The following patterns are available. | |
643 | ++ * \\ \ itself. | |
644 | ++ * \ooo Octal representation of a byte. | |
645 | ++ * \* More than or equals to 0 character other than '/'. | |
646 | ++ * \@ More than or equals to 0 character other than '/' or '.'. | |
647 | ++ * \? 1 byte character other than '/'. | |
648 | ++ * \$ More than or equals to 1 decimal digit. | |
649 | ++ * \+ 1 decimal digit. | |
650 | ++ * \X More than or equals to 1 hexadecimal digit. | |
651 | ++ * \x 1 hexadecimal digit. | |
652 | ++ * \A More than or equals to 1 alphabet character. | |
653 | ++ * \a 1 alphabet character. | |
654 | ++ * \- Subtraction operator. | |
655 | ++ */ | |
656 | ++bool tmy_path_matches_pattern(const struct path_info *filename, | |
657 | ++ const struct path_info *pattern) | |
658 | ++{ | |
659 | ++ /* | |
660 | ++ if (!filename || !pattern) | |
661 | ++ return false; | |
662 | ++ */ | |
663 | ++ const char *f = filename->name; | |
664 | ++ const char *p = pattern->name; | |
665 | ++ const int len = pattern->const_len; | |
666 | ++ | |
667 | ++ /* If @pattern doesn't contain pattern, I can use strcmp(). */ | |
668 | ++ if (!pattern->is_patterned) | |
669 | ++ return !tmy_pathcmp(filename, pattern); | |
670 | ++ /* Dont compare if the number of '/' differs. */ | |
671 | ++ if (filename->depth != pattern->depth) | |
672 | ++ return false; | |
673 | ++ /* Compare the initial length without patterns. */ | |
674 | ++ if (strncmp(f, p, len)) | |
675 | ++ return false; | |
676 | ++ f += len; | |
677 | ++ p += len; | |
678 | ++ /* Main loop. Compare each directory component. */ | |
679 | ++ while (*f && *p) { | |
680 | ++ const char *f_delimiter = strchr(f, '/'); | |
681 | ++ const char *p_delimiter = strchr(p, '/'); | |
682 | ++ if (!f_delimiter) | |
683 | ++ f_delimiter = strchr(f, '\0'); | |
684 | ++ if (!p_delimiter) | |
685 | ++ p_delimiter = strchr(p, '\0'); | |
686 | ++ if (!file_matches_to_pattern(f, f_delimiter, p, p_delimiter)) | |
687 | ++ return false; | |
688 | ++ f = f_delimiter; | |
689 | ++ if (*f) | |
690 | ++ f++; | |
691 | ++ p = p_delimiter; | |
692 | ++ if (*p) | |
693 | ++ p++; | |
694 | ++ } | |
695 | ++ /* Ignore trailing "\*" and "\@" in @pattern. */ | |
696 | ++ while (*p == '\\' && | |
697 | ++ (*(p + 1) == '*' || *(p + 1) == '@')) | |
698 | ++ p += 2; | |
699 | ++ return !*f && !*p; | |
700 | ++} | |
701 | ++ | |
702 | ++/** | |
703 | ++ * tmy_io_printf - Transactional printf() to "struct tmy_io_buffer" structure. | |
704 | ++ * | |
705 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
706 | ++ * @fmt: The printf()'s format string, followed by parameters. | |
707 | ++ * | |
708 | ++ * Returns true on success, false otherwise. | |
709 | ++ * | |
710 | ++ * The snprintf() will truncate, but tmy_io_printf() won't. | |
711 | ++ */ | |
712 | ++bool tmy_io_printf(struct tmy_io_buffer *head, const char *fmt, ...) | |
713 | ++{ | |
714 | ++ va_list args; | |
715 | ++ int len; | |
716 | ++ int pos = head->read_avail; | |
717 | ++ int size = head->readbuf_size - pos; | |
718 | ++ | |
719 | ++ if (size <= 0) | |
720 | ++ return false; | |
721 | ++ va_start(args, fmt); | |
722 | ++ len = vsnprintf(head->read_buf + pos, size, fmt, args); | |
723 | ++ va_end(args); | |
724 | ++ if (pos + len >= head->readbuf_size) | |
725 | ++ return false; | |
726 | ++ head->read_avail += len; | |
727 | ++ return true; | |
728 | ++} | |
729 | ++ | |
730 | ++/** | |
731 | ++ * tmy_get_exe - Get tmy_realpath() of current process. | |
732 | ++ * | |
733 | ++ * Returns the tmy_realpath() of current process on success, NULL otherwise. | |
734 | ++ * | |
735 | ++ * This function uses tmy_alloc(), so the caller must call tmy_free() | |
736 | ++ * if this function didn't return NULL. | |
737 | ++ */ | |
738 | ++static const char *tmy_get_exe(void) | |
739 | ++{ | |
740 | ++ struct mm_struct *mm = current->mm; | |
741 | ++ struct vm_area_struct *vma; | |
742 | ++ const char *cp = NULL; | |
743 | ++ | |
744 | ++ if (!mm) | |
745 | ++ return NULL; | |
746 | ++ down_read(&mm->mmap_sem); | |
747 | ++ for (vma = mm->mmap; vma; vma = vma->vm_next) { | |
748 | ++ if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { | |
749 | ++ cp = tmy_realpath_from_path(&vma->vm_file->f_path); | |
750 | ++ break; | |
751 | ++ } | |
752 | ++ } | |
753 | ++ up_read(&mm->mmap_sem); | |
754 | ++ return cp; | |
755 | ++} | |
756 | ++ | |
757 | ++/** | |
758 | ++ * tmy_get_msg - Get warning message. | |
759 | ++ * | |
760 | ++ * @is_enforce: Is it enforcing mode? | |
761 | ++ * | |
762 | ++ * Returns "ERROR" or "WARNING". | |
763 | ++ */ | |
764 | ++const char *tmy_get_msg(const bool is_enforce) | |
765 | ++{ | |
766 | ++ if (is_enforce) | |
767 | ++ return "ERROR"; | |
768 | ++ else | |
769 | ++ return "WARNING"; | |
770 | ++} | |
771 | ++ | |
772 | ++/** | |
773 | ++ * tmy_check_flags - Check mode for specified functionality. | |
774 | ++ * | |
775 | ++ * @domain: Pointer to "struct domain_info". | |
776 | ++ * @index: The functionality to check mode. | |
777 | ++ * | |
778 | ++ * Returns the mode of specified functionality. | |
779 | ++ */ | |
780 | ++unsigned int tmy_check_flags(const struct domain_info *domain, const u8 index) | |
781 | ++{ | |
782 | ++ const u8 profile = domain->profile; | |
783 | ++ | |
784 | ++ if (unlikely(in_interrupt())) { | |
785 | ++ static u8 count = 20; | |
786 | ++ if (count) { | |
787 | ++ count--; | |
788 | ++ printk(KERN_ERR "BUG: sleeping function called " | |
789 | ++ "from invalid context.\n"); | |
790 | ++ dump_stack(); | |
791 | ++ } | |
792 | ++ return 0; | |
793 | ++ } | |
794 | ++ return sbin_init_started && index < TMY_MAX_CONTROL_INDEX | |
795 | ++#if MAX_PROFILES != 256 | |
796 | ++ && profile < MAX_PROFILES | |
797 | ++#endif | |
798 | ++ && profile_ptr[profile] ? | |
799 | ++ profile_ptr[profile]->value[index] : 0; | |
800 | ++} | |
801 | ++ | |
802 | ++/** | |
803 | ++ * tmy_verbose_mode - Check whether TOMOYO is verbose mode. | |
804 | ++ * | |
805 | ++ * @domain: Pointer to "struct domain_info". | |
806 | ++ * | |
807 | ++ * Returns true if domain policy violation warning should be printed to | |
808 | ++ * console. | |
809 | ++ */ | |
810 | ++bool tmy_verbose_mode(const struct domain_info *domain) | |
811 | ++{ | |
812 | ++ return tmy_check_flags(domain, TMY_TOMOYO_VERBOSE) != 0; | |
813 | ++} | |
814 | ++ | |
815 | ++/** | |
816 | ++ * tmy_check_domain_quota - Check for domain's quota. | |
817 | ++ * | |
818 | ++ * @domain: Pointer to "struct domain_info". | |
819 | ++ * | |
820 | ++ * Returns true if the domain is not exceeded quota, false otherwise. | |
821 | ++ */ | |
822 | ++bool tmy_check_domain_quota(struct domain_info * const domain) | |
823 | ++{ | |
824 | ++ unsigned int count = 0; | |
825 | ++ struct acl_info *ptr; | |
826 | ++ | |
827 | ++ if (!domain) | |
828 | ++ return true; | |
829 | ++ list1_for_each_entry(ptr, &domain->acl_info_list, list) { | |
830 | ++ if (ptr->type & ACL_DELETED) | |
831 | ++ continue; | |
832 | ++ switch (tmy_acl_type2(ptr)) { | |
833 | ++ struct single_path_acl_record *acl1; | |
834 | ++ struct double_path_acl_record *acl2; | |
835 | ++ u16 perm; | |
836 | ++ case TYPE_SINGLE_PATH_ACL: | |
837 | ++ acl1 = container_of(ptr, struct single_path_acl_record, | |
838 | ++ head); | |
839 | ++ perm = acl1->perm; | |
840 | ++ if (perm & (1 << TMY_TYPE_EXECUTE_ACL)) | |
841 | ++ count++; | |
842 | ++ if (perm & | |
843 | ++ ((1 << TMY_TYPE_READ_ACL) | | |
844 | ++ (1 << TMY_TYPE_WRITE_ACL))) | |
845 | ++ count++; | |
846 | ++ if (perm & (1 << TMY_TYPE_CREATE_ACL)) | |
847 | ++ count++; | |
848 | ++ if (perm & (1 << TMY_TYPE_UNLINK_ACL)) | |
849 | ++ count++; | |
850 | ++ if (perm & (1 << TMY_TYPE_MKDIR_ACL)) | |
851 | ++ count++; | |
852 | ++ if (perm & (1 << TMY_TYPE_RMDIR_ACL)) | |
853 | ++ count++; | |
854 | ++ if (perm & (1 << TMY_TYPE_MKFIFO_ACL)) | |
855 | ++ count++; | |
856 | ++ if (perm & (1 << TMY_TYPE_MKSOCK_ACL)) | |
857 | ++ count++; | |
858 | ++ if (perm & (1 << TMY_TYPE_MKBLOCK_ACL)) | |
859 | ++ count++; | |
860 | ++ if (perm & (1 << TMY_TYPE_MKCHAR_ACL)) | |
861 | ++ count++; | |
862 | ++ if (perm & (1 << TMY_TYPE_TRUNCATE_ACL)) | |
863 | ++ count++; | |
864 | ++ if (perm & (1 << TMY_TYPE_SYMLINK_ACL)) | |
865 | ++ count++; | |
866 | ++ if (perm & (1 << TMY_TYPE_REWRITE_ACL)) | |
867 | ++ count++; | |
868 | ++ break; | |
869 | ++ case TYPE_DOUBLE_PATH_ACL: | |
870 | ++ acl2 = container_of(ptr, struct double_path_acl_record, | |
871 | ++ head); | |
872 | ++ perm = acl2->perm; | |
873 | ++ if (perm & (1 << TMY_TYPE_LINK_ACL)) | |
874 | ++ count++; | |
875 | ++ if (perm & (1 << TMY_TYPE_RENAME_ACL)) | |
876 | ++ count++; | |
877 | ++ break; | |
878 | ++ } | |
879 | ++ } | |
880 | ++ if (count < tmy_check_flags(domain, TMY_TOMOYO_MAX_ACCEPT_ENTRY)) | |
881 | ++ return true; | |
882 | ++ if (!domain->quota_warned) { | |
883 | ++ domain->quota_warned = true; | |
884 | ++ printk(KERN_WARNING "TOMOYO-WARNING: " | |
885 | ++ "Domain '%s' has so many ACLs to hold. " | |
886 | ++ "Stopped learning mode.\n", domain->domainname->name); | |
887 | ++ } | |
888 | ++ return false; | |
889 | ++} | |
890 | ++ | |
891 | ++/** | |
892 | ++ * tmy_find_or_assign_new_profile - Create a new profile. | |
893 | ++ * | |
894 | ++ * @profile: Profile number to create. | |
895 | ++ * | |
896 | ++ * Returns pointer to "struct profile" on success, NULL otherwise. | |
897 | ++ */ | |
898 | ++static struct profile *tmy_find_or_assign_new_profile(const unsigned int | |
899 | ++ profile) | |
900 | ++{ | |
901 | ++ static DEFINE_MUTEX(lock); | |
902 | ++ struct profile *ptr = NULL; | |
903 | ++ | |
904 | ++ /***** EXCLUSIVE SECTION START *****/ | |
905 | ++ mutex_lock(&lock); | |
906 | ++ if (profile < MAX_PROFILES) { | |
907 | ++ ptr = profile_ptr[profile]; | |
908 | ++ if (ptr) | |
909 | ++ goto ok; | |
910 | ++ ptr = tmy_alloc_element(sizeof(*ptr)); | |
911 | ++ if (ptr) { | |
912 | ++ int i; | |
913 | ++ for (i = 0; i < TMY_MAX_CONTROL_INDEX; i++) | |
914 | ++ ptr->value[i] | |
915 | ++ = tmy_control_array[i].current_value; | |
916 | ++ mb(); /* Avoid out-of-order execution. */ | |
917 | ++ profile_ptr[profile] = ptr; | |
918 | ++ } | |
919 | ++ } | |
920 | ++ok: | |
921 | ++ mutex_unlock(&lock); | |
922 | ++ /***** EXCLUSIVE SECTION END *****/ | |
923 | ++ return ptr; | |
924 | ++} | |
925 | ++ | |
926 | ++/** | |
927 | ++ * write_profile - Write profile table. | |
928 | ++ * | |
929 | ++ * @head: Pointer to "struct tmy_io_buffer" | |
930 | ++ * | |
931 | ++ * Returns 0 on success, negative value otherwise. | |
932 | ++ */ | |
933 | ++static int write_profile(struct tmy_io_buffer *head) | |
934 | ++{ | |
935 | ++ char *data = head->write_buf; | |
936 | ++ unsigned int i; | |
937 | ++ unsigned int value; | |
938 | ++ char *cp; | |
939 | ++ struct profile *profile; | |
940 | ++ unsigned long num; | |
941 | ++ | |
942 | ++ cp = strchr(data, '-'); | |
943 | ++ if (cp) | |
944 | ++ *cp = '\0'; | |
945 | ++ if (strict_strtoul(data, 10, &num)) | |
946 | ++ return -EINVAL; | |
947 | ++ if (cp) | |
948 | ++ data = cp + 1; | |
949 | ++ profile = tmy_find_or_assign_new_profile(num); | |
950 | ++ if (!profile) | |
951 | ++ return -EINVAL; | |
952 | ++ cp = strchr(data, '='); | |
953 | ++ if (!cp) | |
954 | ++ return -EINVAL; | |
955 | ++ *cp = '\0'; | |
956 | ++ tmy_update_counter(TMY_UPDATES_COUNTER_PROFILE); | |
957 | ++ if (!strcmp(data, "COMMENT")) { | |
958 | ++ profile->comment = tmy_save_name(cp + 1); | |
959 | ++ return 0; | |
960 | ++ } | |
961 | ++ for (i = 0; i < TMY_MAX_CONTROL_INDEX; i++) { | |
962 | ++ if (strcmp(data, tmy_control_array[i].keyword)) | |
963 | ++ continue; | |
964 | ++ if (sscanf(cp + 1, "%u", &value) != 1) { | |
965 | ++ int j; | |
966 | ++ const char **modes; | |
967 | ++ switch (i) { | |
968 | ++ case TMY_TOMOYO_VERBOSE: | |
969 | ++ modes = mode_2; | |
970 | ++ break; | |
971 | ++ default: | |
972 | ++ modes = mode_4; | |
973 | ++ break; | |
974 | ++ } | |
975 | ++ for (j = 0; j < 4; j++) { | |
976 | ++ if (strcmp(cp + 1, modes[j])) | |
977 | ++ continue; | |
978 | ++ value = j; | |
979 | ++ break; | |
980 | ++ } | |
981 | ++ if (j == 4) | |
982 | ++ return -EINVAL; | |
983 | ++ } else if (value > tmy_control_array[i].max_value) { | |
984 | ++ value = tmy_control_array[i].max_value; | |
985 | ++ } | |
986 | ++ profile->value[i] = value; | |
987 | ++ return 0; | |
988 | ++ } | |
989 | ++ return -EINVAL; | |
990 | ++} | |
991 | ++ | |
992 | ++/** | |
993 | ++ * read_profile - Read profile table. | |
994 | ++ * | |
995 | ++ * @head: Pointer to "struct tmy_io_buffer" | |
996 | ++ * | |
997 | ++ * Returns 0. | |
998 | ++ */ | |
999 | ++static int read_profile(struct tmy_io_buffer *head) | |
1000 | ++{ | |
1001 | ++ static const int total = TMY_MAX_CONTROL_INDEX + 1; | |
1002 | ++ int step; | |
1003 | ++ | |
1004 | ++ if (head->read_eof) | |
1005 | ++ return 0; | |
1006 | ++ for (step = head->read_step; step < MAX_PROFILES * total; step++) { | |
1007 | ++ const u8 index = step / total; | |
1008 | ++ u8 type = step % total; | |
1009 | ++ const struct profile *profile = profile_ptr[index]; | |
1010 | ++ head->read_step = step; | |
1011 | ++ if (!profile) | |
1012 | ++ continue; | |
1013 | ++ if (!type) { /* Print profile' comment tag. */ | |
1014 | ++ if (!tmy_io_printf(head, "%u-COMMENT=%s\n", | |
1015 | ++ index, profile->comment ? | |
1016 | ++ profile->comment->name : "")) | |
1017 | ++ break; | |
1018 | ++ continue; | |
1019 | ++ } | |
1020 | ++ type--; | |
1021 | ++ if (type < TMY_MAX_CONTROL_INDEX) { | |
1022 | ++ const unsigned int value = profile->value[type]; | |
1023 | ++ const char **modes = NULL; | |
1024 | ++ const char *keyword = tmy_control_array[type].keyword; | |
1025 | ++ switch (tmy_control_array[type].max_value) { | |
1026 | ++ case 3: | |
1027 | ++ modes = mode_4; | |
1028 | ++ break; | |
1029 | ++ case 1: | |
1030 | ++ modes = mode_2; | |
1031 | ++ break; | |
1032 | ++ } | |
1033 | ++ if (modes) { | |
1034 | ++ if (!tmy_io_printf(head, "%u-%s=%s\n", index, | |
1035 | ++ keyword, modes[value])) | |
1036 | ++ break; | |
1037 | ++ } else { | |
1038 | ++ if (!tmy_io_printf(head, "%u-%s=%u\n", index, | |
1039 | ++ keyword, value)) | |
1040 | ++ break; | |
1041 | ++ } | |
1042 | ++ } | |
1043 | ++ } | |
1044 | ++ if (step == MAX_PROFILES * total) | |
1045 | ++ head->read_eof = true; | |
1046 | ++ return 0; | |
1047 | ++} | |
1048 | ++ | |
1049 | ++/* Structure for policy manager. */ | |
1050 | ++struct policy_manager_entry { | |
1051 | ++ struct list1_head list; | |
1052 | ++ /* A path to program or a domainname. */ | |
1053 | ++ const struct path_info *manager; | |
1054 | ++ bool is_domain; /* True if manager is a domainname. */ | |
1055 | ++ bool is_deleted; /* True if this entry is deleted. */ | |
1056 | ++}; | |
1057 | ++ | |
1058 | ++/* | |
1059 | ++ * The list for "struct policy_manager_entry". | |
1060 | ++ * | |
1061 | ++ * This list is updated only inside update_manager_entry(), thus | |
1062 | ++ * no global mutex exists. | |
1063 | ++ */ | |
1064 | ++static LIST1_HEAD(policy_manager_list); | |
1065 | ++ | |
1066 | ++/** | |
1067 | ++ * update_manager_entry - Add a manager entry. | |
1068 | ++ * | |
1069 | ++ * @manager: The path to manager or the domainnamme. | |
1070 | ++ * @is_delete: True if it is a delete request. | |
1071 | ++ * | |
1072 | ++ * Returns 0 on success, negative value otherwise. | |
1073 | ++ */ | |
1074 | ++static int update_manager_entry(const char *manager, const bool is_delete) | |
1075 | ++{ | |
1076 | ++ struct policy_manager_entry *new_entry; | |
1077 | ++ struct policy_manager_entry *ptr; | |
1078 | ++ static DEFINE_MUTEX(lock); | |
1079 | ++ const struct path_info *saved_manager; | |
1080 | ++ int error = -ENOMEM; | |
1081 | ++ bool is_domain = false; | |
1082 | ++ | |
1083 | ++ if (tmy_is_domain_def(manager)) { | |
1084 | ++ if (!tmy_is_correct_domain(manager, __func__)) | |
1085 | ++ return -EINVAL; | |
1086 | ++ is_domain = true; | |
1087 | ++ } else { | |
1088 | ++ if (!tmy_is_correct_path(manager, 1, -1, -1, __func__)) | |
1089 | ++ return -EINVAL; | |
1090 | ++ } | |
1091 | ++ saved_manager = tmy_save_name(manager); | |
1092 | ++ if (!saved_manager) | |
1093 | ++ return -ENOMEM; | |
1094 | ++ /***** EXCLUSIVE SECTION START *****/ | |
1095 | ++ mutex_lock(&lock); | |
1096 | ++ list1_for_each_entry(ptr, &policy_manager_list, list) { | |
1097 | ++ if (ptr->manager != saved_manager) | |
1098 | ++ continue; | |
1099 | ++ ptr->is_deleted = is_delete; | |
1100 | ++ error = 0; | |
1101 | ++ goto out; | |
1102 | ++ } | |
1103 | ++ if (is_delete) { | |
1104 | ++ error = -ENOENT; | |
1105 | ++ goto out; | |
1106 | ++ } | |
1107 | ++ new_entry = tmy_alloc_element(sizeof(*new_entry)); | |
1108 | ++ if (!new_entry) | |
1109 | ++ goto out; | |
1110 | ++ new_entry->manager = saved_manager; | |
1111 | ++ new_entry->is_domain = is_domain; | |
1112 | ++ list1_add_tail(&new_entry->list, &policy_manager_list); | |
1113 | ++ error = 0; | |
1114 | ++out: | |
1115 | ++ mutex_unlock(&lock); | |
1116 | ++ /***** EXCLUSIVE SECTION END *****/ | |
1117 | ++ if (!error) | |
1118 | ++ tmy_update_counter(TMY_UPDATES_COUNTER_MANAGER); | |
1119 | ++ return error; | |
1120 | ++} | |
1121 | ++ | |
1122 | ++/** | |
1123 | ++ * write_manager_policy - Write manager policy. | |
1124 | ++ * | |
1125 | ++ * @head: Pointer to "struct tmy_io_buffer" | |
1126 | ++ * | |
1127 | ++ * Returns 0 on success, negative value otherwise. | |
1128 | ++ */ | |
1129 | ++static int write_manager_policy(struct tmy_io_buffer *head) | |
1130 | ++{ | |
1131 | ++ char *data = head->write_buf; | |
1132 | ++ bool is_delete = str_starts(&data, KEYWORD_DELETE); | |
1133 | ++ | |
1134 | ++ if (!strcmp(data, "manage_by_non_root")) { | |
1135 | ++ manage_by_non_root = !is_delete; | |
1136 | ++ return 0; | |
1137 | ++ } | |
1138 | ++ return update_manager_entry(data, is_delete); | |
1139 | ++} | |
1140 | ++ | |
1141 | ++/** | |
1142 | ++ * read_manager_policy - Read manager policy. | |
1143 | ++ * | |
1144 | ++ * @head: Pointer to "struct tmy_io_buffer" | |
1145 | ++ * | |
1146 | ++ * Returns 0. | |
1147 | ++ */ | |
1148 | ++static int read_manager_policy(struct tmy_io_buffer *head) | |
1149 | ++{ | |
1150 | ++ struct list1_head *pos; | |
1151 | ++ | |
1152 | ++ if (head->read_eof) | |
1153 | ++ return 0; | |
1154 | ++ list1_for_each_cookie(pos, head->read_var2, &policy_manager_list) { | |
1155 | ++ struct policy_manager_entry *ptr; | |
1156 | ++ ptr = list1_entry(pos, struct policy_manager_entry, list); | |
1157 | ++ if (ptr->is_deleted) | |
1158 | ++ continue; | |
1159 | ++ if (!tmy_io_printf(head, "%s\n", ptr->manager->name)) | |
1160 | ++ return 0; | |
1161 | ++ } | |
1162 | ++ head->read_eof = true; | |
1163 | ++ return 0; | |
1164 | ++} | |
1165 | ++ | |
1166 | ++/** | |
1167 | ++ * is_policy_manager - Check whether the current process is a policy manager. | |
1168 | ++ * | |
1169 | ++ * Returns true if the current process is permitted to modify policy | |
1170 | ++ * via /sys/kernel/security/tomoyo/ interface. | |
1171 | ++ */ | |
1172 | ++static bool is_policy_manager(void) | |
1173 | ++{ | |
1174 | ++ struct policy_manager_entry *ptr; | |
1175 | ++ const char *exe; | |
1176 | ++ const struct task_struct *task = current; | |
1177 | ++ const struct path_info *domainname = tmy_domain()->domainname; | |
1178 | ++ bool found = false; | |
1179 | ++ | |
1180 | ++ if (!sbin_init_started) | |
1181 | ++ return true; | |
1182 | ++ if (!manage_by_non_root && (task->cred->uid || task->cred->euid)) | |
1183 | ++ return false; | |
1184 | ++ list1_for_each_entry(ptr, &policy_manager_list, list) { | |
1185 | ++ if (!ptr->is_deleted && ptr->is_domain | |
1186 | ++ && !tmy_pathcmp(domainname, ptr->manager)) | |
1187 | ++ return true; | |
1188 | ++ } | |
1189 | ++ exe = tmy_get_exe(); | |
1190 | ++ if (!exe) | |
1191 | ++ return false; | |
1192 | ++ list1_for_each_entry(ptr, &policy_manager_list, list) { | |
1193 | ++ if (!ptr->is_deleted && !ptr->is_domain | |
1194 | ++ && !strcmp(exe, ptr->manager->name)) { | |
1195 | ++ found = true; | |
1196 | ++ break; | |
1197 | ++ } | |
1198 | ++ } | |
1199 | ++ if (!found) { /* Reduce error messages. */ | |
1200 | ++ static pid_t last_pid; | |
1201 | ++ const pid_t pid = current->pid; | |
1202 | ++ if (last_pid != pid) { | |
1203 | ++ printk(KERN_WARNING "%s ( %s ) is not permitted to " | |
1204 | ++ "update policies.\n", domainname->name, exe); | |
1205 | ++ last_pid = pid; | |
1206 | ++ } | |
1207 | ++ } | |
1208 | ++ tmy_free(exe); | |
1209 | ++ return found; | |
1210 | ++} | |
1211 | ++ | |
1212 | ++/** | |
1213 | ++ * is_select_one - Parse select command. | |
1214 | ++ * | |
1215 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1216 | ++ * @data: String to parse. | |
1217 | ++ * | |
1218 | ++ * Returns true on success, false otherwise. | |
1219 | ++ */ | |
1220 | ++static bool is_select_one(struct tmy_io_buffer *head, const char *data) | |
1221 | ++{ | |
1222 | ++ unsigned int pid; | |
1223 | ++ struct domain_info *domain = NULL; | |
1224 | ++ | |
1225 | ++ if (sscanf(data, "pid=%u", &pid) == 1) { | |
1226 | ++ struct task_struct *p; | |
1227 | ++ /***** CRITICAL SECTION START *****/ | |
1228 | ++ read_lock(&tasklist_lock); | |
1229 | ++ p = find_task_by_vpid(pid); | |
1230 | ++ if (p) | |
1231 | ++ domain = tmy_real_domain(p); | |
1232 | ++ read_unlock(&tasklist_lock); | |
1233 | ++ /***** CRITICAL SECTION END *****/ | |
1234 | ++ } else if (!strncmp(data, "domain=", 7)) { | |
1235 | ++ if (tmy_is_domain_def(data + 7)) | |
1236 | ++ domain = tmy_find_domain(data + 7); | |
1237 | ++ } else | |
1238 | ++ return false; | |
1239 | ++ head->read_avail = 0; | |
1240 | ++ tmy_io_printf(head, "# select %s\n", data); | |
1241 | ++ head->read_single_domain = true; | |
1242 | ++ head->read_eof = !domain; | |
1243 | ++ if (domain) { | |
1244 | ++ struct domain_info *d; | |
1245 | ++ head->read_var1 = NULL; | |
1246 | ++ list1_for_each_entry(d, &domain_list, list) { | |
1247 | ++ if (d == domain) | |
1248 | ++ break; | |
1249 | ++ head->read_var1 = &d->list; | |
1250 | ++ } | |
1251 | ++ head->read_var2 = NULL; | |
1252 | ++ head->read_bit = 0; | |
1253 | ++ head->read_step = 0; | |
1254 | ++ if (domain->is_deleted) | |
1255 | ++ tmy_io_printf(head, "# This is a deleted domain.\n"); | |
1256 | ++ } | |
1257 | ++ head->write_var1 = domain; | |
1258 | ++ return true; | |
1259 | ++} | |
1260 | ++ | |
1261 | ++/** | |
1262 | ++ * write_domain_policy - Write domain policy. | |
1263 | ++ * | |
1264 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1265 | ++ * | |
1266 | ++ * Returns 0 on success, negative value otherwise. | |
1267 | ++ */ | |
1268 | ++static int write_domain_policy(struct tmy_io_buffer *head) | |
1269 | ++{ | |
1270 | ++ char *data = head->write_buf; | |
1271 | ++ struct domain_info *domain = head->write_var1; | |
1272 | ++ bool is_delete = false; | |
1273 | ++ bool is_select = false; | |
1274 | ++ bool is_undelete = false; | |
1275 | ++ unsigned int profile; | |
1276 | ++ | |
1277 | ++ if (str_starts(&data, KEYWORD_DELETE)) | |
1278 | ++ is_delete = true; | |
1279 | ++ else if (str_starts(&data, KEYWORD_SELECT)) | |
1280 | ++ is_select = true; | |
1281 | ++ else if (str_starts(&data, KEYWORD_UNDELETE)) | |
1282 | ++ is_undelete = true; | |
1283 | ++ if (is_select && is_select_one(head, data)) | |
1284 | ++ return 0; | |
1285 | ++ /* Don't allow updating policies by non manager programs. */ | |
1286 | ++ if (!is_policy_manager()) | |
1287 | ++ return -EPERM; | |
1288 | ++ if (tmy_is_domain_def(data)) { | |
1289 | ++ domain = NULL; | |
1290 | ++ if (is_delete) | |
1291 | ++ tmy_delete_domain(data); | |
1292 | ++ else if (is_select) | |
1293 | ++ domain = tmy_find_domain(data); | |
1294 | ++ else if (is_undelete) | |
1295 | ++ domain = tmy_undelete_domain(data); | |
1296 | ++ else | |
1297 | ++ domain = tmy_find_or_assign_new_domain(data, 0); | |
1298 | ++ head->write_var1 = domain; | |
1299 | ++ tmy_update_counter(TMY_UPDATES_COUNTER_DOMAIN_POLICY); | |
1300 | ++ return 0; | |
1301 | ++ } | |
1302 | ++ if (!domain) | |
1303 | ++ return -EINVAL; | |
1304 | ++ | |
1305 | ++ if (sscanf(data, KEYWORD_USE_PROFILE "%u", &profile) == 1 | |
1306 | ++ && profile < MAX_PROFILES) { | |
1307 | ++ if (profile_ptr[profile] || !sbin_init_started) | |
1308 | ++ domain->profile = (u8) profile; | |
1309 | ++ return 0; | |
1310 | ++ } | |
1311 | ++ if (!strcmp(data, KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) { | |
1312 | ++ tmy_set_domain_flag(domain, is_delete, | |
1313 | ++ DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ); | |
1314 | ++ return 0; | |
1315 | ++ } | |
1316 | ++ return tmy_write_file_policy(data, domain, is_delete); | |
1317 | ++} | |
1318 | ++ | |
1319 | ++/** | |
1320 | ++ * print_single_path_acl - Print a single path ACL entry. | |
1321 | ++ * | |
1322 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1323 | ++ * @ptr: Pointer to "struct single_path_acl_record". | |
1324 | ++ * | |
1325 | ++ * Returns true on success, false otherwise. | |
1326 | ++ */ | |
1327 | ++static bool print_single_path_acl(struct tmy_io_buffer *head, | |
1328 | ++ struct single_path_acl_record *ptr) | |
1329 | ++{ | |
1330 | ++ int pos; | |
1331 | ++ u8 bit; | |
1332 | ++ const char *atmark = ""; | |
1333 | ++ const char *filename; | |
1334 | ++ const u16 perm = ptr->perm; | |
1335 | ++ | |
1336 | ++ filename = ptr->filename->name; | |
1337 | ++ for (bit = head->read_bit; bit < MAX_SINGLE_PATH_OPERATION; bit++) { | |
1338 | ++ const char *msg; | |
1339 | ++ if (!(perm & (1 << bit))) | |
1340 | ++ continue; | |
1341 | ++ /* Print "read/write" instead of "read" and "write". */ | |
1342 | ++ if ((bit == TMY_TYPE_READ_ACL || bit == TMY_TYPE_WRITE_ACL) | |
1343 | ++ && (perm & (1 << TMY_TYPE_READ_WRITE_ACL))) | |
1344 | ++ continue; | |
1345 | ++ msg = tmy_sp2keyword(bit); | |
1346 | ++ pos = head->read_avail; | |
1347 | ++ if (!tmy_io_printf(head, "allow_%s %s%s\n", msg, | |
1348 | ++ atmark, filename)) | |
1349 | ++ goto out; | |
1350 | ++ } | |
1351 | ++ head->read_bit = 0; | |
1352 | ++ return true; | |
1353 | ++out: | |
1354 | ++ head->read_bit = bit; | |
1355 | ++ head->read_avail = pos; | |
1356 | ++ return false; | |
1357 | ++} | |
1358 | ++ | |
1359 | ++/** | |
1360 | ++ * print_double_path_acl - Print a double path ACL entry. | |
1361 | ++ * | |
1362 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1363 | ++ * @ptr: Pointer to "struct double_path_acl_record". | |
1364 | ++ * | |
1365 | ++ * Returns true on success, false otherwise. | |
1366 | ++ */ | |
1367 | ++static bool print_double_path_acl(struct tmy_io_buffer *head, | |
1368 | ++ struct double_path_acl_record *ptr) | |
1369 | ++{ | |
1370 | ++ int pos; | |
1371 | ++ const char *atmark1 = ""; | |
1372 | ++ const char *atmark2 = ""; | |
1373 | ++ const char *filename1; | |
1374 | ++ const char *filename2; | |
1375 | ++ const u8 perm = ptr->perm; | |
1376 | ++ u8 bit; | |
1377 | ++ | |
1378 | ++ filename1 = ptr->filename1->name; | |
1379 | ++ filename2 = ptr->filename2->name; | |
1380 | ++ for (bit = head->read_bit; bit < MAX_DOUBLE_PATH_OPERATION; bit++) { | |
1381 | ++ const char *msg; | |
1382 | ++ if (!(perm & (1 << bit))) | |
1383 | ++ continue; | |
1384 | ++ msg = tmy_dp2keyword(bit); | |
1385 | ++ pos = head->read_avail; | |
1386 | ++ if (!tmy_io_printf(head, "allow_%s %s%s %s%s\n", msg, | |
1387 | ++ atmark1, filename1, atmark2, filename2)) | |
1388 | ++ goto out; | |
1389 | ++ } | |
1390 | ++ head->read_bit = 0; | |
1391 | ++ return true; | |
1392 | ++out: | |
1393 | ++ head->read_bit = bit; | |
1394 | ++ head->read_avail = pos; | |
1395 | ++ return false; | |
1396 | ++} | |
1397 | ++ | |
1398 | ++/** | |
1399 | ++ * print_entry - Print an ACL entry. | |
1400 | ++ * | |
1401 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1402 | ++ * @ptr: Pointer to an ACL entry. | |
1403 | ++ * | |
1404 | ++ * Returns true on success, false otherwise. | |
1405 | ++ */ | |
1406 | ++static bool print_entry(struct tmy_io_buffer *head, struct acl_info *ptr) | |
1407 | ++{ | |
1408 | ++ const u8 acl_type = tmy_acl_type2(ptr); | |
1409 | ++ | |
1410 | ++ if (acl_type & ACL_DELETED) | |
1411 | ++ return true; | |
1412 | ++ if (acl_type == TYPE_SINGLE_PATH_ACL) { | |
1413 | ++ struct single_path_acl_record *acl | |
1414 | ++ = container_of(ptr, struct single_path_acl_record, | |
1415 | ++ head); | |
1416 | ++ return print_single_path_acl(head, acl); | |
1417 | ++ } | |
1418 | ++ if (acl_type == TYPE_DOUBLE_PATH_ACL) { | |
1419 | ++ struct double_path_acl_record *acl | |
1420 | ++ = container_of(ptr, struct double_path_acl_record, | |
1421 | ++ head); | |
1422 | ++ return print_double_path_acl(head, acl); | |
1423 | ++ } | |
1424 | ++ BUG(); /* This must not happen. */ | |
1425 | ++ return false; | |
1426 | ++} | |
1427 | ++ | |
1428 | ++/** | |
1429 | ++ * read_domain_policy - Read domain policy. | |
1430 | ++ * | |
1431 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1432 | ++ * | |
1433 | ++ * Returns 0. | |
1434 | ++ */ | |
1435 | ++static int read_domain_policy(struct tmy_io_buffer *head) | |
1436 | ++{ | |
1437 | ++ struct list1_head *dpos; | |
1438 | ++ struct list1_head *apos; | |
1439 | ++ | |
1440 | ++ if (head->read_eof) | |
1441 | ++ return 0; | |
1442 | ++ if (head->read_step == 0) | |
1443 | ++ head->read_step = 1; | |
1444 | ++ list1_for_each_cookie(dpos, head->read_var1, &domain_list) { | |
1445 | ++ struct domain_info *domain; | |
1446 | ++ const char *quota_exceeded = ""; | |
1447 | ++ const char *transition_failed = ""; | |
1448 | ++ const char *ignore_global_allow_read = ""; | |
1449 | ++ domain = list1_entry(dpos, struct domain_info, list); | |
1450 | ++ if (head->read_step != 1) | |
1451 | ++ goto acl_loop; | |
1452 | ++ if (domain->is_deleted && !head->read_single_domain) | |
1453 | ++ continue; | |
1454 | ++ /* Print domainname and flags. */ | |
1455 | ++ if (domain->quota_warned) | |
1456 | ++ quota_exceeded = "quota_exceeded\n"; | |
1457 | ++ if (domain->flags & DOMAIN_FLAGS_TRANSITION_FAILED) | |
1458 | ++ transition_failed = "transition_failed\n"; | |
1459 | ++ if (domain->flags & DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ) | |
1460 | ++ ignore_global_allow_read | |
1461 | ++ = KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n"; | |
1462 | ++ if (!tmy_io_printf(head, "%s\n" KEYWORD_USE_PROFILE "%u\n" | |
1463 | ++ "%s%s%s\n", domain->domainname->name, | |
1464 | ++ domain->profile, quota_exceeded, | |
1465 | ++ transition_failed, ignore_global_allow_read)) | |
1466 | ++ return 0; | |
1467 | ++ head->read_step = 2; | |
1468 | ++acl_loop: | |
1469 | ++ if (head->read_step == 3) | |
1470 | ++ goto tail_mark; | |
1471 | ++ /* Print ACL entries in the domain. */ | |
1472 | ++ list1_for_each_cookie(apos, head->read_var2, | |
1473 | ++ &domain->acl_info_list) { | |
1474 | ++ struct acl_info *ptr | |
1475 | ++ = list1_entry(apos, struct acl_info, list); | |
1476 | ++ if (!print_entry(head, ptr)) | |
1477 | ++ return 0; | |
1478 | ++ } | |
1479 | ++ head->read_step = 3; | |
1480 | ++tail_mark: | |
1481 | ++ if (!tmy_io_printf(head, "\n")) | |
1482 | ++ return 0; | |
1483 | ++ head->read_step = 1; | |
1484 | ++ if (head->read_single_domain) | |
1485 | ++ break; | |
1486 | ++ } | |
1487 | ++ head->read_eof = true; | |
1488 | ++ return 0; | |
1489 | ++} | |
1490 | ++ | |
1491 | ++/** | |
1492 | ++ * write_domain_profile - Assign profile for specified domain. | |
1493 | ++ * | |
1494 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1495 | ++ * | |
1496 | ++ * Returns 0 on success, -EINVAL otherwise. | |
1497 | ++ * | |
1498 | ++ * This is equivalent to doing | |
1499 | ++ * | |
1500 | ++ * ( echo "select " $domainname; echo "use_profile " $profile ) | | |
1501 | ++ * /usr/lib/ccs/loadpolicy -d | |
1502 | ++ */ | |
1503 | ++static int write_domain_profile(struct tmy_io_buffer *head) | |
1504 | ++{ | |
1505 | ++ char *data = head->write_buf; | |
1506 | ++ char *cp = strchr(data, ' '); | |
1507 | ++ struct domain_info *domain; | |
1508 | ++ unsigned long profile; | |
1509 | ++ | |
1510 | ++ if (!cp) | |
1511 | ++ return -EINVAL; | |
1512 | ++ *cp = '\0'; | |
1513 | ++ domain = tmy_find_domain(cp + 1); | |
1514 | ++ strict_strtoul(data, 10, &profile); | |
1515 | ++ if (domain && profile < MAX_PROFILES | |
1516 | ++ && (profile_ptr[profile] || !sbin_init_started)) | |
1517 | ++ domain->profile = (u8) profile; | |
1518 | ++ tmy_update_counter(TMY_UPDATES_COUNTER_DOMAIN_POLICY); | |
1519 | ++ return 0; | |
1520 | ++} | |
1521 | ++ | |
1522 | ++/** | |
1523 | ++ * read_domain_profile - Read only domainname and profile. | |
1524 | ++ * | |
1525 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1526 | ++ * | |
1527 | ++ * Returns list of profile number and domainname pairs. | |
1528 | ++ * | |
1529 | ++ * This is equivalent to doing | |
1530 | ++ * | |
1531 | ++ * grep -A 1 '^<kernel>' /sys/kernel/security/tomoyo/domain_policy | | |
1532 | ++ * awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" ) | |
1533 | ++ * domainname = $0; } else if ( $1 == "use_profile" ) { | |
1534 | ++ * print $2 " " domainname; domainname = ""; } } ; ' | |
1535 | ++ */ | |
1536 | ++static int read_domain_profile(struct tmy_io_buffer *head) | |
1537 | ++{ | |
1538 | ++ struct list1_head *pos; | |
1539 | ++ | |
1540 | ++ if (head->read_eof) | |
1541 | ++ return 0; | |
1542 | ++ list1_for_each_cookie(pos, head->read_var1, &domain_list) { | |
1543 | ++ struct domain_info *domain; | |
1544 | ++ domain = list1_entry(pos, struct domain_info, list); | |
1545 | ++ if (domain->is_deleted) | |
1546 | ++ continue; | |
1547 | ++ if (!tmy_io_printf(head, "%u %s\n", domain->profile, | |
1548 | ++ domain->domainname->name)) | |
1549 | ++ return 0; | |
1550 | ++ } | |
1551 | ++ head->read_eof = true; | |
1552 | ++ return 0; | |
1553 | ++} | |
1554 | ++ | |
1555 | ++/** | |
1556 | ++ * write_pid: Specify PID to obtain domainname. | |
1557 | ++ * | |
1558 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1559 | ++ * | |
1560 | ++ * Returns 0. | |
1561 | ++ */ | |
1562 | ++static int write_pid(struct tmy_io_buffer *head) | |
1563 | ++{ | |
1564 | ++ unsigned long pid; | |
1565 | ++ strict_strtoul(head->write_buf, 10, &pid); | |
1566 | ++ head->read_step = (int) pid; | |
1567 | ++ head->read_eof = false; | |
1568 | ++ return 0; | |
1569 | ++} | |
1570 | ++ | |
1571 | ++/** | |
1572 | ++ * read_pid - Get domainname of the specified PID. | |
1573 | ++ * | |
1574 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1575 | ++ * | |
1576 | ++ * Returns the domainname which the specified PID is in on success, | |
1577 | ++ * empty string otherwise. | |
1578 | ++ * The PID is specified by write_pid() so that the user can obtain | |
1579 | ++ * using read()/write() interface rather than sysctl() interface. | |
1580 | ++ */ | |
1581 | ++static int read_pid(struct tmy_io_buffer *head) | |
1582 | ++{ | |
1583 | ++ if (head->read_avail == 0 && !head->read_eof) { | |
1584 | ++ const int pid = head->read_step; | |
1585 | ++ struct task_struct *p; | |
1586 | ++ struct domain_info *domain = NULL; | |
1587 | ++ /***** CRITICAL SECTION START *****/ | |
1588 | ++ read_lock(&tasklist_lock); | |
1589 | ++ p = find_task_by_vpid(pid); | |
1590 | ++ if (p) | |
1591 | ++ domain = tmy_real_domain(p); | |
1592 | ++ read_unlock(&tasklist_lock); | |
1593 | ++ /***** CRITICAL SECTION END *****/ | |
1594 | ++ if (domain) | |
1595 | ++ tmy_io_printf(head, "%d %u %s", pid, domain->profile, | |
1596 | ++ domain->domainname->name); | |
1597 | ++ head->read_eof = true; | |
1598 | ++ } | |
1599 | ++ return 0; | |
1600 | ++} | |
1601 | ++ | |
1602 | ++/** | |
1603 | ++ * write_exception_policy - Write exception policy. | |
1604 | ++ * | |
1605 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1606 | ++ * | |
1607 | ++ * Returns 0 on success, negative value otherwise. | |
1608 | ++ */ | |
1609 | ++static int write_exception_policy(struct tmy_io_buffer *head) | |
1610 | ++{ | |
1611 | ++ char *data = head->write_buf; | |
1612 | ++ bool is_delete = str_starts(&data, KEYWORD_DELETE); | |
1613 | ++ | |
1614 | ++ if (str_starts(&data, KEYWORD_KEEP_DOMAIN)) | |
1615 | ++ return tmy_write_domain_keeper_policy(data, false, is_delete); | |
1616 | ++ if (str_starts(&data, KEYWORD_NO_KEEP_DOMAIN)) | |
1617 | ++ return tmy_write_domain_keeper_policy(data, true, is_delete); | |
1618 | ++ if (str_starts(&data, KEYWORD_INITIALIZE_DOMAIN)) | |
1619 | ++ return tmy_write_domain_initializer_policy(data, false, | |
1620 | ++ is_delete); | |
1621 | ++ if (str_starts(&data, KEYWORD_NO_INITIALIZE_DOMAIN)) | |
1622 | ++ return tmy_write_domain_initializer_policy(data, true, | |
1623 | ++ is_delete); | |
1624 | ++ if (str_starts(&data, KEYWORD_ALIAS)) | |
1625 | ++ return tmy_write_alias_policy(data, is_delete); | |
1626 | ++ if (str_starts(&data, KEYWORD_ALLOW_READ)) | |
1627 | ++ return tmy_write_globally_readable_policy(data, is_delete); | |
1628 | ++ if (str_starts(&data, KEYWORD_FILE_PATTERN)) | |
1629 | ++ return tmy_write_pattern_policy(data, is_delete); | |
1630 | ++ if (str_starts(&data, KEYWORD_DENY_REWRITE)) | |
1631 | ++ return tmy_write_no_rewrite_policy(data, is_delete); | |
1632 | ++ return -EINVAL; | |
1633 | ++} | |
1634 | ++ | |
1635 | ++/** | |
1636 | ++ * read_exception_policy - Read exception policy. | |
1637 | ++ * | |
1638 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1639 | ++ * | |
1640 | ++ * Returns 0 on success, -EINVAL otherwise. | |
1641 | ++ */ | |
1642 | ++static int read_exception_policy(struct tmy_io_buffer *head) | |
1643 | ++{ | |
1644 | ++ if (!head->read_eof) { | |
1645 | ++ switch (head->read_step) { | |
1646 | ++ case 0: | |
1647 | ++ head->read_var2 = NULL; | |
1648 | ++ head->read_step = 1; | |
1649 | ++ case 1: | |
1650 | ++ if (!tmy_read_domain_keeper_policy(head)) | |
1651 | ++ break; | |
1652 | ++ head->read_var2 = NULL; | |
1653 | ++ head->read_step = 2; | |
1654 | ++ case 2: | |
1655 | ++ if (!tmy_read_globally_readable_policy(head)) | |
1656 | ++ break; | |
1657 | ++ head->read_var2 = NULL; | |
1658 | ++ head->read_step = 3; | |
1659 | ++ case 3: | |
1660 | ++ head->read_var2 = NULL; | |
1661 | ++ head->read_step = 4; | |
1662 | ++ case 4: | |
1663 | ++ if (!tmy_read_domain_initializer_policy(head)) | |
1664 | ++ break; | |
1665 | ++ head->read_var2 = NULL; | |
1666 | ++ head->read_step = 5; | |
1667 | ++ case 5: | |
1668 | ++ if (!tmy_read_alias_policy(head)) | |
1669 | ++ break; | |
1670 | ++ head->read_var2 = NULL; | |
1671 | ++ head->read_step = 6; | |
1672 | ++ case 6: | |
1673 | ++ head->read_var2 = NULL; | |
1674 | ++ head->read_step = 7; | |
1675 | ++ case 7: | |
1676 | ++ if (!tmy_read_file_pattern(head)) | |
1677 | ++ break; | |
1678 | ++ head->read_var2 = NULL; | |
1679 | ++ head->read_step = 8; | |
1680 | ++ case 8: | |
1681 | ++ if (!tmy_read_no_rewrite_policy(head)) | |
1682 | ++ break; | |
1683 | ++ head->read_var2 = NULL; | |
1684 | ++ head->read_step = 9; | |
1685 | ++ case 9: | |
1686 | ++ head->read_eof = true; | |
1687 | ++ break; | |
1688 | ++ default: | |
1689 | ++ return -EINVAL; | |
1690 | ++ } | |
1691 | ++ } | |
1692 | ++ return 0; | |
1693 | ++} | |
1694 | ++ | |
1695 | ++/* path to policy loader */ | |
1696 | ++static const char *tmy_loader = "/sbin/tomoyo-init"; | |
1697 | ++ | |
1698 | ++/** | |
1699 | ++ * policy_loader_exists - Check whether /sbin/tomoyo-init exists. | |
1700 | ++ * | |
1701 | ++ * Returns true if /sbin/tomoyo-init exists, false otherwise. | |
1702 | ++ */ | |
1703 | ++static bool policy_loader_exists(void) | |
1704 | ++{ | |
1705 | ++ /* | |
1706 | ++ * Don't activate MAC if the policy loader doesn't exist. | |
1707 | ++ * If the initrd includes /sbin/init but real-root-dev has not | |
1708 | ++ * mounted on / yet, activating MAC will block the system since | |
1709 | ++ * policies are not loaded yet. | |
1710 | ++ * Thus, let do_execve() call this function everytime. | |
1711 | ++ */ | |
1712 | ++ struct nameidata nd; | |
1713 | ++ | |
1714 | ++ if (path_lookup(tmy_loader, LOOKUP_FOLLOW, &nd)) { | |
1715 | ++ printk(KERN_INFO "Not activating Mandatory Access Control now " | |
1716 | ++ "since %s doesn't exist.\n", tmy_loader); | |
1717 | ++ return false; | |
1718 | ++ } | |
1719 | ++ path_put(&nd.path); | |
1720 | ++ return true; | |
1721 | ++} | |
1722 | ++ | |
1723 | ++/** | |
1724 | ++ * tmy_load_policy - Run external policy loader to load policy. | |
1725 | ++ * | |
1726 | ++ * @filename: The program about to start. | |
1727 | ++ * | |
1728 | ++ * This function checks whether @filename is /sbin/init , and if so | |
1729 | ++ * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init | |
1730 | ++ * and then continues invocation of /sbin/init. | |
1731 | ++ * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and | |
1732 | ++ * writes to /sys/kernel/security/tomoyo/ interfaces. | |
1733 | ++ * | |
1734 | ++ * Returns nothing. | |
1735 | ++ */ | |
1736 | ++void tmy_load_policy(const char *filename) | |
1737 | ++{ | |
1738 | ++ char *argv[2]; | |
1739 | ++ char *envp[3]; | |
1740 | ++ | |
1741 | ++ if (sbin_init_started) | |
1742 | ++ return; | |
1743 | ++ /* | |
1744 | ++ * Check filename is /sbin/init or /sbin/tomoyo-start. | |
1745 | ++ * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't | |
1746 | ++ * be passed. | |
1747 | ++ * You can create /sbin/tomoyo-start by | |
1748 | ++ * "ln -s /bin/true /sbin/tomoyo-start". | |
1749 | ++ */ | |
1750 | ++ if (strcmp(filename, "/sbin/init") && | |
1751 | ++ strcmp(filename, "/sbin/tomoyo-start")) | |
1752 | ++ return; | |
1753 | ++ if (!policy_loader_exists()) | |
1754 | ++ return; | |
1755 | ++ | |
1756 | ++ printk(KERN_INFO "Calling %s to load policy. Please wait.\n", | |
1757 | ++ tmy_loader); | |
1758 | ++ argv[0] = (char *) tmy_loader; | |
1759 | ++ argv[1] = NULL; | |
1760 | ++ envp[0] = "HOME=/"; | |
1761 | ++ envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; | |
1762 | ++ envp[2] = NULL; | |
1763 | ++ call_usermodehelper(argv[0], argv, envp, 1); | |
1764 | ++ | |
1765 | ++ printk(KERN_INFO "TOMOYO: 2.2.0-pre 2008/10/10\n"); | |
1766 | ++ printk(KERN_INFO "Mandatory Access Control activated.\n"); | |
1767 | ++ sbin_init_started = true; | |
1768 | ++ { /* Check all profiles currently assigned to domains are defined. */ | |
1769 | ++ struct domain_info *domain; | |
1770 | ++ list1_for_each_entry(domain, &domain_list, list) { | |
1771 | ++ const u8 profile = domain->profile; | |
1772 | ++ if (profile_ptr[profile]) | |
1773 | ++ continue; | |
1774 | ++ panic("Profile %u (used by '%s') not defined.\n", | |
1775 | ++ profile, domain->domainname->name); | |
1776 | ++ } | |
1777 | ++ } | |
1778 | ++} | |
1779 | ++ | |
1780 | ++/* Policy updates counter. */ | |
1781 | ++static atomic_t updates_counter[MAX_TMY_UPDATES_COUNTER]; | |
1782 | ++ | |
1783 | ++/** | |
1784 | ++ * tmy_update_counter - Increment policy change counter. | |
1785 | ++ * | |
1786 | ++ * @index: Type of policy. | |
1787 | ++ * | |
1788 | ++ * Returns nothing. | |
1789 | ++ */ | |
1790 | ++void tmy_update_counter(const unsigned char index) | |
1791 | ++{ | |
1792 | ++ if (index < MAX_TMY_UPDATES_COUNTER) | |
1793 | ++ atomic_inc(&updates_counter[index]); | |
1794 | ++} | |
1795 | ++ | |
1796 | ++/** | |
1797 | ++ * read_updates_counter - Check for policy change counter. | |
1798 | ++ * | |
1799 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1800 | ++ * | |
1801 | ++ * Returns how many times policy has changed since the previous check. | |
1802 | ++ */ | |
1803 | ++static int read_updates_counter(struct tmy_io_buffer *head) | |
1804 | ++{ | |
1805 | ++ if (head->read_eof) | |
1806 | ++ return 0; | |
1807 | ++ tmy_io_printf(head, | |
1808 | ++ "/sys/kernel/security/tomoyo/domain_policy: %10u\n" | |
1809 | ++ "/sys/kernel/security/tomoyo/exception_policy: %10u\n" | |
1810 | ++ "/sys/kernel/security/tomoyo/profile: %10u\n" | |
1811 | ++ "/sys/kernel/security/tomoyo/manager: %10u\n", | |
1812 | ++ atomic_xchg(&updates_counter | |
1813 | ++ [TMY_UPDATES_COUNTER_DOMAIN_POLICY], 0), | |
1814 | ++ atomic_xchg(&updates_counter | |
1815 | ++ [TMY_UPDATES_COUNTER_EXCEPTION_POLICY], 0), | |
1816 | ++ atomic_xchg(&updates_counter | |
1817 | ++ [TMY_UPDATES_COUNTER_PROFILE], 0), | |
1818 | ++ atomic_xchg(&updates_counter | |
1819 | ++ [TMY_UPDATES_COUNTER_MANAGER], 0)); | |
1820 | ++ head->read_eof = true; | |
1821 | ++ return 0; | |
1822 | ++} | |
1823 | ++ | |
1824 | ++/** | |
1825 | ++ * read_version: Get version. | |
1826 | ++ * | |
1827 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1828 | ++ * | |
1829 | ++ * Returns version information. | |
1830 | ++ */ | |
1831 | ++static int read_version(struct tmy_io_buffer *head) | |
1832 | ++{ | |
1833 | ++ if (!head->read_eof) { | |
1834 | ++ tmy_io_printf(head, "2.2.0-pre"); | |
1835 | ++ head->read_eof = true; | |
1836 | ++ } | |
1837 | ++ return 0; | |
1838 | ++} | |
1839 | ++ | |
1840 | ++/** | |
1841 | ++ * read_self_domain - Get the current process's domainname. | |
1842 | ++ * | |
1843 | ++ * @head: Pointer to "struct tmy_io_buffer". | |
1844 | ++ * | |
1845 | ++ * Returns the current process's domainname. | |
1846 | ++ */ | |
1847 | ++static int read_self_domain(struct tmy_io_buffer *head) | |
1848 | ++{ | |
1849 | ++ if (!head->read_eof) { | |
1850 | ++ /* | |
1851 | ++ * tmy_domain()->domainname != NULL | |
1852 | ++ * because every process belongs to a domain and | |
1853 | ++ * the domain's name cannot be NULL. | |
1854 | ++ */ | |
1855 | ++ tmy_io_printf(head, "%s", tmy_domain()->domainname->name); | |
1856 | ++ head->read_eof = true; | |
1857 | ++ } | |
1858 | ++ return 0; | |
1859 | ++} | |
1860 | ++ | |
1861 | ++/** | |
1862 | ++ * tmy_open_control - open() for /sys/kernel/security/tomoyo/ interface. | |
1863 | ++ * | |
1864 | ++ * @type: Type of interface. | |
1865 | ++ * @file: Pointer to "struct file". | |
1866 | ++ * | |
1867 | ++ * Associates policy handler and returns 0 on success, -ENOMEM otherwise. | |
1868 | ++ */ | |
1869 | ++static int tmy_open_control(const u8 type, struct file *file) | |
1870 | ++{ | |
1871 | ++ struct tmy_io_buffer *head = tmy_alloc(sizeof(*head)); | |
1872 | ++ | |
1873 | ++ if (!head) | |
1874 | ++ return -ENOMEM; | |
1875 | ++ mutex_init(&head->io_sem); | |
1876 | ++ switch (type) { | |
1877 | ++ case TMY_DOMAINPOLICY: | |
1878 | ++ /* /sys/kernel/security/tomoyo/domain_policy */ | |
1879 | ++ head->write = write_domain_policy; | |
1880 | ++ head->read = read_domain_policy; | |
1881 | ++ break; | |
1882 | ++ case TMY_EXCEPTIONPOLICY: | |
1883 | ++ /* /sys/kernel/security/tomoyo/exception_policy */ | |
1884 | ++ head->write = write_exception_policy; | |
1885 | ++ head->read = read_exception_policy; | |
1886 | ++ break; | |
1887 | ++ case TMY_SELFDOMAIN: | |
1888 | ++ /* /sys/kernel/security/tomoyo/self_domain */ | |
1889 | ++ head->read = read_self_domain; | |
1890 | ++ break; | |
1891 | ++ case TMY_DOMAIN_STATUS: | |
1892 | ++ /* /sys/kernel/security/tomoyo/.domain_status */ | |
1893 | ++ head->write = write_domain_profile; | |
1894 | ++ head->read = read_domain_profile; | |
1895 | ++ break; | |
1896 | ++ case TMY_PROCESS_STATUS: | |
1897 | ++ /* /sys/kernel/security/tomoyo/.process_status */ | |
1898 | ++ head->write = write_pid; | |
1899 | ++ head->read = read_pid; | |
1900 | ++ break; | |
1901 | ++ case TMY_VERSION: | |
1902 | ++ /* /sys/kernel/security/tomoyo/version */ | |
1903 | ++ head->read = read_version; | |
1904 | ++ head->readbuf_size = 128; | |
1905 | ++ break; | |
1906 | ++ case TMY_MEMINFO: | |
1907 | ++ /* /sys/kernel/security/tomoyo/meminfo */ | |
1908 | ++ head->write = tmy_write_memory_quota; | |
1909 | ++ head->read = tmy_read_memory_counter; | |
1910 | ++ head->readbuf_size = 512; | |
1911 | ++ break; | |
1912 | ++ case TMY_PROFILE: | |
1913 | ++ /* /sys/kernel/security/tomoyo/profile */ | |
1914 | ++ head->write = write_profile; | |
1915 | ++ head->read = read_profile; | |
1916 | ++ break; | |
1917 | ++ case TMY_MANAGER: | |
1918 | ++ /* /sys/kernel/security/tomoyo/manager */ | |
1919 | ++ head->write = write_manager_policy; | |
1920 | ++ head->read = read_manager_policy; | |
1921 | ++ break; | |
1922 | ++ case TMY_UPDATESCOUNTER: | |
1923 | ++ /* /sys/kernel/security/tomoyo/.updates_counter */ | |
1924 | ++ head->read = read_updates_counter; | |
1925 | ++ break; | |
1926 | ++ } | |
1927 | ++ if (!(file->f_mode & FMODE_READ)) { | |
1928 | ++ /* | |
1929 | ++ * No need to allocate read_buf since it is not opened | |
1930 | ++ * for reading. | |
1931 | ++ */ | |
1932 | ++ head->read = NULL; | |
1933 | ++ } else { | |
1934 | ++ if (!head->readbuf_size) | |
1935 | ++ head->readbuf_size = 4096 * 2; | |
1936 | ++ head->read_buf = tmy_alloc(head->readbuf_size); | |
1937 | ++ if (!head->read_buf) { | |
1938 | ++ tmy_free(head); | |
1939 | ++ return -ENOMEM; | |
1940 | ++ } | |
1941 | ++ } | |
1942 | ++ if (!(file->f_mode & FMODE_WRITE)) { | |
1943 | ++ /* | |
1944 | ++ * No need to allocate write_buf since it is not opened | |
1945 | ++ * for writing. | |
1946 | ++ */ | |
1947 | ++ head->write = NULL; | |
1948 | ++ } else if (head->write) { | |
1949 | ++ head->writebuf_size = 4096 * 2; | |
1950 | ++ head->write_buf = tmy_alloc(head->writebuf_size); | |
1951 | ++ if (!head->write_buf) { | |
1952 | ++ tmy_free(head->read_buf); | |
1953 | ++ tmy_free(head); | |
1954 | ++ return -ENOMEM; | |
1955 | ++ } | |
1956 | ++ } | |
1957 | ++ file->private_data = head; | |
1958 | ++ /* | |
1959 | ++ * Call the handler now if the file is | |
1960 | ++ * /sys/kernel/security/tomoyo/self_domain | |
1961 | ++ * so that the user can use | |
1962 | ++ * cat < /sys/kernel/security/tomoyo/self_domain" | |
1963 | ++ * to know the current process's domainname. | |
1964 | ++ */ | |
1965 | ++ if (type == TMY_SELFDOMAIN) | |
1966 | ++ tmy_read_control(file, NULL, 0); | |
1967 | ++ return 0; | |
1968 | ++} | |
1969 | ++ | |
1970 | ++/** | |
1971 | ++ * tmy_read_control - read() for /sys/kernel/security/tomoyo/ interface. | |
1972 | ++ * | |
1973 | ++ * @file: Pointer to "struct file". | |
1974 | ++ * @buffer: Poiner to buffer to write to. | |
1975 | ++ * @buffer_len: Size of @buffer. | |
1976 | ++ * | |
1977 | ++ * Returns bytes read on success, negative value otherwise. | |
1978 | ++ */ | |
1979 | ++static int tmy_read_control(struct file *file, char __user *buffer, | |
1980 | ++ const int buffer_len) | |
1981 | ++{ | |
1982 | ++ int len = 0; | |
1983 | ++ struct tmy_io_buffer *head = file->private_data; | |
1984 | ++ char *cp; | |
1985 | ++ | |
1986 | ++ if (!head->read) | |
1987 | ++ return -ENOSYS; | |
1988 | ++ if (!access_ok(VERIFY_WRITE, buffer, buffer_len)) | |
1989 | ++ return -EFAULT; | |
1990 | ++ if (mutex_lock_interruptible(&head->io_sem)) | |
1991 | ++ return -EINTR; | |
1992 | ++ /* Call the policy handler. */ | |
1993 | ++ len = head->read(head); | |
1994 | ++ if (len < 0) | |
1995 | ++ goto out; | |
1996 | ++ /* Write to buffer. */ | |
1997 | ++ len = head->read_avail; | |
1998 | ++ if (len > buffer_len) | |
1999 | ++ len = buffer_len; | |
2000 | ++ if (!len) | |
2001 | ++ goto out; | |
2002 | ++ /* head->read_buf changes by some functions. */ | |
2003 | ++ cp = head->read_buf; | |
2004 | ++ if (copy_to_user(buffer, cp, len)) { | |
2005 | ++ len = -EFAULT; | |
2006 | ++ goto out; | |
2007 | ++ } | |
2008 | ++ head->read_avail -= len; | |
2009 | ++ memmove(cp, cp + len, head->read_avail); | |
2010 | ++out: | |
2011 | ++ mutex_unlock(&head->io_sem); | |
2012 | ++ return len; | |
2013 | ++} | |
2014 | ++ | |
2015 | ++/** | |
2016 | ++ * tmy_write_control - write() for /sys/kernel/security/tomoyo/ interface. | |
2017 | ++ * | |
2018 | ++ * @file: Pointer to "struct file". | |
2019 | ++ * @buffer: Pointer to buffer to read from. | |
2020 | ++ * @buffer_len: Size of @buffer. | |
2021 | ++ * | |
2022 | ++ * Returns @buffer_len on success, negative value otherwise. | |
2023 | ++ */ | |
2024 | ++static int tmy_write_control(struct file *file, const char __user *buffer, | |
2025 | ++ const int buffer_len) | |
2026 | ++{ | |
2027 | ++ struct tmy_io_buffer *head = file->private_data; | |
2028 | ++ int error = buffer_len; | |
2029 | ++ int avail_len = buffer_len; | |
2030 | ++ char *cp0 = head->write_buf; | |
2031 | ++ | |
2032 | ++ if (!head->write) | |
2033 | ++ return -ENOSYS; | |
2034 | ++ if (!access_ok(VERIFY_READ, buffer, buffer_len)) | |
2035 | ++ return -EFAULT; | |
2036 | ++ /* Don't allow updating policies by non manager programs. */ | |
2037 | ++ if (head->write != write_pid && head->write != write_domain_policy && | |
2038 | ++ !is_policy_manager()) | |
2039 | ++ return -EPERM; | |
2040 | ++ if (mutex_lock_interruptible(&head->io_sem)) | |
2041 | ++ return -EINTR; | |
2042 | ++ /* Read a line and dispatch it to the policy handler. */ | |
2043 | ++ while (avail_len > 0) { | |
2044 | ++ char c; | |
2045 | ++ if (head->write_avail >= head->writebuf_size - 1) { | |
2046 | ++ error = -ENOMEM; | |
2047 | ++ break; | |
2048 | ++ } else if (get_user(c, buffer)) { | |
2049 | ++ error = -EFAULT; | |
2050 | ++ break; | |
2051 | ++ } | |
2052 | ++ buffer++; | |
2053 | ++ avail_len--; | |
2054 | ++ cp0[head->write_avail++] = c; | |
2055 | ++ if (c != '\n') | |
2056 | ++ continue; | |
2057 | ++ cp0[head->write_avail - 1] = '\0'; | |
2058 | ++ head->write_avail = 0; | |
2059 | ++ normalize_line(cp0); | |
2060 | ++ head->write(head); | |
2061 | ++ } | |
2062 | ++ mutex_unlock(&head->io_sem); | |
2063 | ++ return error; | |
2064 | ++} | |
2065 | ++ | |
2066 | ++/** | |
2067 | ++ * tmy_close_control - close() for /sys/kernel/security/tomoyo/ interface. | |
2068 | ++ * | |
2069 | ++ * @file: Pointer to "struct file". | |
2070 | ++ * | |
2071 | ++ * Releases memory and returns 0. | |
2072 | ++ */ | |
2073 | ++static int tmy_close_control(struct file *file) | |
2074 | ++{ | |
2075 | ++ struct tmy_io_buffer *head = file->private_data; | |
2076 | ++ | |
2077 | ++ /* Release memory used for policy I/O. */ | |
2078 | ++ tmy_free(head->read_buf); | |
2079 | ++ head->read_buf = NULL; | |
2080 | ++ tmy_free(head->write_buf); | |
2081 | ++ head->write_buf = NULL; | |
2082 | ++ tmy_free(head); | |
2083 | ++ head = NULL; | |
2084 | ++ file->private_data = NULL; | |
2085 | ++ return 0; | |
2086 | ++} | |
2087 | ++ | |
2088 | ++/** | |
2089 | ++ * tmy_alloc_acl_element - Allocate permanent memory for ACL entry. | |
2090 | ++ * | |
2091 | ++ * @acl_type: Type of ACL entry. | |
2092 | ++ * | |
2093 | ++ * Returns pointer to the ACL entry on success, NULL otherwise. | |
2094 | ++ */ | |
2095 | ++void *tmy_alloc_acl_element(const u8 acl_type) | |
2096 | ++{ | |
2097 | ++ int len; | |
2098 | ++ struct acl_info *ptr; | |
2099 | ++ | |
2100 | ++ switch (acl_type) { | |
2101 | ++ case TYPE_SINGLE_PATH_ACL: | |
2102 | ++ len = sizeof(struct single_path_acl_record); | |
2103 | ++ break; | |
2104 | ++ case TYPE_DOUBLE_PATH_ACL: | |
2105 | ++ len = sizeof(struct double_path_acl_record); | |
2106 | ++ break; | |
2107 | ++ default: | |
2108 | ++ return NULL; | |
2109 | ++ } | |
2110 | ++ ptr = tmy_alloc_element(len); | |
2111 | ++ if (!ptr) | |
2112 | ++ return NULL; | |
2113 | ++ ptr->type = acl_type; | |
2114 | ++ return ptr; | |
2115 | ++} | |
2116 | ++ | |
2117 | ++/** | |
2118 | ++ * tmy_open - open() for /sys/kernel/security/tomoyo/ interface. | |
2119 | ++ * | |
2120 | ++ * @inode: Pointer to "struct inode". | |
2121 | ++ * @file: Pointer to "struct file". | |
2122 | ++ * | |
2123 | ++ * Returns 0 on success, negative value otherwise. | |
2124 | ++ */ | |
2125 | ++static int tmy_open(struct inode *inode, struct file *file) | |
2126 | ++{ | |
2127 | ++ return tmy_open_control(((u8 *) file->f_path.dentry->d_inode->i_private) | |
2128 | ++ - ((u8 *) NULL), file); | |
2129 | ++} | |
2130 | ++ | |
2131 | ++/** | |
2132 | ++ * tmy_release - close() for /sys/kernel/security/tomoyo/ interface. | |
2133 | ++ * | |
2134 | ++ * @inode: Pointer to "struct inode". | |
2135 | ++ * @file: Pointer to "struct file". | |
2136 | ++ * | |
2137 | ++ * Returns 0 on success, negative value otherwise. | |
2138 | ++ */ | |
2139 | ++static int tmy_release(struct inode *inode, struct file *file) | |
2140 | ++{ | |
2141 | ++ return tmy_close_control(file); | |
2142 | ++} | |
2143 | ++ | |
2144 | ++/** | |
2145 | ++ * tmy_read - read() for /sys/kernel/security/tomoyo/ interface. | |
2146 | ++ * | |
2147 | ++ * @file: Pointer to "struct file". | |
2148 | ++ * @buf: Pointer to buffer. | |
2149 | ++ * @count: Size of @buf. | |
2150 | ++ * @ppos: Unused. | |
2151 | ++ * | |
2152 | ++ * Returns bytes read on success, negative value otherwise. | |
2153 | ++ */ | |
2154 | ++static ssize_t tmy_read(struct file *file, char __user *buf, size_t count, | |
2155 | ++ loff_t *ppos) | |
2156 | ++{ | |
2157 | ++ return tmy_read_control(file, buf, count); | |
2158 | ++} | |
2159 | ++ | |
2160 | ++/** | |
2161 | ++ * tmy_write - write() for /sys/kernel/security/tomoyo/ interface. | |
2162 | ++ * | |
2163 | ++ * @file: Pointer to "struct file". | |
2164 | ++ * @buf: Pointer to buffer. | |
2165 | ++ * @count: Size of @buf. | |
2166 | ++ * @ppos: Unused. | |
2167 | ++ * | |
2168 | ++ * Returns @count on success, negative value otherwise. | |
2169 | ++ */ | |
2170 | ++static ssize_t tmy_write(struct file *file, const char __user *buf, | |
2171 | ++ size_t count, loff_t *ppos) | |
2172 | ++{ | |
2173 | ++ return tmy_write_control(file, buf, count); | |
2174 | ++} | |
2175 | ++ | |
2176 | ++/* Operations for /sys/kernel/security/tomoyo/ interface. */ | |
2177 | ++static struct file_operations tmy_operations = { | |
2178 | ++ .open = tmy_open, | |
2179 | ++ .release = tmy_release, | |
2180 | ++ .read = tmy_read, | |
2181 | ++ .write = tmy_write, | |
2182 | ++}; | |
2183 | ++ | |
2184 | ++/** | |
2185 | ++ * create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory. | |
2186 | ++ * | |
2187 | ++ * @name: The name of the interface file. | |
2188 | ++ * @mode: The permission of the interface file. | |
2189 | ++ * @parent: The parent directory. | |
2190 | ++ * @key: Type of interface. | |
2191 | ++ * | |
2192 | ++ * Returns nothing. | |
2193 | ++ */ | |
2194 | ++static void __init create_entry(const char *name, const mode_t mode, | |
2195 | ++ struct dentry *parent, const u8 key) | |
2196 | ++{ | |
2197 | ++ securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key, | |
2198 | ++ &tmy_operations); | |
2199 | ++} | |
2200 | ++ | |
2201 | ++/** | |
2202 | ++ * tmy_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface. | |
2203 | ++ * | |
2204 | ++ * Returns 0. | |
2205 | ++ */ | |
2206 | ++static int __init tmy_initerface_init(void) | |
2207 | ++{ | |
2208 | ++ struct dentry *tmy_dir; | |
2209 | ++ | |
2210 | ++ tmy_dir = securityfs_create_dir("tomoyo", NULL); | |
2211 | ++ create_entry("domain_policy", 0600, tmy_dir, TMY_DOMAINPOLICY); | |
2212 | ++ create_entry("exception_policy", 0600, tmy_dir, TMY_EXCEPTIONPOLICY); | |
2213 | ++ create_entry("self_domain", 0400, tmy_dir, TMY_SELFDOMAIN); | |
2214 | ++ create_entry(".domain_status", 0600, tmy_dir, TMY_DOMAIN_STATUS); | |
2215 | ++ create_entry(".process_status", 0600, tmy_dir, TMY_PROCESS_STATUS); | |
2216 | ++ create_entry("meminfo", 0600, tmy_dir, TMY_MEMINFO); | |
2217 | ++ create_entry("profile", 0600, tmy_dir, TMY_PROFILE); | |
2218 | ++ create_entry("manager", 0600, tmy_dir, TMY_MANAGER); | |
2219 | ++ create_entry(".updates_counter", 0400, tmy_dir, TMY_UPDATESCOUNTER); | |
2220 | ++ create_entry("version", 0400, tmy_dir, TMY_VERSION); | |
2221 | ++ return 0; | |
2222 | ++} | |
2223 | ++ | |
2224 | ++fs_initcall(tmy_initerface_init); | |
2225 | +--- /dev/null | |
2226 | ++++ linux-next/security/tomoyo/common.h | |
2227 | +@@ -0,0 +1,320 @@ | |
2228 | ++/* | |
2229 | ++ * security/tomoyo/common.h | |
2230 | ++ * | |
2231 | ++ * Common functions for TOMOYO. | |
2232 | ++ * | |
2233 | ++ * Copyright (C) 2005-2008 NTT DATA CORPORATION | |
2234 | ++ * | |
2235 | ++ * Version: 2.2.0-pre 2008/10/10 | |
2236 | ++ * | |
2237 | ++ */ | |
2238 | ++ | |
2239 | ++#ifndef _SECURITY_TOMOYO_COMMON_H | |
2240 | ++#define _SECURITY_TOMOYO_COMMON_H | |
2241 | ++ | |
2242 | ++#include <linux/string.h> | |
2243 | ++#include <linux/mm.h> | |
2244 | ++#include <linux/file.h> | |
2245 | ++#include <linux/kmod.h> | |
2246 | ++#include <linux/fs.h> | |
2247 | ++#include <linux/sched.h> | |
2248 | ++#include <linux/namei.h> | |
2249 | ++#include <linux/mount.h> | |
2250 | ++#include <linux/list1.h> | |
2251 | ++ | |
2252 | ++struct dentry; | |
2253 | ++struct vfsmount; | |
2254 | ++ | |
2255 | ++#define false 0 | |
2256 | ++#define true 1 | |
2257 | ++ | |
2258 | ++/* Temporary buffer for holding pathnames. */ | |
2259 | ++struct tmy_page_buffer { | |
2260 | ++ char buffer[4096]; | |
2261 | ++}; | |
2262 | ++ | |
2263 | ++/* Structure for attribute checks in addition to pathname checks. */ | |
2264 | ++struct obj_info { | |
2265 | ++ struct tmy_page_buffer *tmp; | |
2266 | ++}; | |
2267 | ++ | |
2268 | ++/* Structure for holding a token. */ | |
2269 | ++struct path_info { | |
2270 | ++ const char *name; | |
2271 | ++ u32 hash; /* = full_name_hash(name, strlen(name)) */ | |
2272 | ++ u16 total_len; /* = strlen(name) */ | |
2273 | ++ u16 const_len; /* = const_part_length(name) */ | |
2274 | ++ bool is_dir; /* = strendswith(name, "/") */ | |
2275 | ++ bool is_patterned; /* = path_contains_pattern(name) */ | |
2276 | ++ u16 depth; /* = path_depth(name) */ | |
2277 | ++}; | |
2278 | ++ | |
2279 | ++/* | |
2280 | ++ * This is the max length of a token. | |
2281 | ++ * | |
2282 | ++ * A token consists of only ASCII printable characters. | |
2283 | ++ * Non printable characters in a token is represented in \ooo style | |
2284 | ++ * octal string. Thus, \ itself is represented as \\. | |
2285 | ++ */ | |
2286 | ++#define TMY_MAX_PATHNAME_LEN 4000 | |
2287 | ++ | |
2288 | ++/* Structure for holding requested pathname. */ | |
2289 | ++struct path_info_with_data { | |
2290 | ++ /* Keep "head" first, for this pointer is passed to tmy_free(). */ | |
2291 | ++ struct path_info head; | |
2292 | ++ char bariier1[16]; /* Safeguard for overrun. */ | |
2293 | ++ char body[TMY_MAX_PATHNAME_LEN]; | |
2294 | ++ char barrier2[16]; /* Safeguard for overrun. */ | |
2295 | ++}; | |
2296 | ++ | |
2297 | ++/* Common header for holding ACL entries. */ | |
2298 | ++struct acl_info { | |
2299 | ++ struct list1_head list; | |
2300 | ++ /* | |
2301 | ++ * Type of this ACL entry. | |
2302 | ++ * | |
2303 | ++ * MSB is is_deleted flag. | |
2304 | ++ */ | |
2305 | ++ u8 type; | |
2306 | ++} __attribute__((__packed__)); | |
2307 | ++ | |
2308 | ++/* This ACL entry is deleted. */ | |
2309 | ++#define ACL_DELETED 0x80 | |
2310 | ++ | |
2311 | ++/* Structure for domain information. */ | |
2312 | ++struct domain_info { | |
2313 | ++ struct list1_head list; | |
2314 | ++ struct list1_head acl_info_list; | |
2315 | ++ /* Name of this domain. Never NULL. */ | |
2316 | ++ const struct path_info *domainname; | |
2317 | ++ u8 profile; /* Profile number to use. */ | |
2318 | ++ u8 is_deleted; /* Delete flag. | |
2319 | ++ 0 = active. | |
2320 | ++ 1 = deleted but undeletable. | |
2321 | ++ 255 = deleted and no longer undeletable. */ | |
2322 | ++ bool quota_warned; /* Quota warnning flag. */ | |
2323 | ++ /* DOMAIN_FLAGS_*. Use tmy_set_domain_flag() to modify. */ | |
2324 | ++ u8 flags; | |
2325 | ++}; | |
2326 | ++ | |
2327 | ++/* Profile number is an integer between 0 and 255. */ | |
2328 | ++#define MAX_PROFILES 256 | |
2329 | ++ | |
2330 | ++/* Ignore "allow_read" directive in exception policy. */ | |
2331 | ++#define DOMAIN_FLAGS_IGNORE_GLOBAL_ALLOW_READ 1 | |
2332 | ++/* | |
2333 | ++ * This domain was unable to create a new domain at tmy_find_next_domain() | |
2334 | ++ * because the name of the domain to be created was too long or | |
2335 | ++ * it could not allocate memory. | |
2336 | ++ * More than one process continued execve() without domain transition. | |
2337 | ++ */ | |
2338 | ++#define DOMAIN_FLAGS_TRANSITION_FAILED 2 | |
2339 | ++ | |
2340 | ++/* | |
2341 | ++ * Structure for "allow_read/write", "allow_execute", "allow_read", | |
2342 | ++ * "allow_write", "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir", | |
2343 | ++ * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar", | |
2344 | ++ * "allow_truncate", "allow_symlink" and "allow_rewrite" directive. | |
2345 | ++ */ | |
2346 | ++struct single_path_acl_record { | |
2347 | ++ struct acl_info head; /* type = TYPE_SINGLE_PATH_ACL */ | |
2348 | ++ u16 perm; | |
2349 | ++ /* Pointer to single pathname. */ | |
2350 | ++ const struct path_info *filename; | |
2351 | ++}; | |
2352 | ++ | |
2353 | ++/* Structure for "allow_rename" and "allow_link" directive. */ | |
2354 | ++struct double_path_acl_record { | |
2355 | ++ struct acl_info head; /* type = TYPE_DOUBLE_PATH_ACL */ | |
2356 | ++ u8 perm; | |
2357 | ++ /* Pointer to single pathname. */ | |
2358 | ++ const struct path_info *filename1; | |
2359 | ++ /* Pointer to single pathname. */ | |
2360 | ++ const struct path_info *filename2; | |
2361 | ++}; | |
2362 | ++ | |
2363 | ++/* Keywords for ACLs. */ | |
2364 | ++#define KEYWORD_ALIAS "alias " | |
2365 | ++#define KEYWORD_ALLOW_READ "allow_read " | |
2366 | ++#define KEYWORD_DELETE "delete " | |
2367 | ++#define KEYWORD_DENY_REWRITE "deny_rewrite " | |
2368 | ++#define KEYWORD_FILE_PATTERN "file_pattern " | |
2369 | ++#define KEYWORD_INITIALIZE_DOMAIN "initialize_domain " | |
2370 | ++#define KEYWORD_KEEP_DOMAIN "keep_domain " | |
2371 | ++#define KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain " | |
2372 | ++#define KEYWORD_NO_KEEP_DOMAIN "no_keep_domain " | |
2373 | ++#define KEYWORD_SELECT "select " | |
2374 | ++#define KEYWORD_UNDELETE "undelete " | |
2375 | ++#define KEYWORD_USE_PROFILE "use_profile " | |
2376 | ++#define KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read" | |
2377 | ++/* A domain definition starts with <kernel>. */ | |
2378 | ++#define ROOT_NAME "<kernel>" | |
2379 | ++#define ROOT_NAME_LEN (sizeof(ROOT_NAME) - 1) | |
2380 | ++ | |
2381 | ++/* Index numbers for Access Controls. */ | |
2382 | ++#define TMY_TOMOYO_MAC_FOR_FILE 0 /* domain_policy.conf */ | |
2383 | ++#define TMY_TOMOYO_MAX_ACCEPT_ENTRY 1 | |
2384 | ++#define TMY_TOMOYO_VERBOSE 2 | |
2385 | ++#define TMY_MAX_CONTROL_INDEX 3 | |
2386 | ++ | |
2387 | ++/* Index numbers for updates counter. */ | |
2388 | ++#define TMY_UPDATES_COUNTER_DOMAIN_POLICY 0 | |
2389 | ++#define TMY_UPDATES_COUNTER_EXCEPTION_POLICY 1 | |
2390 | ++#define TMY_UPDATES_COUNTER_PROFILE 2 | |
2391 | ++#define TMY_UPDATES_COUNTER_MANAGER 3 | |
2392 | ++#define MAX_TMY_UPDATES_COUNTER 4 | |
2393 | ++ | |
2394 | ++/* Structure for reading/writing policy via securityfs interfaces. */ | |
2395 | ++struct tmy_io_buffer { | |
2396 | ++ int (*read) (struct tmy_io_buffer *); | |
2397 | ++ int (*write) (struct tmy_io_buffer *); | |
2398 | ++ /* Exclusive lock for this structure. */ | |
2399 | ++ struct mutex io_sem; | |
2400 | ++ /* The position currently reading from. */ | |
2401 | ++ struct list1_head *read_var1; | |
2402 | ++ /* Extra variables for reading. */ | |
2403 | ++ struct list1_head *read_var2; | |
2404 | ++ /* The position currently writing to. */ | |
2405 | ++ struct domain_info *write_var1; | |
2406 | ++ /* The step for reading. */ | |
2407 | ++ int read_step; | |
2408 | ++ /* Buffer for reading. */ | |
2409 | ++ char *read_buf; | |
2410 | ++ /* EOF flag for reading. */ | |
2411 | ++ bool read_eof; | |
2412 | ++ /* Read domain ACL of specified PID? */ | |
2413 | ++ bool read_single_domain; | |
2414 | ++ /* Extra variable for reading. */ | |
2415 | ++ u8 read_bit; | |
2416 | ++ /* Bytes available for reading. */ | |
2417 | ++ int read_avail; | |
2418 | ++ /* Size of read buffer. */ | |
2419 | ++ int readbuf_size; | |
2420 | ++ /* Buffer for writing. */ | |
2421 | ++ char *write_buf; | |
2422 | ++ /* Bytes available for writing. */ | |
2423 | ++ int write_avail; | |
2424 | ++ /* Size of write buffer. */ | |
2425 | ++ int writebuf_size; | |
2426 | ++}; | |
2427 | ++ | |
2428 | ++/* Check whether the domain has too many ACL entries to hold. */ | |
2429 | ++bool tmy_check_domain_quota(struct domain_info * const domain); | |
2430 | ++/* Transactional sprintf() for policy dump. */ | |
2431 | ++bool tmy_io_printf(struct tmy_io_buffer *head, const char *fmt, ...) | |
2432 | ++ __attribute__ ((format(printf, 2, 3))); | |
2433 | ++/* Check whether the domainname is correct. */ | |
2434 | ++bool tmy_is_correct_domain(const unsigned char *domainname, | |
2435 | ++ const char *function); | |
2436 | ++/* Check whether the token is correct. */ | |
2437 | ++bool tmy_is_correct_path(const char *filename, const s8 start_type, | |
2438 | ++ const s8 pattern_type, const s8 end_type, | |
2439 | ++ const char *function); | |
2440 | ++/* Check whether the token can be a domainname. */ | |
2441 | ++bool tmy_is_domain_def(const unsigned char *buffer); | |
2442 | ++/* Check whether the given filename matches the given pattern. */ | |
2443 | ++bool tmy_path_matches_pattern(const struct path_info *filename, | |
2444 | ++ const struct path_info *pattern); | |
2445 | ++/* Read "alias" entry in exception policy. */ | |
2446 | ++bool tmy_read_alias_policy(struct tmy_io_buffer *head); | |
2447 | ++/* | |
2448 | ++ * Read "initialize_domain" and "no_initialize_domain" entry | |
2449 | ++ * in exception policy. | |
2450 | ++ */ | |
2451 | ++bool tmy_read_domain_initializer_policy(struct tmy_io_buffer *head); | |
2452 | ++/* Read "keep_domain" and "no_keep_domain" entry in exception policy. */ | |
2453 | ++bool tmy_read_domain_keeper_policy(struct tmy_io_buffer *head); | |
2454 | ++/* Read "file_pattern" entry in exception policy. */ | |
2455 | ++bool tmy_read_file_pattern(struct tmy_io_buffer *head); | |
2456 | ++/* Read "allow_read" entry in exception policy. */ | |
2457 | ++bool tmy_read_globally_readable_policy(struct tmy_io_buffer *head); | |
2458 | ++/* Read "deny_rewrite" entry in exception policy. */ | |
2459 | ++bool tmy_read_no_rewrite_policy(struct tmy_io_buffer *head); | |
2460 | ++/* Write domain policy violation warning message to console? */ | |
2461 | ++bool tmy_verbose_mode(const struct domain_info *domain); | |
2462 | ++/* Convert double path operation to operation name. */ | |
2463 | ++const char *tmy_dp2keyword(const u8 operation); | |
2464 | ++/* Get the last component of the given domainname. */ | |
2465 | ++const char *tmy_get_last_name(const struct domain_info *domain); | |
2466 | ++/* Get warning message. */ | |
2467 | ++const char *tmy_get_msg(const bool is_enforce); | |
2468 | ++/* Convert single path operation to operation name. */ | |
2469 | ++const char *tmy_sp2keyword(const u8 operation); | |
2470 | ++/* Delete a domain. */ | |
2471 | ++int tmy_delete_domain(char *data); | |
2472 | ++/* Create "alias" entry in exception policy. */ | |
2473 | ++int tmy_write_alias_policy(char *data, const bool is_delete); | |
2474 | ++/* | |
2475 | ++ * Create "initialize_domain" and "no_initialize_domain" entry | |
2476 | ++ * in exception policy. | |
2477 | ++ */ | |
2478 | ++int tmy_write_domain_initializer_policy(char *data, const bool is_not, | |
2479 | ++ const bool is_delete); | |
2480 | ++/* Create "keep_domain" and "no_keep_domain" entry in exception policy. */ | |
2481 | ++int tmy_write_domain_keeper_policy(char *data, const bool is_not, | |
2482 | ++ const bool is_delete); | |
2483 | ++/* | |
2484 | ++ * Create "allow_read/write", "allow_execute", "allow_read", "allow_write", | |
2485 | ++ * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir", | |
2486 | ++ * "allow_mkfifo", "allow_mksock", "allow_mkblock", "allow_mkchar", | |
2487 | ++ * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and | |
2488 | ++ * "allow_link" entry in domain policy. | |
2489 | ++ */ | |
2490 | ++int tmy_write_file_policy(char *data, struct domain_info *domain, | |
2491 | ++ const bool is_delete); | |
2492 | ++/* Create "allow_read" entry in exception policy. */ | |
2493 | ++int tmy_write_globally_readable_policy(char *data, const bool is_delete); | |
2494 | ++/* Create "deny_rewrite" entry in exception policy. */ | |
2495 | ++int tmy_write_no_rewrite_policy(char *data, const bool is_delete); | |
2496 | ++/* Create "file_pattern" entry in exception policy. */ | |
2497 | ++int tmy_write_pattern_policy(char *data, const bool is_delete); | |
2498 | ++/* Find a domain by the given name. */ | |
2499 | ++struct domain_info *tmy_find_domain(const char *domainname); | |
2500 | ++/* Find or create a domain by the given name. */ | |
2501 | ++struct domain_info *tmy_find_or_assign_new_domain(const char *domainname, | |
2502 | ++ const u8 profile); | |
2503 | ++/* Undelete a domain. */ | |
2504 | ++struct domain_info *tmy_undelete_domain(const char *domainname); | |
2505 | ++/* Check mode for specified functionality. */ | |
2506 | ++unsigned int tmy_check_flags(const struct domain_info *domain, const u8 index); | |
2507 | ++/* Allocate memory for structures. */ | |
2508 | ++void *tmy_alloc_acl_element(const u8 acl_type); | |
2509 | ++/* Fill in "struct path_info" members. */ | |
2510 | ++void tmy_fill_path_info(struct path_info *ptr); | |
2511 | ++/* Run policy loader when /sbin/init starts. */ | |
2512 | ++void tmy_load_policy(const char *filename); | |
2513 | ++/* Change "struct domain_info"->flags. */ | |
2514 | ++void tmy_set_domain_flag(struct domain_info *domain, const bool is_delete, | |
2515 | ++ const u8 flags); | |
2516 | ++/* Update the policy change counter. */ | |
2517 | ++void tmy_update_counter(const unsigned char index); | |
2518 | ++ | |
2519 | ++/* strcmp() for "struct path_info" structure. */ | |
2520 | ++static inline bool tmy_pathcmp(const struct path_info *a, | |
2521 | ++ const struct path_info *b) | |
2522 | ++{ | |
2523 | ++ return a->hash != b->hash || strcmp(a->name, b->name); | |
2524 | ++} | |
2525 | ++ | |
2526 | ++/* Get type of an ACL entry. */ | |
2527 | ++static inline u8 tmy_acl_type1(struct acl_info *ptr) | |
2528 | ++{ | |
2529 | ++ return ptr->type & ~ACL_DELETED; | |
2530 | ++} | |
2531 | ++ | |
2532 | ++/* Get type of an ACL entry. */ | |
2533 | ++static inline u8 tmy_acl_type2(struct acl_info *ptr) | |
2534 | ++{ | |
2535 | ++ return ptr->type; | |
2536 | ++} | |
2537 | ++ | |
2538 | ++/* The list for "struct domain_info". */ | |
2539 | ++extern struct list1_head domain_list; | |
2540 | ++ | |
2541 | ++/* Has /sbin/init started? */ | |
2542 | ++extern bool sbin_init_started; | |
2543 | ++ | |
2544 | ++/* The kernel's domain. */ | |
2545 | ++extern struct domain_info KERNEL_DOMAIN; | |
2546 | ++ | |
2547 | ++#endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */ |
@@ -0,0 +1,85 @@ | ||
1 | +Subject: Add in_execve flag into task_struct. | |
2 | + | |
3 | +This patch allows LSM modules to determine whether current process is in an | |
4 | +execve operation or not so that they can behave differently while an execve | |
5 | +operation is in progress. | |
6 | + | |
7 | +This allows TOMOYO to dispense with a readability check on a file to be | |
8 | +executed under the process's current credentials, and to do it instead under | |
9 | +the proposed credentials. | |
10 | + | |
11 | +This is required with the new COW credentials because TOMOYO is no longer | |
12 | +allowed to mark the state temporarily in the security struct attached to the | |
13 | +task_struct. | |
14 | + | |
15 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
16 | +Signed-off-by: David Howells <dhowells@redhat.com> | |
17 | +--- | |
18 | + fs/compat.c | 3 +++ | |
19 | + fs/exec.c | 3 +++ | |
20 | + include/linux/sched.h | 2 ++ | |
21 | + 3 files changed, 8 insertions(+) | |
22 | + | |
23 | +--- linux-next.orig/fs/compat.c | |
24 | ++++ linux-next/fs/compat.c | |
25 | +@@ -1396,6 +1396,7 @@ int compat_do_execve(char * filename, | |
26 | + retval = mutex_lock_interruptible(¤t->cred_exec_mutex); | |
27 | + if (retval < 0) | |
28 | + goto out_free; | |
29 | ++ current->in_execve = 1; | |
30 | + | |
31 | + retval = -ENOMEM; | |
32 | + bprm->cred = prepare_exec_creds(); | |
33 | +@@ -1448,6 +1449,7 @@ int compat_do_execve(char * filename, | |
34 | + goto out; | |
35 | + | |
36 | + /* execve succeeded */ | |
37 | ++ current->in_execve = 0; | |
38 | + mutex_unlock(¤t->cred_exec_mutex); | |
39 | + acct_update_integrals(current); | |
40 | + free_bprm(bprm); | |
41 | +@@ -1464,6 +1466,7 @@ out_file: | |
42 | + } | |
43 | + | |
44 | + out_unlock: | |
45 | ++ current->in_execve = 0; | |
46 | + mutex_unlock(¤t->cred_exec_mutex); | |
47 | + | |
48 | + out_free: | |
49 | +--- linux-next.orig/fs/exec.c | |
50 | ++++ linux-next/fs/exec.c | |
51 | +@@ -1303,6 +1303,7 @@ int do_execve(char * filename, | |
52 | + retval = mutex_lock_interruptible(¤t->cred_exec_mutex); | |
53 | + if (retval < 0) | |
54 | + goto out_free; | |
55 | ++ current->in_execve = 1; | |
56 | + | |
57 | + retval = -ENOMEM; | |
58 | + bprm->cred = prepare_exec_creds(); | |
59 | +@@ -1356,6 +1357,7 @@ int do_execve(char * filename, | |
60 | + goto out; | |
61 | + | |
62 | + /* execve succeeded */ | |
63 | ++ current->in_execve = 0; | |
64 | + mutex_unlock(¤t->cred_exec_mutex); | |
65 | + acct_update_integrals(current); | |
66 | + free_bprm(bprm); | |
67 | +@@ -1374,6 +1376,7 @@ out_file: | |
68 | + } | |
69 | + | |
70 | + out_unlock: | |
71 | ++ current->in_execve = 0; | |
72 | + mutex_unlock(¤t->cred_exec_mutex); | |
73 | + | |
74 | + out_free: | |
75 | +--- linux-next.orig/include/linux/sched.h | |
76 | ++++ linux-next/include/linux/sched.h | |
77 | +@@ -1089,6 +1089,8 @@ struct task_struct { | |
78 | + /* ??? */ | |
79 | + unsigned int personality; | |
80 | + unsigned did_exec:1; | |
81 | ++ unsigned in_execve:1; /* Tell the LSMs that the process is doing an | |
82 | ++ * execve */ | |
83 | + pid_t pid; | |
84 | + pid_t tgid; | |
85 | + |
@@ -0,0 +1,124 @@ | ||
1 | +Subject: Introduce d_realpath(). | |
2 | + | |
3 | +To remove factors that make pathname based access control difficult | |
4 | +(e.g. symbolic links, "..", "//", chroot() etc.), a variant of d_path() | |
5 | +which traverses up to the root of the namespace is needed. | |
6 | + | |
7 | +Three differences compared to d_path(). | |
8 | +(1) Ignores current process's root directory. | |
9 | +(2) Trailing '/' is added if the pathname refers to a directory. | |
10 | +(3) /proc/PID/ is represented as /proc/self/ if PID equals current->tgid. | |
11 | + | |
12 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
13 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
14 | +Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp> | |
15 | +--- | |
16 | + fs/dcache.c | 79 +++++++++++++++++++++++++++++++++++++++++++++++++ | |
17 | + include/linux/dcache.h | 1 | |
18 | + 2 files changed, 80 insertions(+) | |
19 | + | |
20 | +--- linux-next.orig/fs/dcache.c | |
21 | ++++ linux-next/fs/dcache.c | |
22 | +@@ -33,6 +33,7 @@ | |
23 | + #include <linux/swap.h> | |
24 | + #include <linux/bootmem.h> | |
25 | + #include <linux/backing-dev.h> | |
26 | ++#include <linux/magic.h> | |
27 | + #include "internal.h" | |
28 | + | |
29 | + | |
30 | +@@ -1972,6 +1973,84 @@ Elong: | |
31 | + } | |
32 | + | |
33 | + /** | |
34 | ++ * d_realpath - Get the realpath of a dentry. | |
35 | ++ * | |
36 | ++ * @path: Pointer to "struct path". | |
37 | ++ * @buffer: Pointer to buffer to return value in. | |
38 | ++ * @buflen: Sizeof @buffer. | |
39 | ++ * | |
40 | ++ * Returns pointer to the realpath on success, an error code othersize. | |
41 | ++ * | |
42 | ++ * If @dentry is a directory, trailing '/' is appended. | |
43 | ++ * If /proc/PID/ is replaced by /proc/self/ if PID == current->tgid. | |
44 | ++ */ | |
45 | ++char *d_realpath(struct path *path, char *buffer, int buflen) | |
46 | ++{ | |
47 | ++ struct dentry *dentry = path->dentry; | |
48 | ++ struct vfsmount *vfsmnt = path->mnt; | |
49 | ++ char *end = buffer + buflen; | |
50 | ++ char self_pid[16]; | |
51 | ++ | |
52 | ++ snprintf(self_pid, sizeof(self_pid), "%u", current->tgid); | |
53 | ++ spin_lock(&dcache_lock); | |
54 | ++ spin_lock(&vfsmount_lock); | |
55 | ++ if (buflen < 1 || prepend(&end, &buflen, "", 1)) | |
56 | ++ goto Elong; | |
57 | ++ /* | |
58 | ++ * Exception: Add trailing '/' for directory. | |
59 | ++ */ | |
60 | ++ if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode) && | |
61 | ++ prepend(&end, &buflen, "/", 1)) | |
62 | ++ goto Elong; | |
63 | ++ for (;;) { | |
64 | ++ struct dentry *parent; | |
65 | ++ const char *name; | |
66 | ++ | |
67 | ++ if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) { | |
68 | ++ /* Global root? */ | |
69 | ++ if (vfsmnt->mnt_parent == vfsmnt) | |
70 | ++ break; | |
71 | ++ dentry = vfsmnt->mnt_mountpoint; | |
72 | ++ vfsmnt = vfsmnt->mnt_parent; | |
73 | ++ continue; | |
74 | ++ } | |
75 | ++ parent = dentry->d_parent; | |
76 | ++ prefetch(parent); | |
77 | ++ /* | |
78 | ++ * Exception: Use /proc/self/ rather than /proc/\$/ | |
79 | ++ * for current process. | |
80 | ++ */ | |
81 | ++ name = dentry->d_name.name; | |
82 | ++ if (IS_ROOT(parent) && | |
83 | ++ parent->d_sb->s_magic == PROC_SUPER_MAGIC && | |
84 | ++ !strcmp(name, self_pid)) { | |
85 | ++ if (prepend(&end, &buflen, "self", 4)) | |
86 | ++ goto Elong; | |
87 | ++ } else { | |
88 | ++ if (prepend_name(&end, &buflen, &dentry->d_name)) | |
89 | ++ goto Elong; | |
90 | ++ } | |
91 | ++ if (prepend(&end, &buflen, "/", 1)) | |
92 | ++ goto Elong; | |
93 | ++ dentry = parent; | |
94 | ++ } | |
95 | ++ if (*end == '/') { | |
96 | ++ /* hit the slash */ | |
97 | ++ buflen++; | |
98 | ++ end++; | |
99 | ++ } | |
100 | ++ if (prepend_name(&end, &buflen, &dentry->d_name)) | |
101 | ++ goto Elong; | |
102 | ++ out: | |
103 | ++ spin_unlock(&vfsmount_lock); | |
104 | ++ spin_unlock(&dcache_lock); | |
105 | ++ return end; | |
106 | ++ Elong: | |
107 | ++ end = ERR_PTR(-ENAMETOOLONG); | |
108 | ++ goto out; | |
109 | ++} | |
110 | ++ | |
111 | ++/** | |
112 | + * d_path - return the path of a dentry | |
113 | + * @path: path to report | |
114 | + * @buf: buffer to return value in | |
115 | +--- linux-next.orig/include/linux/dcache.h | |
116 | ++++ linux-next/include/linux/dcache.h | |
117 | +@@ -304,6 +304,7 @@ extern char *dynamic_dname(struct dentry | |
118 | + extern char *__d_path(const struct path *path, struct path *root, char *, int); | |
119 | + extern char *d_path(const struct path *, char *, int); | |
120 | + extern char *dentry_path(struct dentry *, char *, int); | |
121 | ++extern char *d_realpath(struct path *, char *, int); | |
122 | + | |
123 | + /* Allocation counts.. */ | |
124 | + |
@@ -0,0 +1,92 @@ | ||
1 | +Subject: Singly linked list implementation. | |
2 | + | |
3 | +Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | |
4 | +Reviewed-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com> | |
5 | +--- | |
6 | + include/linux/list1.h | 81 ++++++++++++++++++++++++++++++++++++++++++++++++++ | |
7 | + 1 file changed, 81 insertions(+) | |
8 | + | |
9 | +--- /dev/null | |
10 | ++++ linux-next/include/linux/list1.h | |
11 | +@@ -0,0 +1,81 @@ | |
12 | ++#ifndef _LINUX_LIST1_H | |
13 | ++#define _LINUX_LIST1_H | |
14 | ++ | |
15 | ++#include <linux/list.h> | |
16 | ++#include <linux/rcupdate.h> | |
17 | ++ | |
18 | ++/* | |
19 | ++ * Singly linked list implementation. | |
20 | ++ * | |
21 | ++ * This list supports only two operations. | |
22 | ++ * (1) Append an entry to the tail of the list. | |
23 | ++ * (2) Read all entries starting from the head of the list. | |
24 | ++ * | |
25 | ++ * This list is designed for holding "write once, read many" entries. | |
26 | ++ * This list requires no locks for read operation. | |
27 | ++ * This list doesn't support "remove an entry from the list" operation. | |
28 | ++ */ | |
29 | ++ | |
30 | ++/* To reduce memory usage, this list doesn't use "->prev" pointer. */ | |
31 | ++struct list1_head { | |
32 | ++ struct list1_head *next; | |
33 | ++}; | |
34 | ++ | |
35 | ++#define LIST1_HEAD_INIT(name) { &(name) } | |
36 | ++#define LIST1_HEAD(name) struct list1_head name = LIST1_HEAD_INIT(name) | |
37 | ++ | |
38 | ++static inline void INIT_LIST1_HEAD(struct list1_head *list) | |
39 | ++{ | |
40 | ++ list->next = list; | |
41 | ++} | |
42 | ++ | |
43 | ++/* Reuse list_entry because it doesn't use "->prev" pointer. */ | |
44 | ++#define list1_entry list_entry | |
45 | ++ | |
46 | ++/* Reuse list_for_each_rcu because it doesn't use "->prev" pointer. */ | |
47 | ++#define list1_for_each list_for_each_rcu | |
48 | ++/* Reuse list_for_each_entry_rcu because it doesn't use "->prev" pointer. */ | |
49 | ++#define list1_for_each_entry list_for_each_entry_rcu | |
50 | ++ | |
51 | ++/** | |
52 | ++ * list1_for_each_cookie - iterate over a list with cookie. | |
53 | ++ * @pos: the &struct list1_head to use as a loop cursor. | |
54 | ++ * @cookie: the &struct list1_head to use as a cookie. | |
55 | ++ * @head: the head for your list. | |
56 | ++ * | |
57 | ++ * Same with list_for_each_rcu() except that this primitive uses @cookie | |
58 | ++ * so that we can continue iteration. | |
59 | ++ * @cookie must be NULL when iteration starts, and @cookie will become | |
60 | ++ * NULL when iteration finishes. | |
61 | ++ * | |
62 | ++ * Since list elements are never removed, we don't need to get a lock | |
63 | ++ * or a reference count. | |
64 | ++ */ | |
65 | ++#define list1_for_each_cookie(pos, cookie, head) \ | |
66 | ++ for (({ if (!cookie) \ | |
67 | ++ cookie = head; }), \ | |
68 | ++ pos = rcu_dereference((cookie)->next); \ | |
69 | ++ prefetch(pos->next), pos != (head) || ((cookie) = NULL); \ | |
70 | ++ (cookie) = pos, pos = rcu_dereference(pos->next)) | |
71 | ++ | |
72 | ++/** | |
73 | ++ * list1_add_tail - add a new entry to list1 list. | |
74 | ++ * @new: new entry to be added. | |
75 | ++ * @head: list head to add it before. | |
76 | ++ * | |
77 | ++ * Same with list_add_tail_rcu() without "->prev" pointer. | |
78 | ++ * | |
79 | ++ * Caller must hold a lock for protecting @head. | |
80 | ++ */ | |
81 | ++static inline void list1_add_tail(struct list1_head *new, | |
82 | ++ struct list1_head *head) | |
83 | ++{ | |
84 | ++ struct list1_head *prev = head; | |
85 | ++ | |
86 | ++ new->next = head; | |
87 | ++ while (prev->next != head) | |
88 | ++ prev = prev->next; | |
89 | ++ rcu_assign_pointer(prev->next, new); | |
90 | ++} | |
91 | ++ | |
92 | ++#endif |
@@ -0,0 +1,38 @@ | ||
1 | +Subject: MAINTAINERS info | |
2 | + | |
3 | +The archive of tomoyo-users-en mailing list is available at | |
4 | +http://lists.sourceforge.jp/mailman/archives/tomoyo-users-en/ . | |
5 | +Mailing lists for Japanese users are at | |
6 | +http://lists.sourceforge.jp/mailman/archives/tomoyo-users/ and | |
7 | +http://lists.sourceforge.jp/mailman/archives/tomoyo-dev/ . | |
8 | + | |
9 | +TOMOYO Linux English portal is at | |
10 | +http://elinux.org/TomoyoLinux . | |
11 | + | |
12 | +Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> | |
13 | +--- | |
14 | + MAINTAINERS | 13 +++++++++++++ | |
15 | + 1 file changed, 13 insertions(+) | |
16 | + | |
17 | +--- linux-next.orig/MAINTAINERS | |
18 | ++++ linux-next/MAINTAINERS | |
19 | +@@ -4110,6 +4110,19 @@ L: tlan-devel@lists.sourceforge.net (sub | |
20 | + W: http://sourceforge.net/projects/tlan/ | |
21 | + S: Maintained | |
22 | + | |
23 | ++TOMOYO SECURITY MODULE | |
24 | ++P: Kentaro Takeda | |
25 | ++M: takedakn@nttdata.co.jp | |
26 | ++P: Tetsuo Handa | |
27 | ++M: penguin-kernel@I-love.SAKURA.ne.jp | |
28 | ++L: linux-kernel@vger.kernel.org (kernel issues) | |
29 | ++L: tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for developers and users in English) | |
30 | ++L: tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese) | |
31 | ++L: tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese) | |
32 | ++W: http://tomoyo.sourceforge.jp/ | |
33 | ++T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.2.x/tomoyo-lsm/patches/ | |
34 | ++S: Maintained | |
35 | ++ | |
36 | + TOSHIBA ACPI EXTRAS DRIVER | |
37 | + P: John Belmonte | |
38 | + M: toshiba_acpi@memebeam.org |