diff options
Diffstat (limited to 'security/tomoyo/realpath.c')
-rw-r--r-- | security/tomoyo/realpath.c | 269 |
1 files changed, 71 insertions, 198 deletions
diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 18369d497eb8..c00df45c7ede 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c | |||
@@ -14,9 +14,8 @@ | |||
14 | #include <linux/mnt_namespace.h> | 14 | #include <linux/mnt_namespace.h> |
15 | #include <linux/fs_struct.h> | 15 | #include <linux/fs_struct.h> |
16 | #include <linux/hash.h> | 16 | #include <linux/hash.h> |
17 | 17 | #include <linux/magic.h> | |
18 | #include "common.h" | 18 | #include "common.h" |
19 | #include "realpath.h" | ||
20 | 19 | ||
21 | /** | 20 | /** |
22 | * tomoyo_encode: Convert binary string to ascii string. | 21 | * tomoyo_encode: Convert binary string to ascii string. |
@@ -112,7 +111,7 @@ int tomoyo_realpath_from_path2(struct path *path, char *newname, | |||
112 | path_put(&ns_root); | 111 | path_put(&ns_root); |
113 | /* Prepend "/proc" prefix if using internal proc vfs mount. */ | 112 | /* Prepend "/proc" prefix if using internal proc vfs mount. */ |
114 | if (!IS_ERR(sp) && (path->mnt->mnt_parent == path->mnt) && | 113 | if (!IS_ERR(sp) && (path->mnt->mnt_parent == path->mnt) && |
115 | (strcmp(path->mnt->mnt_sb->s_type->name, "proc") == 0)) { | 114 | (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) { |
116 | sp -= 5; | 115 | sp -= 5; |
117 | if (sp >= newname) | 116 | if (sp >= newname) |
118 | memcpy(sp, "/proc", 5); | 117 | memcpy(sp, "/proc", 5); |
@@ -149,12 +148,12 @@ int tomoyo_realpath_from_path2(struct path *path, char *newname, | |||
149 | * | 148 | * |
150 | * Returns the realpath of the given @path on success, NULL otherwise. | 149 | * Returns the realpath of the given @path on success, NULL otherwise. |
151 | * | 150 | * |
152 | * These functions use tomoyo_alloc(), so the caller must call tomoyo_free() | 151 | * These functions use kzalloc(), so the caller must call kfree() |
153 | * if these functions didn't return NULL. | 152 | * if these functions didn't return NULL. |
154 | */ | 153 | */ |
155 | char *tomoyo_realpath_from_path(struct path *path) | 154 | char *tomoyo_realpath_from_path(struct path *path) |
156 | { | 155 | { |
157 | char *buf = tomoyo_alloc(sizeof(struct tomoyo_page_buffer)); | 156 | char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_KERNEL); |
158 | 157 | ||
159 | BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer) | 158 | BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer) |
160 | <= TOMOYO_MAX_PATHNAME_LEN - 1); | 159 | <= TOMOYO_MAX_PATHNAME_LEN - 1); |
@@ -163,7 +162,7 @@ char *tomoyo_realpath_from_path(struct path *path) | |||
163 | if (tomoyo_realpath_from_path2(path, buf, | 162 | if (tomoyo_realpath_from_path2(path, buf, |
164 | TOMOYO_MAX_PATHNAME_LEN - 1) == 0) | 163 | TOMOYO_MAX_PATHNAME_LEN - 1) == 0) |
165 | return buf; | 164 | return buf; |
166 | tomoyo_free(buf); | 165 | kfree(buf); |
167 | return NULL; | 166 | return NULL; |
168 | } | 167 | } |
169 | 168 | ||
@@ -206,98 +205,47 @@ char *tomoyo_realpath_nofollow(const char *pathname) | |||
206 | } | 205 | } |
207 | 206 | ||
208 | /* Memory allocated for non-string data. */ | 207 | /* Memory allocated for non-string data. */ |
209 | static unsigned int tomoyo_allocated_memory_for_elements; | 208 | static atomic_t tomoyo_policy_memory_size; |
210 | /* Quota for holding non-string data. */ | 209 | /* Quota for holding policy. */ |
211 | static unsigned int tomoyo_quota_for_elements; | 210 | static unsigned int tomoyo_quota_for_policy; |
212 | 211 | ||
213 | /** | 212 | /** |
214 | * tomoyo_alloc_element - Allocate permanent memory for structures. | 213 | * tomoyo_memory_ok - Check memory quota. |
215 | * | 214 | * |
216 | * @size: Size in bytes. | 215 | * @ptr: Pointer to allocated memory. |
217 | * | 216 | * |
218 | * Returns pointer to allocated memory on success, NULL otherwise. | 217 | * Returns true on success, false otherwise. |
219 | * | 218 | * |
220 | * Memory has to be zeroed. | 219 | * Caller holds tomoyo_policy_lock. |
221 | * The RAM is chunked, so NEVER try to kfree() the returned pointer. | 220 | * Memory pointed by @ptr will be zeroed on success. |
222 | */ | 221 | */ |
223 | void *tomoyo_alloc_element(const unsigned int size) | 222 | bool tomoyo_memory_ok(void *ptr) |
224 | { | 223 | { |
225 | static char *buf; | 224 | int allocated_len = ptr ? ksize(ptr) : 0; |
226 | static DEFINE_MUTEX(lock); | 225 | atomic_add(allocated_len, &tomoyo_policy_memory_size); |
227 | static unsigned int buf_used_len = PATH_MAX; | 226 | if (ptr && (!tomoyo_quota_for_policy || |
228 | char *ptr = NULL; | 227 | atomic_read(&tomoyo_policy_memory_size) |
229 | /*Assumes sizeof(void *) >= sizeof(long) is true. */ | 228 | <= tomoyo_quota_for_policy)) { |
230 | const unsigned int word_aligned_size | 229 | memset(ptr, 0, allocated_len); |
231 | = roundup(size, max(sizeof(void *), sizeof(long))); | 230 | return true; |
232 | if (word_aligned_size > PATH_MAX) | ||
233 | return NULL; | ||
234 | mutex_lock(&lock); | ||
235 | if (buf_used_len + word_aligned_size > PATH_MAX) { | ||
236 | if (!tomoyo_quota_for_elements || | ||
237 | tomoyo_allocated_memory_for_elements | ||
238 | + PATH_MAX <= tomoyo_quota_for_elements) | ||
239 | ptr = kzalloc(PATH_MAX, GFP_KERNEL); | ||
240 | if (!ptr) { | ||
241 | printk(KERN_WARNING "ERROR: Out of memory " | ||
242 | "for tomoyo_alloc_element().\n"); | ||
243 | if (!tomoyo_policy_loaded) | ||
244 | panic("MAC Initialization failed.\n"); | ||
245 | } else { | ||
246 | buf = ptr; | ||
247 | tomoyo_allocated_memory_for_elements += PATH_MAX; | ||
248 | buf_used_len = word_aligned_size; | ||
249 | ptr = buf; | ||
250 | } | ||
251 | } else if (word_aligned_size) { | ||
252 | int i; | ||
253 | ptr = buf + buf_used_len; | ||
254 | buf_used_len += word_aligned_size; | ||
255 | for (i = 0; i < word_aligned_size; i++) { | ||
256 | if (!ptr[i]) | ||
257 | continue; | ||
258 | printk(KERN_ERR "WARNING: Reserved memory was tainted! " | ||
259 | "The system might go wrong.\n"); | ||
260 | ptr[i] = '\0'; | ||
261 | } | ||
262 | } | 231 | } |
263 | mutex_unlock(&lock); | 232 | printk(KERN_WARNING "ERROR: Out of memory " |
264 | return ptr; | 233 | "for tomoyo_alloc_element().\n"); |
234 | if (!tomoyo_policy_loaded) | ||
235 | panic("MAC Initialization failed.\n"); | ||
236 | return false; | ||
265 | } | 237 | } |
266 | 238 | ||
267 | /* Memory allocated for string data in bytes. */ | 239 | /** |
268 | static unsigned int tomoyo_allocated_memory_for_savename; | 240 | * tomoyo_memory_free - Free memory for elements. |
269 | /* Quota for holding string data in bytes. */ | ||
270 | static unsigned int tomoyo_quota_for_savename; | ||
271 | |||
272 | /* | ||
273 | * TOMOYO uses this hash only when appending a string into the string | ||
274 | * table. Frequency of appending strings is very low. So we don't need | ||
275 | * large (e.g. 64k) hash size. 256 will be sufficient. | ||
276 | */ | ||
277 | #define TOMOYO_HASH_BITS 8 | ||
278 | #define TOMOYO_MAX_HASH (1u<<TOMOYO_HASH_BITS) | ||
279 | |||
280 | /* | ||
281 | * tomoyo_name_entry is a structure which is used for linking | ||
282 | * "struct tomoyo_path_info" into tomoyo_name_list . | ||
283 | * | 241 | * |
284 | * Since tomoyo_name_list manages a list of strings which are shared by | 242 | * @ptr: Pointer to allocated memory. |
285 | * multiple processes (whereas "struct tomoyo_path_info" inside | ||
286 | * "struct tomoyo_path_info_with_data" is not shared), a reference counter will | ||
287 | * be added to "struct tomoyo_name_entry" rather than "struct tomoyo_path_info" | ||
288 | * when TOMOYO starts supporting garbage collector. | ||
289 | */ | 243 | */ |
290 | struct tomoyo_name_entry { | 244 | void tomoyo_memory_free(void *ptr) |
291 | struct list_head list; | 245 | { |
292 | struct tomoyo_path_info entry; | 246 | atomic_sub(ksize(ptr), &tomoyo_policy_memory_size); |
293 | }; | 247 | kfree(ptr); |
294 | 248 | } | |
295 | /* Structure for available memory region. */ | ||
296 | struct tomoyo_free_memory_block_list { | ||
297 | struct list_head list; | ||
298 | char *ptr; /* Pointer to a free area. */ | ||
299 | int len; /* Length of the area. */ | ||
300 | }; | ||
301 | 249 | ||
302 | /* | 250 | /* |
303 | * tomoyo_name_list is used for holding string data used by TOMOYO. | 251 | * tomoyo_name_list is used for holding string data used by TOMOYO. |
@@ -305,87 +253,58 @@ struct tomoyo_free_memory_block_list { | |||
305 | * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of | 253 | * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of |
306 | * "const struct tomoyo_path_info *". | 254 | * "const struct tomoyo_path_info *". |
307 | */ | 255 | */ |
308 | static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; | 256 | struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; |
257 | /* Lock for protecting tomoyo_name_list . */ | ||
258 | DEFINE_MUTEX(tomoyo_name_list_lock); | ||
309 | 259 | ||
310 | /** | 260 | /** |
311 | * tomoyo_save_name - Allocate permanent memory for string data. | 261 | * tomoyo_get_name - Allocate permanent memory for string data. |
312 | * | 262 | * |
313 | * @name: The string to store into the permernent memory. | 263 | * @name: The string to store into the permernent memory. |
314 | * | 264 | * |
315 | * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. | 265 | * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. |
316 | * | ||
317 | * The RAM is shared, so NEVER try to modify or kfree() the returned name. | ||
318 | */ | 266 | */ |
319 | const struct tomoyo_path_info *tomoyo_save_name(const char *name) | 267 | const struct tomoyo_path_info *tomoyo_get_name(const char *name) |
320 | { | 268 | { |
321 | static LIST_HEAD(fmb_list); | ||
322 | static DEFINE_MUTEX(lock); | ||
323 | struct tomoyo_name_entry *ptr; | 269 | struct tomoyo_name_entry *ptr; |
324 | unsigned int hash; | 270 | unsigned int hash; |
325 | /* fmb contains available size in bytes. | ||
326 | fmb is removed from the fmb_list when fmb->len becomes 0. */ | ||
327 | struct tomoyo_free_memory_block_list *fmb; | ||
328 | int len; | 271 | int len; |
329 | char *cp; | 272 | int allocated_len; |
330 | struct list_head *head; | 273 | struct list_head *head; |
331 | 274 | ||
332 | if (!name) | 275 | if (!name) |
333 | return NULL; | 276 | return NULL; |
334 | len = strlen(name) + 1; | 277 | len = strlen(name) + 1; |
335 | if (len > TOMOYO_MAX_PATHNAME_LEN) { | ||
336 | printk(KERN_WARNING "ERROR: Name too long " | ||
337 | "for tomoyo_save_name().\n"); | ||
338 | return NULL; | ||
339 | } | ||
340 | hash = full_name_hash((const unsigned char *) name, len - 1); | 278 | hash = full_name_hash((const unsigned char *) name, len - 1); |
341 | head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)]; | 279 | head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)]; |
342 | 280 | mutex_lock(&tomoyo_name_list_lock); | |
343 | mutex_lock(&lock); | ||
344 | list_for_each_entry(ptr, head, list) { | 281 | list_for_each_entry(ptr, head, list) { |
345 | if (hash == ptr->entry.hash && !strcmp(name, ptr->entry.name)) | 282 | if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) |
346 | goto out; | 283 | continue; |
347 | } | 284 | atomic_inc(&ptr->users); |
348 | list_for_each_entry(fmb, &fmb_list, list) { | 285 | goto out; |
349 | if (len <= fmb->len) | ||
350 | goto ready; | ||
351 | } | 286 | } |
352 | if (!tomoyo_quota_for_savename || | 287 | ptr = kzalloc(sizeof(*ptr) + len, GFP_KERNEL); |
353 | tomoyo_allocated_memory_for_savename + PATH_MAX | 288 | allocated_len = ptr ? ksize(ptr) : 0; |
354 | <= tomoyo_quota_for_savename) | 289 | if (!ptr || (tomoyo_quota_for_policy && |
355 | cp = kzalloc(PATH_MAX, GFP_KERNEL); | 290 | atomic_read(&tomoyo_policy_memory_size) + allocated_len |
356 | else | 291 | > tomoyo_quota_for_policy)) { |
357 | cp = NULL; | 292 | kfree(ptr); |
358 | fmb = kzalloc(sizeof(*fmb), GFP_KERNEL); | ||
359 | if (!cp || !fmb) { | ||
360 | kfree(cp); | ||
361 | kfree(fmb); | ||
362 | printk(KERN_WARNING "ERROR: Out of memory " | 293 | printk(KERN_WARNING "ERROR: Out of memory " |
363 | "for tomoyo_save_name().\n"); | 294 | "for tomoyo_get_name().\n"); |
364 | if (!tomoyo_policy_loaded) | 295 | if (!tomoyo_policy_loaded) |
365 | panic("MAC Initialization failed.\n"); | 296 | panic("MAC Initialization failed.\n"); |
366 | ptr = NULL; | 297 | ptr = NULL; |
367 | goto out; | 298 | goto out; |
368 | } | 299 | } |
369 | tomoyo_allocated_memory_for_savename += PATH_MAX; | 300 | atomic_add(allocated_len, &tomoyo_policy_memory_size); |
370 | list_add(&fmb->list, &fmb_list); | 301 | ptr->entry.name = ((char *) ptr) + sizeof(*ptr); |
371 | fmb->ptr = cp; | 302 | memmove((char *) ptr->entry.name, name, len); |
372 | fmb->len = PATH_MAX; | 303 | atomic_set(&ptr->users, 1); |
373 | ready: | ||
374 | ptr = tomoyo_alloc_element(sizeof(*ptr)); | ||
375 | if (!ptr) | ||
376 | goto out; | ||
377 | ptr->entry.name = fmb->ptr; | ||
378 | memmove(fmb->ptr, name, len); | ||
379 | tomoyo_fill_path_info(&ptr->entry); | 304 | tomoyo_fill_path_info(&ptr->entry); |
380 | fmb->ptr += len; | ||
381 | fmb->len -= len; | ||
382 | list_add_tail(&ptr->list, head); | 305 | list_add_tail(&ptr->list, head); |
383 | if (fmb->len == 0) { | ||
384 | list_del(&fmb->list); | ||
385 | kfree(fmb); | ||
386 | } | ||
387 | out: | 306 | out: |
388 | mutex_unlock(&lock); | 307 | mutex_unlock(&tomoyo_name_list_lock); |
389 | return ptr ? &ptr->entry : NULL; | 308 | return ptr ? &ptr->entry : NULL; |
390 | } | 309 | } |
391 | 310 | ||
@@ -400,45 +319,14 @@ void __init tomoyo_realpath_init(void) | |||
400 | for (i = 0; i < TOMOYO_MAX_HASH; i++) | 319 | for (i = 0; i < TOMOYO_MAX_HASH; i++) |
401 | INIT_LIST_HEAD(&tomoyo_name_list[i]); | 320 | INIT_LIST_HEAD(&tomoyo_name_list[i]); |
402 | INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); | 321 | INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); |
403 | tomoyo_kernel_domain.domainname = tomoyo_save_name(TOMOYO_ROOT_NAME); | 322 | tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME); |
404 | list_add_tail(&tomoyo_kernel_domain.list, &tomoyo_domain_list); | 323 | /* |
405 | down_read(&tomoyo_domain_list_lock); | 324 | * tomoyo_read_lock() is not needed because this function is |
325 | * called before the first "delete" request. | ||
326 | */ | ||
327 | list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list); | ||
406 | if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) | 328 | if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) |
407 | panic("Can't register tomoyo_kernel_domain"); | 329 | panic("Can't register tomoyo_kernel_domain"); |
408 | up_read(&tomoyo_domain_list_lock); | ||
409 | } | ||
410 | |||
411 | /* Memory allocated for temporary purpose. */ | ||
412 | static atomic_t tomoyo_dynamic_memory_size; | ||
413 | |||
414 | /** | ||
415 | * tomoyo_alloc - Allocate memory for temporary purpose. | ||
416 | * | ||
417 | * @size: Size in bytes. | ||
418 | * | ||
419 | * Returns pointer to allocated memory on success, NULL otherwise. | ||
420 | */ | ||
421 | void *tomoyo_alloc(const size_t size) | ||
422 | { | ||
423 | void *p = kzalloc(size, GFP_KERNEL); | ||
424 | if (p) | ||
425 | atomic_add(ksize(p), &tomoyo_dynamic_memory_size); | ||
426 | return p; | ||
427 | } | ||
428 | |||
429 | /** | ||
430 | * tomoyo_free - Release memory allocated by tomoyo_alloc(). | ||
431 | * | ||
432 | * @p: Pointer returned by tomoyo_alloc(). May be NULL. | ||
433 | * | ||
434 | * Returns nothing. | ||
435 | */ | ||
436 | void tomoyo_free(const void *p) | ||
437 | { | ||
438 | if (p) { | ||
439 | atomic_sub(ksize(p), &tomoyo_dynamic_memory_size); | ||
440 | kfree(p); | ||
441 | } | ||
442 | } | 330 | } |
443 | 331 | ||
444 | /** | 332 | /** |
@@ -451,32 +339,19 @@ void tomoyo_free(const void *p) | |||
451 | int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) | 339 | int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) |
452 | { | 340 | { |
453 | if (!head->read_eof) { | 341 | if (!head->read_eof) { |
454 | const unsigned int shared | 342 | const unsigned int policy |
455 | = tomoyo_allocated_memory_for_savename; | 343 | = atomic_read(&tomoyo_policy_memory_size); |
456 | const unsigned int private | ||
457 | = tomoyo_allocated_memory_for_elements; | ||
458 | const unsigned int dynamic | ||
459 | = atomic_read(&tomoyo_dynamic_memory_size); | ||
460 | char buffer[64]; | 344 | char buffer[64]; |
461 | 345 | ||
462 | memset(buffer, 0, sizeof(buffer)); | 346 | memset(buffer, 0, sizeof(buffer)); |
463 | if (tomoyo_quota_for_savename) | 347 | if (tomoyo_quota_for_policy) |
464 | snprintf(buffer, sizeof(buffer) - 1, | ||
465 | " (Quota: %10u)", | ||
466 | tomoyo_quota_for_savename); | ||
467 | else | ||
468 | buffer[0] = '\0'; | ||
469 | tomoyo_io_printf(head, "Shared: %10u%s\n", shared, buffer); | ||
470 | if (tomoyo_quota_for_elements) | ||
471 | snprintf(buffer, sizeof(buffer) - 1, | 348 | snprintf(buffer, sizeof(buffer) - 1, |
472 | " (Quota: %10u)", | 349 | " (Quota: %10u)", |
473 | tomoyo_quota_for_elements); | 350 | tomoyo_quota_for_policy); |
474 | else | 351 | else |
475 | buffer[0] = '\0'; | 352 | buffer[0] = '\0'; |
476 | tomoyo_io_printf(head, "Private: %10u%s\n", private, buffer); | 353 | tomoyo_io_printf(head, "Policy: %10u%s\n", policy, buffer); |
477 | tomoyo_io_printf(head, "Dynamic: %10u\n", dynamic); | 354 | tomoyo_io_printf(head, "Total: %10u\n", policy); |
478 | tomoyo_io_printf(head, "Total: %10u\n", | ||
479 | shared + private + dynamic); | ||
480 | head->read_eof = true; | 355 | head->read_eof = true; |
481 | } | 356 | } |
482 | return 0; | 357 | return 0; |
@@ -494,9 +369,7 @@ int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) | |||
494 | char *data = head->write_buf; | 369 | char *data = head->write_buf; |
495 | unsigned int size; | 370 | unsigned int size; |
496 | 371 | ||
497 | if (sscanf(data, "Shared: %u", &size) == 1) | 372 | if (sscanf(data, "Policy: %u", &size) == 1) |
498 | tomoyo_quota_for_savename = size; | 373 | tomoyo_quota_for_policy = size; |
499 | else if (sscanf(data, "Private: %u", &size) == 1) | ||
500 | tomoyo_quota_for_elements = size; | ||
501 | return 0; | 374 | return 0; |
502 | } | 375 | } |