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

Subversion リポジトリの参照

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 990 - (show annotations) (download) (as text)
Sat Feb 16 07:51:34 2008 UTC (16 years, 3 months ago) by kumaneko
Original Path: trunk/1.6.x/ccs-patch/fs/realpath.c
File MIME type: text/x-csrc
File size: 12361 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.0-pre 2008/02/16
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 extern int sbin_init_started;
36
37 /***** realpath handler *****/
38
39 /*
40 * GetAbsolutePath - return the path of a dentry but ignores chroot'ed root.
41 * @dentry: dentry to report
42 * @vfsmnt: vfsmnt to which the dentry belongs
43 * @buffer: buffer to return value in
44 * @buflen: buffer length
45 *
46 * Caller holds the dcache_lock.
47 * Based on __d_path() in fs/dcache.c
48 *
49 * If dentry is a directory, trailing '/' is appended.
50 * Characters other than ' ' < c < 127 are converted to \ooo style octal string.
51 * Character \ is converted to \\ string.
52 */
53 static int GetAbsolutePath(struct dentry *dentry, struct vfsmount *vfsmnt, char *buffer, int buflen)
54 {
55 char *start = buffer;
56 char *end = buffer + buflen;
57 bool is_dir = (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode));
58
59 if (buflen < 256) goto out;
60
61 *--end = '\0';
62 buflen--;
63
64 for (;;) {
65 struct dentry *parent;
66
67 if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
68 /* Global root? */
69 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
70 spin_lock(&vfsmount_lock);
71 #endif
72 if (vfsmnt->mnt_parent == vfsmnt) {
73 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
74 spin_unlock(&vfsmount_lock);
75 #endif
76 break;
77 }
78 dentry = vfsmnt->mnt_mountpoint;
79 vfsmnt = vfsmnt->mnt_parent;
80 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
81 spin_unlock(&vfsmount_lock);
82 #endif
83 continue;
84 }
85 if (is_dir) {
86 is_dir = 0; *--end = '/'; buflen--;
87 }
88 parent = dentry->d_parent;
89 {
90 const char *sp = dentry->d_name.name;
91 const char *cp = sp + dentry->d_name.len - 1;
92 unsigned char c;
93
94 /* Exception: Use /proc/self/ rather than /proc/\$/ for current process. */
95 if (IS_ROOT(parent) && *sp > '0' && *sp <= '9' && parent->d_sb && parent->d_sb->s_magic == PROC_SUPER_MAGIC) {
96 char *ep;
97 const pid_t pid = (pid_t) simple_strtoul(sp, &ep, 10);
98 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
99 if (!*ep && pid == current->tgid) { sp = "self"; cp = sp + 3; }
100 #else
101 if (!*ep && pid == current->pid) { sp = "self"; cp = sp + 3; }
102 #endif
103 }
104
105 while (sp <= cp) {
106 c = * (unsigned char *) cp;
107 if (c == '\\') {
108 buflen -= 2;
109 if (buflen < 0) goto out;
110 *--end = '\\';
111 *--end = '\\';
112 } else if (c > ' ' && c < 127) {
113 if (--buflen < 0) goto out;
114 *--end = (char) c;
115 } else {
116 buflen -= 4;
117 if (buflen < 0) goto out;
118 *--end = (c & 7) + '0';
119 *--end = ((c >> 3) & 7) + '0';
120 *--end = (c >> 6) + '0';
121 *--end = '\\';
122 }
123 cp--;
124 }
125 if (--buflen < 0) goto out;
126 *--end = '/';
127 }
128 dentry = parent;
129 }
130 if (*end == '/') { buflen++; end++; }
131 {
132 const char *sp = dentry->d_name.name;
133 const char *cp = sp + dentry->d_name.len - 1;
134 unsigned char c;
135 while (sp <= cp) {
136 c = * (unsigned char *) cp;
137 if (c == '\\') {
138 buflen -= 2;
139 if (buflen < 0) goto out;
140 *--end = '\\';
141 *--end = '\\';
142 } else if (c > ' ' && c < 127) {
143 if (--buflen < 0) goto out;
144 *--end = (char) c;
145 } else {
146 buflen -= 4;
147 if (buflen < 0) goto out;
148 *--end = (c & 7) + '0';
149 *--end = ((c >> 3) & 7) + '0';
150 *--end = (c >> 6) + '0';
151 *--end = '\\';
152 }
153 cp--;
154 }
155 }
156 /* Move the pathname to the top of the buffer. */
157 memmove(start, end, strlen(end) + 1);
158 return 0;
159 out:
160 return -ENOMEM;
161 }
162
163 /* Returns realpath(3) of the given dentry but ignores chroot'ed root. */
164 int realpath_from_dentry2(struct dentry *dentry, struct vfsmount *mnt, char *newname, int newname_len)
165 {
166 int error;
167 struct dentry *d_dentry;
168 struct vfsmount *d_mnt;
169 if (!dentry || !mnt || !newname || newname_len <= 0) return -EINVAL;
170 d_dentry = dget(dentry);
171 d_mnt = mntget(mnt);
172 /***** CRITICAL SECTION START *****/
173 spin_lock(&dcache_lock);
174 error = GetAbsolutePath(d_dentry, d_mnt, newname, newname_len);
175 spin_unlock(&dcache_lock);
176 /***** CRITICAL SECTION END *****/
177 dput(d_dentry);
178 mntput(d_mnt);
179 return error;
180 }
181
182 /* Returns realpath(3) of the given pathname but ignores chroot'ed root. */
183 /* These functions use ccs_alloc(), so caller must ccs_free() if these functions didn't return NULL. */
184 char *realpath_from_dentry(struct dentry *dentry, struct vfsmount *mnt)
185 {
186 char *buf = ccs_alloc(CCS_MAX_PATHNAME_LEN);
187 if (buf && realpath_from_dentry2(dentry, mnt, buf, CCS_MAX_PATHNAME_LEN - 1) == 0) return buf;
188 ccs_free(buf);
189 return NULL;
190 }
191
192 char *realpath(const char *pathname)
193 {
194 struct nameidata nd;
195 if (pathname && path_lookup(pathname, lookup_flags, &nd) == 0) {
196 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,25)
197 char *buf = realpath_from_dentry(nd.path.dentry, nd.path.mnt);
198 path_put(&nd.path);
199 #else
200 char *buf = realpath_from_dentry(nd.dentry, nd.mnt);
201 path_release(&nd);
202 #endif
203 return buf;
204 }
205 return NULL;
206 }
207
208 char *realpath_nofollow(const char *pathname)
209 {
210 struct nameidata nd;
211 if (pathname && path_lookup(pathname, lookup_flags ^ LOOKUP_FOLLOW, &nd) == 0) {
212 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,25)
213 char *buf = realpath_from_dentry(nd.path.dentry, nd.path.mnt);
214 path_put(&nd.path);
215 #else
216 char *buf = realpath_from_dentry(nd.dentry, nd.mnt);
217 path_release(&nd);
218 #endif
219 return buf;
220 }
221 return NULL;
222 }
223
224 /***** Private memory allocator. *****/
225
226 /*
227 * Round up an integer so that the returned pointers are appropriately aligned.
228 * FIXME: Are there more requirements that is needed for assigning value atomically?
229 */
230 static inline unsigned int ROUNDUP(const unsigned int size) {
231 if (sizeof(void *) >= sizeof(long)) {
232 return ((size + sizeof(void *) - 1) / sizeof(void *)) * sizeof(void *);
233 } else {
234 return ((size + sizeof(long) - 1) / sizeof(long)) * sizeof(long);
235 }
236 }
237
238 static unsigned int allocated_memory_for_elements = 0;
239
240 unsigned int GetMemoryUsedForElements(void)
241 {
242 return allocated_memory_for_elements;
243 }
244
245 /* Allocate memory for structures. The RAM is chunked, so NEVER try to kfree() the returned pointer. */
246 void *alloc_element(const unsigned int size)
247 {
248 static DEFINE_MUTEX(lock);
249 static char *buf = NULL;
250 static unsigned int buf_used_len = PAGE_SIZE;
251 char *ptr = NULL;
252 const unsigned int word_aligned_size = ROUNDUP(size);
253 if (word_aligned_size > PAGE_SIZE) return NULL;
254 mutex_lock(&lock);
255 if (buf_used_len + word_aligned_size > PAGE_SIZE) {
256 if ((ptr = kmalloc(PAGE_SIZE, GFP_KERNEL)) == NULL) {
257 printk("ERROR: Out of memory for alloc_element().\n");
258 if (!sbin_init_started) panic("MAC Initialization failed.\n");
259 } else {
260 memset(ptr, 0, PAGE_SIZE);
261 buf = ptr;
262 allocated_memory_for_elements += PAGE_SIZE;
263 buf_used_len = word_aligned_size;
264 ptr = buf;
265 }
266 } else if (word_aligned_size) {
267 int i;
268 ptr = buf + buf_used_len;
269 buf_used_len += word_aligned_size;
270 for (i = 0; i < word_aligned_size; i++) {
271 if (ptr[i]) {
272 printk(KERN_ERR "WARNING: Reserved memory was tainted! The system might go wrong.\n");
273 ptr[i] = '\0';
274 }
275 }
276 }
277 mutex_unlock(&lock);
278 return ptr;
279 }
280
281 /***** Shared memory allocator. *****/
282
283 static unsigned int allocated_memory_for_savename = 0;
284
285 unsigned int GetMemoryUsedForSaveName(void)
286 {
287 return allocated_memory_for_savename;
288 }
289
290 #define MAX_HASH 256
291
292 struct name_entry {
293 struct list1_head list;
294 struct path_info entry;
295 };
296
297 struct free_memory_block_list {
298 struct list_head list;
299 char *ptr; /* Pointer to a free area. */
300 int len; /* Length of the area. */
301 };
302
303 static struct list1_head name_list[MAX_HASH]; /* The list of names. */
304
305 /* Keep the given name on the RAM. The RAM is shared, so NEVER try to modify or kfree() the returned name. */
306 const struct path_info *SaveName(const char *name)
307 {
308 static LIST_HEAD(fmb_list);
309 static DEFINE_MUTEX(lock);
310 struct name_entry *ptr;
311 unsigned int hash;
312 struct free_memory_block_list *fmb;
313 int len;
314 char *cp;
315 if (!name) return NULL;
316 len = strlen(name) + 1;
317 if (len > CCS_MAX_PATHNAME_LEN) {
318 printk("ERROR: Name too long for SaveName().\n");
319 return NULL;
320 }
321 hash = full_name_hash((const unsigned char *) name, len - 1);
322 mutex_lock(&lock);
323 list1_for_each_entry(ptr, &name_list[hash % MAX_HASH], list) {
324 if (hash == ptr->entry.hash && strcmp(name, ptr->entry.name) == 0) goto out;
325 }
326 list_for_each_entry(fmb, &fmb_list, list) {
327 if (len <= fmb->len) goto ready;
328 }
329 cp = kmalloc(PAGE_SIZE, GFP_KERNEL);
330 fmb = kmalloc(sizeof(*fmb), GFP_KERNEL);
331 if (!cp || !fmb) {
332 kfree(cp);
333 kfree(fmb);
334 printk("ERROR: Out of memory for SaveName().\n");
335 if (!sbin_init_started) panic("MAC Initialization failed.\n");
336 ptr = NULL;
337 goto out;
338 }
339 memset(cp, 0, PAGE_SIZE);
340 allocated_memory_for_savename += PAGE_SIZE;
341 list_add(&fmb->list, &fmb_list);
342 fmb->ptr = cp;
343 fmb->len = PAGE_SIZE;
344 ready:
345 ptr = alloc_element(sizeof(*ptr));
346 if (!ptr) goto out;
347 ptr->entry.name = fmb->ptr;
348 memmove(fmb->ptr, name, len);
349 fill_path_info(&ptr->entry);
350 fmb->ptr += len;
351 fmb->len -= len;
352 list1_add_tail_mb(&ptr->list, &name_list[hash % MAX_HASH]);
353 if (fmb->len == 0) {
354 list_del(&fmb->list);
355 kfree(fmb);
356 }
357 out:
358 mutex_unlock(&lock);
359 return ptr ? &ptr->entry : NULL;
360 }
361
362 /***** Dynamic memory allocator. *****/
363
364 struct cache_entry {
365 struct list_head list;
366 void *ptr;
367 int size;
368 };
369
370 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
371 static struct kmem_cache *ccs_cachep = NULL;
372 #else
373 static kmem_cache_t *ccs_cachep = NULL;
374 #endif
375
376 void __init realpath_Init(void)
377 {
378 int i;
379 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,23)
380 ccs_cachep = kmem_cache_create("ccs_cache", sizeof(struct cache_entry), 0, 0, NULL);
381 #else
382 ccs_cachep = kmem_cache_create("ccs_cache", sizeof(struct cache_entry), 0, 0, NULL, NULL);
383 #endif
384 if (!ccs_cachep) panic("Can't create cache.\n");
385 for (i = 0; i < MAX_HASH; i++) {
386 INIT_LIST1_HEAD(&name_list[i]);
387 }
388 if (CCS_MAX_PATHNAME_LEN > PAGE_SIZE) panic("Bad size.");
389 INIT_LIST1_HEAD(&KERNEL_DOMAIN.acl_info_list);
390 KERNEL_DOMAIN.domainname = SaveName(ROOT_NAME);
391 list1_add_tail_mb(&KERNEL_DOMAIN.list, &domain_list);
392 if (FindDomain(ROOT_NAME) != &KERNEL_DOMAIN) panic("Can't register KERNEL_DOMAIN");
393 }
394
395 static LIST_HEAD(cache_list);
396 static spinlock_t cache_list_lock = SPIN_LOCK_UNLOCKED;
397 static unsigned int dynamic_memory_size = 0;
398
399 unsigned int GetMemoryUsedForDynamic(void)
400 {
401 return dynamic_memory_size;
402 }
403
404 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
405 static int round2(size_t size)
406 {
407 #if PAGE_SIZE == 4096
408 size_t bsize = 32;
409 #else
410 size_t bsize = 64;
411 #endif
412 while (size > bsize) bsize <<= 1;
413 return bsize;
414 }
415 #endif
416
417 void *ccs_alloc(const size_t size)
418 {
419 void *ret = kmalloc(size, GFP_KERNEL);
420 if (ret) {
421 struct cache_entry *new_entry = kmem_cache_alloc(ccs_cachep, GFP_KERNEL);
422 if (!new_entry) {
423 kfree(ret); ret = NULL;
424 } else {
425 INIT_LIST_HEAD(&new_entry->list);
426 new_entry->ptr = ret;
427 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
428 new_entry->size = ksize(ret);
429 #else
430 new_entry->size = round2(size);
431 #endif
432 spin_lock(&cache_list_lock);
433 list_add_tail(&new_entry->list, &cache_list);
434 dynamic_memory_size += new_entry->size;
435 spin_unlock(&cache_list_lock);
436 memset(ret, 0, size);
437 }
438 }
439 return ret;
440 }
441
442 void ccs_free(const void *p)
443 {
444 struct list_head *v;
445 struct cache_entry *entry = NULL;
446 if (!p) return;
447 spin_lock(&cache_list_lock);
448 list_for_each(v, &cache_list) {
449 entry = list_entry(v, struct cache_entry, list);
450 if (entry->ptr != p) {
451 entry = NULL; continue;
452 }
453 list_del(&entry->list);
454 dynamic_memory_size -= entry->size;
455 break;
456 }
457 spin_unlock(&cache_list_lock);
458 if (entry) {
459 kfree(p);
460 kmem_cache_free(ccs_cachep, entry);
461 } else {
462 printk("BUG: ccs_free() with invalid pointer.\n");
463 }
464 }

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