オープンソース・ソフトウェアの開発とダウンロード

Subversion リポジトリの参照

Contents of /trunk/1.7.x/ccs-patch/security/ccsecurity/realpath.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1384 - (show annotations) (download) (as text)
Tue Jul 15 03:09:32 2008 UTC (15 years, 10 months ago) by kumaneko
Original Path: trunk/1.6.x/ccs-patch/fs/realpath.c
File MIME type: text/x-csrc
File size: 17164 byte(s)


1 /*
2 * fs/realpath.c
3 *
4 * Get the canonicalized absolute pathnames. The basis for SAKURA and TOMOYO.
5 *
6 * Copyright (C) 2005-2008 NTT DATA CORPORATION
7 *
8 * Version: 1.6.3 2008/07/15
9 *
10 * This file is applicable to both 2.4.30 and 2.6.11 and later.
11 * See README.ccs for ChangeLog.
12 *
13 */
14 #include <linux/string.h>
15 #include <linux/mm.h>
16 #include <linux/utime.h>
17 #include <linux/file.h>
18 #include <linux/smp_lock.h>
19 #include <linux/module.h>
20 #include <linux/slab.h>
21 #include <asm/uaccess.h>
22 #include <asm/atomic.h>
23 #include <linux/version.h>
24 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
25 #include <linux/namei.h>
26 #include <linux/mount.h>
27 static const int lookup_flags = LOOKUP_FOLLOW;
28 #else
29 static const int lookup_flags = LOOKUP_FOLLOW | LOOKUP_POSITIVE;
30 #endif
31 #include <linux/realpath.h>
32 #include <linux/proc_fs.h>
33 #include <linux/ccs_common.h>
34
35 /**
36 * get_absolute_path - Get the path of a dentry but ignores chroot'ed root.
37 *
38 * @dentry: Pointer to "struct dentry".
39 * @vfsmnt: Pointer to "struct vfsmount".
40 * @buffer: Pointer to buffer to return value in.
41 * @buflen: Sizeof @buffer.
42 *
43 * Returns 0 on success, -ENOMEM otherwise.
44 *
45 * Caller holds the dcache_lock and vfsmount_lock.
46 * Based on __d_path() in fs/dcache.c
47 *
48 * If dentry is a directory, trailing '/' is appended.
49 * Characters out of 0x20 < c < 0x7F range are converted to
50 * \ooo style octal string.
51 * Character \ is converted to \\ string.
52 */
53 static int get_absolute_path(struct dentry *dentry, struct vfsmount *vfsmnt,
54 char *buffer, int buflen)
55 {
56 /***** CRITICAL SECTION START *****/
57 char *start = buffer;
58 char *end = buffer + buflen;
59 bool is_dir = (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode));
60
61 if (buflen < 256)
62 goto out;
63
64 *--end = '\0';
65 buflen--;
66
67 for (;;) {
68 struct dentry *parent;
69
70 if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
71 /* Global root? */
72 if (vfsmnt->mnt_parent == vfsmnt)
73 break;
74 dentry = vfsmnt->mnt_mountpoint;
75 vfsmnt = vfsmnt->mnt_parent;
76 continue;
77 }
78 if (is_dir) {
79 is_dir = false;
80 *--end = '/';
81 buflen--;
82 }
83 parent = dentry->d_parent;
84 {
85 const char *sp = dentry->d_name.name;
86 const char *cp = sp + dentry->d_name.len - 1;
87 unsigned char c;
88
89 /*
90 * Exception: Use /proc/self/ rather than
91 * /proc/\$/ for current process.
92 */
93 if (IS_ROOT(parent) && *sp > '0' && *sp <= '9' &&
94 parent->d_sb &&
95 parent->d_sb->s_magic == PROC_SUPER_MAGIC) {
96 char *ep;
97 const pid_t pid
98 = (pid_t) simple_strtoul(sp, &ep, 10);
99 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
100 if (!*ep && pid == current->tgid) {
101 sp = "self";
102 cp = sp + 3;
103 }
104 #else
105 if (!*ep && pid == current->pid) {
106 sp = "self";
107 cp = sp + 3;
108 }
109 #endif
110 }
111
112 while (sp <= cp) {
113 c = *(unsigned char *) cp;
114 if (c == '\\') {
115 buflen -= 2;
116 if (buflen < 0)
117 goto out;
118 *--end = '\\';
119 *--end = '\\';
120 } else if (c > ' ' && c < 127) {
121 if (--buflen < 0)
122 goto out;
123 *--end = (char) c;
124 } else {
125 buflen -= 4;
126 if (buflen < 0)
127 goto out;
128 *--end = (c & 7) + '0';
129 *--end = ((c >> 3) & 7) + '0';
130 *--end = (c >> 6) + '0';
131 *--end = '\\';
132 }
133 cp--;
134 }
135 if (--buflen < 0)
136 goto out;
137 *--end = '/';
138 }
139 dentry = parent;
140 }
141 if (*end == '/') {
142 buflen++;
143 end++;
144 }
145 {
146 const char *sp = dentry->d_name.name;
147 const char *cp = sp + dentry->d_name.len - 1;
148 unsigned char c;
149 while (sp <= cp) {
150 c = *(unsigned char *) cp;
151 if (c == '\\') {
152 buflen -= 2;
153 if (buflen < 0)
154 goto out;
155 *--end = '\\';
156 *--end = '\\';
157 } else if (c > ' ' && c < 127) {
158 if (--buflen < 0)
159 goto out;
160 *--end = (char) c;
161 } else {
162 buflen -= 4;
163 if (buflen < 0)
164 goto out;
165 *--end = (c & 7) + '0';
166 *--end = ((c >> 3) & 7) + '0';
167 *--end = (c >> 6) + '0';
168 *--end = '\\';
169 }
170 cp--;
171 }
172 }
173 /* Move the pathname to the top of the buffer. */
174 memmove(start, end, strlen(end) + 1);
175 return 0;
176 out:
177 return -ENOMEM;
178 /***** CRITICAL SECTION END *****/
179 }
180
181 /**
182 * ccs_realpath_from_dentry2 - Returns realpath(3) of the given dentry but ignores chroot'ed root.
183 *
184 * @dentry: Pointer to "struct dentry".
185 * @mnt: Pointer to "struct vfsmount".
186 * @newname: Pointer to buffer to return value in.
187 * @newname_len: Size of @newname.
188 *
189 * Returns 0 on success, negative value otherwise.
190 */
191 int ccs_realpath_from_dentry2(struct dentry *dentry, struct vfsmount *mnt,
192 char *newname, int newname_len)
193 {
194 int error = -EINVAL;
195 struct dentry *d_dentry;
196 struct vfsmount *d_mnt;
197 if (!dentry || !newname || newname_len <= 2048)
198 goto out;
199 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 22)
200 if (dentry->d_op && dentry->d_op->d_dname) {
201 /* For "socket:[\$]" and "pipe:[\$]". */
202 static const int offset = 1536;
203 char *dp = newname;
204 char *sp = dentry->d_op->d_dname(dentry, newname + offset,
205 newname_len - offset);
206 if (IS_ERR(sp)) {
207 error = PTR_ERR(sp);
208 goto out;
209 }
210 error = -ENOMEM;
211 newname += offset;
212 while (1) {
213 const unsigned char c = *(unsigned char *) sp++;
214 if (c == '\\') {
215 if (dp + 2 >= newname)
216 break;
217 *dp++ = '\\';
218 *dp++ = '\\';
219 } else if (c > ' ' && c < 127) {
220 if (dp + 1 >= newname)
221 break;
222 *dp++ = (char) c;
223 } else if (c) {
224 if (dp + 4 >= newname)
225 break;
226 *dp++ = '\\';
227 *dp++ = (c >> 6) + '0';
228 *dp++ = ((c >> 3) & 7) + '0';
229 *dp++ = (c & 7) + '0';
230 } else {
231 *dp = '\0';
232 return 0;
233 }
234 }
235 goto out;
236 }
237 #endif
238 if (!mnt)
239 goto out;
240 d_dentry = dget(dentry);
241 d_mnt = mntget(mnt);
242 /***** CRITICAL SECTION START *****/
243 spin_lock(&dcache_lock);
244 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
245 spin_lock(&vfsmount_lock);
246 #endif
247 error = get_absolute_path(d_dentry, d_mnt, newname, newname_len);
248 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
249 spin_unlock(&vfsmount_lock);
250 #endif
251 spin_unlock(&dcache_lock);
252 /***** CRITICAL SECTION END *****/
253 dput(d_dentry);
254 mntput(d_mnt);
255 out:
256 if (error)
257 printk(KERN_WARNING "ccs_realpath: Pathname too long. (%d)\n",
258 error);
259 return error;
260 }
261
262 /**
263 * ccs_realpath_from_dentry - Returns realpath(3) of the given pathname but ignores chroot'ed root.
264 *
265 * @dentry: Pointer to "struct dentry".
266 * @mnt: Pointer to "struct vfsmount".
267 *
268 * Returns the realpath of the given @dentry and @mnt on success,
269 * NULL otherwise.
270 *
271 * These functions use ccs_alloc(), so caller must ccs_free()
272 * if these functions didn't return NULL.
273 */
274 char *ccs_realpath_from_dentry(struct dentry *dentry, struct vfsmount *mnt)
275 {
276 char *buf = ccs_alloc(sizeof(struct ccs_page_buffer));
277 if (buf && ccs_realpath_from_dentry2(dentry, mnt, buf,
278 CCS_MAX_PATHNAME_LEN - 1) == 0)
279 return buf;
280 ccs_free(buf);
281 return NULL;
282 }
283
284 /**
285 * ccs_realpath - Get realpath of a pathname.
286 *
287 * @pathname: The pathname to solve.
288 *
289 * Returns the realpath of @pathname on success, NULL otherwise.
290 */
291 char *ccs_realpath(const char *pathname)
292 {
293 struct nameidata nd;
294 if (pathname && path_lookup(pathname, lookup_flags, &nd) == 0) {
295 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
296 char *buf = ccs_realpath_from_dentry(nd.path.dentry,
297 nd.path.mnt);
298 path_put(&nd.path);
299 #else
300 char *buf = ccs_realpath_from_dentry(nd.dentry, nd.mnt);
301 path_release(&nd);
302 #endif
303 return buf;
304 }
305 return NULL;
306 }
307
308 /**
309 * ccs_realpath_nofollow - Get realpath of a pathname.
310 *
311 * @pathname: The pathname to solve.
312 *
313 * Returns the realpath of @pathname on success, NULL otherwise.
314 */
315 char *ccs_realpath_nofollow(const char *pathname)
316 {
317 struct nameidata nd;
318 if (pathname && path_lookup(pathname, lookup_flags ^ LOOKUP_FOLLOW,
319 &nd) == 0) {
320 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 25)
321 char *buf = ccs_realpath_from_dentry(nd.path.dentry,
322 nd.path.mnt);
323 path_put(&nd.path);
324 #else
325 char *buf = ccs_realpath_from_dentry(nd.dentry, nd.mnt);
326 path_release(&nd);
327 #endif
328 return buf;
329 }
330 return NULL;
331 }
332
333 /**
334 * round_up - Round up an integer so that the returned pointers are appropriately aligned.
335 *
336 * @size: Size in bytes.
337 *
338 * Returns rounded value of @size.
339 *
340 * FIXME: Are there more requirements that is needed for assigning value
341 * atomically?
342 */
343 static inline unsigned int round_up(const unsigned int size)
344 {
345 if (sizeof(void *) >= sizeof(long))
346 return ((size + sizeof(void *) - 1)
347 / sizeof(void *)) * sizeof(void *);
348 else
349 return ((size + sizeof(long) - 1)
350 / sizeof(long)) * sizeof(long);
351 }
352
353 static unsigned int allocated_memory_for_elements;
354 static unsigned int quota_for_elements;
355
356 /**
357 * ccs_alloc_element - Allocate permanent memory for structures.
358 *
359 * @size: Size in bytes.
360 *
361 * Returns pointer to allocated memory on success, NULL otherwise.
362 *
363 * The RAM is chunked, so NEVER try to kfree() the returned pointer.
364 */
365 void *ccs_alloc_element(const unsigned int size)
366 {
367 static DEFINE_MUTEX(lock);
368 static char *buf;
369 static unsigned int buf_used_len = PAGE_SIZE;
370 char *ptr = NULL;
371 const unsigned int word_aligned_size = round_up(size);
372 if (word_aligned_size > PAGE_SIZE)
373 return NULL;
374 mutex_lock(&lock);
375 if (buf_used_len + word_aligned_size > PAGE_SIZE) {
376 if (!quota_for_elements || allocated_memory_for_elements
377 + PAGE_SIZE <= quota_for_elements)
378 ptr = kzalloc(PAGE_SIZE, GFP_KERNEL);
379 if (!ptr) {
380 printk(KERN_WARNING "ERROR: Out of memory "
381 "for ccs_alloc_element().\n");
382 if (!sbin_init_started)
383 panic("MAC Initialization failed.\n");
384 } else {
385 buf = ptr;
386 allocated_memory_for_elements += PAGE_SIZE;
387 buf_used_len = word_aligned_size;
388 ptr = buf;
389 }
390 } else if (word_aligned_size) {
391 int i;
392 ptr = buf + buf_used_len;
393 buf_used_len += word_aligned_size;
394 for (i = 0; i < word_aligned_size; i++) {
395 if (!ptr[i])
396 continue;
397 printk(KERN_ERR "WARNING: Reserved memory was tainted! "
398 "The system might go wrong.\n");
399 ptr[i] = '\0';
400 }
401 }
402 mutex_unlock(&lock);
403 return ptr;
404 }
405
406 static unsigned int allocated_memory_for_savename;
407 static unsigned int quota_for_savename;
408
409 #define MAX_HASH 256
410
411 /* Structure for string data. */
412 struct name_entry {
413 struct list1_head list;
414 struct path_info entry;
415 };
416
417 /* Structure for available memory region. */
418 struct free_memory_block_list {
419 struct list_head list;
420 char *ptr; /* Pointer to a free area. */
421 int len; /* Length of the area. */
422 };
423
424 /* The list for "struct name_entry". */
425 static struct list1_head name_list[MAX_HASH];
426
427 /**
428 * ccs_save_name - Allocate permanent memory for string data.
429 *
430 * @name: The string to store into the permernent memory.
431 *
432 * Returns pointer to "struct path_info" on success, NULL otherwise.
433 *
434 * The RAM is shared, so NEVER try to modify or kfree() the returned name.
435 */
436 const struct path_info *ccs_save_name(const char *name)
437 {
438 static LIST_HEAD(fmb_list);
439 static DEFINE_MUTEX(lock);
440 struct name_entry *ptr;
441 unsigned int hash;
442 struct free_memory_block_list *fmb;
443 int len;
444 char *cp;
445 if (!name)
446 return NULL;
447 len = strlen(name) + 1;
448 if (len > CCS_MAX_PATHNAME_LEN) {
449 printk(KERN_WARNING "ERROR: Name too long "
450 "for ccs_save_name().\n");
451 return NULL;
452 }
453 hash = full_name_hash((const unsigned char *) name, len - 1);
454 mutex_lock(&lock);
455 list1_for_each_entry(ptr, &name_list[hash % MAX_HASH], list) {
456 if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name))
457 goto out;
458 }
459 list_for_each_entry(fmb, &fmb_list, list) {
460 if (len <= fmb->len)
461 goto ready;
462 }
463 if (!quota_for_savename || allocated_memory_for_savename + PAGE_SIZE
464 <= quota_for_savename)
465 cp = kzalloc(PAGE_SIZE, GFP_KERNEL);
466 else
467 cp = NULL;
468 fmb = kzalloc(sizeof(*fmb), GFP_KERNEL);
469 if (!cp || !fmb) {
470 kfree(cp);
471 kfree(fmb);
472 printk(KERN_WARNING "ERROR: Out of memory "
473 "for ccs_save_name().\n");
474 if (!sbin_init_started)
475 panic("MAC Initialization failed.\n");
476 ptr = NULL;
477 goto out;
478 }
479 allocated_memory_for_savename += PAGE_SIZE;
480 list_add(&fmb->list, &fmb_list);
481 fmb->ptr = cp;
482 fmb->len = PAGE_SIZE;
483 ready:
484 ptr = ccs_alloc_element(sizeof(*ptr));
485 if (!ptr)
486 goto out;
487 ptr->entry.name = fmb->ptr;
488 memmove(fmb->ptr, name, len);
489 ccs_fill_path_info(&ptr->entry);
490 fmb->ptr += len;
491 fmb->len -= len;
492 list1_add_tail_mb(&ptr->list, &name_list[hash % MAX_HASH]);
493 if (fmb->len == 0) {
494 list_del(&fmb->list);
495 kfree(fmb);
496 }
497 out:
498 mutex_unlock(&lock);
499 return ptr ? &ptr->entry : NULL;
500 }
501
502 /* Structure for temporarily allocated memory. */
503 struct cache_entry {
504 struct list_head list;
505 void *ptr;
506 int size;
507 };
508
509 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20)
510 static struct kmem_cache *ccs_cachep;
511 #else
512 static kmem_cache_t *ccs_cachep;
513 #endif
514
515 /**
516 * ccs_realpath_init - Initialize realpath related code.
517 *
518 * Returns 0.
519 */
520 static int __init ccs_realpath_init(void)
521 {
522 int i;
523 if (CCS_MAX_PATHNAME_LEN > PAGE_SIZE)
524 panic("Bad size.");
525 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 23)
526 ccs_cachep = kmem_cache_create("ccs_cache", sizeof(struct cache_entry),
527 0, 0, NULL);
528 #else
529 ccs_cachep = kmem_cache_create("ccs_cache", sizeof(struct cache_entry),
530 0, 0, NULL, NULL);
531 #endif
532 if (!ccs_cachep)
533 panic("Can't create cache.\n");
534 for (i = 0; i < MAX_HASH; i++)
535 INIT_LIST1_HEAD(&name_list[i]);
536 INIT_LIST1_HEAD(&KERNEL_DOMAIN.acl_info_list);
537 KERNEL_DOMAIN.domainname = ccs_save_name(ROOT_NAME);
538 list1_add_tail_mb(&KERNEL_DOMAIN.list, &domain_list);
539 if (ccs_find_domain(ROOT_NAME) != &KERNEL_DOMAIN)
540 panic("Can't register KERNEL_DOMAIN");
541 return 0;
542 }
543
544 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
545 __initcall(ccs_realpath_init);
546 #else
547 core_initcall(ccs_realpath_init);
548 #endif
549
550 /* The list for "struct cache_entry". */
551 static LIST_HEAD(cache_list);
552
553 static DEFINE_SPINLOCK(cache_list_lock);
554
555 static unsigned int dynamic_memory_size;
556
557 #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 0)
558 /**
559 * round2 - Rounded up to power-of-two value.
560 *
561 * @size: Size in bytes.
562 *
563 * Returns power-of-two of @size.
564 */
565 static int round2(size_t size)
566 {
567 #if PAGE_SIZE == 4096
568 size_t bsize = 32;
569 #else
570 size_t bsize = 64;
571 #endif
572 while (size > bsize)
573 bsize <<= 1;
574 return bsize;
575 }
576 #endif
577
578 /**
579 * ccs_alloc - Allocate memory for temporal purpose.
580 *
581 * @size: Size in bytes.
582 *
583 * Returns pointer to allocated memory on success, NULL otherwise.
584 */
585 void *ccs_alloc(const size_t size)
586 {
587 struct cache_entry *new_entry;
588 void *ret = kzalloc(size, GFP_KERNEL);
589 if (!ret)
590 goto out;
591 new_entry = kmem_cache_alloc(ccs_cachep, GFP_KERNEL);
592 if (!new_entry) {
593 kfree(ret);
594 ret = NULL;
595 goto out;
596 }
597 INIT_LIST_HEAD(&new_entry->list);
598 new_entry->ptr = ret;
599 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 5, 0)
600 new_entry->size = ksize(ret);
601 #else
602 new_entry->size = round2(size);
603 #endif
604 /***** CRITICAL SECTION START *****/
605 spin_lock(&cache_list_lock);
606 list_add_tail(&new_entry->list, &cache_list);
607 dynamic_memory_size += new_entry->size;
608 spin_unlock(&cache_list_lock);
609 /***** CRITICAL SECTION END *****/
610 out:
611 return ret;
612 }
613
614 /**
615 * ccs_free - Release memory allocated by ccs_alloc().
616 *
617 * @p: Pointer returned by ccs_alloc(). May be NULL.
618 *
619 * Returns nothing.
620 */
621 void ccs_free(const void *p)
622 {
623 struct list_head *v;
624 struct cache_entry *entry = NULL;
625 if (!p)
626 return;
627 /***** CRITICAL SECTION START *****/
628 spin_lock(&cache_list_lock);
629 list_for_each(v, &cache_list) {
630 entry = list_entry(v, struct cache_entry, list);
631 if (entry->ptr != p) {
632 entry = NULL;
633 continue;
634 }
635 list_del(&entry->list);
636 dynamic_memory_size -= entry->size;
637 break;
638 }
639 spin_unlock(&cache_list_lock);
640 /***** CRITICAL SECTION END *****/
641 if (entry) {
642 kfree(p);
643 kmem_cache_free(ccs_cachep, entry);
644 } else {
645 printk(KERN_WARNING "BUG: ccs_free() with invalid pointer.\n");
646 }
647 }
648
649 /**
650 * ccs_read_memory_counter - Check for memory usage.
651 *
652 * @head: Pointer to "struct ccs_io_buffer".
653 *
654 * Returns memory usage.
655 */
656 int ccs_read_memory_counter(struct ccs_io_buffer *head)
657 {
658 if (!head->read_eof) {
659 const unsigned int shared = allocated_memory_for_savename;
660 const unsigned int private = allocated_memory_for_elements;
661 const unsigned int dynamic = dynamic_memory_size;
662 char buffer[64];
663 memset(buffer, 0, sizeof(buffer));
664 if (quota_for_savename)
665 snprintf(buffer, sizeof(buffer) - 1,
666 " (Quota: %10u)", quota_for_savename);
667 else
668 buffer[0] = '\0';
669 ccs_io_printf(head, "Shared: %10u%s\n", shared, buffer);
670 if (quota_for_elements)
671 snprintf(buffer, sizeof(buffer) - 1,
672 " (Quota: %10u)", quota_for_elements);
673 else
674 buffer[0] = '\0';
675 ccs_io_printf(head, "Private: %10u%s\n", private, buffer);
676 ccs_io_printf(head, "Dynamic: %10u\n", dynamic);
677 ccs_io_printf(head, "Total: %10u\n",
678 shared + private + dynamic);
679 head->read_eof = true;
680 }
681 return 0;
682 }
683
684 /**
685 * ccs_write_memory_quota - Set memory quota.
686 *
687 * @head: Pointer to "struct ccs_io_buffer".
688 *
689 * Returns 0.
690 */
691 int ccs_write_memory_quota(struct ccs_io_buffer *head)
692 {
693 char *data = head->write_buf;
694 unsigned int size;
695 if (sscanf(data, "Shared: %u", &size) == 1)
696 quota_for_savename = size;
697 else if (sscanf(data, "Private: %u", &size) == 1)
698 quota_for_elements = size;
699 return 0;
700 }

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26