This patch contains memory management code for TOMOYO. Signed-off-by: Tetsuo Handa --- security/tomoyo/memory.c | 391 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) --- /dev/null +++ security-testing-2.6/security/tomoyo/memory.c @@ -0,0 +1,391 @@ +/* + * security/tomoyo/memory.c + * + * Copyright (C) 2005-2009 NTT DATA CORPORATION + */ +#include "internal.h" + +void tomoyo_warn_oom(const char *function) +{ + /* Reduce error messages. */ + static pid_t tomoyo_last_pid; + const pid_t pid = current->pid; + if (tomoyo_last_pid != pid) { + printk(KERN_WARNING "ERROR: Out of memory at %s.\n", + function); + tomoyo_last_pid = pid; + } + if (!tomoyo_policy_loaded) + panic("MAC Initialization failed.\n"); +} + +static atomic_t tomoyo_policy_memory_size; +static unsigned int tomoyo_quota_for_policy; + +/** + * tomoyo_memory_ok - Check memory quota. + * + * @ptr: Pointer to allocated memory. + * @size: Size in byte. + * + * Returns true if @ptr is not NULL and quota not exceeded, false otehrwise. + */ +bool tomoyo_memory_ok(const void *ptr, const unsigned int size) +{ + size_t s = tomoyo_round2(size); + atomic_add(s, &tomoyo_policy_memory_size); + if (ptr && (!tomoyo_quota_for_policy || + atomic_read(&tomoyo_policy_memory_size) + <= tomoyo_quota_for_policy)) + return true; + atomic_sub(s, &tomoyo_policy_memory_size); + tomoyo_warn_oom(__func__); + return false; +} + +/** + * tomoyo_commit_ok - Check memory quota. + * + * @ptr: Pointer to allocated memory. + * @data: Data to copy from. + * @size: Size in byte. + * + * Returns true if @ptr is not NULL and quota not exceeded, false otehrwise. + */ +bool tomoyo_commit_ok(void *ptr, void *data, const unsigned int size) +{ + if (tomoyo_memory_ok(ptr, size)) { + memmove(ptr, data, size); + memset(data, 0, size); + return true; + } + return false; +} + + +/** + * tomoyo_memory_free - Free memory for elements. + * + * @ptr: Pointer to allocated memory. + * @size: Size in byte. + */ +void tomoyo_memory_free(const void *ptr, size_t size) +{ + atomic_sub(tomoyo_round2(size), &tomoyo_policy_memory_size); + kfree(ptr); +} + +static LIST_HEAD(tomoyo_address_list); + +/** + * tomoyo_get_ipv6_address - Keep the given IPv6 address on the RAM. + * + * @addr: Pointer to "struct in6_addr". + * + * Returns pointer to "struct in6_addr" on success, NULL otherwise. + * + * The RAM is shared, so NEVER try to modify or kfree() the returned address. + */ +const struct in6_addr *tomoyo_get_ipv6_address(const struct in6_addr *addr) +{ + struct tomoyo_ipv6addr_entry *entry; + struct tomoyo_ipv6addr_entry *ptr; + int error = -ENOMEM; + if (!addr) + return NULL; + entry = kzalloc(sizeof(*entry), GFP_KERNEL); + mutex_lock(&tomoyo_policy_lock); + list_for_each_entry(ptr, &tomoyo_address_list, list) { + if (memcmp(&ptr->addr, addr, sizeof(*addr))) + continue; + atomic_inc(&ptr->users); + error = 0; + break; + } + if (error && tomoyo_memory_ok(entry, sizeof(*entry))) { + ptr = entry; + ptr->addr = *addr; + atomic_set(&ptr->users, 1); + list_add_tail(&ptr->list, &tomoyo_address_list); + entry = NULL; + } + mutex_unlock(&tomoyo_policy_lock); + kfree(entry); + return ptr ? &ptr->addr : NULL; +} + +/** + * tomoyo_put_ipv6_address - Delete the given IPv6 address on the RAM. + * + * @addr: Pointer to "struct in6_addr". + */ +void tomoyo_put_ipv6_address(const struct in6_addr *addr) +{ + struct tomoyo_ipv6addr_entry *ptr; + bool can_delete = false; + if (!addr) + return; + ptr = container_of(addr, struct tomoyo_ipv6addr_entry, addr); + mutex_lock(&tomoyo_policy_lock); + if (atomic_dec_and_test(&ptr->users)) { + list_del(&ptr->list); + can_delete = true; + } + mutex_unlock(&tomoyo_policy_lock); + if (can_delete) + tomoyo_memory_free(ptr, sizeof(*ptr)); +} + +/** + * tomoyo_put_condition - Delete memory for "struct tomoyo_condition". + * + * @cond: Pointer to "struct tomoyo_condition". + */ +void tomoyo_put_condition(struct tomoyo_condition *cond) +{ + const struct tomoyo_condition_element *condp; + struct tomoyo_number_union *numbers_p; + struct tomoyo_name_union *names_p; + const struct tomoyo_argv_entry *argv; + const struct tomoyo_envp_entry *envp; + u16 condc; + u16 numbers_count; + u16 names_count; + u16 argc; + u16 envc; + u16 i; + bool can_delete = false; + if (!cond) + return; + BUG_ON(atomic_read(&cond->users) <= 0); + mutex_lock(&tomoyo_policy_lock); + if (atomic_dec_and_test(&cond->users)) { + list_del(&cond->list); + can_delete = true; + } + mutex_unlock(&tomoyo_policy_lock); + if (!can_delete) + return; + condc = cond->condc; + numbers_count = cond->numbers_count; + names_count = cond->names_count; + argc = cond->argc; + envc = cond->envc; + condp = (const struct tomoyo_condition_element *) (cond + 1); + numbers_p = (struct tomoyo_number_union *) (condp + condc); + names_p = (struct tomoyo_name_union *) (numbers_p + numbers_count); + argv = (const struct tomoyo_argv_entry *) (names_p + names_count); + envp = (const struct tomoyo_envp_entry *) (argv + argc); + for (i = 0; i < numbers_count; i++) + tomoyo_put_number_union(numbers_p++); + for (i = 0; i < names_count; i++) + tomoyo_put_name_union(names_p++); + for (i = 0; i < argc; argv++, i++) + tomoyo_put_name(argv->value); + for (i = 0; i < envc; envp++, i++) { + tomoyo_put_name(envp->name); + tomoyo_put_name(envp->value); + } + tomoyo_memory_free(cond, cond->size); +} + +/* + * TOMOYO uses this hash only when appending a string into the string + * table. Frequency of appending strings is very low. So we don't need + * large (e.g. 64k) hash size. 256 will be sufficient. + */ +#define TOMOYO_MAX_HASH 256 + +/* + * tomoyo_name_entry is a structure which is used for linking + * "struct tomoyo_path_info" into tomoyo_name_list . + */ +struct tomoyo_name_entry { + struct list_head list; + atomic_t users; + int size; + struct tomoyo_path_info entry; +}; + +/* + * tomoyo_name_list is used for holding string data used by TOMOYO. + * Since same string data is likely used for multiple times (e.g. + * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of + * "const struct tomoyo_path_info *". + */ +static struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; +static DEFINE_MUTEX(tomoyo_name_list_lock); + +/** + * tomoyo_get_name - Allocate memory for string data. + * + * @name: The string to store into the permernent memory. + * + * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. + */ +const struct tomoyo_path_info *tomoyo_get_name(const char *name) +{ + struct tomoyo_name_entry *ptr; + unsigned int hash; + int len; + int allocated_len; + + if (!name) + return NULL; + len = strlen(name) + 1; + hash = full_name_hash((const unsigned char *) name, len - 1); + mutex_lock(&tomoyo_name_list_lock); + list_for_each_entry(ptr, &tomoyo_name_list[hash % TOMOYO_MAX_HASH], + list) { + if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) + continue; + atomic_inc(&ptr->users); + goto out; + } + allocated_len = tomoyo_round2(sizeof(*ptr) + len); + ptr = kzalloc(allocated_len, GFP_KERNEL); + if (!ptr || (tomoyo_quota_for_policy && + atomic_read(&tomoyo_policy_memory_size) + allocated_len + > tomoyo_quota_for_policy)) { + kfree(ptr); + ptr = NULL; + tomoyo_warn_oom(__func__); + goto out; + } + atomic_add(allocated_len, &tomoyo_policy_memory_size); + ptr->entry.name = ((char *) ptr) + sizeof(*ptr); + memmove((char *) ptr->entry.name, name, len); + atomic_set(&ptr->users, 1); + tomoyo_fill_path_info(&ptr->entry); + ptr->size = allocated_len; + list_add_tail(&ptr->list, &tomoyo_name_list[hash % TOMOYO_MAX_HASH]); + out: + mutex_unlock(&tomoyo_name_list_lock); + return ptr ? &ptr->entry : NULL; +} + +/** + * tomoyo_put_name - Delete shared memory for string data. + * + * @name: Pointer to "struct tomoyo_path_info". + */ +void tomoyo_put_name(const struct tomoyo_path_info *name) +{ + struct tomoyo_name_entry *ptr; + bool can_delete = false; + if (!name) + return; + ptr = container_of(name, struct tomoyo_name_entry, entry); + mutex_lock(&tomoyo_name_list_lock); + if (atomic_dec_and_test(&ptr->users)) { + list_del(&ptr->list); + can_delete = true; + } + mutex_unlock(&tomoyo_name_list_lock); + if (can_delete) { + atomic_sub(ptr->size, &tomoyo_policy_memory_size); + kfree(ptr); + } +} + +/** + * tomoyo_mm_init - Initialize mm code. + * + * Returns 0. + */ +static int __init tomoyo_mm_init(void) +{ + int i; + if (!tomoyo_registered) + return 0; + for (i = 0; i < TOMOYO_MAX_HASH; i++) + INIT_LIST_HEAD(&tomoyo_name_list[i]); + INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); + tomoyo_kernel_domain.domainname = tomoyo_get_name(ROOT_NAME); + list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list); + if (tomoyo_find_domain(ROOT_NAME) != &tomoyo_kernel_domain) + panic("Can't register tomoyo_kernel_domain"); +#ifdef CONFIG_SECURITY_TOMOYO_BUILTIN_INITIALIZERS + { + /* Load built-in policy. */ + static char tomoyo_builtin_initializers[] __initdata + = CONFIG_SECURITY_TOMOYO_BUILTIN_INITIALIZERS; + char *cp = tomoyo_builtin_initializers; + tomoyo_normalize_line(cp); + while (cp && *cp) { + char *cp2 = strchr(cp, ' '); + if (cp2) + *cp2++ = '\0'; + tomoyo_write_domain_initializer_policy(cp, false, + false); + cp = cp2; + } + } +#endif + return 0; +} +core_initcall(tomoyo_mm_init); + +unsigned int tomoyo_audit_log_memory_size; +unsigned int tomoyo_quota_for_audit_log; + +unsigned int tomoyo_query_memory_size; +unsigned int tomoyo_quota_for_query; + +/** + * tomoyo_read_memory_counter - Check for memory usage. + * + * @head: Pointer to "struct tomoyo_io_buffer". + */ +void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) +{ + const unsigned int usage[3] = { + atomic_read(&tomoyo_policy_memory_size), + tomoyo_audit_log_memory_size, + tomoyo_query_memory_size + }; + const unsigned int quota[3] = { + tomoyo_quota_for_policy, + tomoyo_quota_for_audit_log, + tomoyo_quota_for_query + }; + static const char *header[4] = { + "Policy: ", + "Audit logs: ", + "Query lists:", + "Total: " + }; + unsigned int total = 0; + int i; + if (head->read_eof) + return; + for (i = 0; i < 3; i++) { + total += usage[i]; + tomoyo_io_printf(head, "%s %10u", header[i], usage[i]); + if (quota[i]) + tomoyo_io_printf(head, " (Quota: %10u)", quota[i]); + tomoyo_io_printf(head, "\n"); + } + tomoyo_io_printf(head, "%s %10u\n", header[3], total); + head->read_eof = true; +} + +/** + * tomoyo_write_memory_quota - Set memory quota. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0. + */ +int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) +{ + char *data = head->write_buf; + unsigned int size; + if (sscanf(data, "Policy: %u", &size) == 1) + tomoyo_quota_for_policy = size; + else if (sscanf(data, "Audit logs: %u", &size) == 1) + tomoyo_quota_for_audit_log = size; + else if (sscanf(data, "Query lists: %u", &size) == 1) + tomoyo_quota_for_query = size; + return 0; +} -- -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/