aboutsummaryrefslogtreecommitdiffstats
path: root/security/tomoyo/realpath.c
diff options
context:
space:
mode:
authorKentaro Takeda <takedakn@nttdata.co.jp>2009-02-05 03:18:12 -0500
committerJames Morris <jmorris@namei.org>2009-02-11 23:15:04 -0500
commitc73bd6d473ceb5d643d3afd7e75b7dc2e6918558 (patch)
tree76a800f3080d000215ec74f4c66fc73560b83a8f /security/tomoyo/realpath.c
parentf9ce1f1cda8b73a36f47e424975a9dfa78b7840c (diff)
Memory and pathname management functions.
TOMOYO Linux performs pathname based access control. To remove factors that make pathname based access control difficult (e.g. symbolic links, "..", "//" etc.), TOMOYO Linux derives realpath of requested pathname from "struct dentry" and "struct vfsmount". The maximum length of string data is limited to 4000 including trailing '\0'. Since TOMOYO Linux uses '\ooo' style representation for non ASCII printable characters, maybe TOMOYO Linux should be able to support 16336 (which means (NAME_MAX * (PATH_MAX / (NAME_MAX + 1)) * 4 + (PATH_MAX / (NAME_MAX + 1))) including trailing '\0'), but I think 4000 is enough for practical use. TOMOYO uses only 0x21 - 0x7E (as printable characters) and 0x20 (as word delimiter) and 0x0A (as line delimiter). 0x01 - 0x20 and 0x80 - 0xFF is handled in \ooo style representation. The reason to use \ooo is to guarantee that "%s" won't damage logs. Userland program can request open("/tmp/file granted.\nAccess /tmp/file ", O_WRONLY | O_CREAT, 0600) and logging such crazy pathname using "Access %s denied.\n" format will cause "fabrication of logs" like Access /tmp/file granted. Access /tmp/file denied. TOMOYO converts such characters to \ooo so that the logs will become Access /tmp/file\040granted.\012Access\040/tmp/file denied. and the administrator can read the logs safely using /bin/cat . Likewise, a crazy request like open("/tmp/\x01\x02\x03\x04\x05\x06\x07\x08\x09", O_WRONLY | O_CREAT, 0600) will be processed safely by converting to Access /tmp/\001\002\003\004\005\006\007\010\011 denied. Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp> Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/tomoyo/realpath.c')
-rw-r--r--security/tomoyo/realpath.c487
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 */
27int 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 */
74int 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(&current->fs->lock);
95 root = current->fs->root;
96 path_get(&root);
97 read_unlock(&current->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 */
143char *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 */
165char *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 */
184char *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. */
197static unsigned int tomoyo_allocated_memory_for_elements;
198/* Quota for holding non-string data. */
199static 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 */
211void *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. */
258static unsigned int tomoyo_allocated_memory_for_savename;
259/* Quota for holding string data in bytes. */
260static 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. */
270struct tomoyo_name_entry {
271 struct list_head list;
272 struct tomoyo_path_info entry;
273};
274
275/* Structure for available memory region. */
276struct 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 */
288static 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 */
299const 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 */
377static 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
394security_initcall(tomoyo_realpath_init);
395
396/* Memory allocated for temporary purpose. */
397static 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 */
406void *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 */
421void 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 */
436int 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 */
477int 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}