diff options
Diffstat (limited to 'security/tomoyo/realpath.c')
-rw-r--r-- | security/tomoyo/realpath.c | 487 |
1 files changed, 487 insertions, 0 deletions
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c new file mode 100644 index 000000000000..5fd48d23a217 --- /dev/null +++ b/security/tomoyo/realpath.c | |||
@@ -0,0 +1,487 @@ | |||
1 | /* | ||
2 | * security/tomoyo/realpath.c | ||
3 | * | ||
4 | * Get the canonicalized absolute pathnames. The basis for TOMOYO. | ||
5 | * | ||
6 | * Copyright (C) 2005-2009 NTT DATA CORPORATION | ||
7 | * | ||
8 | * Version: 2.2.0-pre 2009/02/01 | ||
9 | * | ||
10 | */ | ||
11 | |||
12 | #include <linux/types.h> | ||
13 | #include <linux/mount.h> | ||
14 | #include <linux/mnt_namespace.h> | ||
15 | #include "common.h" | ||
16 | #include "realpath.h" | ||
17 | |||
18 | /** | ||
19 | * tomoyo_encode: Convert binary string to ascii string. | ||
20 | * | ||
21 | * @buffer: Buffer for ASCII string. | ||
22 | * @buflen: Size of @buffer. | ||
23 | * @str: Binary string. | ||
24 | * | ||
25 | * Returns 0 on success, -ENOMEM otherwise. | ||
26 | */ | ||
27 | int tomoyo_encode(char *buffer, int buflen, const char *str) | ||
28 | { | ||
29 | while (1) { | ||
30 | const unsigned char c = *(unsigned char *) str++; | ||
31 | |||
32 | if (tomoyo_is_valid(c)) { | ||
33 | if (--buflen <= 0) | ||
34 | break; | ||
35 | *buffer++ = (char) c; | ||
36 | if (c != '\\') | ||
37 | continue; | ||
38 | if (--buflen <= 0) | ||
39 | break; | ||
40 | *buffer++ = (char) c; | ||
41 | continue; | ||
42 | } | ||
43 | if (!c) { | ||
44 | if (--buflen <= 0) | ||
45 | break; | ||
46 | *buffer = '\0'; | ||
47 | return 0; | ||
48 | } | ||
49 | buflen -= 4; | ||
50 | if (buflen <= 0) | ||
51 | break; | ||
52 | *buffer++ = '\\'; | ||
53 | *buffer++ = (c >> 6) + '0'; | ||
54 | *buffer++ = ((c >> 3) & 7) + '0'; | ||
55 | *buffer++ = (c & 7) + '0'; | ||
56 | } | ||
57 | return -ENOMEM; | ||
58 | } | ||
59 | |||
60 | /** | ||
61 | * tomoyo_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root. | ||
62 | * | ||
63 | * @path: Pointer to "struct path". | ||
64 | * @newname: Pointer to buffer to return value in. | ||
65 | * @newname_len: Size of @newname. | ||
66 | * | ||
67 | * Returns 0 on success, negative value otherwise. | ||
68 | * | ||
69 | * If dentry is a directory, trailing '/' is appended. | ||
70 | * Characters out of 0x20 < c < 0x7F range are converted to | ||
71 | * \ooo style octal string. | ||
72 | * Character \ is converted to \\ string. | ||
73 | */ | ||
74 | int tomoyo_realpath_from_path2(struct path *path, char *newname, | ||
75 | int newname_len) | ||
76 | { | ||
77 | int error = -ENOMEM; | ||
78 | struct dentry *dentry = path->dentry; | ||
79 | char *sp; | ||
80 | |||
81 | if (!dentry || !path->mnt || !newname || newname_len <= 2048) | ||
82 | return -EINVAL; | ||
83 | if (dentry->d_op && dentry->d_op->d_dname) { | ||
84 | /* For "socket:[\$]" and "pipe:[\$]". */ | ||
85 | static const int offset = 1536; | ||
86 | sp = dentry->d_op->d_dname(dentry, newname + offset, | ||
87 | newname_len - offset); | ||
88 | } else { | ||
89 | /* Taken from d_namespace_path(). */ | ||
90 | struct path root; | ||
91 | struct path ns_root = { }; | ||
92 | struct path tmp; | ||
93 | |||
94 | read_lock(¤t->fs->lock); | ||
95 | root = current->fs->root; | ||
96 | path_get(&root); | ||
97 | read_unlock(¤t->fs->lock); | ||
98 | spin_lock(&vfsmount_lock); | ||
99 | if (root.mnt && root.mnt->mnt_ns) | ||
100 | ns_root.mnt = mntget(root.mnt->mnt_ns->root); | ||
101 | if (ns_root.mnt) | ||
102 | ns_root.dentry = dget(ns_root.mnt->mnt_root); | ||
103 | spin_unlock(&vfsmount_lock); | ||
104 | spin_lock(&dcache_lock); | ||
105 | tmp = ns_root; | ||
106 | sp = __d_path(path, &tmp, newname, newname_len); | ||
107 | spin_unlock(&dcache_lock); | ||
108 | path_put(&root); | ||
109 | path_put(&ns_root); | ||
110 | } | ||
111 | if (IS_ERR(sp)) | ||
112 | error = PTR_ERR(sp); | ||
113 | else | ||
114 | error = tomoyo_encode(newname, sp - newname, sp); | ||
115 | /* Append trailing '/' if dentry is a directory. */ | ||
116 | if (!error && dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode) | ||
117 | && *newname) { | ||
118 | sp = newname + strlen(newname); | ||
119 | if (*(sp - 1) != '/') { | ||
120 | if (sp < newname + newname_len - 4) { | ||
121 | *sp++ = '/'; | ||
122 | *sp = '\0'; | ||
123 | } else { | ||
124 | error = -ENOMEM; | ||
125 | } | ||
126 | } | ||
127 | } | ||
128 | if (error) | ||
129 | printk(KERN_WARNING "tomoyo_realpath: Pathname too long.\n"); | ||
130 | return error; | ||
131 | } | ||
132 | |||
133 | /** | ||
134 | * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. | ||
135 | * | ||
136 | * @path: Pointer to "struct path". | ||
137 | * | ||
138 | * Returns the realpath of the given @path on success, NULL otherwise. | ||
139 | * | ||
140 | * These functions use tomoyo_alloc(), so the caller must call tomoyo_free() | ||
141 | * if these functions didn't return NULL. | ||
142 | */ | ||
143 | char *tomoyo_realpath_from_path(struct path *path) | ||
144 | { | ||
145 | char *buf = tomoyo_alloc(sizeof(struct tomoyo_page_buffer)); | ||
146 | |||
147 | BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer) | ||
148 | <= TOMOYO_MAX_PATHNAME_LEN - 1); | ||
149 | if (!buf) | ||
150 | return NULL; | ||
151 | if (tomoyo_realpath_from_path2(path, buf, | ||
152 | TOMOYO_MAX_PATHNAME_LEN - 1) == 0) | ||
153 | return buf; | ||
154 | tomoyo_free(buf); | ||
155 | return NULL; | ||
156 | } | ||
157 | |||
158 | /** | ||
159 | * tomoyo_realpath - Get realpath of a pathname. | ||
160 | * | ||
161 | * @pathname: The pathname to solve. | ||
162 | * | ||
163 | * Returns the realpath of @pathname on success, NULL otherwise. | ||
164 | */ | ||
165 | char *tomoyo_realpath(const char *pathname) | ||
166 | { | ||
167 | struct nameidata nd; | ||
168 | |||
169 | if (pathname && path_lookup(pathname, LOOKUP_FOLLOW, &nd) == 0) { | ||
170 | char *buf = tomoyo_realpath_from_path(&nd.path); | ||
171 | path_put(&nd.path); | ||
172 | return buf; | ||
173 | } | ||
174 | return NULL; | ||
175 | } | ||
176 | |||
177 | /** | ||
178 | * tomoyo_realpath_nofollow - Get realpath of a pathname. | ||
179 | * | ||
180 | * @pathname: The pathname to solve. | ||
181 | * | ||
182 | * Returns the realpath of @pathname on success, NULL otherwise. | ||
183 | */ | ||
184 | char *tomoyo_realpath_nofollow(const char *pathname) | ||
185 | { | ||
186 | struct nameidata nd; | ||
187 | |||
188 | if (pathname && path_lookup(pathname, 0, &nd) == 0) { | ||
189 | char *buf = tomoyo_realpath_from_path(&nd.path); | ||
190 | path_put(&nd.path); | ||
191 | return buf; | ||
192 | } | ||
193 | return NULL; | ||
194 | } | ||
195 | |||
196 | /* Memory allocated for non-string data. */ | ||
197 | static unsigned int tomoyo_allocated_memory_for_elements; | ||
198 | /* Quota for holding non-string data. */ | ||
199 | static unsigned int tomoyo_quota_for_elements; | ||
200 | |||
201 | /** | ||
202 | * tomoyo_alloc_element - Allocate permanent memory for structures. | ||
203 | * | ||
204 | * @size: Size in bytes. | ||
205 | * | ||
206 | * Returns pointer to allocated memory on success, NULL otherwise. | ||
207 | * | ||
208 | * Memory has to be zeroed. | ||
209 | * The RAM is chunked, so NEVER try to kfree() the returned pointer. | ||
210 | */ | ||
211 | void *tomoyo_alloc_element(const unsigned int size) | ||
212 | { | ||
213 | static char *buf; | ||
214 | static DEFINE_MUTEX(lock); | ||
215 | static unsigned int buf_used_len = PATH_MAX; | ||
216 | char *ptr = NULL; | ||
217 | /*Assumes sizeof(void *) >= sizeof(long) is true. */ | ||
218 | const unsigned int word_aligned_size | ||
219 | = roundup(size, max(sizeof(void *), sizeof(long))); | ||
220 | if (word_aligned_size > PATH_MAX) | ||
221 | return NULL; | ||
222 | /***** EXCLUSIVE SECTION START *****/ | ||
223 | mutex_lock(&lock); | ||
224 | if (buf_used_len + word_aligned_size > PATH_MAX) { | ||
225 | if (!tomoyo_quota_for_elements || | ||
226 | tomoyo_allocated_memory_for_elements | ||
227 | + PATH_MAX <= tomoyo_quota_for_elements) | ||
228 | ptr = kzalloc(PATH_MAX, GFP_KERNEL); | ||
229 | if (!ptr) { | ||
230 | printk(KERN_WARNING "ERROR: Out of memory " | ||
231 | "for tomoyo_alloc_element().\n"); | ||
232 | if (!tomoyo_policy_loaded) | ||
233 | panic("MAC Initialization failed.\n"); | ||
234 | } else { | ||
235 | buf = ptr; | ||
236 | tomoyo_allocated_memory_for_elements += PATH_MAX; | ||
237 | buf_used_len = word_aligned_size; | ||
238 | ptr = buf; | ||
239 | } | ||
240 | } else if (word_aligned_size) { | ||
241 | int i; | ||
242 | ptr = buf + buf_used_len; | ||
243 | buf_used_len += word_aligned_size; | ||
244 | for (i = 0; i < word_aligned_size; i++) { | ||
245 | if (!ptr[i]) | ||
246 | continue; | ||
247 | printk(KERN_ERR "WARNING: Reserved memory was tainted! " | ||
248 | "The system might go wrong.\n"); | ||
249 | ptr[i] = '\0'; | ||
250 | } | ||
251 | } | ||
252 | mutex_unlock(&lock); | ||
253 | /***** EXCLUSIVE SECTION END *****/ | ||
254 | return ptr; | ||
255 | } | ||
256 | |||
257 | /* Memory allocated for string data in bytes. */ | ||
258 | static unsigned int tomoyo_allocated_memory_for_savename; | ||
259 | /* Quota for holding string data in bytes. */ | ||
260 | static unsigned int tomoyo_quota_for_savename; | ||
261 | |||
262 | /* | ||
263 | * TOMOYO uses this hash only when appending a string into the string | ||
264 | * table. Frequency of appending strings is very low. So we don't need | ||
265 | * large (e.g. 64k) hash size. 256 will be sufficient. | ||
266 | */ | ||
267 | #define TOMOYO_MAX_HASH 256 | ||
268 | |||
269 | /* Structure for string data. */ | ||
270 | struct tomoyo_name_entry { | ||
271 | struct list_head list; | ||
272 | struct tomoyo_path_info entry; | ||
273 | }; | ||
274 | |||
275 | /* Structure for available memory region. */ | ||
276 | struct tomoyo_free_memory_block_list { | ||
277 | struct list_head list; | ||
278 | char *ptr; /* Pointer to a free area. */ | ||
279 | int len; /* Length of the area. */ | ||
280 | }; | ||
281 | |||
282 | /* | ||
283 | * The list for "struct tomoyo_name_entry". | ||
284 | * | ||
285 | * This list is updated only inside tomoyo_save_name(), thus | ||
286 | * no global mutex exists. | ||
287 | */ | ||
288 | static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; | ||
289 | |||
290 | /** | ||
291 | * tomoyo_save_name - Allocate permanent memory for string data. | ||
292 | * | ||
293 | * @name: The string to store into the permernent memory. | ||
294 | * | ||
295 | * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. | ||
296 | * | ||
297 | * The RAM is shared, so NEVER try to modify or kfree() the returned name. | ||
298 | */ | ||
299 | const struct tomoyo_path_info *tomoyo_save_name(const char *name) | ||
300 | { | ||
301 | static LIST_HEAD(fmb_list); | ||
302 | static DEFINE_MUTEX(lock); | ||
303 | struct tomoyo_name_entry *ptr; | ||
304 | unsigned int hash; | ||
305 | /* fmb contains available size in bytes. | ||
306 | fmb is removed from the fmb_list when fmb->len becomes 0. */ | ||
307 | struct tomoyo_free_memory_block_list *fmb; | ||
308 | int len; | ||
309 | char *cp; | ||
310 | |||
311 | if (!name) | ||
312 | return NULL; | ||
313 | len = strlen(name) + 1; | ||
314 | if (len > TOMOYO_MAX_PATHNAME_LEN) { | ||
315 | printk(KERN_WARNING "ERROR: Name too long " | ||
316 | "for tomoyo_save_name().\n"); | ||
317 | return NULL; | ||
318 | } | ||
319 | hash = full_name_hash((const unsigned char *) name, len - 1); | ||
320 | /***** EXCLUSIVE SECTION START *****/ | ||
321 | mutex_lock(&lock); | ||
322 | list_for_each_entry(ptr, &tomoyo_name_list[hash % TOMOYO_MAX_HASH], | ||
323 | list) { | ||
324 | if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name)) | ||
325 | goto out; | ||
326 | } | ||
327 | list_for_each_entry(fmb, &fmb_list, list) { | ||
328 | if (len <= fmb->len) | ||
329 | goto ready; | ||
330 | } | ||
331 | if (!tomoyo_quota_for_savename || | ||
332 | tomoyo_allocated_memory_for_savename + PATH_MAX | ||
333 | <= tomoyo_quota_for_savename) | ||
334 | cp = kzalloc(PATH_MAX, GFP_KERNEL); | ||
335 | else | ||
336 | cp = NULL; | ||
337 | fmb = kzalloc(sizeof(*fmb), GFP_KERNEL); | ||
338 | if (!cp || !fmb) { | ||
339 | kfree(cp); | ||
340 | kfree(fmb); | ||
341 | printk(KERN_WARNING "ERROR: Out of memory " | ||
342 | "for tomoyo_save_name().\n"); | ||
343 | if (!tomoyo_policy_loaded) | ||
344 | panic("MAC Initialization failed.\n"); | ||
345 | ptr = NULL; | ||
346 | goto out; | ||
347 | } | ||
348 | tomoyo_allocated_memory_for_savename += PATH_MAX; | ||
349 | list_add(&fmb->list, &fmb_list); | ||
350 | fmb->ptr = cp; | ||
351 | fmb->len = PATH_MAX; | ||
352 | ready: | ||
353 | ptr = tomoyo_alloc_element(sizeof(*ptr)); | ||
354 | if (!ptr) | ||
355 | goto out; | ||
356 | ptr->entry.name = fmb->ptr; | ||
357 | memmove(fmb->ptr, name, len); | ||
358 | tomoyo_fill_path_info(&ptr->entry); | ||
359 | fmb->ptr += len; | ||
360 | fmb->len -= len; | ||
361 | list_add_tail(&ptr->list, &tomoyo_name_list[hash % TOMOYO_MAX_HASH]); | ||
362 | if (fmb->len == 0) { | ||
363 | list_del(&fmb->list); | ||
364 | kfree(fmb); | ||
365 | } | ||
366 | out: | ||
367 | mutex_unlock(&lock); | ||
368 | /***** EXCLUSIVE SECTION END *****/ | ||
369 | return ptr ? &ptr->entry : NULL; | ||
370 | } | ||
371 | |||
372 | /** | ||
373 | * tomoyo_realpath_init - Initialize realpath related code. | ||
374 | * | ||
375 | * Returns 0. | ||
376 | */ | ||
377 | static int __init tomoyo_realpath_init(void) | ||
378 | { | ||
379 | int i; | ||
380 | |||
381 | BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX); | ||
382 | for (i = 0; i < TOMOYO_MAX_HASH; i++) | ||
383 | INIT_LIST_HEAD(&tomoyo_name_list[i]); | ||
384 | INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); | ||
385 | tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME); | ||
386 | list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list); | ||
387 | down_read(&tomoyo_domain_list_lock); | ||
388 | if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) | ||
389 | panic("Can't register tomoyo_kernel_domain"); | ||
390 | up_read(&tomoyo_domain_list_lock); | ||
391 | return 0; | ||
392 | } | ||
393 | |||
394 | security_initcall(tomoyo_realpath_init); | ||
395 | |||
396 | /* Memory allocated for temporary purpose. */ | ||
397 | static atomic_t tomoyo_dynamic_memory_size; | ||
398 | |||
399 | /** | ||
400 | * tomoyo_alloc - Allocate memory for temporary purpose. | ||
401 | * | ||
402 | * @size: Size in bytes. | ||
403 | * | ||
404 | * Returns pointer to allocated memory on success, NULL otherwise. | ||
405 | */ | ||
406 | void *tomoyo_alloc(const size_t size) | ||
407 | { | ||
408 | void *p = kzalloc(size, GFP_KERNEL); | ||
409 | if (p) | ||
410 | atomic_add(ksize(p), &tomoyo_dynamic_memory_size); | ||
411 | return p; | ||
412 | } | ||
413 | |||
414 | /** | ||
415 | * tomoyo_free - Release memory allocated by tomoyo_alloc(). | ||
416 | * | ||
417 | * @p: Pointer returned by tomoyo_alloc(). May be NULL. | ||
418 | * | ||
419 | * Returns nothing. | ||
420 | */ | ||
421 | void tomoyo_free(const void *p) | ||
422 | { | ||
423 | if (p) { | ||
424 | atomic_sub(ksize(p), &tomoyo_dynamic_memory_size); | ||
425 | kfree(p); | ||
426 | } | ||
427 | } | ||
428 | |||
429 | /** | ||
430 | * tomoyo_read_memory_counter - Check for memory usage in bytes. | ||
431 | * | ||
432 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
433 | * | ||
434 | * Returns memory usage. | ||
435 | */ | ||
436 | int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) | ||
437 | { | ||
438 | if (!head->read_eof) { | ||
439 | const unsigned int shared | ||
440 | = tomoyo_allocated_memory_for_savename; | ||
441 | const unsigned int private | ||
442 | = tomoyo_allocated_memory_for_elements; | ||
443 | const unsigned int dynamic | ||
444 | = atomic_read(&tomoyo_dynamic_memory_size); | ||
445 | char buffer[64]; | ||
446 | |||
447 | memset(buffer, 0, sizeof(buffer)); | ||
448 | if (tomoyo_quota_for_savename) | ||
449 | snprintf(buffer, sizeof(buffer) - 1, | ||
450 | " (Quota: %10u)", | ||
451 | tomoyo_quota_for_savename); | ||
452 | else | ||
453 | buffer[0] = '\0'; | ||
454 | tomoyo_io_printf(head, "Shared: %10u%s\n", shared, buffer); | ||
455 | if (tomoyo_quota_for_elements) | ||
456 | snprintf(buffer, sizeof(buffer) - 1, | ||
457 | " (Quota: %10u)", | ||
458 | tomoyo_quota_for_elements); | ||
459 | else | ||
460 | buffer[0] = '\0'; | ||
461 | tomoyo_io_printf(head, "Private: %10u%s\n", private, buffer); | ||
462 | tomoyo_io_printf(head, "Dynamic: %10u\n", dynamic); | ||
463 | tomoyo_io_printf(head, "Total: %10u\n", | ||
464 | shared + private + dynamic); | ||
465 | head->read_eof = true; | ||
466 | } | ||
467 | return 0; | ||
468 | } | ||
469 | |||
470 | /** | ||
471 | * tomoyo_write_memory_quota - Set memory quota. | ||
472 | * | ||
473 | * @head: Pointer to "struct tomoyo_io_buffer". | ||
474 | * | ||
475 | * Returns 0. | ||
476 | */ | ||
477 | int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) | ||
478 | { | ||
479 | char *data = head->write_buf; | ||
480 | unsigned int size; | ||
481 | |||
482 | if (sscanf(data, "Shared: %u", &size) == 1) | ||
483 | tomoyo_quota_for_savename = size; | ||
484 | else if (sscanf(data, "Private: %u", &size) == 1) | ||
485 | tomoyo_quota_for_elements = size; | ||
486 | return 0; | ||
487 | } | ||