From 09f464bf0961aba3cd917d4939597bafb269fb95 Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Tue, 16 Aug 2011 20:34:05 +0200 Subject: tomoyo: remove tomoyo_gc_thread()->daemonize() daemonize() is only needed when a user-space task does kernel_thread(). tomoyo_gc_thread() is kthread_create()'ed and thus it doesn't need the soon-to-be-deprecated daemonize(). Signed-off-by: Oleg Nesterov Acked-by: Tejun Heo Acked-by: Matt Fleming Signed-off-by: James Morris --- security/tomoyo/gc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/tomoyo') diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index ae135fbbbe95..5295b5caaa21 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -660,7 +660,7 @@ static int tomoyo_gc_thread(void *unused) static DEFINE_MUTEX(tomoyo_gc_mutex); if (!mutex_trylock(&tomoyo_gc_mutex)) goto out; - daemonize("GC for TOMOYO"); + do { tomoyo_collect_entry(); if (list_empty(&tomoyo_gc_list)) -- cgit v1.2.3 From 852584157c55c1689bcf3809ea44b79870c3e409 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 25 Aug 2011 21:15:00 +0900 Subject: TOMOYO: Fix incorrect enforce mode. In tomoyo_get_mode() since 2.6.36, CONFIG::file::execute was by error used in place of CONFIG::file if CONFIG::file::execute was set to other than default. As a result, enforcing mode was not applied in a way documentation says. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'security/tomoyo') diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index c36bd1107fc8..6a4195a4b93c 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -925,7 +925,8 @@ int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, return TOMOYO_CONFIG_DISABLED; mode = tomoyo_profile(ns, profile)->config[index]; if (mode == TOMOYO_CONFIG_USE_DEFAULT) - mode = tomoyo_profile(ns, profile)->config[category]; + mode = tomoyo_profile(ns, profile)->config + [category + TOMOYO_MAX_MAC_INDEX]; if (mode == TOMOYO_CONFIG_USE_DEFAULT) mode = tomoyo_profile(ns, profile)->default_config; return mode & 3; -- cgit v1.2.3 From d58e0da854376841ac99defeb117a83f086715c6 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sat, 10 Sep 2011 15:22:48 +0900 Subject: TOMOYO: Add environment variable name restriction support. This patch adds support for checking environment variable's names. Although TOMOYO already provides ability to check argv[]/envp[] passed to execve() requests, file execute /bin/sh exec.envp["LD_LIBRARY_PATH"]="bar" will reject execution of /bin/sh if environment variable LD_LIBRARY_PATH is not defined. To grant execution of /bin/sh if LD_LIBRARY_PATH is not defined, administrators have to specify like file execute /bin/sh exec.envp["LD_LIBRARY_PATH"]="/system/lib" file execute /bin/sh exec.envp["LD_LIBRARY_PATH"]=NULL . Since there are many environment variables whereas conditional checks are applied as "&&", it is difficult to cover all combinations. Therefore, this patch supports conditional checks that are applied as "||", by specifying like file execute /bin/sh misc env LD_LIBRARY_PATH exec.envp["LD_LIBRARY_PATH"]="/system/lib" which means "grant execution of /bin/sh if environment variable is not defined or is defined and its value is /system/lib". Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/Makefile | 2 +- security/tomoyo/common.c | 20 ++++++-- security/tomoyo/common.h | 14 ++++++ security/tomoyo/domain.c | 95 +++++++++++++++++++++++++++++++++++- security/tomoyo/environ.c | 122 ++++++++++++++++++++++++++++++++++++++++++++++ security/tomoyo/gc.c | 9 ++++ security/tomoyo/util.c | 14 ++++-- 7 files changed, 266 insertions(+), 10 deletions(-) create mode 100644 security/tomoyo/environ.c (limited to 'security/tomoyo') diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index 95278b71fc21..f7ade960f6cf 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile @@ -1,4 +1,4 @@ -obj-y = audit.o common.o condition.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o +obj-y = audit.o common.o condition.o domain.o environ.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o $(obj)/policy/profile.conf: @mkdir -p $(obj)/policy/ diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index c8439cf2a448..d116e1ece3e6 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -20,6 +20,7 @@ const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = { /* String table for /sys/kernel/security/tomoyo/profile */ const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { + /* CONFIG::file group */ [TOMOYO_MAC_FILE_EXECUTE] = "execute", [TOMOYO_MAC_FILE_OPEN] = "open", [TOMOYO_MAC_FILE_CREATE] = "create", @@ -43,7 +44,11 @@ const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX [TOMOYO_MAC_FILE_MOUNT] = "mount", [TOMOYO_MAC_FILE_UMOUNT] = "unmount", [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root", + /* CONFIG::misc group */ + [TOMOYO_MAC_ENVIRON] = "env", + /* CONFIG group */ [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", + [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_MISC] = "misc", }; /* String table for conditions. */ @@ -133,7 +138,8 @@ const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { /* String table for categories. */ static const char * const tomoyo_category_keywords [TOMOYO_MAX_MAC_CATEGORY_INDEX] = { - [TOMOYO_MAC_CATEGORY_FILE] = "file", + [TOMOYO_MAC_CATEGORY_FILE] = "file", + [TOMOYO_MAC_CATEGORY_MISC] = "misc", }; /* Permit policy management by non-root user? */ @@ -1036,11 +1042,13 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns, static const struct { const char *keyword; int (*write) (struct tomoyo_acl_param *); - } tomoyo_callback[1] = { + } tomoyo_callback[2] = { { "file ", tomoyo_write_file }, + { "misc ", tomoyo_write_misc }, }; u8 i; - for (i = 0; i < 1; i++) { + + for (i = 0; i < ARRAY_SIZE(tomoyo_callback); i++) { if (!tomoyo_str_starts(¶m.data, tomoyo_callback[i].keyword)) continue; @@ -1375,6 +1383,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, tomoyo_print_name_union(head, &ptr->dir_name); tomoyo_print_name_union(head, &ptr->fs_type); tomoyo_print_number_union(head, &ptr->flags); + } else if (acl_type == TOMOYO_TYPE_ENV_ACL) { + struct tomoyo_env_acl *ptr = + container_of(acl, typeof(*ptr), head); + + tomoyo_set_group(head, "misc env "); + tomoyo_set_string(head, ptr->env->name); } if (acl->cond) { head->r.print_cond_part = true; diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index f7fbaa66e443..63720a328edd 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -196,6 +196,7 @@ enum tomoyo_acl_entry_type_index { TOMOYO_TYPE_PATH_NUMBER_ACL, TOMOYO_TYPE_MKDEV_ACL, TOMOYO_TYPE_MOUNT_ACL, + TOMOYO_TYPE_ENV_ACL, }; /* Index numbers for access controls with one pathname. */ @@ -300,12 +301,14 @@ enum tomoyo_mac_index { TOMOYO_MAC_FILE_MOUNT, TOMOYO_MAC_FILE_UMOUNT, TOMOYO_MAC_FILE_PIVOT_ROOT, + TOMOYO_MAC_ENVIRON, TOMOYO_MAX_MAC_INDEX }; /* Index numbers for category of functionality. */ enum tomoyo_mac_category_index { TOMOYO_MAC_CATEGORY_FILE, + TOMOYO_MAC_CATEGORY_MISC, TOMOYO_MAX_MAC_CATEGORY_INDEX }; @@ -396,6 +399,9 @@ struct tomoyo_request_info { */ u8 operation; } path_number; + struct { + const struct tomoyo_path_info *name; + } environ; struct { const struct tomoyo_path_info *type; const struct tomoyo_path_info *dir; @@ -638,6 +644,12 @@ struct tomoyo_mount_acl { struct tomoyo_number_union flags; }; +/* Structure for "misc env" directive in domain policy. */ +struct tomoyo_env_acl { + struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_ENV_ACL */ + const struct tomoyo_path_info *env; /* environment variable */ +}; + /* Structure for holding a line from /sys/kernel/security/tomoyo/ interface. */ struct tomoyo_acl_param { char *data; @@ -820,6 +832,7 @@ const struct tomoyo_path_info *tomoyo_path_matches_group int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct path *path, const int flag); int tomoyo_close_control(struct tomoyo_io_buffer *head); +int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env); int tomoyo_find_next_domain(struct linux_binprm *bprm); int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, const u8 index); @@ -860,6 +873,7 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, int tomoyo_write_aggregator(struct tomoyo_acl_param *param); int tomoyo_write_file(struct tomoyo_acl_param *param); int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type); +int tomoyo_write_misc(struct tomoyo_acl_param *param); int tomoyo_write_transition_control(struct tomoyo_acl_param *param, const u8 type); ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index cd0f92d88bb4..5931fb1c04d5 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -562,6 +562,92 @@ out: return entry; } +/** + * tomoyo_environ - Check permission for environment variable names. + * + * @ee: Pointer to "struct tomoyo_execve". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_environ(struct tomoyo_execve *ee) +{ + struct tomoyo_request_info *r = &ee->r; + struct linux_binprm *bprm = ee->bprm; + /* env_page.data is allocated by tomoyo_dump_page(). */ + struct tomoyo_page_dump env_page = { }; + char *arg_ptr; /* Size is TOMOYO_EXEC_TMPSIZE bytes */ + int arg_len = 0; + unsigned long pos = bprm->p; + int offset = pos % PAGE_SIZE; + int argv_count = bprm->argc; + int envp_count = bprm->envc; + int error = -ENOMEM; + + ee->r.type = TOMOYO_MAC_ENVIRON; + ee->r.profile = r->domain->profile; + ee->r.mode = tomoyo_get_mode(r->domain->ns, ee->r.profile, + TOMOYO_MAC_ENVIRON); + if (!r->mode || !envp_count) + return 0; + arg_ptr = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); + if (!arg_ptr) + goto out; + while (error == -ENOMEM) { + if (!tomoyo_dump_page(bprm, pos, &env_page)) + goto out; + pos += PAGE_SIZE - offset; + /* Read. */ + while (argv_count && offset < PAGE_SIZE) { + if (!env_page.data[offset++]) + argv_count--; + } + if (argv_count) { + offset = 0; + continue; + } + while (offset < PAGE_SIZE) { + const unsigned char c = env_page.data[offset++]; + + if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) { + if (c == '=') { + arg_ptr[arg_len++] = '\0'; + } else if (c == '\\') { + arg_ptr[arg_len++] = '\\'; + arg_ptr[arg_len++] = '\\'; + } else if (c > ' ' && c < 127) { + arg_ptr[arg_len++] = c; + } else { + arg_ptr[arg_len++] = '\\'; + arg_ptr[arg_len++] = (c >> 6) + '0'; + arg_ptr[arg_len++] + = ((c >> 3) & 7) + '0'; + arg_ptr[arg_len++] = (c & 7) + '0'; + } + } else { + arg_ptr[arg_len] = '\0'; + } + if (c) + continue; + if (tomoyo_env_perm(r, arg_ptr)) { + error = -EPERM; + break; + } + if (!--envp_count) { + error = 0; + break; + } + arg_len = 0; + } + offset = 0; + } +out: + if (r->mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + kfree(env_page.data); + kfree(arg_ptr); + return error; +} + /** * tomoyo_find_next_domain - Find a domain. * @@ -581,6 +667,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) bool reject_on_transition_failure = false; struct tomoyo_path_info rn = { }; /* real name */ struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS); + if (!ee) return -ENOMEM; ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); @@ -713,6 +800,10 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) bprm->cred->security = domain; if (need_kfree) kfree(rn.name); + if (!retval) { + ee->r.domain = domain; + retval = tomoyo_environ(ee); + } kfree(ee->tmp); kfree(ee->dump.data); kfree(ee); @@ -732,7 +823,8 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos, struct tomoyo_page_dump *dump) { struct page *page; - /* dump->data is released by tomoyo_finish_execve(). */ + + /* dump->data is released by tomoyo_find_next_domain(). */ if (!dump->data) { dump->data = kzalloc(PAGE_SIZE, GFP_NOFS); if (!dump->data) @@ -753,6 +845,7 @@ bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos, * So do I. */ char *kaddr = kmap_atomic(page, KM_USER0); + dump->page = page; memcpy(dump->data + offset, kaddr + offset, PAGE_SIZE - offset); diff --git a/security/tomoyo/environ.c b/security/tomoyo/environ.c new file mode 100644 index 000000000000..ad4c6e18a437 --- /dev/null +++ b/security/tomoyo/environ.c @@ -0,0 +1,122 @@ +/* + * security/tomoyo/environ.c + * + * Copyright (C) 2005-2011 NTT DATA CORPORATION + */ + +#include "common.h" + +/** + * tomoyo_check_env_acl - Check permission for environment variable's name. + * + * @r: Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */ +static bool tomoyo_check_env_acl(struct tomoyo_request_info *r, + const struct tomoyo_acl_info *ptr) +{ + const struct tomoyo_env_acl *acl = + container_of(ptr, typeof(*acl), head); + + return tomoyo_path_matches_pattern(r->param.environ.name, acl->env); +} + +/** + * tomoyo_audit_env_log - Audit environment variable name log. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_env_log(struct tomoyo_request_info *r) +{ + return tomoyo_supervisor(r, "misc env %s\n", + r->param.environ.name->name); +} + +/** + * tomoyo_env_perm - Check permission for environment variable's name. + * + * @r: Pointer to "struct tomoyo_request_info". + * @env: The name of environment variable. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env) +{ + struct tomoyo_path_info environ; + int error; + + if (!env || !*env) + return 0; + environ.name = env; + tomoyo_fill_path_info(&environ); + r->param_type = TOMOYO_TYPE_ENV_ACL; + r->param.environ.name = &environ; + do { + tomoyo_check_acl(r, tomoyo_check_env_acl); + error = tomoyo_audit_env_log(r); + } while (error == TOMOYO_RETRY_REQUEST); + return error; +} + +/** + * tomoyo_same_env_acl - Check for duplicated "struct tomoyo_env_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b, false otherwise. + */ +static bool tomoyo_same_env_acl(const struct tomoyo_acl_info *a, + const struct tomoyo_acl_info *b) +{ + const struct tomoyo_env_acl *p1 = container_of(a, typeof(*p1), head); + const struct tomoyo_env_acl *p2 = container_of(b, typeof(*p2), head); + + return p1->env == p2->env; +} + +/** + * tomoyo_write_env - Write "struct tomoyo_env_acl" list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_write_env(struct tomoyo_acl_param *param) +{ + struct tomoyo_env_acl e = { .head.type = TOMOYO_TYPE_ENV_ACL }; + int error = -ENOMEM; + const char *data = tomoyo_read_token(param); + + if (!tomoyo_correct_word(data) || strchr(data, '=')) + return -EINVAL; + e.env = tomoyo_get_name(data); + if (!e.env) + return error; + error = tomoyo_update_domain(&e.head, sizeof(e), param, + tomoyo_same_env_acl, NULL); + tomoyo_put_name(e.env); + return error; +} + +/** + * tomoyo_write_misc - Update environment variable list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_write_misc(struct tomoyo_acl_param *param) +{ + if (tomoyo_str_starts(¶m->data, "env ")) + return tomoyo_write_env(param); + return -EINVAL; +} diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 5295b5caaa21..818b07998111 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -36,6 +36,7 @@ static const u8 tomoyo_acl_size[] = { [TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl), [TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl), [TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl), + [TOMOYO_TYPE_ENV_ACL] = sizeof(struct tomoyo_env_acl), }; /** @@ -293,6 +294,14 @@ static void tomoyo_del_acl(struct list_head *element) tomoyo_put_number_union(&entry->flags); } break; + case TOMOYO_TYPE_ENV_ACL: + { + struct tomoyo_env_acl *entry = + container_of(acl, typeof(*entry), head); + + tomoyo_put_name(entry->env); + } + break; } } diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 6a4195a4b93c..cb7d507b6312 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -42,6 +42,8 @@ const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = { [TOMOYO_MAC_FILE_MOUNT] = TOMOYO_MAC_CATEGORY_FILE, [TOMOYO_MAC_FILE_UMOUNT] = TOMOYO_MAC_CATEGORY_FILE, [TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE, + /* CONFIG::misc group */ + [TOMOYO_MAC_ENVIRON] = TOMOYO_MAC_CATEGORY_MISC, }; /** @@ -920,15 +922,17 @@ int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, const u8 index) { u8 mode; - const u8 category = TOMOYO_MAC_CATEGORY_FILE; + struct tomoyo_profile *p; + if (!tomoyo_policy_loaded) return TOMOYO_CONFIG_DISABLED; - mode = tomoyo_profile(ns, profile)->config[index]; + p = tomoyo_profile(ns, profile); + mode = p->config[index]; if (mode == TOMOYO_CONFIG_USE_DEFAULT) - mode = tomoyo_profile(ns, profile)->config - [category + TOMOYO_MAX_MAC_INDEX]; + mode = p->config[tomoyo_index2category[index] + + TOMOYO_MAX_MAC_INDEX]; if (mode == TOMOYO_CONFIG_USE_DEFAULT) - mode = tomoyo_profile(ns, profile)->default_config; + mode = p->default_config; return mode & 3; } -- cgit v1.2.3 From 059d84dbb3897d4ee494a9c842c5dda54316cb47 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sat, 10 Sep 2011 15:23:54 +0900 Subject: TOMOYO: Add socket operation restriction support. This patch adds support for permission checks for PF_INET/PF_INET6/PF_UNIX socket's bind()/listen()/connect()/send() operations. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/Kconfig | 2 + security/tomoyo/Makefile | 2 +- security/tomoyo/common.c | 104 +++++- security/tomoyo/common.h | 127 +++++++- security/tomoyo/gc.c | 40 ++- security/tomoyo/group.c | 61 +++- security/tomoyo/network.c | 771 +++++++++++++++++++++++++++++++++++++++++++++ security/tomoyo/realpath.c | 32 +- security/tomoyo/tomoyo.c | 62 ++++ security/tomoyo/util.c | 31 ++ 10 files changed, 1215 insertions(+), 17 deletions(-) create mode 100644 security/tomoyo/network.c (limited to 'security/tomoyo') diff --git a/security/tomoyo/Kconfig b/security/tomoyo/Kconfig index 7c7f8c16c10f..8eb779b9d77f 100644 --- a/security/tomoyo/Kconfig +++ b/security/tomoyo/Kconfig @@ -1,8 +1,10 @@ config SECURITY_TOMOYO bool "TOMOYO Linux Support" depends on SECURITY + depends on NET select SECURITYFS select SECURITY_PATH + select SECURITY_NETWORK default n help This selects TOMOYO Linux, pathname-based access control. diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index f7ade960f6cf..fc2a8ce40301 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile @@ -1,4 +1,4 @@ -obj-y = audit.o common.o condition.o domain.o environ.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o +obj-y = audit.o common.o condition.o domain.o environ.o file.o gc.o group.o load_policy.o memory.o mount.o network.o realpath.o securityfs_if.o tomoyo.o util.o $(obj)/policy/profile.conf: @mkdir -p $(obj)/policy/ diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index d116e1ece3e6..85d915587a71 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -44,10 +44,27 @@ const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX [TOMOYO_MAC_FILE_MOUNT] = "mount", [TOMOYO_MAC_FILE_UMOUNT] = "unmount", [TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root", + /* CONFIG::network group */ + [TOMOYO_MAC_NETWORK_INET_STREAM_BIND] = "inet_stream_bind", + [TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN] = "inet_stream_listen", + [TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT] = "inet_stream_connect", + [TOMOYO_MAC_NETWORK_INET_DGRAM_BIND] = "inet_dgram_bind", + [TOMOYO_MAC_NETWORK_INET_DGRAM_SEND] = "inet_dgram_send", + [TOMOYO_MAC_NETWORK_INET_RAW_BIND] = "inet_raw_bind", + [TOMOYO_MAC_NETWORK_INET_RAW_SEND] = "inet_raw_send", + [TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND] = "unix_stream_bind", + [TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN] = "unix_stream_listen", + [TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT] = "unix_stream_connect", + [TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND] = "unix_dgram_bind", + [TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND] = "unix_dgram_send", + [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND] = "unix_seqpacket_bind", + [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = "unix_seqpacket_listen", + [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = "unix_seqpacket_connect", /* CONFIG::misc group */ [TOMOYO_MAC_ENVIRON] = "env", /* CONFIG group */ [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", + [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_NETWORK] = "network", [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_MISC] = "misc", }; @@ -135,11 +152,20 @@ const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { [TOMOYO_TYPE_UMOUNT] = "unmount", }; +/* String table for socket's operation. */ +const char * const tomoyo_socket_keyword[TOMOYO_MAX_NETWORK_OPERATION] = { + [TOMOYO_NETWORK_BIND] = "bind", + [TOMOYO_NETWORK_LISTEN] = "listen", + [TOMOYO_NETWORK_CONNECT] = "connect", + [TOMOYO_NETWORK_SEND] = "send", +}; + /* String table for categories. */ static const char * const tomoyo_category_keywords [TOMOYO_MAX_MAC_CATEGORY_INDEX] = { - [TOMOYO_MAC_CATEGORY_FILE] = "file", - [TOMOYO_MAC_CATEGORY_MISC] = "misc", + [TOMOYO_MAC_CATEGORY_FILE] = "file", + [TOMOYO_MAC_CATEGORY_NETWORK] = "network", + [TOMOYO_MAC_CATEGORY_MISC] = "misc", }; /* Permit policy management by non-root user? */ @@ -1042,8 +1068,10 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns, static const struct { const char *keyword; int (*write) (struct tomoyo_acl_param *); - } tomoyo_callback[2] = { + } tomoyo_callback[4] = { { "file ", tomoyo_write_file }, + { "network inet ", tomoyo_write_inet_network }, + { "network unix ", tomoyo_write_unix_network }, { "misc ", tomoyo_write_misc }, }; u8 i; @@ -1375,6 +1403,60 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, tomoyo_print_number_union(head, &ptr->mode); tomoyo_print_number_union(head, &ptr->major); tomoyo_print_number_union(head, &ptr->minor); + } else if (acl_type == TOMOYO_TYPE_INET_ACL) { + struct tomoyo_inet_acl *ptr = + container_of(acl, typeof(*ptr), head); + const u8 perm = ptr->perm; + + for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) { + if (!(perm & (1 << bit))) + continue; + if (first) { + tomoyo_set_group(head, "network inet "); + tomoyo_set_string(head, tomoyo_proto_keyword + [ptr->protocol]); + tomoyo_set_space(head); + first = false; + } else { + tomoyo_set_slash(head); + } + tomoyo_set_string(head, tomoyo_socket_keyword[bit]); + } + if (first) + return true; + tomoyo_set_space(head); + if (ptr->address.group) { + tomoyo_set_string(head, "@"); + tomoyo_set_string(head, ptr->address.group->group_name + ->name); + } else { + char buf[128]; + tomoyo_print_ip(buf, sizeof(buf), &ptr->address); + tomoyo_io_printf(head, "%s", buf); + } + tomoyo_print_number_union(head, &ptr->port); + } else if (acl_type == TOMOYO_TYPE_UNIX_ACL) { + struct tomoyo_unix_acl *ptr = + container_of(acl, typeof(*ptr), head); + const u8 perm = ptr->perm; + + for (bit = 0; bit < TOMOYO_MAX_NETWORK_OPERATION; bit++) { + if (!(perm & (1 << bit))) + continue; + if (first) { + tomoyo_set_group(head, "network unix "); + tomoyo_set_string(head, tomoyo_proto_keyword + [ptr->protocol]); + tomoyo_set_space(head); + first = false; + } else { + tomoyo_set_slash(head); + } + tomoyo_set_string(head, tomoyo_socket_keyword[bit]); + } + if (first) + return true; + tomoyo_print_name_union(head, &ptr->name); } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { struct tomoyo_mount_acl *ptr = container_of(acl, typeof(*ptr), head); @@ -1548,8 +1630,9 @@ static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = { /* String table for grouping keywords. */ static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { - [TOMOYO_PATH_GROUP] = "path_group ", - [TOMOYO_NUMBER_GROUP] = "number_group ", + [TOMOYO_PATH_GROUP] = "path_group ", + [TOMOYO_NUMBER_GROUP] = "number_group ", + [TOMOYO_ADDRESS_GROUP] = "address_group ", }; /** @@ -1591,7 +1674,7 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head) } /** - * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group" list. + * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list. * * @head: Pointer to "struct tomoyo_io_buffer". * @idx: Index number. @@ -1628,6 +1711,15 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) (ptr, struct tomoyo_number_group, head)->number); + } else if (idx == TOMOYO_ADDRESS_GROUP) { + char buffer[128]; + + struct tomoyo_address_group *member = + container_of(ptr, typeof(*member), + head); + tomoyo_print_ip(buffer, sizeof(buffer), + &member->address); + tomoyo_io_printf(head, " %s", buffer); } tomoyo_set_lf(head); } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 63720a328edd..d1c758e7f92b 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -23,6 +23,16 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /********** Constants definitions. **********/ @@ -34,6 +44,12 @@ #define TOMOYO_HASH_BITS 8 #define TOMOYO_MAX_HASH (1u<value_type[1] == b->value_type[1]; } +/** + * tomoyo_same_ipaddr_union - Check for duplicated "struct tomoyo_ipaddr_union" entry. + * + * @a: Pointer to "struct tomoyo_ipaddr_union". + * @b: Pointer to "struct tomoyo_ipaddr_union". + * + * Returns true if @a == @b, false otherwise. + */ +static inline bool tomoyo_same_ipaddr_union +(const struct tomoyo_ipaddr_union *a, const struct tomoyo_ipaddr_union *b) +{ + return !memcmp(a->ip, b->ip, sizeof(a->ip)) && a->group == b->group && + a->is_ipv6 == b->is_ipv6; +} + /** * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread. * diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 818b07998111..7747ceb9a221 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -16,6 +16,7 @@ static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock); /* Size of an element. */ static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = { [TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group), + [TOMOYO_ID_ADDRESS_GROUP] = sizeof(struct tomoyo_address_group), [TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group), [TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group), [TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator), @@ -36,6 +37,8 @@ static const u8 tomoyo_acl_size[] = { [TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl), [TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl), [TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl), + [TOMOYO_TYPE_INET_ACL] = sizeof(struct tomoyo_inet_acl), + [TOMOYO_TYPE_UNIX_ACL] = sizeof(struct tomoyo_unix_acl), [TOMOYO_TYPE_ENV_ACL] = sizeof(struct tomoyo_env_acl), }; @@ -302,6 +305,23 @@ static void tomoyo_del_acl(struct list_head *element) tomoyo_put_name(entry->env); } break; + case TOMOYO_TYPE_INET_ACL: + { + struct tomoyo_inet_acl *entry = + container_of(acl, typeof(*entry), head); + + tomoyo_put_group(entry->address.group); + tomoyo_put_number_union(&entry->port); + } + break; + case TOMOYO_TYPE_UNIX_ACL: + { + struct tomoyo_unix_acl *entry = + container_of(acl, typeof(*entry), head); + + tomoyo_put_name_union(&entry->name); + } + break; } } @@ -430,6 +450,18 @@ static void tomoyo_del_group(struct list_head *element) tomoyo_put_name(group->group_name); } +/** + * tomoyo_del_address_group - Delete members in "struct tomoyo_address_group". + * + * @element: Pointer to "struct list_head". + * + * Returns nothing. + */ +static inline void tomoyo_del_address_group(struct list_head *element) +{ + /* Nothing to do. */ +} + /** * tomoyo_del_number_group - Delete members in "struct tomoyo_number_group". * @@ -527,9 +559,12 @@ static void tomoyo_collect_entry(void) case 0: id = TOMOYO_ID_PATH_GROUP; break; - default: + case 1: id = TOMOYO_ID_NUMBER_GROUP; break; + default: + id = TOMOYO_ID_ADDRESS_GROUP; + break; } list_for_each_entry(group, list, head.list) { if (!tomoyo_collect_member @@ -634,6 +669,9 @@ static bool tomoyo_kfree_entry(void) case TOMOYO_ID_PATH_GROUP: tomoyo_del_path_group(element); break; + case TOMOYO_ID_ADDRESS_GROUP: + tomoyo_del_address_group(element); + break; case TOMOYO_ID_GROUP: tomoyo_del_group(element); break; diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c index 5fb0e1298400..50092534ec54 100644 --- a/security/tomoyo/group.c +++ b/security/tomoyo/group.c @@ -42,7 +42,26 @@ static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a, } /** - * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list. + * tomoyo_same_address_group - Check for duplicated "struct tomoyo_address_group" entry. + * + * @a: Pointer to "struct tomoyo_acl_head". + * @b: Pointer to "struct tomoyo_acl_head". + * + * Returns true if @a == @b, false otherwise. + */ +static bool tomoyo_same_address_group(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + const struct tomoyo_address_group *p1 = container_of(a, typeof(*p1), + head); + const struct tomoyo_address_group *p2 = container_of(b, typeof(*p2), + head); + + return tomoyo_same_ipaddr_union(&p1->address, &p2->address); +} + +/** + * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group"/"struct tomoyo_address_group" list. * * @param: Pointer to "struct tomoyo_acl_param". * @type: Type of this group. @@ -77,6 +96,14 @@ int tomoyo_write_group(struct tomoyo_acl_param *param, const u8 type) * tomoyo_put_number_union() is not needed because * param->data[0] != '@'. */ + } else { + struct tomoyo_address_group e = { }; + + if (param->data[0] == '@' || + !tomoyo_parse_ipaddr_union(param, &e.address)) + goto out; + error = tomoyo_update_policy(&e.head, sizeof(e), param, + tomoyo_same_address_group); } out: tomoyo_put_group(group); @@ -137,3 +164,35 @@ bool tomoyo_number_matches_group(const unsigned long min, } return matched; } + +/** + * tomoyo_address_matches_group - Check whether the given address matches members of the given address group. + * + * @is_ipv6: True if @address is an IPv6 address. + * @address: An IPv4 or IPv6 address. + * @group: Pointer to "struct tomoyo_address_group". + * + * Returns true if @address matches addresses in @group group, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_address_matches_group(const bool is_ipv6, const __be32 *address, + const struct tomoyo_group *group) +{ + struct tomoyo_address_group *member; + bool matched = false; + const u8 size = is_ipv6 ? 16 : 4; + + list_for_each_entry_rcu(member, &group->member_list, head.list) { + if (member->head.is_deleted) + continue; + if (member->address.is_ipv6 != is_ipv6) + continue; + if (memcmp(&member->address.ip[0], address, size) > 0 || + memcmp(address, &member->address.ip[1], size) > 0) + continue; + matched = true; + break; + } + return matched; +} diff --git a/security/tomoyo/network.c b/security/tomoyo/network.c new file mode 100644 index 000000000000..97527710a72a --- /dev/null +++ b/security/tomoyo/network.c @@ -0,0 +1,771 @@ +/* + * security/tomoyo/network.c + * + * Copyright (C) 2005-2011 NTT DATA CORPORATION + */ + +#include "common.h" +#include + +/* Structure for holding inet domain socket's address. */ +struct tomoyo_inet_addr_info { + __be16 port; /* In network byte order. */ + const __be32 *address; /* In network byte order. */ + bool is_ipv6; +}; + +/* Structure for holding unix domain socket's address. */ +struct tomoyo_unix_addr_info { + u8 *addr; /* This may not be '\0' terminated string. */ + unsigned int addr_len; +}; + +/* Structure for holding socket address. */ +struct tomoyo_addr_info { + u8 protocol; + u8 operation; + struct tomoyo_inet_addr_info inet; + struct tomoyo_unix_addr_info unix0; +}; + +/* String table for socket's protocols. */ +const char * const tomoyo_proto_keyword[TOMOYO_SOCK_MAX] = { + [SOCK_STREAM] = "stream", + [SOCK_DGRAM] = "dgram", + [SOCK_RAW] = "raw", + [SOCK_SEQPACKET] = "seqpacket", + [0] = " ", /* Dummy for avoiding NULL pointer dereference. */ + [4] = " ", /* Dummy for avoiding NULL pointer dereference. */ +}; + +/** + * tomoyo_parse_ipaddr_union - Parse an IP address. + * + * @param: Pointer to "struct tomoyo_acl_param". + * @ptr: Pointer to "struct tomoyo_ipaddr_union". + * + * Returns true on success, false otherwise. + */ +bool tomoyo_parse_ipaddr_union(struct tomoyo_acl_param *param, + struct tomoyo_ipaddr_union *ptr) +{ + u8 * const min = ptr->ip[0].in6_u.u6_addr8; + u8 * const max = ptr->ip[1].in6_u.u6_addr8; + char *address = tomoyo_read_token(param); + const char *end; + + if (!strchr(address, ':') && + in4_pton(address, -1, min, '-', &end) > 0) { + ptr->is_ipv6 = false; + if (!*end) + ptr->ip[1].s6_addr32[0] = ptr->ip[0].s6_addr32[0]; + else if (*end++ != '-' || + in4_pton(end, -1, max, '\0', &end) <= 0 || *end) + return false; + return true; + } + if (in6_pton(address, -1, min, '-', &end) > 0) { + ptr->is_ipv6 = true; + if (!*end) + memmove(max, min, sizeof(u16) * 8); + else if (*end++ != '-' || + in6_pton(end, -1, max, '\0', &end) <= 0 || *end) + return false; + return true; + } + return false; +} + +/** + * tomoyo_print_ipv4 - Print an IPv4 address. + * + * @buffer: Buffer to write to. + * @buffer_len: Size of @buffer. + * @min_ip: Pointer to __be32. + * @max_ip: Pointer to __be32. + * + * Returns nothing. + */ +static void tomoyo_print_ipv4(char *buffer, const unsigned int buffer_len, + const __be32 *min_ip, const __be32 *max_ip) +{ + snprintf(buffer, buffer_len, "%pI4%c%pI4", min_ip, + *min_ip == *max_ip ? '\0' : '-', max_ip); +} + +/** + * tomoyo_print_ipv6 - Print an IPv6 address. + * + * @buffer: Buffer to write to. + * @buffer_len: Size of @buffer. + * @min_ip: Pointer to "struct in6_addr". + * @max_ip: Pointer to "struct in6_addr". + * + * Returns nothing. + */ +static void tomoyo_print_ipv6(char *buffer, const unsigned int buffer_len, + const struct in6_addr *min_ip, + const struct in6_addr *max_ip) +{ + snprintf(buffer, buffer_len, "%pI6c%c%pI6c", min_ip, + !memcmp(min_ip, max_ip, 16) ? '\0' : '-', max_ip); +} + +/** + * tomoyo_print_ip - Print an IP address. + * + * @buf: Buffer to write to. + * @size: Size of @buf. + * @ptr: Pointer to "struct ipaddr_union". + * + * Returns nothing. + */ +void tomoyo_print_ip(char *buf, const unsigned int size, + const struct tomoyo_ipaddr_union *ptr) +{ + if (ptr->is_ipv6) + tomoyo_print_ipv6(buf, size, &ptr->ip[0], &ptr->ip[1]); + else + tomoyo_print_ipv4(buf, size, &ptr->ip[0].s6_addr32[0], + &ptr->ip[1].s6_addr32[0]); +} + +/* + * Mapping table from "enum tomoyo_network_acl_index" to + * "enum tomoyo_mac_index" for inet domain socket. + */ +static const u8 tomoyo_inet2mac +[TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = { + [SOCK_STREAM] = { + [TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_INET_STREAM_BIND, + [TOMOYO_NETWORK_LISTEN] = + TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN, + [TOMOYO_NETWORK_CONNECT] = + TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT, + }, + [SOCK_DGRAM] = { + [TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_INET_DGRAM_BIND, + [TOMOYO_NETWORK_SEND] = TOMOYO_MAC_NETWORK_INET_DGRAM_SEND, + }, + [SOCK_RAW] = { + [TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_INET_RAW_BIND, + [TOMOYO_NETWORK_SEND] = TOMOYO_MAC_NETWORK_INET_RAW_SEND, + }, +}; + +/* + * Mapping table from "enum tomoyo_network_acl_index" to + * "enum tomoyo_mac_index" for unix domain socket. + */ +static const u8 tomoyo_unix2mac +[TOMOYO_SOCK_MAX][TOMOYO_MAX_NETWORK_OPERATION] = { + [SOCK_STREAM] = { + [TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND, + [TOMOYO_NETWORK_LISTEN] = + TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN, + [TOMOYO_NETWORK_CONNECT] = + TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT, + }, + [SOCK_DGRAM] = { + [TOMOYO_NETWORK_BIND] = TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND, + [TOMOYO_NETWORK_SEND] = TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND, + }, + [SOCK_SEQPACKET] = { + [TOMOYO_NETWORK_BIND] = + TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND, + [TOMOYO_NETWORK_LISTEN] = + TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN, + [TOMOYO_NETWORK_CONNECT] = + TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT, + }, +}; + +/** + * tomoyo_same_inet_acl - Check for duplicated "struct tomoyo_inet_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b except permission bits, false otherwise. + */ +static bool tomoyo_same_inet_acl(const struct tomoyo_acl_info *a, + const struct tomoyo_acl_info *b) +{ + const struct tomoyo_inet_acl *p1 = container_of(a, typeof(*p1), head); + const struct tomoyo_inet_acl *p2 = container_of(b, typeof(*p2), head); + + return p1->protocol == p2->protocol && + tomoyo_same_ipaddr_union(&p1->address, &p2->address) && + tomoyo_same_number_union(&p1->port, &p2->port); +} + +/** + * tomoyo_same_unix_acl - Check for duplicated "struct tomoyo_unix_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b except permission bits, false otherwise. + */ +static bool tomoyo_same_unix_acl(const struct tomoyo_acl_info *a, + const struct tomoyo_acl_info *b) +{ + const struct tomoyo_unix_acl *p1 = container_of(a, typeof(*p1), head); + const struct tomoyo_unix_acl *p2 = container_of(b, typeof(*p2), head); + + return p1->protocol == p2->protocol && + tomoyo_same_name_union(&p1->name, &p2->name); +} + +/** + * tomoyo_merge_inet_acl - Merge duplicated "struct tomoyo_inet_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * @is_delete: True for @a &= ~@b, false for @a |= @b. + * + * Returns true if @a is empty, false otherwise. + */ +static bool tomoyo_merge_inet_acl(struct tomoyo_acl_info *a, + struct tomoyo_acl_info *b, + const bool is_delete) +{ + u8 * const a_perm = + &container_of(a, struct tomoyo_inet_acl, head)->perm; + u8 perm = *a_perm; + const u8 b_perm = container_of(b, struct tomoyo_inet_acl, head)->perm; + + if (is_delete) + perm &= ~b_perm; + else + perm |= b_perm; + *a_perm = perm; + return !perm; +} + +/** + * tomoyo_merge_unix_acl - Merge duplicated "struct tomoyo_unix_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * @is_delete: True for @a &= ~@b, false for @a |= @b. + * + * Returns true if @a is empty, false otherwise. + */ +static bool tomoyo_merge_unix_acl(struct tomoyo_acl_info *a, + struct tomoyo_acl_info *b, + const bool is_delete) +{ + u8 * const a_perm = + &container_of(a, struct tomoyo_unix_acl, head)->perm; + u8 perm = *a_perm; + const u8 b_perm = container_of(b, struct tomoyo_unix_acl, head)->perm; + + if (is_delete) + perm &= ~b_perm; + else + perm |= b_perm; + *a_perm = perm; + return !perm; +} + +/** + * tomoyo_write_inet_network - Write "struct tomoyo_inet_acl" list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_write_inet_network(struct tomoyo_acl_param *param) +{ + struct tomoyo_inet_acl e = { .head.type = TOMOYO_TYPE_INET_ACL }; + int error = -EINVAL; + u8 type; + const char *protocol = tomoyo_read_token(param); + const char *operation = tomoyo_read_token(param); + + for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++) + if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol])) + break; + for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++) + if (tomoyo_permstr(operation, tomoyo_socket_keyword[type])) + e.perm |= 1 << type; + if (e.protocol == TOMOYO_SOCK_MAX || !e.perm) + return -EINVAL; + if (param->data[0] == '@') { + param->data++; + e.address.group = + tomoyo_get_group(param, TOMOYO_ADDRESS_GROUP); + if (!e.address.group) + return -ENOMEM; + } else { + if (!tomoyo_parse_ipaddr_union(param, &e.address)) + goto out; + } + if (!tomoyo_parse_number_union(param, &e.port) || + e.port.values[1] > 65535) + goto out; + error = tomoyo_update_domain(&e.head, sizeof(e), param, + tomoyo_same_inet_acl, + tomoyo_merge_inet_acl); +out: + tomoyo_put_group(e.address.group); + tomoyo_put_number_union(&e.port); + return error; +} + +/** + * tomoyo_write_unix_network - Write "struct tomoyo_unix_acl" list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_write_unix_network(struct tomoyo_acl_param *param) +{ + struct tomoyo_unix_acl e = { .head.type = TOMOYO_TYPE_UNIX_ACL }; + int error; + u8 type; + const char *protocol = tomoyo_read_token(param); + const char *operation = tomoyo_read_token(param); + + for (e.protocol = 0; e.protocol < TOMOYO_SOCK_MAX; e.protocol++) + if (!strcmp(protocol, tomoyo_proto_keyword[e.protocol])) + break; + for (type = 0; type < TOMOYO_MAX_NETWORK_OPERATION; type++) + if (tomoyo_permstr(operation, tomoyo_socket_keyword[type])) + e.perm |= 1 << type; + if (e.protocol == TOMOYO_SOCK_MAX || !e.perm) + return -EINVAL; + if (!tomoyo_parse_name_union(param, &e.name)) + return -EINVAL; + error = tomoyo_update_domain(&e.head, sizeof(e), param, + tomoyo_same_unix_acl, + tomoyo_merge_unix_acl); + tomoyo_put_name_union(&e.name); + return error; +} + +/** + * tomoyo_audit_net_log - Audit network log. + * + * @r: Pointer to "struct tomoyo_request_info". + * @family: Name of socket family ("inet" or "unix"). + * @protocol: Name of protocol in @family. + * @operation: Name of socket operation. + * @address: Name of address. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_net_log(struct tomoyo_request_info *r, + const char *family, const u8 protocol, + const u8 operation, const char *address) +{ + return tomoyo_supervisor(r, "network %s %s %s %s\n", family, + tomoyo_proto_keyword[protocol], + tomoyo_socket_keyword[operation], address); +} + +/** + * tomoyo_audit_inet_log - Audit INET network log. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_inet_log(struct tomoyo_request_info *r) +{ + char buf[128]; + int len; + const __be32 *address = r->param.inet_network.address; + + if (r->param.inet_network.is_ipv6) + tomoyo_print_ipv6(buf, sizeof(buf), (const struct in6_addr *) + address, (const struct in6_addr *) address); + else + tomoyo_print_ipv4(buf, sizeof(buf), address, address); + len = strlen(buf); + snprintf(buf + len, sizeof(buf) - len, " %u", + r->param.inet_network.port); + return tomoyo_audit_net_log(r, "inet", r->param.inet_network.protocol, + r->param.inet_network.operation, buf); +} + +/** + * tomoyo_audit_unix_log - Audit UNIX network log. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_unix_log(struct tomoyo_request_info *r) +{ + return tomoyo_audit_net_log(r, "unix", r->param.unix_network.protocol, + r->param.unix_network.operation, + r->param.unix_network.address->name); +} + +/** + * tomoyo_check_inet_acl - Check permission for inet domain socket operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */ +static bool tomoyo_check_inet_acl(struct tomoyo_request_info *r, + const struct tomoyo_acl_info *ptr) +{ + const struct tomoyo_inet_acl *acl = + container_of(ptr, typeof(*acl), head); + const u8 size = r->param.inet_network.is_ipv6 ? 16 : 4; + + if (!(acl->perm & (1 << r->param.inet_network.operation)) || + !tomoyo_compare_number_union(r->param.inet_network.port, + &acl->port)) + return false; + if (acl->address.group) + return tomoyo_address_matches_group + (r->param.inet_network.is_ipv6, + r->param.inet_network.address, acl->address.group); + return acl->address.is_ipv6 == r->param.inet_network.is_ipv6 && + memcmp(&acl->address.ip[0], + r->param.inet_network.address, size) <= 0 && + memcmp(r->param.inet_network.address, + &acl->address.ip[1], size) <= 0; +} + +/** + * tomoyo_check_unix_acl - Check permission for unix domain socket operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */ +static bool tomoyo_check_unix_acl(struct tomoyo_request_info *r, + const struct tomoyo_acl_info *ptr) +{ + const struct tomoyo_unix_acl *acl = + container_of(ptr, typeof(*acl), head); + + return (acl->perm & (1 << r->param.unix_network.operation)) && + tomoyo_compare_name_union(r->param.unix_network.address, + &acl->name); +} + +/** + * tomoyo_inet_entry - Check permission for INET network operation. + * + * @address: Pointer to "struct tomoyo_addr_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_inet_entry(const struct tomoyo_addr_info *address) +{ + const int idx = tomoyo_read_lock(); + struct tomoyo_request_info r; + int error = 0; + const u8 type = tomoyo_inet2mac[address->protocol][address->operation]; + + if (type && tomoyo_init_request_info(&r, NULL, type) + != TOMOYO_CONFIG_DISABLED) { + r.param_type = TOMOYO_TYPE_INET_ACL; + r.param.inet_network.protocol = address->protocol; + r.param.inet_network.operation = address->operation; + r.param.inet_network.is_ipv6 = address->inet.is_ipv6; + r.param.inet_network.address = address->inet.address; + r.param.inet_network.port = ntohs(address->inet.port); + do { + tomoyo_check_acl(&r, tomoyo_check_inet_acl); + error = tomoyo_audit_inet_log(&r); + } while (error == TOMOYO_RETRY_REQUEST); + } + tomoyo_read_unlock(idx); + return error; +} + +/** + * tomoyo_check_inet_address - Check permission for inet domain socket's operation. + * + * @addr: Pointer to "struct sockaddr". + * @addr_len: Size of @addr. + * @port: Port number. + * @address: Pointer to "struct tomoyo_addr_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_check_inet_address(const struct sockaddr *addr, + const unsigned int addr_len, + const u16 port, + struct tomoyo_addr_info *address) +{ + struct tomoyo_inet_addr_info *i = &address->inet; + + switch (addr->sa_family) { + case AF_INET6: + if (addr_len < SIN6_LEN_RFC2133) + goto skip; + i->is_ipv6 = true; + i->address = (__be32 *) + ((struct sockaddr_in6 *) addr)->sin6_addr.s6_addr; + i->port = ((struct sockaddr_in6 *) addr)->sin6_port; + break; + case AF_INET: + if (addr_len < sizeof(struct sockaddr_in)) + goto skip; + i->is_ipv6 = false; + i->address = (__be32 *) + &((struct sockaddr_in *) addr)->sin_addr; + i->port = ((struct sockaddr_in *) addr)->sin_port; + break; + default: + goto skip; + } + if (address->protocol == SOCK_RAW) + i->port = htons(port); + return tomoyo_inet_entry(address); +skip: + return 0; +} + +/** + * tomoyo_unix_entry - Check permission for UNIX network operation. + * + * @address: Pointer to "struct tomoyo_addr_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_unix_entry(const struct tomoyo_addr_info *address) +{ + const int idx = tomoyo_read_lock(); + struct tomoyo_request_info r; + int error = 0; + const u8 type = tomoyo_unix2mac[address->protocol][address->operation]; + + if (type && tomoyo_init_request_info(&r, NULL, type) + != TOMOYO_CONFIG_DISABLED) { + char *buf = address->unix0.addr; + int len = address->unix0.addr_len - sizeof(sa_family_t); + + if (len <= 0) { + buf = "anonymous"; + len = 9; + } else if (buf[0]) { + len = strnlen(buf, len); + } + buf = tomoyo_encode2(buf, len); + if (buf) { + struct tomoyo_path_info addr; + + addr.name = buf; + tomoyo_fill_path_info(&addr); + r.param_type = TOMOYO_TYPE_UNIX_ACL; + r.param.unix_network.protocol = address->protocol; + r.param.unix_network.operation = address->operation; + r.param.unix_network.address = &addr; + do { + tomoyo_check_acl(&r, tomoyo_check_unix_acl); + error = tomoyo_audit_unix_log(&r); + } while (error == TOMOYO_RETRY_REQUEST); + kfree(buf); + } else + error = -ENOMEM; + } + tomoyo_read_unlock(idx); + return error; +} + +/** + * tomoyo_check_unix_address - Check permission for unix domain socket's operation. + * + * @addr: Pointer to "struct sockaddr". + * @addr_len: Size of @addr. + * @address: Pointer to "struct tomoyo_addr_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_check_unix_address(struct sockaddr *addr, + const unsigned int addr_len, + struct tomoyo_addr_info *address) +{ + struct tomoyo_unix_addr_info *u = &address->unix0; + + if (addr->sa_family != AF_UNIX) + return 0; + u->addr = ((struct sockaddr_un *) addr)->sun_path; + u->addr_len = addr_len; + return tomoyo_unix_entry(address); +} + +/** + * tomoyo_kernel_service - Check whether I'm kernel service or not. + * + * Returns true if I'm kernel service, false otherwise. + */ +static bool tomoyo_kernel_service(void) +{ + /* Nothing to do if I am a kernel service. */ + return segment_eq(get_fs(), KERNEL_DS); +} + +/** + * tomoyo_sock_family - Get socket's family. + * + * @sk: Pointer to "struct sock". + * + * Returns one of PF_INET, PF_INET6, PF_UNIX or 0. + */ +static u8 tomoyo_sock_family(struct sock *sk) +{ + u8 family; + + if (tomoyo_kernel_service()) + return 0; + family = sk->sk_family; + switch (family) { + case PF_INET: + case PF_INET6: + case PF_UNIX: + return family; + default: + return 0; + } +} + +/** + * tomoyo_socket_listen_permission - Check permission for listening a socket. + * + * @sock: Pointer to "struct socket". + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_listen_permission(struct socket *sock) +{ + struct tomoyo_addr_info address; + const u8 family = tomoyo_sock_family(sock->sk); + const unsigned int type = sock->type; + struct sockaddr_storage addr; + int addr_len; + + if (!family || (type != SOCK_STREAM && type != SOCK_SEQPACKET)) + return 0; + { + const int error = sock->ops->getname(sock, (struct sockaddr *) + &addr, &addr_len, 0); + + if (error) + return error; + } + address.protocol = type; + address.operation = TOMOYO_NETWORK_LISTEN; + if (family == PF_UNIX) + return tomoyo_check_unix_address((struct sockaddr *) &addr, + addr_len, &address); + return tomoyo_check_inet_address((struct sockaddr *) &addr, addr_len, + 0, &address); +} + +/** + * tomoyo_socket_connect_permission - Check permission for setting the remote address of a socket. + * + * @sock: Pointer to "struct socket". + * @addr: Pointer to "struct sockaddr". + * @addr_len: Size of @addr. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_connect_permission(struct socket *sock, + struct sockaddr *addr, int addr_len) +{ + struct tomoyo_addr_info address; + const u8 family = tomoyo_sock_family(sock->sk); + const unsigned int type = sock->type; + + if (!family) + return 0; + address.protocol = type; + switch (type) { + case SOCK_DGRAM: + case SOCK_RAW: + address.operation = TOMOYO_NETWORK_SEND; + break; + case SOCK_STREAM: + case SOCK_SEQPACKET: + address.operation = TOMOYO_NETWORK_CONNECT; + break; + default: + return 0; + } + if (family == PF_UNIX) + return tomoyo_check_unix_address(addr, addr_len, &address); + return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol, + &address); +} + +/** + * tomoyo_socket_bind_permission - Check permission for setting the local address of a socket. + * + * @sock: Pointer to "struct socket". + * @addr: Pointer to "struct sockaddr". + * @addr_len: Size of @addr. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr, + int addr_len) +{ + struct tomoyo_addr_info address; + const u8 family = tomoyo_sock_family(sock->sk); + const unsigned int type = sock->type; + + if (!family) + return 0; + switch (type) { + case SOCK_STREAM: + case SOCK_DGRAM: + case SOCK_RAW: + case SOCK_SEQPACKET: + address.protocol = type; + address.operation = TOMOYO_NETWORK_BIND; + break; + default: + return 0; + } + if (family == PF_UNIX) + return tomoyo_check_unix_address(addr, addr_len, &address); + return tomoyo_check_inet_address(addr, addr_len, sock->sk->sk_protocol, + &address); +} + +/** + * tomoyo_socket_sendmsg_permission - Check permission for sending a datagram. + * + * @sock: Pointer to "struct socket". + * @msg: Pointer to "struct msghdr". + * @size: Unused. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_socket_sendmsg_permission(struct socket *sock, struct msghdr *msg, + int size) +{ + struct tomoyo_addr_info address; + const u8 family = tomoyo_sock_family(sock->sk); + const unsigned int type = sock->type; + + if (!msg->msg_name || !family || + (type != SOCK_DGRAM && type != SOCK_RAW)) + return 0; + address.protocol = type; + address.operation = TOMOYO_NETWORK_SEND; + if (family == PF_UNIX) + return tomoyo_check_unix_address((struct sockaddr *) + msg->msg_name, + msg->msg_namelen, &address); + return tomoyo_check_inet_address((struct sockaddr *) msg->msg_name, + msg->msg_namelen, + sock->sk->sk_protocol, &address); +} diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 6c601bd300f3..738bbdf8d4c7 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -15,17 +15,19 @@ #include "../../fs/internal.h" /** - * tomoyo_encode: Convert binary string to ascii string. + * tomoyo_encode2 - Encode binary string to ascii string. * - * @str: String in binary format. + * @str: String in binary format. + * @str_len: Size of @str in byte. * * Returns pointer to @str in ascii format on success, NULL otherwise. * * This function uses kzalloc(), so caller must kfree() if this function * didn't return NULL. */ -char *tomoyo_encode(const char *str) +char *tomoyo_encode2(const char *str, int str_len) { + int i; int len = 0; const char *p = str; char *cp; @@ -33,8 +35,9 @@ char *tomoyo_encode(const char *str) if (!p) return NULL; - while (*p) { - const unsigned char c = *p++; + for (i = 0; i < str_len; i++) { + const unsigned char c = p[i]; + if (c == '\\') len += 2; else if (c > ' ' && c < 127) @@ -49,8 +52,8 @@ char *tomoyo_encode(const char *str) return NULL; cp0 = cp; p = str; - while (*p) { - const unsigned char c = *p++; + for (i = 0; i < str_len; i++) { + const unsigned char c = p[i]; if (c == '\\') { *cp++ = '\\'; @@ -67,6 +70,21 @@ char *tomoyo_encode(const char *str) return cp0; } +/** + * tomoyo_encode - Encode binary string to ascii string. + * + * @str: String in binary format. + * + * Returns pointer to @str in ascii format on success, NULL otherwise. + * + * This function uses kzalloc(), so caller must kfree() if this function + * didn't return NULL. + */ +char *tomoyo_encode(const char *str) +{ + return str ? tomoyo_encode2(str, strlen(str)) : NULL; +} + /** * tomoyo_get_absolute_path - Get the path of a dentry but ignores chroot'ed root. * diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index f776400a8f31..4b327b691745 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -442,6 +442,64 @@ static int tomoyo_sb_pivotroot(struct path *old_path, struct path *new_path) return tomoyo_path2_perm(TOMOYO_TYPE_PIVOT_ROOT, new_path, old_path); } +/** + * tomoyo_socket_listen - Check permission for listen(). + * + * @sock: Pointer to "struct socket". + * @backlog: Backlog parameter. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_socket_listen(struct socket *sock, int backlog) +{ + return tomoyo_socket_listen_permission(sock); +} + +/** + * tomoyo_socket_connect - Check permission for connect(). + * + * @sock: Pointer to "struct socket". + * @addr: Pointer to "struct sockaddr". + * @addr_len: Size of @addr. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_socket_connect(struct socket *sock, struct sockaddr *addr, + int addr_len) +{ + return tomoyo_socket_connect_permission(sock, addr, addr_len); +} + +/** + * tomoyo_socket_bind - Check permission for bind(). + * + * @sock: Pointer to "struct socket". + * @addr: Pointer to "struct sockaddr". + * @addr_len: Size of @addr. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_socket_bind(struct socket *sock, struct sockaddr *addr, + int addr_len) +{ + return tomoyo_socket_bind_permission(sock, addr, addr_len); +} + +/** + * tomoyo_socket_sendmsg - Check permission for sendmsg(). + * + * @sock: Pointer to "struct socket". + * @msg: Pointer to "struct msghdr". + * @size: Size of message. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg, + int size) +{ + return tomoyo_socket_sendmsg_permission(sock, msg, size); +} + /* * tomoyo_security_ops is a "struct security_operations" which is used for * registering TOMOYO. @@ -472,6 +530,10 @@ static struct security_operations tomoyo_security_ops = { .sb_mount = tomoyo_sb_mount, .sb_umount = tomoyo_sb_umount, .sb_pivotroot = tomoyo_sb_pivotroot, + .socket_bind = tomoyo_socket_bind, + .socket_connect = tomoyo_socket_connect, + .socket_listen = tomoyo_socket_listen, + .socket_sendmsg = tomoyo_socket_sendmsg, }; /* Lock for GC. */ diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index cb7d507b6312..a1c3d9ccebfa 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -42,6 +42,37 @@ const u8 tomoyo_index2category[TOMOYO_MAX_MAC_INDEX] = { [TOMOYO_MAC_FILE_MOUNT] = TOMOYO_MAC_CATEGORY_FILE, [TOMOYO_MAC_FILE_UMOUNT] = TOMOYO_MAC_CATEGORY_FILE, [TOMOYO_MAC_FILE_PIVOT_ROOT] = TOMOYO_MAC_CATEGORY_FILE, + /* CONFIG::network group */ + [TOMOYO_MAC_NETWORK_INET_STREAM_BIND] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_INET_STREAM_LISTEN] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_INET_STREAM_CONNECT] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_INET_DGRAM_BIND] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_INET_DGRAM_SEND] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_INET_RAW_BIND] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_INET_RAW_SEND] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_UNIX_STREAM_BIND] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_UNIX_STREAM_LISTEN] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_UNIX_STREAM_CONNECT] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_UNIX_DGRAM_BIND] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_UNIX_DGRAM_SEND] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_BIND] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_LISTEN] = + TOMOYO_MAC_CATEGORY_NETWORK, + [TOMOYO_MAC_NETWORK_UNIX_SEQPACKET_CONNECT] = + TOMOYO_MAC_CATEGORY_NETWORK, /* CONFIG::misc group */ [TOMOYO_MAC_ENVIRON] = TOMOYO_MAC_CATEGORY_MISC, }; -- cgit v1.2.3 From 1f067a682a9bd252107ac6f6946b7332fde42344 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sat, 10 Sep 2011 15:24:56 +0900 Subject: TOMOYO: Allow controlling generation of access granted logs for per an entry basis. Add per-entry flag which controls generation of grant logs because Xen and KVM issues ioctl requests so frequently. For example, file ioctl /dev/null 0x5401 grant_log=no will suppress /sys/kernel/security/tomoyo/audit even if preference says grant_log=yes . Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/audit.c | 7 ++++++- security/tomoyo/common.c | 4 ++++ security/tomoyo/common.h | 12 ++++++++++++ security/tomoyo/condition.c | 15 +++++++++++++++ security/tomoyo/domain.c | 1 + 5 files changed, 38 insertions(+), 1 deletion(-) (limited to 'security/tomoyo') diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c index 5dbb1f7617c0..075c3a6d1649 100644 --- a/security/tomoyo/audit.c +++ b/security/tomoyo/audit.c @@ -313,6 +313,7 @@ static unsigned int tomoyo_log_count; */ static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns, const u8 profile, const u8 index, + const struct tomoyo_acl_info *matched_acl, const bool is_granted) { u8 mode; @@ -324,6 +325,9 @@ static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns, p = tomoyo_profile(ns, profile); if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG]) return false; + if (is_granted && matched_acl && matched_acl->cond && + matched_acl->cond->grant_log != TOMOYO_GRANTLOG_AUTO) + return matched_acl->cond->grant_log == TOMOYO_GRANTLOG_YES; mode = p->config[index]; if (mode == TOMOYO_CONFIG_USE_DEFAULT) mode = p->config[category]; @@ -350,7 +354,8 @@ void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt, char *buf; struct tomoyo_log *entry; bool quota_exceeded = false; - if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, r->granted)) + if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, + r->matched_acl, r->granted)) goto out; buf = tomoyo_init_log(r, len, fmt, args); if (!buf) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 85d915587a71..2704c384bf1e 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1272,6 +1272,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, head->r.cond_step++; /* fall through */ case 3: + if (cond->grant_log != TOMOYO_GRANTLOG_AUTO) + tomoyo_io_printf(head, " grant_log=%s", + tomoyo_yesno(cond->grant_log == + TOMOYO_GRANTLOG_YES)); tomoyo_set_lf(head); return true; } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index d1c758e7f92b..435b3d869fc5 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -179,6 +179,16 @@ enum tomoyo_domain_info_flags_index { TOMOYO_MAX_DOMAIN_INFO_FLAGS }; +/* Index numbers for audit type. */ +enum tomoyo_grant_log { + /* Follow profile's configuration. */ + TOMOYO_GRANTLOG_AUTO, + /* Do not generate grant log. */ + TOMOYO_GRANTLOG_NO, + /* Generate grant_log. */ + TOMOYO_GRANTLOG_YES, +}; + /* Index numbers for group entries. */ enum tomoyo_group_id { TOMOYO_PATH_GROUP, @@ -471,6 +481,7 @@ struct tomoyo_request_info { int need_dev; } mount; } param; + struct tomoyo_acl_info *matched_acl; u8 param_type; bool granted; u8 retry; @@ -635,6 +646,7 @@ struct tomoyo_condition { u16 names_count; /* Number of "struct tomoyo_name_union names". */ u16 argc; /* Number of "struct tomoyo_argv". */ u16 envc; /* Number of "struct tomoyo_envp". */ + u8 grant_log; /* One of values in "enum tomoyo_grant_log". */ /* * struct tomoyo_condition_element condition[condc]; * struct tomoyo_number_union values[numbers_count]; diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c index 8a05f71eaf67..3a05eb3e2a64 100644 --- a/security/tomoyo/condition.c +++ b/security/tomoyo/condition.c @@ -348,6 +348,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a, a->numbers_count == b->numbers_count && a->names_count == b->names_count && a->argc == b->argc && a->envc == b->envc && + a->grant_log == b->grant_log && !memcmp(a + 1, b + 1, a->size - sizeof(*a)); } @@ -486,6 +487,20 @@ rerun: goto out; dprintk(KERN_WARNING "%u: <%s>%s=<%s>\n", __LINE__, left_word, is_not ? "!" : "", right_word); + if (!strcmp(left_word, "grant_log")) { + if (entry) { + if (is_not || + entry->grant_log != TOMOYO_GRANTLOG_AUTO) + goto out; + else if (!strcmp(right_word, "yes")) + entry->grant_log = TOMOYO_GRANTLOG_YES; + else if (!strcmp(right_word, "no")) + entry->grant_log = TOMOYO_GRANTLOG_NO; + else + goto out; + } + continue; + } if (!strncmp(left_word, "exec.argv[", 10)) { if (!argv) { e.argc++; diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 5931fb1c04d5..498fea732f48 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -157,6 +157,7 @@ retry: continue; if (!tomoyo_condition(r, ptr->cond)) continue; + r->matched_acl = ptr; r->granted = true; return; } -- cgit v1.2.3 From 731d37aa70c7b9de3be6bf2c8287366223bf5ce5 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sat, 10 Sep 2011 15:25:58 +0900 Subject: TOMOYO: Allow domain transition without execve(). To be able to split permissions for Apache's CGI programs which are executed without execve(), add special domain transition which is performed by writing a TOMOYO's domainname to /sys/kernel/security/tomoyo/self_domain interface. This is an API for TOMOYO-aware userland applications. However, since I expect TOMOYO and other LSM modules to run in parallel, this patch does not use /proc/self/attr/ interface in order to avoid conflicts with other LSM modules when it became possible to run multiple LSM modules in parallel. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 75 ++++++++++++++++-------- security/tomoyo/common.h | 16 +++++- security/tomoyo/securityfs_if.c | 122 +++++++++++++++++++++++++++++++++++++++- security/tomoyo/util.c | 25 ++++++++ 4 files changed, 210 insertions(+), 28 deletions(-) (limited to 'security/tomoyo') diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 2704c384bf1e..1fd0fc1059ba 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1010,6 +1010,48 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, return true; } +/** + * tomoyo_same_task_acl - Check for duplicated "struct tomoyo_task_acl" entry. + * + * @a: Pointer to "struct tomoyo_acl_info". + * @b: Pointer to "struct tomoyo_acl_info". + * + * Returns true if @a == @b, false otherwise. + */ +static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a, + const struct tomoyo_acl_info *b) +{ + const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head); + const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head); + return p1->domainname == p2->domainname; +} + +/** + * tomoyo_write_task - Update task related list. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_write_task(struct tomoyo_acl_param *param) +{ + int error = -EINVAL; + if (tomoyo_str_starts(¶m->data, "manual_domain_transition ")) { + struct tomoyo_task_acl e = { + .head.type = TOMOYO_TYPE_MANUAL_TASK_ACL, + .domainname = tomoyo_get_domainname(param), + }; + if (e.domainname) + error = tomoyo_update_domain(&e.head, sizeof(e), param, + tomoyo_same_task_acl, + NULL); + tomoyo_put_name(e.domainname); + } + return error; +} + /** * tomoyo_delete_domain - Delete a domain. * @@ -1068,11 +1110,12 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns, static const struct { const char *keyword; int (*write) (struct tomoyo_acl_param *); - } tomoyo_callback[4] = { + } tomoyo_callback[5] = { { "file ", tomoyo_write_file }, { "network inet ", tomoyo_write_inet_network }, { "network unix ", tomoyo_write_unix_network }, { "misc ", tomoyo_write_misc }, + { "task ", tomoyo_write_task }, }; u8 i; @@ -1343,6 +1386,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, if (first) return true; tomoyo_print_name_union(head, &ptr->name); + } else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) { + struct tomoyo_task_acl *ptr = + container_of(acl, typeof(*ptr), head); + tomoyo_set_group(head, "task "); + tomoyo_set_string(head, "manual_domain_transition "); + tomoyo_set_string(head, ptr->domainname->name); } else if (head->r.print_transition_related_only) { return true; } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) { @@ -2178,26 +2227,6 @@ static void tomoyo_read_version(struct tomoyo_io_buffer *head) } } -/** - * tomoyo_read_self_domain - Get the current process's domainname. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns the current process's domainname. - */ -static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head) -{ - if (!head->r.eof) { - /* - * tomoyo_domain()->domainname != NULL - * because every process belongs to a domain and - * the domain's name cannot be NULL. - */ - tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name); - head->r.eof = true; - } -} - /* String table for /sys/kernel/security/tomoyo/stat interface. */ static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = { [TOMOYO_STAT_POLICY_UPDATES] = "update:", @@ -2328,10 +2357,6 @@ int tomoyo_open_control(const u8 type, struct file *file) head->poll = tomoyo_poll_log; head->read = tomoyo_read_log; break; - case TOMOYO_SELFDOMAIN: - /* /sys/kernel/security/tomoyo/self_domain */ - head->read = tomoyo_read_self_domain; - break; case TOMOYO_PROCESS_STATUS: /* /sys/kernel/security/tomoyo/.process_status */ head->write = tomoyo_write_pid; diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 435b3d869fc5..af82683df7ff 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -227,6 +227,7 @@ enum tomoyo_acl_entry_type_index { TOMOYO_TYPE_INET_ACL, TOMOYO_TYPE_UNIX_ACL, TOMOYO_TYPE_ENV_ACL, + TOMOYO_TYPE_MANUAL_TASK_ACL, }; /* Index numbers for access controls with one pathname. */ @@ -295,7 +296,6 @@ enum tomoyo_securityfs_interface_index { TOMOYO_EXCEPTIONPOLICY, TOMOYO_PROCESS_STATUS, TOMOYO_STAT, - TOMOYO_SELFDOMAIN, TOMOYO_AUDIT, TOMOYO_VERSION, TOMOYO_PROFILE, @@ -480,6 +480,9 @@ struct tomoyo_request_info { unsigned long flags; int need_dev; } mount; + struct { + const struct tomoyo_path_info *domainname; + } task; } param; struct tomoyo_acl_info *matched_acl; u8 param_type; @@ -679,6 +682,15 @@ struct tomoyo_domain_info { atomic_t users; /* Number of referring credentials. */ }; +/* + * Structure for "task manual_domain_transition" directive. + */ +struct tomoyo_task_acl { + struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MANUAL_TASK_ACL */ + /* Pointer to domainname. */ + const struct tomoyo_path_info *domainname; +}; + /* * Structure for "file execute", "file read", "file write", "file append", * "file unlink", "file getattr", "file rmdir", "file truncate", @@ -935,6 +947,8 @@ const char *tomoyo_get_exe(void); const char *tomoyo_yesno(const unsigned int value); const struct tomoyo_path_info *tomoyo_compare_name_union (const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr); +const struct tomoyo_path_info *tomoyo_get_domainname +(struct tomoyo_acl_param *param); const struct tomoyo_path_info *tomoyo_get_name(const char *name); const struct tomoyo_path_info *tomoyo_path_matches_group (const struct tomoyo_path_info *pathname, const struct tomoyo_group *group); diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index a49c3bfd4dd5..d08296a4882b 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c @@ -7,6 +7,124 @@ #include #include "common.h" +/** + * tomoyo_check_task_acl - Check permission for task operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @ptr: Pointer to "struct tomoyo_acl_info". + * + * Returns true if granted, false otherwise. + */ +static bool tomoyo_check_task_acl(struct tomoyo_request_info *r, + const struct tomoyo_acl_info *ptr) +{ + const struct tomoyo_task_acl *acl = container_of(ptr, typeof(*acl), + head); + return !tomoyo_pathcmp(r->param.task.domainname, acl->domainname); +} + +/** + * tomoyo_write_self - write() for /sys/kernel/security/tomoyo/self_domain interface. + * + * @file: Pointer to "struct file". + * @buf: Domainname to transit to. + * @count: Size of @buf. + * @ppos: Unused. + * + * Returns @count on success, negative value otherwise. + * + * If domain transition was permitted but the domain transition failed, this + * function returns error rather than terminating current thread with SIGKILL. + */ +static ssize_t tomoyo_write_self(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + char *data; + int error; + if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10) + return -ENOMEM; + data = kzalloc(count + 1, GFP_NOFS); + if (!data) + return -ENOMEM; + if (copy_from_user(data, buf, count)) { + error = -EFAULT; + goto out; + } + tomoyo_normalize_line(data); + if (tomoyo_correct_domain(data)) { + const int idx = tomoyo_read_lock(); + struct tomoyo_path_info name; + struct tomoyo_request_info r; + name.name = data; + tomoyo_fill_path_info(&name); + /* Check "task manual_domain_transition" permission. */ + tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); + r.param_type = TOMOYO_TYPE_MANUAL_TASK_ACL; + r.param.task.domainname = &name; + tomoyo_check_acl(&r, tomoyo_check_task_acl); + if (!r.granted) + error = -EPERM; + else { + struct tomoyo_domain_info *new_domain = + tomoyo_assign_domain(data, true); + if (!new_domain) { + error = -ENOENT; + } else { + struct cred *cred = prepare_creds(); + if (!cred) { + error = -ENOMEM; + } else { + struct tomoyo_domain_info *old_domain = + cred->security; + cred->security = new_domain; + atomic_inc(&new_domain->users); + atomic_dec(&old_domain->users); + commit_creds(cred); + error = 0; + } + } + } + tomoyo_read_unlock(idx); + } else + error = -EINVAL; +out: + kfree(data); + return error ? error : count; +} + +/** + * tomoyo_read_self - read() for /sys/kernel/security/tomoyo/self_domain interface. + * + * @file: Pointer to "struct file". + * @buf: Domainname which current thread belongs to. + * @count: Size of @buf. + * @ppos: Bytes read by now. + * + * Returns read size on success, negative value otherwise. + */ +static ssize_t tomoyo_read_self(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + const char *domain = tomoyo_domain()->domainname->name; + loff_t len = strlen(domain); + loff_t pos = *ppos; + if (pos >= len || !count) + return 0; + len -= pos; + if (count < len) + len = count; + if (copy_to_user(buf, domain + pos, len)) + return -EFAULT; + *ppos += len; + return len; +} + +/* Operations for /sys/kernel/security/tomoyo/self_domain interface. */ +static const struct file_operations tomoyo_self_operations = { + .write = tomoyo_write_self, + .read = tomoyo_read_self, +}; + /** * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. * @@ -135,8 +253,6 @@ static int __init tomoyo_initerface_init(void) TOMOYO_EXCEPTIONPOLICY); tomoyo_create_entry("audit", 0400, tomoyo_dir, TOMOYO_AUDIT); - tomoyo_create_entry("self_domain", 0400, tomoyo_dir, - TOMOYO_SELFDOMAIN); tomoyo_create_entry(".process_status", 0600, tomoyo_dir, TOMOYO_PROCESS_STATUS); tomoyo_create_entry("stat", 0644, tomoyo_dir, @@ -147,6 +263,8 @@ static int __init tomoyo_initerface_init(void) TOMOYO_MANAGER); tomoyo_create_entry("version", 0400, tomoyo_dir, TOMOYO_VERSION); + securityfs_create_file("self_domain", 0666, tomoyo_dir, NULL, + &tomoyo_self_operations); return 0; } diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index a1c3d9ccebfa..50e9b4c73ceb 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -158,6 +158,31 @@ char *tomoyo_read_token(struct tomoyo_acl_param *param) return pos; } +/** + * tomoyo_get_domainname - Read a domainname from a line. + * + * @param: Pointer to "struct tomoyo_acl_param". + * + * Returns a domainname on success, NULL otherwise. + */ +const struct tomoyo_path_info *tomoyo_get_domainname +(struct tomoyo_acl_param *param) +{ + char *start = param->data; + char *pos = start; + while (*pos) { + if (*pos++ != ' ' || *pos++ == '/') + continue; + pos -= 2; + *pos++ = '\0'; + break; + } + param->data = pos; + if (tomoyo_correct_domain(start)) + return tomoyo_get_name(start); + return NULL; +} + /** * tomoyo_parse_ulong - Parse an "unsigned long" value. * -- cgit v1.2.3 From a8f7640963ada66c412314c3559c11ff6946c1a5 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sat, 10 Sep 2011 15:27:12 +0900 Subject: TOMOYO: Avoid race when retrying "file execute" permission check. There was a race window that the pathname which is subjected to "file execute" permission check when retrying via supervisor's decision because the pathname was recalculated upon retry. Though, there is an inevitable race window even without supervisor, for we have to calculate the symbolic link's pathname from "struct linux_binprm"->filename rather than from "struct linux_binprm"->file because we cannot back calculate the symbolic link's pathname from the dereferenced pathname. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/domain.c | 56 +++++++++++++++++++----------------------------- 1 file changed, 22 insertions(+), 34 deletions(-) (limited to 'security/tomoyo') diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 498fea732f48..a1fc6b5f6125 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -664,9 +664,9 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) struct tomoyo_domain_info *domain = NULL; const char *original_name = bprm->filename; int retval = -ENOMEM; - bool need_kfree = false; bool reject_on_transition_failure = false; - struct tomoyo_path_info rn = { }; /* real name */ + const struct tomoyo_path_info *candidate; + struct tomoyo_path_info exename; struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS); if (!ee) @@ -682,40 +682,33 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) ee->bprm = bprm; ee->r.obj = &ee->obj; ee->obj.path1 = bprm->file->f_path; - retry: - if (need_kfree) { - kfree(rn.name); - need_kfree = false; - } /* Get symlink's pathname of program. */ retval = -ENOENT; - rn.name = tomoyo_realpath_nofollow(original_name); - if (!rn.name) + exename.name = tomoyo_realpath_nofollow(original_name); + if (!exename.name) goto out; - tomoyo_fill_path_info(&rn); - need_kfree = true; - + tomoyo_fill_path_info(&exename); +retry: /* Check 'aggregator' directive. */ { struct tomoyo_aggregator *ptr; struct list_head *list = &old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR]; /* Check 'aggregator' directive. */ + candidate = &exename; list_for_each_entry_rcu(ptr, list, head.list) { if (ptr->head.is_deleted || - !tomoyo_path_matches_pattern(&rn, + !tomoyo_path_matches_pattern(&exename, ptr->original_name)) continue; - kfree(rn.name); - need_kfree = false; - /* This is OK because it is read only. */ - rn = *ptr->aggregated_name; + candidate = ptr->aggregated_name; break; } } /* Check execute permission. */ - retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, &rn); + retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, + candidate); if (retval == TOMOYO_RETRY_REQUEST) goto retry; if (retval < 0) @@ -726,20 +719,16 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) * wildcard) rather than the pathname passed to execve() * (which never contains wildcard). */ - if (ee->r.param.path.matched_path) { - if (need_kfree) - kfree(rn.name); - need_kfree = false; - /* This is OK because it is read only. */ - rn = *ee->r.param.path.matched_path; - } + if (ee->r.param.path.matched_path) + candidate = ee->r.param.path.matched_path; /* Calculate domain to transit to. */ switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname, - &rn)) { + candidate)) { case TOMOYO_TRANSITION_CONTROL_RESET: /* Transit to the root of specified namespace. */ - snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", rn.name); + snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", + candidate->name); /* * Make do_execve() fail if domain transition across namespaces * has failed. @@ -749,7 +738,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) case TOMOYO_TRANSITION_CONTROL_INITIALIZE: /* Transit to the child of current namespace's root. */ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", - old_domain->ns->name, rn.name); + old_domain->ns->name, candidate->name); break; case TOMOYO_TRANSITION_CONTROL_KEEP: /* Keep current domain. */ @@ -765,11 +754,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) * before /sbin/init. */ domain = old_domain; - } else { - /* Normal domain transition. */ - snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", - old_domain->domainname->name, rn.name); + break; } + /* Normal domain transition. */ + snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", + old_domain->domainname->name, candidate->name); break; } if (!domain) @@ -799,8 +788,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) /* Update reference count on "struct tomoyo_domain_info". */ atomic_inc(&domain->users); bprm->cred->security = domain; - if (need_kfree) - kfree(rn.name); + kfree(exename.name); if (!retval) { ee->r.domain = domain; retval = tomoyo_environ(ee); -- cgit v1.2.3 From 843d183cdd816549b73e6bd3ae07f64adddf714b Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 14 Sep 2011 17:03:19 +0900 Subject: TOMOYO: Bump version. Tell userland tools that this is TOMOYO 2.5. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/Makefile | 2 +- security/tomoyo/common.c | 12 ++++++------ security/tomoyo/common.h | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'security/tomoyo') diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index fc2a8ce40301..56a0c7be409e 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile @@ -27,7 +27,7 @@ $(obj)/policy/stat.conf: @touch $@ $(obj)/builtin-policy.h: $(obj)/policy/profile.conf $(obj)/policy/exception_policy.conf $(obj)/policy/domain_policy.conf $(obj)/policy/manager.conf $(obj)/policy/stat.conf - @echo Generating built-in policy for TOMOYO 2.4.x. + @echo Generating built-in policy for TOMOYO 2.5.x. @echo "static char tomoyo_builtin_profile[] __initdata =" > $@.tmp @sed -e 's/\\/\\\\/g' -e 's/\"/\\"/g' -e 's/\(.*\)/"\1\\n"/' < $(obj)/policy/profile.conf >> $@.tmp @echo "\"\";" >> $@.tmp diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 1fd0fc1059ba..084018351b4f 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -345,7 +345,7 @@ void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns) INIT_LIST_HEAD(&ns->group_list[idx]); for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++) INIT_LIST_HEAD(&ns->policy_list[idx]); - ns->profile_version = 20100903; + ns->profile_version = 20110903; tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list); list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list); } @@ -2222,7 +2222,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) static void tomoyo_read_version(struct tomoyo_io_buffer *head) { if (!head->r.eof) { - tomoyo_io_printf(head, "2.4.0"); + tomoyo_io_printf(head, "2.5.0"); head->r.eof = true; } } @@ -2694,11 +2694,11 @@ void tomoyo_check_profile(void) struct tomoyo_domain_info *domain; const int idx = tomoyo_read_lock(); tomoyo_policy_loaded = true; - printk(KERN_INFO "TOMOYO: 2.4.0\n"); + printk(KERN_INFO "TOMOYO: 2.5.0\n"); list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { const u8 profile = domain->profile; const struct tomoyo_policy_namespace *ns = domain->ns; - if (ns->profile_version != 20100903) + if (ns->profile_version != 20110903) printk(KERN_ERR "Profile version %u is not supported.\n", ns->profile_version); @@ -2709,9 +2709,9 @@ void tomoyo_check_profile(void) else continue; printk(KERN_ERR - "Userland tools for TOMOYO 2.4 must be installed and " + "Userland tools for TOMOYO 2.5 must be installed and " "policy must be initialized.\n"); - printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ " + printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.5/ " "for more information.\n"); panic("STOP!"); } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index af82683df7ff..471c9f9afc18 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -3,7 +3,7 @@ * * Header file for TOMOYO. * - * Copyright (C) 2005-2010 NTT DATA CORPORATION + * Copyright (C) 2005-2011 NTT DATA CORPORATION */ #ifndef _SECURITY_TOMOYO_COMMON_H @@ -901,7 +901,7 @@ struct tomoyo_policy_namespace { struct list_head acl_group[TOMOYO_MAX_ACL_GROUPS]; /* List for connecting to tomoyo_namespace_list list. */ struct list_head namespace_list; - /* Profile version. Currently only 20100903 is defined. */ + /* Profile version. Currently only 20110903 is defined. */ unsigned int profile_version; /* Name of this namespace (e.g. "", "" ). */ const char *name; -- cgit v1.2.3 From 6bce98edc3365a8f780ff3944ac7992544c194fe Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Fri, 16 Sep 2011 22:54:25 +0900 Subject: TOMOYO: Allow specifying domain transition preference. I got an opinion that it is difficult to use exception policy's domain transition control directives because they need to match the pathname specified to "file execute" directives. For example, if "file execute /bin/\*\-ls\-cat" is given, corresponding domain transition control directive needs to be like "no_keep_domain /bin/\*\-ls\-cat from any". If we can specify like below, it will become more convenient. file execute /bin/ls keep exec.realpath="/bin/ls" exec.argv[0]="ls" file execute /bin/cat keep exec.realpath="/bin/cat" exec.argv[0]="cat" file execute /bin/\*\-ls\-cat child file execute /usr/sbin/httpd exec.realpath="/usr/sbin/httpd" exec.argv[0]="/usr/sbin/httpd" In above examples, "keep" works as if keep_domain is specified, "child" works as if "no_reset_domain" and "no_initialize_domain" and "no_keep_domain" are specified, "" causes domain transition to domain upon successful execve() operation. Moreover, we can also allow transition to different domains based on conditions like below example. /usr/sbin/sshd file execute /bin/bash /usr/sbin/sshd //batch-session exec.argc=2 exec.argv[1]="-c" file execute /bin/bash /usr/sbin/sshd //root-session task.uid=0 file execute /bin/bash /usr/sbin/sshd //nonroot-session task.uid!=0 Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 4 ++++ security/tomoyo/common.h | 4 ++++ security/tomoyo/condition.c | 50 +++++++++++++++++++++++++++++++++++++++--- security/tomoyo/domain.c | 53 ++++++++++++++++++++++++++++++++++++++++++--- security/tomoyo/file.c | 38 +++++++++++++++++++++++++++----- 5 files changed, 137 insertions(+), 12 deletions(-) (limited to 'security/tomoyo') diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 084018351b4f..0994948f3edc 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1203,6 +1203,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, case 0: head->r.cond_index = 0; head->r.cond_step++; + if (cond->transit) { + tomoyo_set_space(head); + tomoyo_set_string(head, cond->transit->name); + } /* fall through */ case 1: { diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 471c9f9afc18..a2bc33fc60b6 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -615,6 +615,7 @@ struct tomoyo_execve { struct tomoyo_request_info r; struct tomoyo_obj_info obj; struct linux_binprm *bprm; + const struct tomoyo_path_info *transition; /* For dumping argv[] and envp[]. */ struct tomoyo_page_dump dump; /* For temporary use. */ @@ -650,6 +651,7 @@ struct tomoyo_condition { u16 argc; /* Number of "struct tomoyo_argv". */ u16 envc; /* Number of "struct tomoyo_envp". */ u8 grant_log; /* One of values in "enum tomoyo_grant_log". */ + const struct tomoyo_path_info *transit; /* Maybe NULL. */ /* * struct tomoyo_condition_element condition[condc]; * struct tomoyo_number_union values[numbers_count]; @@ -956,6 +958,8 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct path *path, const int flag); int tomoyo_close_control(struct tomoyo_io_buffer *head); int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env); +int tomoyo_execute_permission(struct tomoyo_request_info *r, + const struct tomoyo_path_info *filename); int tomoyo_find_next_domain(struct linux_binprm *bprm); int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, const u8 index); diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c index 3a05eb3e2a64..b854959c0fd4 100644 --- a/security/tomoyo/condition.c +++ b/security/tomoyo/condition.c @@ -348,7 +348,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a, a->numbers_count == b->numbers_count && a->names_count == b->names_count && a->argc == b->argc && a->envc == b->envc && - a->grant_log == b->grant_log && + a->grant_log == b->grant_log && a->transit == b->transit && !memcmp(a + 1, b + 1, a->size - sizeof(*a)); } @@ -428,6 +428,46 @@ out: return entry; } +/** + * tomoyo_get_transit_preference - Parse domain transition preference for execve(). + * + * @param: Pointer to "struct tomoyo_acl_param". + * @e: Pointer to "struct tomoyo_condition". + * + * Returns the condition string part. + */ +static char *tomoyo_get_transit_preference(struct tomoyo_acl_param *param, + struct tomoyo_condition *e) +{ + char * const pos = param->data; + bool flag; + if (*pos == '<') { + e->transit = tomoyo_get_domainname(param); + goto done; + } + { + char *cp = strchr(pos, ' '); + if (cp) + *cp = '\0'; + flag = tomoyo_correct_path(pos) || !strcmp(pos, "keep") || + !strcmp(pos, "initialize") || !strcmp(pos, "reset") || + !strcmp(pos, "child") || !strcmp(pos, "parent"); + if (cp) + *cp = ' '; + } + if (!flag) + return pos; + e->transit = tomoyo_get_name(tomoyo_read_token(param)); +done: + if (e->transit) + return param->data; + /* + * Return a bad read-only condition string that will let + * tomoyo_get_condition() return NULL. + */ + return "/"; +} + /** * tomoyo_get_condition - Parse condition part. * @@ -444,7 +484,8 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) struct tomoyo_argv *argv = NULL; struct tomoyo_envp *envp = NULL; struct tomoyo_condition e = { }; - char * const start_of_string = param->data; + char * const start_of_string = + tomoyo_get_transit_preference(param, &e); char * const end_of_string = start_of_string + strlen(start_of_string); char *pos; rerun: @@ -608,8 +649,9 @@ store_value: + e.envc * sizeof(struct tomoyo_envp); entry = kzalloc(e.size, GFP_NOFS); if (!entry) - return NULL; + goto out2; *entry = e; + e.transit = NULL; condp = (struct tomoyo_condition_element *) (entry + 1); numbers_p = (struct tomoyo_number_union *) (condp + e.condc); names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count); @@ -636,6 +678,8 @@ out: tomoyo_del_condition(&entry->head.list); kfree(entry); } +out2: + tomoyo_put_name(e.transit); return NULL; } diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index a1fc6b5f6125..860390ee1fbe 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -102,6 +102,15 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, new_entry->cond = tomoyo_get_condition(param); if (!new_entry->cond) return -EINVAL; + /* + * Domain transition preference is allowed for only + * "file execute" entries. + */ + if (new_entry->cond->transit && + !(new_entry->type == TOMOYO_TYPE_PATH_ACL && + container_of(new_entry, struct tomoyo_path_acl, head) + ->perm == 1 << TOMOYO_TYPE_EXECUTE)) + goto out; } if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; @@ -707,8 +716,7 @@ retry: } /* Check execute permission. */ - retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, - candidate); + retval = tomoyo_execute_permission(&ee->r, candidate); if (retval == TOMOYO_RETRY_REQUEST) goto retry; if (retval < 0) @@ -722,10 +730,45 @@ retry: if (ee->r.param.path.matched_path) candidate = ee->r.param.path.matched_path; - /* Calculate domain to transit to. */ + /* + * Check for domain transition preference if "file execute" matched. + * If preference is given, make do_execve() fail if domain transition + * has failed, for domain transition preference should be used with + * destination domain defined. + */ + if (ee->transition) { + const char *domainname = ee->transition->name; + reject_on_transition_failure = true; + if (!strcmp(domainname, "keep")) + goto force_keep_domain; + if (!strcmp(domainname, "child")) + goto force_child_domain; + if (!strcmp(domainname, "reset")) + goto force_reset_domain; + if (!strcmp(domainname, "initialize")) + goto force_initialize_domain; + if (!strcmp(domainname, "parent")) { + char *cp; + strncpy(ee->tmp, old_domain->domainname->name, + TOMOYO_EXEC_TMPSIZE - 1); + cp = strrchr(ee->tmp, ' '); + if (cp) + *cp = '\0'; + } else if (*domainname == '<') + strncpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE - 1); + else + snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", + old_domain->domainname->name, domainname); + goto force_jump_domain; + } + /* + * No domain transition preference specified. + * Calculate domain to transit to. + */ switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname, candidate)) { case TOMOYO_TRANSITION_CONTROL_RESET: +force_reset_domain: /* Transit to the root of specified namespace. */ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", candidate->name); @@ -736,11 +779,13 @@ retry: reject_on_transition_failure = true; break; case TOMOYO_TRANSITION_CONTROL_INITIALIZE: +force_initialize_domain: /* Transit to the child of current namespace's root. */ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", old_domain->ns->name, candidate->name); break; case TOMOYO_TRANSITION_CONTROL_KEEP: +force_keep_domain: /* Keep current domain. */ domain = old_domain; break; @@ -756,11 +801,13 @@ retry: domain = old_domain; break; } +force_child_domain: /* Normal domain transition. */ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", old_domain->domainname->name, candidate->name); break; } +force_jump_domain: if (!domain) domain = tomoyo_assign_domain(ee->tmp, true); if (domain) diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 743c35f5084a..b280c1bd652d 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -570,15 +570,41 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, do { tomoyo_check_acl(r, tomoyo_check_path_acl); error = tomoyo_audit_path_log(r); - /* - * Do not retry for execute request, for alias may have - * changed. - */ - } while (error == TOMOYO_RETRY_REQUEST && - operation != TOMOYO_TYPE_EXECUTE); + } while (error == TOMOYO_RETRY_REQUEST); return error; } +/** + * tomoyo_execute_permission - Check permission for execute operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @filename: Filename to check. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_execute_permission(struct tomoyo_request_info *r, + const struct tomoyo_path_info *filename) +{ + /* + * Unlike other permission checks, this check is done regardless of + * profile mode settings in order to check for domain transition + * preference. + */ + r->type = TOMOYO_MAC_FILE_EXECUTE; + r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type); + r->param_type = TOMOYO_TYPE_PATH_ACL; + r->param.path.filename = filename; + r->param.path.operation = TOMOYO_TYPE_EXECUTE; + tomoyo_check_acl(r, tomoyo_check_path_acl); + r->ee->transition = r->matched_acl && r->matched_acl->cond ? + r->matched_acl->cond->transit : NULL; + if (r->mode != TOMOYO_CONFIG_DISABLED) + return tomoyo_audit_path_log(r); + return 0; +} + /** * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry. * -- cgit v1.2.3 From 778c4a4d60d932c1df6d270dcbc88365823c3963 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sun, 25 Sep 2011 17:49:09 +0900 Subject: TOMOYO: Fix make namespacecheck warnings. Commit efe836ab "TOMOYO: Add built-in policy support." introduced tomoyo_load_builtin_policy() but was by error called from nowhere. Commit b22b8b9f "TOMOYO: Rename meminfo to stat and show more statistics." introduced tomoyo_update_stat() but was by error not called from tomoyo_assign_domain(). Also, mark tomoyo_io_printf() and tomoyo_path_permission() static functions, as reported by "make namespacecheck". Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 6 +++++- security/tomoyo/common.h | 4 ---- security/tomoyo/domain.c | 1 + security/tomoyo/file.c | 4 ++-- security/tomoyo/securityfs_if.c | 1 + 5 files changed, 9 insertions(+), 7 deletions(-) (limited to 'security/tomoyo') diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 0994948f3edc..2e2802060eef 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -262,13 +262,17 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string) WARN_ON(1); } +static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, + ...) __printf(2, 3); + /** * tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure. * * @head: Pointer to "struct tomoyo_io_buffer". * @fmt: The printf()'s format string, followed by parameters. */ -void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) +static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, + ...) { va_list args; size_t len; diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index a2bc33fc60b6..1a19ad3e67ea 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -978,8 +978,6 @@ int tomoyo_path_number_perm(const u8 operation, struct path *path, unsigned long number); int tomoyo_path_perm(const u8 operation, struct path *path, const char *target); -int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, - const struct tomoyo_path_info *filename); int tomoyo_poll_control(struct file *file, poll_table *wait); int tomoyo_poll_log(struct file *file, poll_table *wait); int tomoyo_socket_bind_permission(struct socket *sock, struct sockaddr *addr, @@ -1041,8 +1039,6 @@ void tomoyo_del_condition(struct list_head *element); void tomoyo_fill_path_info(struct tomoyo_path_info *ptr); void tomoyo_get_attributes(struct tomoyo_obj_info *obj); void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns); -void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) - __printf(2, 3); void tomoyo_load_policy(const char *filename); void tomoyo_memory_free(void *ptr); void tomoyo_normalize_line(unsigned char *buffer); diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 860390ee1fbe..70acf7aebbda 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -567,6 +567,7 @@ out: tomoyo_write_log(&r, "use_profile %u\n", entry->profile); tomoyo_write_log(&r, "use_group %u\n", entry->group); + tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES); } } return entry; diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index b280c1bd652d..400390790745 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -555,8 +555,8 @@ static int tomoyo_update_path2_acl(const u8 perm, * * Caller holds tomoyo_read_lock(). */ -int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, - const struct tomoyo_path_info *filename) +static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, + const struct tomoyo_path_info *filename) { int error; diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index d08296a4882b..2672ac4f3beb 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c @@ -265,6 +265,7 @@ static int __init tomoyo_initerface_init(void) TOMOYO_VERSION); securityfs_create_file("self_domain", 0666, tomoyo_dir, NULL, &tomoyo_self_operations); + tomoyo_load_builtin_policy(); return 0; } -- cgit v1.2.3 From f9732ea145886786a6f8b0493bc2239e70cbacdb Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sun, 25 Sep 2011 17:50:23 +0900 Subject: TOMOYO: Simplify garbage collector. When TOMOYO started using garbage collector at commit 847b173e "TOMOYO: Add garbage collector.", we waited for close() before kfree(). Thus, elements to be kfree()d were queued up using tomoyo_gc_list list. But it turned out that tomoyo_element_linked_by_gc() tends to choke garbage collector when certain pattern of entries are queued. Since garbage collector is no longer waiting for close() since commit 2e503bbb "TOMOYO: Fix lockdep warning.", we can remove tomoyo_gc_list list and tomoyo_element_linked_by_gc() by doing sequential processing. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.h | 7 +- security/tomoyo/condition.c | 8 +- security/tomoyo/domain.c | 4 + security/tomoyo/gc.c | 480 ++++++++++++++++---------------------------- security/tomoyo/memory.c | 6 +- 5 files changed, 186 insertions(+), 319 deletions(-) (limited to 'security/tomoyo') diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 1a19ad3e67ea..a0212fbf60fb 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -52,6 +52,9 @@ #define TOMOYO_EXEC_TMPSIZE 4096 +/* Garbage collector is trying to kfree() this element. */ +#define TOMOYO_GC_IN_PROGRESS -1 + /* Profile number is an integer between 0 and 255. */ #define TOMOYO_MAX_PROFILES 256 @@ -398,7 +401,7 @@ enum tomoyo_pref_index { /* Common header for holding ACL entries. */ struct tomoyo_acl_head { struct list_head list; - bool is_deleted; + s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */ } __packed; /* Common header for shared entries. */ @@ -665,7 +668,7 @@ struct tomoyo_condition { struct tomoyo_acl_info { struct list_head list; struct tomoyo_condition *cond; /* Maybe NULL. */ - bool is_deleted; + s8 is_deleted; /* true or false or TOMOYO_GC_IN_PROGRESS */ u8 type; /* One of values in "enum tomoyo_acl_entry_type_index". */ } __packed; diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c index b854959c0fd4..986330b8c73e 100644 --- a/security/tomoyo/condition.c +++ b/security/tomoyo/condition.c @@ -400,8 +400,9 @@ static struct tomoyo_condition *tomoyo_commit_condition found = true; goto out; } - list_for_each_entry_rcu(ptr, &tomoyo_condition_list, head.list) { - if (!tomoyo_same_condition(ptr, entry)) + list_for_each_entry(ptr, &tomoyo_condition_list, head.list) { + if (!tomoyo_same_condition(ptr, entry) || + atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS) continue; /* Same entry found. Share this entry. */ atomic_inc(&ptr->head.users); @@ -411,8 +412,7 @@ static struct tomoyo_condition *tomoyo_commit_condition if (!found) { if (tomoyo_memory_ok(entry)) { atomic_set(&entry->head.users, 1); - list_add_rcu(&entry->head.list, - &tomoyo_condition_list); + list_add(&entry->head.list, &tomoyo_condition_list); } else { found = true; ptr = NULL; diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 70acf7aebbda..da16dfeed728 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -39,6 +39,8 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, if (mutex_lock_interruptible(&tomoyo_policy_lock)) return -ENOMEM; list_for_each_entry_rcu(entry, list, list) { + if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS) + continue; if (!check_duplicate(entry, new_entry)) continue; entry->is_deleted = param->is_delete; @@ -115,6 +117,8 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; list_for_each_entry_rcu(entry, list, list) { + if (entry->is_deleted == TOMOYO_GC_IN_PROGRESS) + continue; if (!tomoyo_same_acl_head(entry, new_entry) || !check_duplicate(entry, new_entry)) continue; diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 7747ceb9a221..f2295c65f1e4 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -13,35 +13,6 @@ static LIST_HEAD(tomoyo_io_buffer_list); /* Lock for protecting tomoyo_io_buffer_list. */ static DEFINE_SPINLOCK(tomoyo_io_buffer_list_lock); -/* Size of an element. */ -static const u8 tomoyo_element_size[TOMOYO_MAX_POLICY] = { - [TOMOYO_ID_GROUP] = sizeof(struct tomoyo_group), - [TOMOYO_ID_ADDRESS_GROUP] = sizeof(struct tomoyo_address_group), - [TOMOYO_ID_PATH_GROUP] = sizeof(struct tomoyo_path_group), - [TOMOYO_ID_NUMBER_GROUP] = sizeof(struct tomoyo_number_group), - [TOMOYO_ID_AGGREGATOR] = sizeof(struct tomoyo_aggregator), - [TOMOYO_ID_TRANSITION_CONTROL] = - sizeof(struct tomoyo_transition_control), - [TOMOYO_ID_MANAGER] = sizeof(struct tomoyo_manager), - /* [TOMOYO_ID_CONDITION] = "struct tomoyo_condition"->size, */ - /* [TOMOYO_ID_NAME] = "struct tomoyo_name"->size, */ - /* [TOMOYO_ID_ACL] = - tomoyo_acl_size["struct tomoyo_acl_info"->type], */ - [TOMOYO_ID_DOMAIN] = sizeof(struct tomoyo_domain_info), -}; - -/* Size of a domain ACL element. */ -static const u8 tomoyo_acl_size[] = { - [TOMOYO_TYPE_PATH_ACL] = sizeof(struct tomoyo_path_acl), - [TOMOYO_TYPE_PATH2_ACL] = sizeof(struct tomoyo_path2_acl), - [TOMOYO_TYPE_PATH_NUMBER_ACL] = sizeof(struct tomoyo_path_number_acl), - [TOMOYO_TYPE_MKDEV_ACL] = sizeof(struct tomoyo_mkdev_acl), - [TOMOYO_TYPE_MOUNT_ACL] = sizeof(struct tomoyo_mount_acl), - [TOMOYO_TYPE_INET_ACL] = sizeof(struct tomoyo_inet_acl), - [TOMOYO_TYPE_UNIX_ACL] = sizeof(struct tomoyo_unix_acl), - [TOMOYO_TYPE_ENV_ACL] = sizeof(struct tomoyo_env_acl), -}; - /** * tomoyo_struct_used_by_io_buffer - Check whether the list element is used by /sys/kernel/security/tomoyo/ users or not. * @@ -59,15 +30,11 @@ static bool tomoyo_struct_used_by_io_buffer(const struct list_head *element) list_for_each_entry(head, &tomoyo_io_buffer_list, list) { head->users++; spin_unlock(&tomoyo_io_buffer_list_lock); - if (mutex_lock_interruptible(&head->io_sem)) { - in_use = true; - goto out; - } + mutex_lock(&head->io_sem); if (head->r.domain == element || head->r.group == element || head->r.acl == element || &head->w.domain->list == element) in_use = true; mutex_unlock(&head->io_sem); -out: spin_lock(&tomoyo_io_buffer_list_lock); head->users--; if (in_use) @@ -81,15 +48,14 @@ out: * tomoyo_name_used_by_io_buffer - Check whether the string is used by /sys/kernel/security/tomoyo/ users or not. * * @string: String to check. - * @size: Memory allocated for @string . * * Returns true if @string is used by /sys/kernel/security/tomoyo/ users, * false otherwise. */ -static bool tomoyo_name_used_by_io_buffer(const char *string, - const size_t size) +static bool tomoyo_name_used_by_io_buffer(const char *string) { struct tomoyo_io_buffer *head; + const size_t size = strlen(string) + 1; bool in_use = false; spin_lock(&tomoyo_io_buffer_list_lock); @@ -97,10 +63,7 @@ static bool tomoyo_name_used_by_io_buffer(const char *string, int i; head->users++; spin_unlock(&tomoyo_io_buffer_list_lock); - if (mutex_lock_interruptible(&head->io_sem)) { - in_use = true; - goto out; - } + mutex_lock(&head->io_sem); for (i = 0; i < TOMOYO_MAX_IO_READ_QUEUE; i++) { const char *w = head->r.w[i]; if (w < string || w > string + size) @@ -109,7 +72,6 @@ static bool tomoyo_name_used_by_io_buffer(const char *string, break; } mutex_unlock(&head->io_sem); -out: spin_lock(&tomoyo_io_buffer_list_lock); head->users--; if (in_use) @@ -119,84 +81,6 @@ out: return in_use; } -/* Structure for garbage collection. */ -struct tomoyo_gc { - struct list_head list; - enum tomoyo_policy_id type; - size_t size; - struct list_head *element; -}; -/* List of entries to be deleted. */ -static LIST_HEAD(tomoyo_gc_list); -/* Length of tomoyo_gc_list. */ -static int tomoyo_gc_list_len; - -/** - * tomoyo_add_to_gc - Add an entry to to be deleted list. - * - * @type: One of values in "enum tomoyo_policy_id". - * @element: Pointer to "struct list_head". - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_policy_lock mutex. - * - * Adding an entry needs kmalloc(). Thus, if we try to add thousands of - * entries at once, it will take too long time. Thus, do not add more than 128 - * entries per a scan. But to be able to handle worst case where all entries - * are in-use, we accept one more entry per a scan. - * - * If we use singly linked list using "struct list_head"->prev (which is - * LIST_POISON2), we can avoid kmalloc(). - */ -static bool tomoyo_add_to_gc(const int type, struct list_head *element) -{ - struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); - if (!entry) - return false; - entry->type = type; - if (type == TOMOYO_ID_ACL) - entry->size = tomoyo_acl_size[ - container_of(element, - typeof(struct tomoyo_acl_info), - list)->type]; - else if (type == TOMOYO_ID_NAME) - entry->size = strlen(container_of(element, - typeof(struct tomoyo_name), - head.list)->entry.name) + 1; - else if (type == TOMOYO_ID_CONDITION) - entry->size = - container_of(element, typeof(struct tomoyo_condition), - head.list)->size; - else - entry->size = tomoyo_element_size[type]; - entry->element = element; - list_add(&entry->list, &tomoyo_gc_list); - list_del_rcu(element); - return tomoyo_gc_list_len++ < 128; -} - -/** - * tomoyo_element_linked_by_gc - Validate next element of an entry. - * - * @element: Pointer to an element. - * @size: Size of @element in byte. - * - * Returns true if @element is linked by other elements in the garbage - * collector's queue, false otherwise. - */ -static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size) -{ - struct tomoyo_gc *p; - list_for_each_entry(p, &tomoyo_gc_list, list) { - const u8 *ptr = (const u8 *) p->element->next; - if (ptr < element || element + size < ptr) - continue; - return true; - } - return false; -} - /** * tomoyo_del_transition_control - Delete members in "struct tomoyo_transition_control". * @@ -204,7 +88,7 @@ static bool tomoyo_element_linked_by_gc(const u8 *element, const size_t size) * * Returns nothing. */ -static void tomoyo_del_transition_control(struct list_head *element) +static inline void tomoyo_del_transition_control(struct list_head *element) { struct tomoyo_transition_control *ptr = container_of(element, typeof(*ptr), head.list); @@ -219,7 +103,7 @@ static void tomoyo_del_transition_control(struct list_head *element) * * Returns nothing. */ -static void tomoyo_del_aggregator(struct list_head *element) +static inline void tomoyo_del_aggregator(struct list_head *element) { struct tomoyo_aggregator *ptr = container_of(element, typeof(*ptr), head.list); @@ -234,7 +118,7 @@ static void tomoyo_del_aggregator(struct list_head *element) * * Returns nothing. */ -static void tomoyo_del_manager(struct list_head *element) +static inline void tomoyo_del_manager(struct list_head *element) { struct tomoyo_manager *ptr = container_of(element, typeof(*ptr), head.list); @@ -330,44 +214,24 @@ static void tomoyo_del_acl(struct list_head *element) * * @element: Pointer to "struct list_head". * - * Returns true if deleted, false otherwise. + * Returns nothing. */ -static bool tomoyo_del_domain(struct list_head *element) +static inline void tomoyo_del_domain(struct list_head *element) { struct tomoyo_domain_info *domain = container_of(element, typeof(*domain), list); struct tomoyo_acl_info *acl; struct tomoyo_acl_info *tmp; /* - * Since we don't protect whole execve() operation using SRCU, - * we need to recheck domain->users at this point. - * - * (1) Reader starts SRCU section upon execve(). - * (2) Reader traverses tomoyo_domain_list and finds this domain. - * (3) Writer marks this domain as deleted. - * (4) Garbage collector removes this domain from tomoyo_domain_list - * because this domain is marked as deleted and used by nobody. - * (5) Reader saves reference to this domain into - * "struct linux_binprm"->cred->security . - * (6) Reader finishes SRCU section, although execve() operation has - * not finished yet. - * (7) Garbage collector waits for SRCU synchronization. - * (8) Garbage collector kfree() this domain because this domain is - * used by nobody. - * (9) Reader finishes execve() operation and restores this domain from - * "struct linux_binprm"->cred->security. - * - * By updating domain->users at (5), we can solve this race problem - * by rechecking domain->users at (8). + * Since this domain is referenced from neither + * "struct tomoyo_io_buffer" nor "struct cred"->security, we can delete + * elements without checking for is_deleted flag. */ - if (atomic_read(&domain->users)) - return false; list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { tomoyo_del_acl(&acl->list); tomoyo_memory_free(acl); } tomoyo_put_name(domain->domainname); - return true; } /** @@ -416,10 +280,9 @@ void tomoyo_del_condition(struct list_head *element) * * Returns nothing. */ -static void tomoyo_del_name(struct list_head *element) +static inline void tomoyo_del_name(struct list_head *element) { - const struct tomoyo_name *ptr = - container_of(element, typeof(*ptr), head.list); + /* Nothing to do. */ } /** @@ -429,7 +292,7 @@ static void tomoyo_del_name(struct list_head *element) * * Returns nothing. */ -static void tomoyo_del_path_group(struct list_head *element) +static inline void tomoyo_del_path_group(struct list_head *element) { struct tomoyo_path_group *member = container_of(element, typeof(*member), head.list); @@ -443,7 +306,7 @@ static void tomoyo_del_path_group(struct list_head *element) * * Returns nothing. */ -static void tomoyo_del_group(struct list_head *element) +static inline void tomoyo_del_group(struct list_head *element) { struct tomoyo_group *group = container_of(element, typeof(*group), head.list); @@ -469,10 +332,109 @@ static inline void tomoyo_del_address_group(struct list_head *element) * * Returns nothing. */ -static void tomoyo_del_number_group(struct list_head *element) +static inline void tomoyo_del_number_group(struct list_head *element) { - struct tomoyo_number_group *member = - container_of(element, typeof(*member), head.list); + /* Nothing to do. */ +} + +/** + * tomoyo_try_to_gc - Try to kfree() an entry. + * + * @type: One of values in "enum tomoyo_policy_id". + * @element: Pointer to "struct list_head". + * + * Returns nothing. + * + * Caller holds tomoyo_policy_lock mutex. + */ +static void tomoyo_try_to_gc(const enum tomoyo_policy_id type, + struct list_head *element) +{ + /* + * __list_del_entry() guarantees that the list element became no longer + * reachable from the list which the element was originally on (e.g. + * tomoyo_domain_list). Also, synchronize_srcu() guarantees that the + * list element became no longer referenced by syscall users. + */ + __list_del_entry(element); + mutex_unlock(&tomoyo_policy_lock); + synchronize_srcu(&tomoyo_ss); + /* + * However, there are two users which may still be using the list + * element. We need to defer until both users forget this element. + * + * Don't kfree() until "struct tomoyo_io_buffer"->r.{domain,group,acl} + * and "struct tomoyo_io_buffer"->w.domain forget this element. + */ + if (tomoyo_struct_used_by_io_buffer(element)) + goto reinject; + switch (type) { + case TOMOYO_ID_TRANSITION_CONTROL: + tomoyo_del_transition_control(element); + break; + case TOMOYO_ID_MANAGER: + tomoyo_del_manager(element); + break; + case TOMOYO_ID_AGGREGATOR: + tomoyo_del_aggregator(element); + break; + case TOMOYO_ID_GROUP: + tomoyo_del_group(element); + break; + case TOMOYO_ID_PATH_GROUP: + tomoyo_del_path_group(element); + break; + case TOMOYO_ID_ADDRESS_GROUP: + tomoyo_del_address_group(element); + break; + case TOMOYO_ID_NUMBER_GROUP: + tomoyo_del_number_group(element); + break; + case TOMOYO_ID_CONDITION: + tomoyo_del_condition(element); + break; + case TOMOYO_ID_NAME: + /* + * Don't kfree() until all "struct tomoyo_io_buffer"->r.w[] + * forget this element. + */ + if (tomoyo_name_used_by_io_buffer + (container_of(element, typeof(struct tomoyo_name), + head.list)->entry.name)) + goto reinject; + tomoyo_del_name(element); + break; + case TOMOYO_ID_ACL: + tomoyo_del_acl(element); + break; + case TOMOYO_ID_DOMAIN: + /* + * Don't kfree() until all "struct cred"->security forget this + * element. + */ + if (atomic_read(&container_of + (element, typeof(struct tomoyo_domain_info), + list)->users)) + goto reinject; + tomoyo_del_domain(element); + break; + case TOMOYO_MAX_POLICY: + break; + } + mutex_lock(&tomoyo_policy_lock); + tomoyo_memory_free(element); + return; +reinject: + /* + * We can safely reinject this element here bacause + * (1) Appending list elements and removing list elements are protected + * by tomoyo_policy_lock mutex. + * (2) Only this function removes list elements and this function is + * exclusively executed by tomoyo_gc_mutex mutex. + * are true. + */ + mutex_lock(&tomoyo_policy_lock); + list_add_rcu(element, element->prev); } /** @@ -481,19 +443,19 @@ static void tomoyo_del_number_group(struct list_head *element) * @id: One of values in "enum tomoyo_policy_id". * @member_list: Pointer to "struct list_head". * - * Returns true if some elements are deleted, false otherwise. + * Returns nothing. */ -static bool tomoyo_collect_member(const enum tomoyo_policy_id id, +static void tomoyo_collect_member(const enum tomoyo_policy_id id, struct list_head *member_list) { struct tomoyo_acl_head *member; - list_for_each_entry(member, member_list, list) { + struct tomoyo_acl_head *tmp; + list_for_each_entry_safe(member, tmp, member_list, list) { if (!member->is_deleted) continue; - if (!tomoyo_add_to_gc(id, &member->list)) - return false; + member->is_deleted = TOMOYO_GC_IN_PROGRESS; + tomoyo_try_to_gc(id, &member->list); } - return true; } /** @@ -501,22 +463,22 @@ static bool tomoyo_collect_member(const enum tomoyo_policy_id id, * * @list: Pointer to "struct list_head". * - * Returns true if some elements are deleted, false otherwise. + * Returns nothing. */ -static bool tomoyo_collect_acl(struct list_head *list) +static void tomoyo_collect_acl(struct list_head *list) { struct tomoyo_acl_info *acl; - list_for_each_entry(acl, list, list) { + struct tomoyo_acl_info *tmp; + list_for_each_entry_safe(acl, tmp, list, list) { if (!acl->is_deleted) continue; - if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list)) - return false; + acl->is_deleted = TOMOYO_GC_IN_PROGRESS; + tomoyo_try_to_gc(TOMOYO_ID_ACL, &acl->list); } - return true; } /** - * tomoyo_collect_entry - Scan lists for deleted elements. + * tomoyo_collect_entry - Try to kfree() deleted elements. * * Returns nothing. */ @@ -525,36 +487,40 @@ static void tomoyo_collect_entry(void) int i; enum tomoyo_policy_id id; struct tomoyo_policy_namespace *ns; - int idx; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - return; - idx = tomoyo_read_lock(); + mutex_lock(&tomoyo_policy_lock); { struct tomoyo_domain_info *domain; - list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { - if (!tomoyo_collect_acl(&domain->acl_info_list)) - goto unlock; + struct tomoyo_domain_info *tmp; + list_for_each_entry_safe(domain, tmp, &tomoyo_domain_list, + list) { + tomoyo_collect_acl(&domain->acl_info_list); if (!domain->is_deleted || atomic_read(&domain->users)) continue; - /* - * Nobody is referring this domain. But somebody may - * refer this domain after successful execve(). - * We recheck domain->users after SRCU synchronization. - */ - if (!tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, &domain->list)) - goto unlock; + tomoyo_try_to_gc(TOMOYO_ID_DOMAIN, &domain->list); } } - list_for_each_entry_rcu(ns, &tomoyo_namespace_list, namespace_list) { + list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { for (id = 0; id < TOMOYO_MAX_POLICY; id++) - if (!tomoyo_collect_member(id, &ns->policy_list[id])) - goto unlock; + tomoyo_collect_member(id, &ns->policy_list[id]); for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++) - if (!tomoyo_collect_acl(&ns->acl_group[i])) - goto unlock; + tomoyo_collect_acl(&ns->acl_group[i]); + } + { + struct tomoyo_shared_acl_head *ptr; + struct tomoyo_shared_acl_head *tmp; + list_for_each_entry_safe(ptr, tmp, &tomoyo_condition_list, + list) { + if (atomic_read(&ptr->users) > 0) + continue; + atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS); + tomoyo_try_to_gc(TOMOYO_ID_CONDITION, &ptr->list); + } + } + list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { for (i = 0; i < TOMOYO_MAX_GROUP; i++) { struct list_head *list = &ns->group_list[i]; struct tomoyo_group *group; + struct tomoyo_group *tmp; switch (i) { case 0: id = TOMOYO_ID_PATH_GROUP; @@ -566,139 +532,37 @@ static void tomoyo_collect_entry(void) id = TOMOYO_ID_ADDRESS_GROUP; break; } - list_for_each_entry(group, list, head.list) { - if (!tomoyo_collect_member - (id, &group->member_list)) - goto unlock; + list_for_each_entry_safe(group, tmp, list, head.list) { + tomoyo_collect_member(id, &group->member_list); if (!list_empty(&group->member_list) || - atomic_read(&group->head.users)) + atomic_read(&group->head.users) > 0) continue; - if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, - &group->head.list)) - goto unlock; + atomic_set(&group->head.users, + TOMOYO_GC_IN_PROGRESS); + tomoyo_try_to_gc(TOMOYO_ID_GROUP, + &group->head.list); } } } - id = TOMOYO_ID_CONDITION; - for (i = 0; i < TOMOYO_MAX_HASH + 1; i++) { - struct list_head *list = !i ? - &tomoyo_condition_list : &tomoyo_name_list[i - 1]; + for (i = 0; i < TOMOYO_MAX_HASH; i++) { + struct list_head *list = &tomoyo_name_list[i]; struct tomoyo_shared_acl_head *ptr; - list_for_each_entry(ptr, list, list) { - if (atomic_read(&ptr->users)) + struct tomoyo_shared_acl_head *tmp; + list_for_each_entry_safe(ptr, tmp, list, list) { + if (atomic_read(&ptr->users) > 0) continue; - if (!tomoyo_add_to_gc(id, &ptr->list)) - goto unlock; + atomic_set(&ptr->users, TOMOYO_GC_IN_PROGRESS); + tomoyo_try_to_gc(TOMOYO_ID_NAME, &ptr->list); } - id = TOMOYO_ID_NAME; } -unlock: - tomoyo_read_unlock(idx); mutex_unlock(&tomoyo_policy_lock); } -/** - * tomoyo_kfree_entry - Delete entries in tomoyo_gc_list. - * - * Returns true if some entries were kfree()d, false otherwise. - */ -static bool tomoyo_kfree_entry(void) -{ - struct tomoyo_gc *p; - struct tomoyo_gc *tmp; - bool result = false; - - list_for_each_entry_safe(p, tmp, &tomoyo_gc_list, list) { - struct list_head *element = p->element; - - /* - * list_del_rcu() in tomoyo_add_to_gc() guarantees that the - * list element became no longer reachable from the list which - * the element was originally on (e.g. tomoyo_domain_list). - * Also, synchronize_srcu() in tomoyo_gc_thread() guarantees - * that the list element became no longer referenced by syscall - * users. - * - * However, there are three users which may still be using the - * list element. We need to defer until all of these users - * forget the list element. - * - * Firstly, defer until "struct tomoyo_io_buffer"->r.{domain, - * group,acl} and "struct tomoyo_io_buffer"->w.domain forget - * the list element. - */ - if (tomoyo_struct_used_by_io_buffer(element)) - continue; - /* - * Secondly, defer until all other elements in the - * tomoyo_gc_list list forget the list element. - */ - if (tomoyo_element_linked_by_gc((const u8 *) element, p->size)) - continue; - switch (p->type) { - case TOMOYO_ID_TRANSITION_CONTROL: - tomoyo_del_transition_control(element); - break; - case TOMOYO_ID_AGGREGATOR: - tomoyo_del_aggregator(element); - break; - case TOMOYO_ID_MANAGER: - tomoyo_del_manager(element); - break; - case TOMOYO_ID_CONDITION: - tomoyo_del_condition(element); - break; - case TOMOYO_ID_NAME: - /* - * Thirdly, defer until all "struct tomoyo_io_buffer" - * ->r.w[] forget the list element. - */ - if (tomoyo_name_used_by_io_buffer( - container_of(element, typeof(struct tomoyo_name), - head.list)->entry.name, p->size)) - continue; - tomoyo_del_name(element); - break; - case TOMOYO_ID_ACL: - tomoyo_del_acl(element); - break; - case TOMOYO_ID_DOMAIN: - if (!tomoyo_del_domain(element)) - continue; - break; - case TOMOYO_ID_PATH_GROUP: - tomoyo_del_path_group(element); - break; - case TOMOYO_ID_ADDRESS_GROUP: - tomoyo_del_address_group(element); - break; - case TOMOYO_ID_GROUP: - tomoyo_del_group(element); - break; - case TOMOYO_ID_NUMBER_GROUP: - tomoyo_del_number_group(element); - break; - case TOMOYO_MAX_POLICY: - break; - } - tomoyo_memory_free(element); - list_del(&p->list); - kfree(p); - tomoyo_gc_list_len--; - result = true; - } - return result; -} - /** * tomoyo_gc_thread - Garbage collector thread function. * * @unused: Unused. * - * In case OOM-killer choose this thread for termination, we create this thread - * as a short live thread whenever /sys/kernel/security/tomoyo/ interface was - * close()d. - * * Returns 0. */ static int tomoyo_gc_thread(void *unused) @@ -707,13 +571,7 @@ static int tomoyo_gc_thread(void *unused) static DEFINE_MUTEX(tomoyo_gc_mutex); if (!mutex_trylock(&tomoyo_gc_mutex)) goto out; - - do { - tomoyo_collect_entry(); - if (list_empty(&tomoyo_gc_list)) - break; - synchronize_srcu(&tomoyo_ss); - } while (tomoyo_kfree_entry()); + tomoyo_collect_entry(); { struct tomoyo_io_buffer *head; struct tomoyo_io_buffer *tmp; diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 7a56051146c2..277b9ade4408 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -123,7 +123,8 @@ struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param, goto out; list = ¶m->ns->group_list[idx]; list_for_each_entry(group, list, head.list) { - if (e.group_name != group->group_name) + if (e.group_name != group->group_name || + atomic_read(&group->head.users) == TOMOYO_GC_IN_PROGRESS) continue; atomic_inc(&group->head.users); found = true; @@ -175,7 +176,8 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name) if (mutex_lock_interruptible(&tomoyo_policy_lock)) return NULL; list_for_each_entry(ptr, head, head.list) { - if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) + if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name) || + atomic_read(&ptr->head.users) == TOMOYO_GC_IN_PROGRESS) continue; atomic_inc(&ptr->head.users); goto out; -- cgit v1.2.3 From a427fd14d3edf6396c4b9638dbc8e2972afaa05b Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sun, 25 Sep 2011 17:51:06 +0900 Subject: TOMOYO: Remove tomoyo_policy_memory_lock spinlock. tomoyo_policy_lock mutex already protects it. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.h | 1 - security/tomoyo/gc.c | 20 +++++++++++++++++++- security/tomoyo/memory.c | 33 ++++++++------------------------- 3 files changed, 27 insertions(+), 27 deletions(-) (limited to 'security/tomoyo') diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index a0212fbf60fb..ed311d7a8ce0 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -1043,7 +1043,6 @@ void tomoyo_fill_path_info(struct tomoyo_path_info *ptr); void tomoyo_get_attributes(struct tomoyo_obj_info *obj); void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns); void tomoyo_load_policy(const char *filename); -void tomoyo_memory_free(void *ptr); void tomoyo_normalize_line(unsigned char *buffer); void tomoyo_notify_gc(struct tomoyo_io_buffer *head, const bool is_register); void tomoyo_print_ip(char *buf, const unsigned int size, diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index f2295c65f1e4..c3214b32dbfb 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -8,6 +8,21 @@ #include #include +/** + * tomoyo_memory_free - Free memory for elements. + * + * @ptr: Pointer to allocated memory. + * + * Returns nothing. + * + * Caller holds tomoyo_policy_lock mutex. + */ +static inline void tomoyo_memory_free(void *ptr) +{ + tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= ksize(ptr); + kfree(ptr); +} + /* The list for "struct tomoyo_io_buffer". */ static LIST_HEAD(tomoyo_io_buffer_list); /* Lock for protecting tomoyo_io_buffer_list. */ @@ -215,6 +230,8 @@ static void tomoyo_del_acl(struct list_head *element) * @element: Pointer to "struct list_head". * * Returns nothing. + * + * Caller holds tomoyo_policy_lock mutex. */ static inline void tomoyo_del_domain(struct list_head *element) { @@ -416,12 +433,13 @@ static void tomoyo_try_to_gc(const enum tomoyo_policy_id type, (element, typeof(struct tomoyo_domain_info), list)->users)) goto reinject; - tomoyo_del_domain(element); break; case TOMOYO_MAX_POLICY: break; } mutex_lock(&tomoyo_policy_lock); + if (type == TOMOYO_ID_DOMAIN) + tomoyo_del_domain(element); tomoyo_memory_free(element); return; reinject: diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 277b9ade4408..0e995716cc25 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -27,8 +27,6 @@ void tomoyo_warn_oom(const char *function) panic("MAC Initialization failed.\n"); } -/* Lock for protecting tomoyo_memory_used. */ -static DEFINE_SPINLOCK(tomoyo_policy_memory_lock); /* Memoy currently used by policy/audit log/query. */ unsigned int tomoyo_memory_used[TOMOYO_MAX_MEMORY_STAT]; /* Memory quota for "policy"/"audit log"/"query". */ @@ -42,22 +40,19 @@ unsigned int tomoyo_memory_quota[TOMOYO_MAX_MEMORY_STAT]; * Returns true on success, false otherwise. * * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. + * + * Caller holds tomoyo_policy_lock mutex. */ bool tomoyo_memory_ok(void *ptr) { if (ptr) { const size_t s = ksize(ptr); - bool result; - spin_lock(&tomoyo_policy_memory_lock); tomoyo_memory_used[TOMOYO_MEMORY_POLICY] += s; - result = !tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] || - tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <= - tomoyo_memory_quota[TOMOYO_MEMORY_POLICY]; - if (!result) - tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s; - spin_unlock(&tomoyo_policy_memory_lock); - if (result) + if (!tomoyo_memory_quota[TOMOYO_MEMORY_POLICY] || + tomoyo_memory_used[TOMOYO_MEMORY_POLICY] <= + tomoyo_memory_quota[TOMOYO_MEMORY_POLICY]) return true; + tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s; } tomoyo_warn_oom(__func__); return false; @@ -71,6 +66,8 @@ bool tomoyo_memory_ok(void *ptr) * * Returns pointer to allocated memory on success, NULL otherwise. * @data is zero-cleared on success. + * + * Caller holds tomoyo_policy_lock mutex. */ void *tomoyo_commit_ok(void *data, const unsigned int size) { @@ -84,20 +81,6 @@ void *tomoyo_commit_ok(void *data, const unsigned int size) return NULL; } -/** - * tomoyo_memory_free - Free memory for elements. - * - * @ptr: Pointer to allocated memory. - */ -void tomoyo_memory_free(void *ptr) -{ - size_t s = ksize(ptr); - spin_lock(&tomoyo_policy_memory_lock); - tomoyo_memory_used[TOMOYO_MEMORY_POLICY] -= s; - spin_unlock(&tomoyo_policy_memory_lock); - kfree(ptr); -} - /** * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group". * -- cgit v1.2.3 From e00fb3f7af111d1b3252f7d622213d2e22be65f5 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 27 Sep 2011 11:48:53 +0900 Subject: TOMOYO: Fix domain transition failure warning. Commit bd03a3e4 "TOMOYO: Add policy namespace support." introduced policy namespace. But as of /sbin/modprobe is executed from initramfs/initrd, profiles for target domain's namespace is not defined because /sbin/tomoyo-init is not yet called. Reported-by: Jamie Nguyen Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/domain.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'security/tomoyo') diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index da16dfeed728..9027ac1534af 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -515,7 +515,8 @@ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, * that domain. Do not perform domain transition if * profile for that domain is not yet created. */ - if (!entry->ns->profile_ptr[entry->profile]) + if (tomoyo_policy_loaded && + !entry->ns->profile_ptr[entry->profile]) return NULL; } return entry; -- cgit v1.2.3 From e2b8b25a6795488eba7bb757706b3ac725c31fac Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 11 Oct 2011 14:05:08 +0900 Subject: TOMOYO: Remove redundant tasklist_lock. rcu_read_lock() is sufficient for calling find_task_by_pid_ns()/find_task_by_vpid(). Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'security/tomoyo') diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 2e2802060eef..365f3bddee79 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -984,14 +984,12 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { struct task_struct *p; rcu_read_lock(); - read_lock(&tasklist_lock); if (global_pid) p = find_task_by_pid_ns(pid, &init_pid_ns); else p = find_task_by_vpid(pid); if (p) domain = tomoyo_real_domain(p); - read_unlock(&tasklist_lock); rcu_read_unlock(); } else if (!strncmp(data, "domain=", 7)) { if (tomoyo_domain_def(data + 7)) @@ -1664,14 +1662,12 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head) global_pid = true; pid = (unsigned int) simple_strtoul(buf, NULL, 10); rcu_read_lock(); - read_lock(&tasklist_lock); if (global_pid) p = find_task_by_pid_ns(pid, &init_pid_ns); else p = find_task_by_vpid(pid); if (p) domain = tomoyo_real_domain(p); - read_unlock(&tasklist_lock); rcu_read_unlock(); if (!domain) return; -- cgit v1.2.3 From 545a7260343bbaf11c7f1a4b8c3d9660bb9266e5 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 11 Oct 2011 14:06:41 +0900 Subject: TOMOYO: Fix quota and garbage collector. Commit 059d84db "TOMOYO: Add socket operation restriction support" and commit 731d37aa "TOMOYO: Allow domain transition without execve()." forgot to update tomoyo_domain_quota_is_ok() and tomoyo_del_acl() which results in incorrect quota counting and memory leak. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/gc.c | 7 +++++++ security/tomoyo/util.c | 11 +++++++++++ 2 files changed, 18 insertions(+) (limited to 'security/tomoyo') diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index c3214b32dbfb..986a6a756868 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -221,6 +221,13 @@ static void tomoyo_del_acl(struct list_head *element) tomoyo_put_name_union(&entry->name); } break; + case TOMOYO_TYPE_MANUAL_TASK_ACL: + { + struct tomoyo_task_acl *entry = + container_of(acl, typeof(*entry), head); + tomoyo_put_name(entry->domainname); + } + break; } } diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 50e9b4c73ceb..4a9b4b2eb755 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -1057,6 +1057,17 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) perm = container_of(ptr, struct tomoyo_mkdev_acl, head)->perm; break; + case TOMOYO_TYPE_INET_ACL: + perm = container_of(ptr, struct tomoyo_inet_acl, + head)->perm; + break; + case TOMOYO_TYPE_UNIX_ACL: + perm = container_of(ptr, struct tomoyo_unix_acl, + head)->perm; + break; + case TOMOYO_TYPE_MANUAL_TASK_ACL: + perm = 0; + break; default: perm = 1; } -- cgit v1.2.3 From 6afcb3b7393f5aa388a0d077c490ed411ab3cd27 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sun, 16 Oct 2011 09:43:46 +0900 Subject: TOMOYO: Fix unused kernel config option. CONFIG_SECURITY_TOMOYO_MAX_{ACCEPT_ENTRY,AUDIT_LOG} introduced by commit 0e4ae0e0 "TOMOYO: Make several options configurable." were by error not used. Reported-by: Paul Bolle Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'security/tomoyo') diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 365f3bddee79..96b7233a0df6 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -502,8 +502,10 @@ static struct tomoyo_profile *tomoyo_assign_profile TOMOYO_CONFIG_WANT_REJECT_LOG; memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT, sizeof(ptr->config)); - ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = 1024; - ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = 2048; + ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = + CONFIG_SECURITY_TOMOYO_MAX_AUDIT_LOG; + ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = + CONFIG_SECURITY_TOMOYO_MAX_ACCEPT_ENTRY; mb(); /* Avoid out-of-order execution. */ ns->profile_ptr[profile] = ptr; entry = NULL; -- cgit v1.2.3 From e0b057b406a33501a656dc8d67ea945d7bcdad61 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Fri, 21 Oct 2011 12:37:13 +0900 Subject: TOMOYO: Fix incomplete read after seek. Commit f23571e8 "TOMOYO: Copy directly to userspace buffer." introduced tomoyo_flush() that flushes data to be read as soon as possible. tomoyo_select_domain() (which is called by write()) enqueues data which meant to be read by next read(), but previous read()'s read buffer's size was not cleared. As a result, since 2.6.36, sequence like char *cp = "select global-pid=1\n"; read(fd, buf1, sizeof(buf1)); write(fd, cp, strlen(cp)); read(fd, buf2, sizeof(buf2)); causes enqueued data to be flushed to buf1 rather than buf2. Fix this bug by clearing read buffer's size upon write() request. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 1 + 1 file changed, 1 insertion(+) (limited to 'security/tomoyo') diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 96b7233a0df6..d41900de8a69 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -2591,6 +2591,7 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, return -EFAULT; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; + head->read_user_buf_avail = 0; idx = tomoyo_read_lock(); /* Read a line and dispatch it to the policy handler. */ while (avail_len > 0) { -- cgit v1.2.3