diff --git a/security/tomoyo/audit.c b/security/tomoyo/audit.c index e882f17065f2..ef2172f29583 100644 --- a/security/tomoyo/audit.c +++ b/security/tomoyo/audit.c @@ -151,13 +151,15 @@ static unsigned int tomoyo_log_count; /** * tomoyo_get_audit - Get audit mode. * + * @ns: Pointer to "struct tomoyo_policy_namespace". * @profile: Profile number. * @index: Index number of functionality. * @is_granted: True if granted log, false otherwise. * * Returns true if this request should be audited, false otherwise. */ -static bool tomoyo_get_audit(const u8 profile, const u8 index, +static bool tomoyo_get_audit(const struct tomoyo_policy_namespace *ns, + const u8 profile, const u8 index, const bool is_granted) { u8 mode; @@ -165,7 +167,7 @@ static bool tomoyo_get_audit(const u8 profile, const u8 index, struct tomoyo_profile *p; if (!tomoyo_policy_loaded) return false; - p = tomoyo_profile(profile); + p = tomoyo_profile(ns, profile); if (tomoyo_log_count >= p->pref[TOMOYO_PREF_MAX_AUDIT_LOG]) return false; mode = p->config[index]; @@ -194,7 +196,7 @@ 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->profile, r->type, r->granted)) + if (!tomoyo_get_audit(r->domain->ns, r->profile, r->type, 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 507ebf01e43b..50481d2cf970 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -11,12 +11,6 @@ #include #include "common.h" -/* Profile version. Currently only 20090903 is defined. */ -static unsigned int tomoyo_profile_version; - -/* Profile table. Memory is allocated as needed. */ -static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; - /* String table for operation mode. */ const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = { [TOMOYO_CONFIG_DISABLED] = "disabled", @@ -216,6 +210,50 @@ static void tomoyo_set_slash(struct tomoyo_io_buffer *head) tomoyo_set_string(head, "/"); } +/* List of namespaces. */ +LIST_HEAD(tomoyo_namespace_list); +/* True if namespace other than tomoyo_kernel_namespace is defined. */ +static bool tomoyo_namespace_enabled; + +/** + * tomoyo_init_policy_namespace - Initialize namespace. + * + * @ns: Pointer to "struct tomoyo_policy_namespace". + * + * Returns nothing. + */ +void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns) +{ + unsigned int idx; + for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++) + INIT_LIST_HEAD(&ns->acl_group[idx]); + for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++) + 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; + tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list); + list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list); +} + +/** + * tomoyo_print_namespace - Print namespace header. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static void tomoyo_print_namespace(struct tomoyo_io_buffer *head) +{ + if (!tomoyo_namespace_enabled) + return; + tomoyo_set_string(head, + container_of(head->r.ns, + struct tomoyo_policy_namespace, + namespace_list)->name); + tomoyo_set_space(head); +} + /** * tomoyo_print_name_union - Print a tomoyo_name_union. * @@ -283,23 +321,25 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, /** * tomoyo_assign_profile - Create a new profile. * + * @ns: Pointer to "struct tomoyo_policy_namespace". * @profile: Profile number to create. * * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise. */ -static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile) +static struct tomoyo_profile *tomoyo_assign_profile +(struct tomoyo_policy_namespace *ns, const unsigned int profile) { struct tomoyo_profile *ptr; struct tomoyo_profile *entry; if (profile >= TOMOYO_MAX_PROFILES) return NULL; - ptr = tomoyo_profile_ptr[profile]; + ptr = ns->profile_ptr[profile]; if (ptr) return ptr; entry = kzalloc(sizeof(*entry), GFP_NOFS); if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - ptr = tomoyo_profile_ptr[profile]; + ptr = ns->profile_ptr[profile]; if (!ptr && tomoyo_memory_ok(entry)) { ptr = entry; ptr->default_config = TOMOYO_CONFIG_DISABLED | @@ -310,7 +350,7 @@ static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile) ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = 1024; ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = 2048; mb(); /* Avoid out-of-order execution. */ - tomoyo_profile_ptr[profile] = ptr; + ns->profile_ptr[profile] = ptr; entry = NULL; } mutex_unlock(&tomoyo_policy_lock); @@ -322,14 +362,16 @@ static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile) /** * tomoyo_profile - Find a profile. * + * @ns: Pointer to "struct tomoyo_policy_namespace". * @profile: Profile number to find. * * Returns pointer to "struct tomoyo_profile". */ -struct tomoyo_profile *tomoyo_profile(const u8 profile) +struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns, + const u8 profile) { static struct tomoyo_profile tomoyo_null_profile; - struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile]; + struct tomoyo_profile *ptr = ns->profile_ptr[profile]; if (!ptr) ptr = &tomoyo_null_profile; return ptr; @@ -454,13 +496,14 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) unsigned int i; char *cp; struct tomoyo_profile *profile; - if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1) + if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->profile_version) + == 1) return 0; i = simple_strtoul(data, &cp, 10); if (*cp != '-') return -EINVAL; data = cp + 1; - profile = tomoyo_assign_profile(i); + profile = tomoyo_assign_profile(head->w.ns, i); if (!profile) return -EINVAL; cp = strchr(data, '='); @@ -518,19 +561,25 @@ static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config) static void tomoyo_read_profile(struct tomoyo_io_buffer *head) { u8 index; + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); const struct tomoyo_profile *profile; + if (head->r.eof) + return; next: index = head->r.index; - profile = tomoyo_profile_ptr[index]; + profile = ns->profile_ptr[index]; switch (head->r.step) { case 0: - tomoyo_io_printf(head, "PROFILE_VERSION=%u\n", 20090903); + tomoyo_print_namespace(head); + tomoyo_io_printf(head, "PROFILE_VERSION=%u\n", + ns->profile_version); head->r.step++; break; case 1: for ( ; head->r.index < TOMOYO_MAX_PROFILES; head->r.index++) - if (tomoyo_profile_ptr[head->r.index]) + if (ns->profile_ptr[head->r.index]) break; if (head->r.index == TOMOYO_MAX_PROFILES) return; @@ -541,6 +590,7 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) u8 i; const struct tomoyo_path_info *comment = profile->comment; + tomoyo_print_namespace(head); tomoyo_io_printf(head, "%u-COMMENT=", index); tomoyo_set_string(head, comment ? comment->name : ""); tomoyo_set_lf(head); @@ -555,6 +605,7 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) break; case 3: { + tomoyo_print_namespace(head); tomoyo_io_printf(head, "%u-%s", index, "CONFIG"); tomoyo_print_config(head, profile->default_config); head->r.bit = 0; @@ -568,6 +619,7 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) const u8 config = profile->config[i]; if (config == TOMOYO_CONFIG_USE_DEFAULT) continue; + tomoyo_print_namespace(head); tomoyo_io_printf(head, "%u-%s%s", index, "CONFIG::", tomoyo_mac_keywords[i]); tomoyo_print_config(head, config); @@ -607,8 +659,10 @@ static int tomoyo_update_manager_entry(const char *manager, { struct tomoyo_manager e = { }; struct tomoyo_acl_param param = { + /* .ns = &tomoyo_kernel_namespace, */ .is_delete = is_delete, - .list = &tomoyo_policy_list[TOMOYO_ID_MANAGER], + .list = &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER], }; int error = is_delete ? -ENOENT : -ENOMEM; if (tomoyo_domain_def(manager)) { @@ -640,13 +694,12 @@ static int tomoyo_update_manager_entry(const char *manager, static int tomoyo_write_manager(struct tomoyo_io_buffer *head) { char *data = head->write_buf; - bool is_delete = tomoyo_str_starts(&data, "delete "); if (!strcmp(data, "manage_by_non_root")) { - tomoyo_manage_by_non_root = !is_delete; + tomoyo_manage_by_non_root = !head->w.is_delete; return 0; } - return tomoyo_update_manager_entry(data, is_delete); + return tomoyo_update_manager_entry(data, head->w.is_delete); } /** @@ -660,8 +713,8 @@ static void tomoyo_read_manager(struct tomoyo_io_buffer *head) { if (head->r.eof) return; - list_for_each_cookie(head->r.acl, - &tomoyo_policy_list[TOMOYO_ID_MANAGER]) { + list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER]) { struct tomoyo_manager *ptr = list_entry(head->r.acl, typeof(*ptr), head.list); if (ptr->head.is_deleted) @@ -694,8 +747,8 @@ static bool tomoyo_manager(void) return true; if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid)) return false; - list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], - head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER], head.list) { if (!ptr->head.is_deleted && ptr->is_domain && !tomoyo_pathcmp(domainname, ptr->manager)) { found = true; @@ -707,8 +760,8 @@ static bool tomoyo_manager(void) exe = tomoyo_get_exe(); if (!exe) return false; - list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], - head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. + policy_list[TOMOYO_ID_MANAGER], head.list) { if (!ptr->head.is_deleted && !ptr->is_domain && !strcmp(exe, ptr->manager->name)) { found = true; @@ -729,7 +782,7 @@ static bool tomoyo_manager(void) } /** - * tomoyo_select_one - Parse select command. + * tomoyo_select_domain - Parse select command. * * @head: Pointer to "struct tomoyo_io_buffer". * @data: String to parse. @@ -738,16 +791,15 @@ static bool tomoyo_manager(void) * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) +static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, + const char *data) { unsigned int pid; struct tomoyo_domain_info *domain = NULL; bool global_pid = false; - - if (!strcmp(data, "allow_execute")) { - head->r.print_execute_only = true; - return true; - } + if (strncmp(data, "select ", 7)) + return false; + data += 7; if (sscanf(data, "pid=%u", &pid) == 1 || (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { struct task_struct *p; @@ -818,6 +870,7 @@ static int tomoyo_delete_domain(char *domainname) /** * tomoyo_write_domain2 - Write domain policy. * + * @ns: Pointer to "struct tomoyo_policy_namespace". * @list: Pointer to "struct list_head". * @data: Policy to be interpreted. * @is_delete: True if it is a delete request. @@ -826,10 +879,12 @@ static int tomoyo_delete_domain(char *domainname) * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_write_domain2(struct list_head *list, char *data, +static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns, + struct list_head *list, char *data, const bool is_delete) { struct tomoyo_acl_param param = { + .ns = ns, .list = list, .data = data, .is_delete = is_delete, @@ -862,37 +917,28 @@ static int tomoyo_write_domain2(struct list_head *list, char *data, static int tomoyo_write_domain(struct tomoyo_io_buffer *head) { char *data = head->write_buf; + struct tomoyo_policy_namespace *ns; struct tomoyo_domain_info *domain = head->w.domain; - bool is_delete = false; - bool is_select = false; + const bool is_delete = head->w.is_delete; + bool is_select = !is_delete && tomoyo_str_starts(&data, "select "); unsigned int profile; - - if (tomoyo_str_starts(&data, "delete ")) - is_delete = true; - else if (tomoyo_str_starts(&data, "select ")) - is_select = true; - if (is_select && tomoyo_select_one(head, data)) - return 0; - /* Don't allow updating policies by non manager programs. */ - if (!tomoyo_manager()) - return -EPERM; - if (tomoyo_domain_def(data)) { + if (*data == '<') { domain = NULL; if (is_delete) tomoyo_delete_domain(data); else if (is_select) domain = tomoyo_find_domain(data); else - domain = tomoyo_assign_domain(data, 0); + domain = tomoyo_assign_domain(data, false); head->w.domain = domain; return 0; } if (!domain) return -EINVAL; - + ns = domain->ns; if (sscanf(data, "use_profile %u", &profile) == 1 && profile < TOMOYO_MAX_PROFILES) { - if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded) + if (!tomoyo_policy_loaded || ns->profile_ptr[profile]) domain->profile = (u8) profile; return 0; } @@ -910,7 +956,8 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head) domain->transition_failed = !is_delete; return 0; } - return tomoyo_write_domain2(&domain->acl_info_list, data, is_delete); + return tomoyo_write_domain2(ns, &domain->acl_info_list, data, + is_delete); } /** @@ -924,9 +971,11 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head) static void tomoyo_set_group(struct tomoyo_io_buffer *head, const char *category) { - if (head->type == TOMOYO_EXCEPTIONPOLICY) + if (head->type == TOMOYO_EXCEPTIONPOLICY) { + tomoyo_print_namespace(head); tomoyo_io_printf(head, "acl_group %u ", head->r.acl_group_index); + } tomoyo_set_string(head, category); } @@ -956,7 +1005,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, for (bit = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; - if (head->r.print_execute_only && + if (head->r.print_transition_related_only && bit != TOMOYO_TYPE_EXECUTE) continue; if (first) { @@ -970,7 +1019,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, if (first) return true; tomoyo_print_name_union(head, &ptr->name); - } else if (head->r.print_execute_only) { + } else if (head->r.print_transition_related_only) { return true; } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) { struct tomoyo_path2_acl *ptr = @@ -1147,8 +1196,8 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) domain = tomoyo_find_domain(cp + 1); if (strict_strtoul(data, 10, &profile)) return -EINVAL; - if (domain && profile < TOMOYO_MAX_PROFILES - && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)) + if (domain && (!tomoyo_policy_loaded || + head->w.ns->profile_ptr[(u8) profile])) domain->profile = (u8) profile; return 0; } @@ -1246,10 +1295,12 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head) } static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = { - [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain", - [TOMOYO_TRANSITION_CONTROL_INITIALIZE] = "initialize_domain", - [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = "no_keep_domain", - [TOMOYO_TRANSITION_CONTROL_KEEP] = "keep_domain", + [TOMOYO_TRANSITION_CONTROL_NO_RESET] = "no_reset_domain ", + [TOMOYO_TRANSITION_CONTROL_RESET] = "reset_domain ", + [TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ", + [TOMOYO_TRANSITION_CONTROL_INITIALIZE] = "initialize_domain ", + [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = "no_keep_domain ", + [TOMOYO_TRANSITION_CONTROL_KEEP] = "keep_domain ", }; static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { @@ -1268,19 +1319,13 @@ static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { */ static int tomoyo_write_exception(struct tomoyo_io_buffer *head) { + const bool is_delete = head->w.is_delete; struct tomoyo_acl_param param = { + .ns = head->w.ns, + .is_delete = is_delete, .data = head->write_buf, }; u8 i; - param.is_delete = tomoyo_str_starts(¶m.data, "delete "); - if (!param.is_delete && tomoyo_str_starts(¶m.data, "select ") && - !strcmp(param.data, "execute_only")) { - head->r.print_execute_only = true; - return 0; - } - /* Don't allow updating policies by non manager programs. */ - if (!tomoyo_manager()) - return -EPERM; if (tomoyo_str_starts(¶m.data, "aggregator ")) return tomoyo_write_aggregator(¶m); for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++) @@ -1294,8 +1339,9 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head) char *data; group = simple_strtoul(param.data, &data, 10); if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ') - return tomoyo_write_domain2(&tomoyo_acl_group[group], - data, param.is_delete); + return tomoyo_write_domain2 + (head->w.ns, &head->w.ns->acl_group[group], + data, is_delete); } return -EINVAL; } @@ -1312,7 +1358,10 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head) */ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) { - list_for_each_cookie(head->r.group, &tomoyo_group_list[idx]) { + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); + struct list_head *list = &ns->group_list[idx]; + list_for_each_cookie(head->r.group, list) { struct tomoyo_group *group = list_entry(head->r.group, typeof(*group), head.list); list_for_each_cookie(head->r.acl, &group->member_list) { @@ -1322,6 +1371,7 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) continue; if (!tomoyo_flush(head)) return false; + tomoyo_print_namespace(head); tomoyo_set_string(head, tomoyo_group_name[idx]); tomoyo_set_string(head, group->group_name->name); if (idx == TOMOYO_PATH_GROUP) { @@ -1355,7 +1405,10 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) */ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) { - list_for_each_cookie(head->r.acl, &tomoyo_policy_list[idx]) { + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); + struct list_head *list = &ns->policy_list[idx]; + list_for_each_cookie(head->r.acl, list) { struct tomoyo_acl_head *acl = container_of(head->r.acl, typeof(*acl), list); if (acl->is_deleted) @@ -1367,6 +1420,7 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) { struct tomoyo_transition_control *ptr = container_of(acl, typeof(*ptr), head); + tomoyo_print_namespace(head); tomoyo_set_string(head, tomoyo_transition_type [ptr->type]); tomoyo_set_string(head, ptr->program ? @@ -1381,6 +1435,7 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) { struct tomoyo_aggregator *ptr = container_of(acl, typeof(*ptr), head); + tomoyo_print_namespace(head); tomoyo_set_string(head, "aggregator "); tomoyo_set_string(head, ptr->original_name->name); @@ -1407,6 +1462,8 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) */ static void tomoyo_read_exception(struct tomoyo_io_buffer *head) { + struct tomoyo_policy_namespace *ns = + container_of(head->r.ns, typeof(*ns), namespace_list); if (head->r.eof) return; while (head->r.step < TOMOYO_MAX_POLICY && @@ -1423,7 +1480,7 @@ static void tomoyo_read_exception(struct tomoyo_io_buffer *head) + TOMOYO_MAX_ACL_GROUPS) { head->r.acl_group_index = head->r.step - TOMOYO_MAX_POLICY - TOMOYO_MAX_GROUP; - if (!tomoyo_read_domain2(head, &tomoyo_acl_group + if (!tomoyo_read_domain2(head, &ns->acl_group [head->r.acl_group_index])) return; head->r.step++; @@ -1484,7 +1541,8 @@ static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header) return; snprintf(buffer, len - 1, "%s", cp); tomoyo_normalize_line(buffer); - tomoyo_write_domain2(&domain->acl_info_list, buffer, false); + tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer, + false); kfree(buffer); } @@ -1895,6 +1953,45 @@ int tomoyo_poll_control(struct file *file, poll_table *wait) return head->poll(file, wait); } +/** + * tomoyo_set_namespace_cursor - Set namespace to read. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head) +{ + struct list_head *ns; + if (head->type != TOMOYO_EXCEPTIONPOLICY && + head->type != TOMOYO_PROFILE) + return; + /* + * If this is the first read, or reading previous namespace finished + * and has more namespaces to read, update the namespace cursor. + */ + ns = head->r.ns; + if (!ns || (head->r.eof && ns->next != &tomoyo_namespace_list)) { + /* Clearing is OK because tomoyo_flush() returned true. */ + memset(&head->r, 0, sizeof(head->r)); + head->r.ns = ns ? ns->next : tomoyo_namespace_list.next; + } +} + +/** + * tomoyo_has_more_namespace - Check for unread namespaces. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns true if we have more entries to print, false otherwise. + */ +static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head) +{ + return (head->type == TOMOYO_EXCEPTIONPOLICY || + head->type == TOMOYO_PROFILE) && head->r.eof && + head->r.ns->next != &tomoyo_namespace_list; +} + /** * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. * @@ -1919,13 +2016,53 @@ int tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, head->read_user_buf_avail = buffer_len; if (tomoyo_flush(head)) /* Call the policy handler. */ - head->read(head); - tomoyo_flush(head); + do { + tomoyo_set_namespace_cursor(head); + head->read(head); + } while (tomoyo_flush(head) && + tomoyo_has_more_namespace(head)); len = head->read_user_buf - buffer; mutex_unlock(&head->io_sem); return len; } +/** + * tomoyo_parse_policy - Parse a policy line. + * + * @head: Poiter to "struct tomoyo_io_buffer". + * @line: Line to parse. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line) +{ + /* Delete request? */ + head->w.is_delete = !strncmp(line, "delete ", 7); + if (head->w.is_delete) + memmove(line, line + 7, strlen(line + 7) + 1); + /* Selecting namespace to update. */ + if (head->type == TOMOYO_EXCEPTIONPOLICY || + head->type == TOMOYO_PROFILE) { + if (*line == '<') { + char *cp = strchr(line, ' '); + if (cp) { + *cp++ = '\0'; + head->w.ns = tomoyo_assign_namespace(line); + memmove(line, cp, strlen(cp) + 1); + } else + head->w.ns = NULL; + } else + head->w.ns = &tomoyo_kernel_namespace; + /* Don't allow updating if namespace is invalid. */ + if (!head->w.ns) + return -ENOENT; + } + /* Do the update. */ + return head->write(head); +} + /** * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface. * @@ -1941,27 +2078,31 @@ int tomoyo_write_control(struct tomoyo_io_buffer *head, const char __user *buffer, const int buffer_len) { int error = buffer_len; - int avail_len = buffer_len; + size_t avail_len = buffer_len; char *cp0 = head->write_buf; - if (!head->write) return -ENOSYS; if (!access_ok(VERIFY_READ, buffer, buffer_len)) return -EFAULT; - /* Don't allow updating policies by non manager programs. */ - if (head->write != tomoyo_write_pid && - head->write != tomoyo_write_domain && - head->write != tomoyo_write_exception && !tomoyo_manager()) - return -EPERM; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; /* Read a line and dispatch it to the policy handler. */ while (avail_len > 0) { char c; if (head->w.avail >= head->writebuf_size - 1) { - error = -ENOMEM; - break; - } else if (get_user(c, buffer)) { + const int len = head->writebuf_size * 2; + char *cp = kzalloc(len, GFP_NOFS); + if (!cp) { + error = -ENOMEM; + break; + } + memmove(cp, cp0, head->w.avail); + kfree(cp0); + head->write_buf = cp; + cp0 = cp; + head->writebuf_size = len; + } + if (get_user(c, buffer)) { error = -EFAULT; break; } @@ -1973,8 +2114,40 @@ int tomoyo_write_control(struct tomoyo_io_buffer *head, cp0[head->w.avail - 1] = '\0'; head->w.avail = 0; tomoyo_normalize_line(cp0); - head->write(head); + if (!strcmp(cp0, "reset")) { + head->w.ns = &tomoyo_kernel_namespace; + head->w.domain = NULL; + memset(&head->r, 0, sizeof(head->r)); + continue; + } + /* Don't allow updating policies by non manager programs. */ + switch (head->type) { + case TOMOYO_PROCESS_STATUS: + /* This does not write anything. */ + break; + case TOMOYO_DOMAINPOLICY: + if (tomoyo_select_domain(head, cp0)) + continue; + /* fall through */ + case TOMOYO_EXCEPTIONPOLICY: + if (!strcmp(cp0, "select transition_only")) { + head->r.print_transition_related_only = true; + continue; + } + /* fall through */ + default: + if (!tomoyo_manager()) { + error = -EPERM; + goto out; + } + } + switch (tomoyo_parse_policy(head, cp0)) { + case -EPERM: + error = -EPERM; + goto out; + } } +out: mutex_unlock(&head->io_sem); return error; } @@ -2019,27 +2192,27 @@ void tomoyo_check_profile(void) struct tomoyo_domain_info *domain; const int idx = tomoyo_read_lock(); tomoyo_policy_loaded = true; - /* Check all profiles currently assigned to domains are defined. */ + printk(KERN_INFO "TOMOYO: 2.4.0\n"); list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { const u8 profile = domain->profile; - if (tomoyo_profile_ptr[profile]) + const struct tomoyo_policy_namespace *ns = domain->ns; + if (ns->profile_version != 20100903) + printk(KERN_ERR + "Profile version %u is not supported.\n", + ns->profile_version); + else if (!ns->profile_ptr[profile]) + printk(KERN_ERR + "Profile %u (used by '%s') is not defined.\n", + profile, domain->domainname->name); + else continue; - printk(KERN_ERR "You need to define profile %u before using it.\n", - profile); - printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ " + printk(KERN_ERR + "Userland tools for TOMOYO 2.4 must be installed and " + "policy must be initialized.\n"); + printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ " "for more information.\n"); - panic("Profile %u (used by '%s') not defined.\n", - profile, domain->domainname->name); + panic("STOP!"); } tomoyo_read_unlock(idx); - if (tomoyo_profile_version != 20090903) { - printk(KERN_ERR "You need to install userland programs for " - "TOMOYO 2.3 and initialize policy configuration.\n"); - printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ " - "for more information.\n"); - panic("Profile version %u is not supported.\n", - tomoyo_profile_version); - } - printk(KERN_INFO "TOMOYO: 2.3.0\n"); printk(KERN_INFO "Mandatory Access Control activated.\n"); } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 4bc3975516cb..53c8798e38b7 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -74,10 +74,6 @@ enum tomoyo_group_id { TOMOYO_MAX_GROUP }; -/* A domain definition starts with . */ -#define TOMOYO_ROOT_NAME "" -#define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1) - /* Index numbers for type of numeric values. */ enum tomoyo_value_type { TOMOYO_VALUE_TYPE_INVALID, @@ -89,6 +85,8 @@ enum tomoyo_value_type { /* Index numbers for domain transition control keywords. */ enum tomoyo_transition_type { /* Do not change this order, */ + TOMOYO_TRANSITION_CONTROL_NO_RESET, + TOMOYO_TRANSITION_CONTROL_RESET, TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE, TOMOYO_TRANSITION_CONTROL_INITIALIZE, TOMOYO_TRANSITION_CONTROL_NO_KEEP, @@ -246,6 +244,8 @@ struct tomoyo_shared_acl_head { atomic_t users; } __packed; +struct tomoyo_policy_namespace; + /* Structure for request info. */ struct tomoyo_request_info { struct tomoyo_domain_info *domain; @@ -359,6 +359,8 @@ struct tomoyo_domain_info { struct list_head acl_info_list; /* Name of this domain. Never NULL. */ const struct tomoyo_path_info *domainname; + /* Namespace for this domain. Never NULL. */ + struct tomoyo_policy_namespace *ns; u8 profile; /* Profile number to use. */ u8 group; /* Group number to use. */ bool is_deleted; /* Delete flag. */ @@ -423,6 +425,7 @@ struct tomoyo_mount_acl { struct tomoyo_acl_param { char *data; struct list_head *list; + struct tomoyo_policy_namespace *ns; bool is_delete; }; @@ -443,6 +446,7 @@ struct tomoyo_io_buffer { char __user *read_user_buf; int read_user_buf_avail; struct { + struct list_head *ns; struct list_head *domain; struct list_head *group; struct list_head *acl; @@ -455,14 +459,16 @@ struct tomoyo_io_buffer { u8 w_pos; bool eof; bool print_this_domain_only; - bool print_execute_only; + bool print_transition_related_only; const char *w[TOMOYO_MAX_IO_READ_QUEUE]; } r; struct { + struct tomoyo_policy_namespace *ns; /* The position currently writing to. */ struct tomoyo_domain_info *domain; /* Bytes available for writing. */ int avail; + bool is_delete; } w; /* Buffer for reading. */ char *read_buf; @@ -533,8 +539,27 @@ struct tomoyo_time { u8 sec; }; +/* Structure for policy namespace. */ +struct tomoyo_policy_namespace { + /* Profile table. Memory is allocated as needed. */ + struct tomoyo_profile *profile_ptr[TOMOYO_MAX_PROFILES]; + /* List of "struct tomoyo_group". */ + struct list_head group_list[TOMOYO_MAX_GROUP]; + /* List of policy. */ + struct list_head policy_list[TOMOYO_MAX_POLICY]; + /* The global ACL referred by "use_group" keyword. */ + 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. */ + unsigned int profile_version; + /* Name of this namespace (e.g. "", "" ). */ + const char *name; +}; + /********** Function prototypes. **********/ +void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns); bool tomoyo_str_starts(char **src, const char *find); const char *tomoyo_get_exe(void); void tomoyo_normalize_line(unsigned char *buffer); @@ -553,7 +578,8 @@ tomoyo_compare_name_union(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr); bool tomoyo_compare_number_union(const unsigned long value, const struct tomoyo_number_union *ptr); -int tomoyo_get_mode(const u8 profile, const u8 index); +int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, + const u8 index); void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); bool tomoyo_correct_domain(const unsigned char *domainname); @@ -589,8 +615,11 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, - const u8 profile); -struct tomoyo_profile *tomoyo_profile(const u8 profile); + const bool transit); +struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns, + const u8 profile); +struct tomoyo_policy_namespace *tomoyo_assign_namespace +(const char *domainname); struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param, const u8 idx); unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, @@ -646,6 +675,8 @@ char *tomoyo_read_token(struct tomoyo_acl_param *param); bool tomoyo_permstr(const char *string, const char *keyword); const char *tomoyo_yesno(const unsigned int value); +void tomoyo_write_log(struct tomoyo_request_info *r, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); void tomoyo_write_log2(struct tomoyo_request_info *r, int len, const char *fmt, va_list args); void tomoyo_read_log(struct tomoyo_io_buffer *head); @@ -661,8 +692,6 @@ extern struct srcu_struct tomoyo_ss; /* The list for "struct tomoyo_domain_info". */ extern struct list_head tomoyo_domain_list; -extern struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY]; -extern struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP]; extern struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; /* Lock for protecting policy. */ @@ -671,10 +700,10 @@ extern struct mutex tomoyo_policy_lock; /* Has /sbin/init started? */ extern bool tomoyo_policy_loaded; -extern struct list_head tomoyo_acl_group[TOMOYO_MAX_ACL_GROUPS]; - /* The kernel's domain. */ extern struct tomoyo_domain_info tomoyo_kernel_domain; +extern struct tomoyo_policy_namespace tomoyo_kernel_namespace; +extern struct list_head tomoyo_namespace_list; extern const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION]; extern const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION]; @@ -809,6 +838,16 @@ static inline bool tomoyo_same_number_union a->value_type[1] == b->value_type[1]; } +/** + * tomoyo_current_namespace - Get "struct tomoyo_policy_namespace" for current thread. + * + * Returns pointer to "struct tomoyo_policy_namespace" for current thread. + */ +static inline struct tomoyo_policy_namespace *tomoyo_current_namespace(void) +{ + return tomoyo_domain()->ns; +} + #if defined(CONFIG_SLOB) /** diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index af5f325e2f33..71acebc747c3 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -12,9 +12,6 @@ /* Variables definitions.*/ -/* The global ACL referred by "use_group" keyword. */ -struct list_head tomoyo_acl_group[TOMOYO_MAX_ACL_GROUPS]; - /* The initial domain. */ struct tomoyo_domain_info tomoyo_kernel_domain; @@ -158,7 +155,7 @@ retry: } if (!retried) { retried = true; - list = &tomoyo_acl_group[domain->group]; + list = &domain->ns->acl_group[domain->group]; goto retry; } r->granted = false; @@ -167,13 +164,10 @@ retry: /* The list for "struct tomoyo_domain_info". */ LIST_HEAD(tomoyo_domain_list); -struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY]; -struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP]; - /** * tomoyo_last_word - Get last component of a domainname. * - * @domainname: Domainname to check. + * @name: Domainname to check. * * Returns the last word of @domainname. */ @@ -247,7 +241,7 @@ int tomoyo_write_transition_control(struct tomoyo_acl_param *param, if (!e.domainname) goto out; } - param->list = &tomoyo_policy_list[TOMOYO_ID_TRANSITION_CONTROL]; + param->list = ¶m->ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL]; error = tomoyo_update_policy(&e.head, sizeof(e), param, tomoyo_same_transition_control); out: @@ -257,59 +251,88 @@ out: } /** - * tomoyo_transition_type - Get domain transition type. + * tomoyo_scan_transition - Try to find specific domain transition type. * - * @domainname: The name of domain. - * @program: The name of program. + * @list: Pointer to "struct list_head". + * @domainname: The name of current domain. + * @program: The name of requested program. + * @last_name: The last component of @domainname. + * @type: One of values in "enum tomoyo_transition_type". * - * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program - * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing - * @program suppresses domain transition, others otherwise. + * Returns true if found one, false otherwise. * * Caller holds tomoyo_read_lock(). */ -static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname, - const struct tomoyo_path_info *program) +static inline bool tomoyo_scan_transition +(const struct list_head *list, const struct tomoyo_path_info *domainname, + const struct tomoyo_path_info *program, const char *last_name, + const enum tomoyo_transition_type type) { const struct tomoyo_transition_control *ptr; - const char *last_name = tomoyo_last_word(domainname->name); - u8 type; - for (type = 0; type < TOMOYO_MAX_TRANSITION_TYPE; type++) { - next: - list_for_each_entry_rcu(ptr, &tomoyo_policy_list - [TOMOYO_ID_TRANSITION_CONTROL], - head.list) { - if (ptr->head.is_deleted || ptr->type != type) - continue; - if (ptr->domainname) { - if (!ptr->is_last_name) { - if (ptr->domainname != domainname) - continue; - } else { - /* - * Use direct strcmp() since this is - * unlikely used. - */ - if (strcmp(ptr->domainname->name, - last_name)) - continue; - } - } - if (ptr->program && - tomoyo_pathcmp(ptr->program, program)) - continue; - if (type == TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) { + list_for_each_entry_rcu(ptr, list, head.list) { + if (ptr->head.is_deleted || ptr->type != type) + continue; + if (ptr->domainname) { + if (!ptr->is_last_name) { + if (ptr->domainname != domainname) + continue; + } else { /* - * Do not check for initialize_domain if - * no_initialize_domain matched. + * Use direct strcmp() since this is + * unlikely used. */ - type = TOMOYO_TRANSITION_CONTROL_NO_KEEP; - goto next; + if (strcmp(ptr->domainname->name, last_name)) + continue; } - goto done; } + if (ptr->program && tomoyo_pathcmp(ptr->program, program)) + continue; + return true; + } + return false; +} + +/** + * tomoyo_transition_type - Get domain transition type. + * + * @ns: Pointer to "struct tomoyo_policy_namespace". + * @domainname: The name of current domain. + * @program: The name of requested program. + * + * Returns TOMOYO_TRANSITION_CONTROL_TRANSIT if executing @program causes + * domain transition across namespaces, TOMOYO_TRANSITION_CONTROL_INITIALIZE if + * executing @program reinitializes domain transition within that namespace, + * TOMOYO_TRANSITION_CONTROL_KEEP if executing @program stays at @domainname , + * others otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static enum tomoyo_transition_type tomoyo_transition_type +(const struct tomoyo_policy_namespace *ns, + const struct tomoyo_path_info *domainname, + const struct tomoyo_path_info *program) +{ + const char *last_name = tomoyo_last_word(domainname->name); + enum tomoyo_transition_type type = TOMOYO_TRANSITION_CONTROL_NO_RESET; + while (type < TOMOYO_MAX_TRANSITION_TYPE) { + const struct list_head * const list = + &ns->policy_list[TOMOYO_ID_TRANSITION_CONTROL]; + if (!tomoyo_scan_transition(list, domainname, program, + last_name, type)) { + type++; + continue; + } + if (type != TOMOYO_TRANSITION_CONTROL_NO_RESET && + type != TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE) + break; + /* + * Do not check for reset_domain if no_reset_domain matched. + * Do not check for initialize_domain if no_initialize_domain + * matched. + */ + type++; + type++; } - done: return type; } @@ -355,7 +378,7 @@ int tomoyo_write_aggregator(struct tomoyo_acl_param *param) if (!e.original_name || !e.aggregated_name || e.aggregated_name->is_patterned) /* No patterns allowed. */ goto out; - param->list = &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR]; + param->list = ¶m->ns->policy_list[TOMOYO_ID_AGGREGATOR]; error = tomoyo_update_policy(&e.head, sizeof(e), param, tomoyo_same_aggregator); out: @@ -365,53 +388,171 @@ out: } /** - * tomoyo_assign_domain - Create a domain. + * tomoyo_find_namespace - Find specified namespace. + * + * @name: Name of namespace to find. + * @len: Length of @name. + * + * Returns pointer to "struct tomoyo_policy_namespace" if found, + * NULL otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static struct tomoyo_policy_namespace *tomoyo_find_namespace +(const char *name, const unsigned int len) +{ + struct tomoyo_policy_namespace *ns; + list_for_each_entry(ns, &tomoyo_namespace_list, namespace_list) { + if (strncmp(name, ns->name, len) || + (name[len] && name[len] != ' ')) + continue; + return ns; + } + return NULL; +} + +/** + * tomoyo_assign_namespace - Create a new namespace. + * + * @domainname: Name of namespace to create. + * + * Returns pointer to "struct tomoyo_policy_namespace" on success, + * NULL otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +struct tomoyo_policy_namespace *tomoyo_assign_namespace(const char *domainname) +{ + struct tomoyo_policy_namespace *ptr; + struct tomoyo_policy_namespace *entry; + const char *cp = domainname; + unsigned int len = 0; + while (*cp && *cp++ != ' ') + len++; + ptr = tomoyo_find_namespace(domainname, len); + if (ptr) + return ptr; + if (len >= TOMOYO_EXEC_TMPSIZE - 10 || !tomoyo_domain_def(domainname)) + return NULL; + entry = kzalloc(sizeof(*entry) + len + 1, GFP_NOFS); + if (!entry) + return NULL; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + ptr = tomoyo_find_namespace(domainname, len); + if (!ptr && tomoyo_memory_ok(entry)) { + char *name = (char *) (entry + 1); + ptr = entry; + memmove(name, domainname, len); + name[len] = '\0'; + entry->name = name; + tomoyo_init_policy_namespace(entry); + entry = NULL; + } + mutex_unlock(&tomoyo_policy_lock); +out: + kfree(entry); + return ptr; +} + +/** + * tomoyo_namespace_jump - Check for namespace jump. + * + * @domainname: Name of domain. + * + * Returns true if namespace differs, false otherwise. + */ +static bool tomoyo_namespace_jump(const char *domainname) +{ + const char *namespace = tomoyo_current_namespace()->name; + const int len = strlen(namespace); + return strncmp(domainname, namespace, len) || + (domainname[len] && domainname[len] != ' '); +} + +/** + * tomoyo_assign_domain - Create a domain or a namespace. * * @domainname: The name of domain. - * @profile: Profile number to assign if the domain was newly created. + * @transit: True if transit to domain found or created. * * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise. * * Caller holds tomoyo_read_lock(). */ struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, - const u8 profile) + const bool transit) { - struct tomoyo_domain_info *entry; - struct tomoyo_domain_info *domain = NULL; - const struct tomoyo_path_info *saved_domainname; - bool found = false; - - if (!tomoyo_correct_domain(domainname)) + struct tomoyo_domain_info e = { }; + struct tomoyo_domain_info *entry = tomoyo_find_domain(domainname); + bool created = false; + if (entry) { + if (transit) { + /* + * Since namespace is created at runtime, profiles may + * not be created by the moment the process transits to + * that domain. Do not perform domain transition if + * profile for that domain is not yet created. + */ + if (!entry->ns->profile_ptr[entry->profile]) + return NULL; + } + return entry; + } + /* Requested domain does not exist. */ + /* Don't create requested domain if domainname is invalid. */ + if (strlen(domainname) >= TOMOYO_EXEC_TMPSIZE - 10 || + !tomoyo_correct_domain(domainname)) return NULL; - saved_domainname = tomoyo_get_name(domainname); - if (!saved_domainname) + /* + * Since definition of profiles and acl_groups may differ across + * namespaces, do not inherit "use_profile" and "use_group" settings + * by automatically creating requested domain upon domain transition. + */ + if (transit && tomoyo_namespace_jump(domainname)) + return NULL; + e.ns = tomoyo_assign_namespace(domainname); + if (!e.ns) + return NULL; + /* + * "use_profile" and "use_group" settings for automatically created + * domains are inherited from current domain. These are 0 for manually + * created domains. + */ + if (transit) { + const struct tomoyo_domain_info *domain = tomoyo_domain(); + e.profile = domain->profile; + e.group = domain->group; + } + e.domainname = tomoyo_get_name(domainname); + if (!e.domainname) return NULL; - entry = kzalloc(sizeof(*entry), GFP_NOFS); if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { - if (domain->is_deleted || - tomoyo_pathcmp(saved_domainname, domain->domainname)) - continue; - found = true; - break; - } - if (!found && tomoyo_memory_ok(entry)) { - INIT_LIST_HEAD(&entry->acl_info_list); - entry->domainname = saved_domainname; - saved_domainname = NULL; - entry->profile = profile; - list_add_tail_rcu(&entry->list, &tomoyo_domain_list); - domain = entry; - entry = NULL; - found = true; + entry = tomoyo_find_domain(domainname); + if (!entry) { + entry = tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + INIT_LIST_HEAD(&entry->acl_info_list); + list_add_tail_rcu(&entry->list, &tomoyo_domain_list); + created = true; + } } mutex_unlock(&tomoyo_policy_lock); - out: - tomoyo_put_name(saved_domainname); - kfree(entry); - return found ? domain : NULL; +out: + tomoyo_put_name(e.domainname); + if (entry && transit) { + if (created) { + struct tomoyo_request_info r; + tomoyo_init_request_info(&r, entry, + TOMOYO_MAC_FILE_EXECUTE); + r.granted = false; + tomoyo_write_log(&r, "use_profile %u\n", + entry->profile); + tomoyo_write_log(&r, "use_group %u\n", entry->group); + } + } + return entry; } /** @@ -434,6 +575,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) bool is_enforce; int retval = -ENOMEM; bool need_kfree = false; + bool reject_on_transition_failure = false; struct tomoyo_path_info rn = { }; /* real name */ mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); @@ -457,8 +599,10 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) /* Check 'aggregator' directive. */ { struct tomoyo_aggregator *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_policy_list - [TOMOYO_ID_AGGREGATOR], head.list) { + struct list_head *list = + &old_domain->ns->policy_list[TOMOYO_ID_AGGREGATOR]; + /* Check 'aggregator' directive. */ + list_for_each_entry_rcu(ptr, list, head.list) { if (ptr->head.is_deleted || !tomoyo_path_matches_pattern(&rn, ptr->original_name)) @@ -492,11 +636,21 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) } /* Calculate domain to transit to. */ - switch (tomoyo_transition_type(old_domain->domainname, &rn)) { + switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname, + &rn)) { + case TOMOYO_TRANSITION_CONTROL_RESET: + /* Transit to the root of specified namespace. */ + snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", rn.name); + /* + * Make do_execve() fail if domain transition across namespaces + * has failed. + */ + reject_on_transition_failure = true; + break; case TOMOYO_TRANSITION_CONTROL_INITIALIZE: - /* Transit to the child of tomoyo_kernel_domain domain. */ - snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " " - "%s", rn.name); + /* Transit to the child of current namespace's root. */ + snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", + old_domain->ns->name, rn.name); break; case TOMOYO_TRANSITION_CONTROL_KEEP: /* Keep current domain. */ @@ -519,19 +673,25 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) } break; } - if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10) - goto done; - domain = tomoyo_find_domain(tmp); if (!domain) - domain = tomoyo_assign_domain(tmp, old_domain->profile); - done: + domain = tomoyo_assign_domain(tmp, true); if (domain) - goto out; - printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp); - if (is_enforce) - retval = -EPERM; - else - old_domain->transition_failed = true; + retval = 0; + else if (reject_on_transition_failure) { + printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n", tmp); + retval = -ENOMEM; + } else if (r.mode == TOMOYO_CONFIG_ENFORCING) + retval = -ENOMEM; + else { + retval = 0; + if (!old_domain->transition_failed) { + old_domain->transition_failed = true; + r.granted = false; + tomoyo_write_log(&r, "%s", "transition_failed\n"); + printk(KERN_WARNING + "ERROR: Domain '%s' not defined.\n", tmp); + } + } out: if (!domain) domain = old_domain; diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 4f8526af9069..323ddc73a125 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -603,7 +603,7 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, int error; r->type = tomoyo_p2mac[operation]; - r->mode = tomoyo_get_mode(r->profile, r->type); + r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type); if (r->mode == TOMOYO_CONFIG_DISABLED) return 0; r->param_type = TOMOYO_TYPE_PATH_ACL; diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 412ee8309c23..782e844dca7f 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -292,15 +292,12 @@ static bool tomoyo_collect_acl(struct list_head *list) 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; - for (i = 0; i < TOMOYO_MAX_POLICY; i++) { - if (!tomoyo_collect_member(i, &tomoyo_policy_list[i])) - goto unlock; - } - for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++) - if (!tomoyo_collect_acl(&tomoyo_acl_group[i])) - goto unlock; + idx = tomoyo_read_lock(); { struct tomoyo_domain_info *domain; list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { @@ -317,39 +314,49 @@ static void tomoyo_collect_entry(void) goto unlock; } } + list_for_each_entry_rcu(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; + for (i = 0; i < TOMOYO_MAX_ACL_GROUPS; i++) + if (!tomoyo_collect_acl(&ns->acl_group[i])) + goto unlock; + for (i = 0; i < TOMOYO_MAX_GROUP; i++) { + struct list_head *list = &ns->group_list[i]; + struct tomoyo_group *group; + switch (i) { + case 0: + id = TOMOYO_ID_PATH_GROUP; + break; + default: + id = TOMOYO_ID_NUMBER_GROUP; + break; + } + list_for_each_entry(group, list, head.list) { + if (!tomoyo_collect_member + (id, &group->member_list)) + goto unlock; + if (!list_empty(&group->member_list) || + atomic_read(&group->head.users)) + continue; + if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, + &group->head.list)) + goto unlock; + } + } + } for (i = 0; i < TOMOYO_MAX_HASH; i++) { - struct tomoyo_name *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], head.list) { - if (atomic_read(&ptr->head.users)) + 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)) continue; - if (!tomoyo_add_to_gc(TOMOYO_ID_NAME, &ptr->head.list)) + if (!tomoyo_add_to_gc(TOMOYO_ID_NAME, &ptr->list)) goto unlock; } } - for (i = 0; i < TOMOYO_MAX_GROUP; i++) { - struct list_head *list = &tomoyo_group_list[i]; - int id; - struct tomoyo_group *group; - switch (i) { - case 0: - id = TOMOYO_ID_PATH_GROUP; - break; - default: - id = TOMOYO_ID_NUMBER_GROUP; - break; - } - list_for_each_entry(group, list, head.list) { - if (!tomoyo_collect_member(id, &group->member_list)) - goto unlock; - if (!list_empty(&group->member_list) || - atomic_read(&group->head.users)) - continue; - if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, - &group->head.list)) - goto unlock; - } - } - unlock: +unlock: + tomoyo_read_unlock(idx); mutex_unlock(&tomoyo_policy_lock); } diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 7a0493943d6d..39d012823f84 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -118,7 +118,7 @@ struct tomoyo_group *tomoyo_get_group(struct tomoyo_acl_param *param, return NULL; if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list = &tomoyo_group_list[idx]; + list = ¶m->ns->group_list[idx]; list_for_each_entry(group, list, head.list) { if (e.group_name != group->group_name) continue; @@ -199,27 +199,23 @@ const struct tomoyo_path_info *tomoyo_get_name(const char *name) return ptr ? &ptr->entry : NULL; } +/* Initial namespace.*/ +struct tomoyo_policy_namespace tomoyo_kernel_namespace; + /** * tomoyo_mm_init - Initialize mm related code. */ void __init tomoyo_mm_init(void) { int idx; - - for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++) - INIT_LIST_HEAD(&tomoyo_policy_list[idx]); - for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++) - INIT_LIST_HEAD(&tomoyo_group_list[idx]); for (idx = 0; idx < TOMOYO_MAX_HASH; idx++) INIT_LIST_HEAD(&tomoyo_name_list[idx]); + tomoyo_kernel_namespace.name = ""; + tomoyo_init_policy_namespace(&tomoyo_kernel_namespace); + tomoyo_kernel_domain.ns = &tomoyo_kernel_namespace; INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); - for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++) - INIT_LIST_HEAD(&tomoyo_acl_group[idx]); - tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME); + tomoyo_kernel_domain.domainname = tomoyo_get_name(""); list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list); - idx = tomoyo_read_lock(); - if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) - panic("Can't register tomoyo_kernel_domain"); #if 0 /* Will be replaced with tomoyo_load_builtin_policy(). */ { @@ -230,7 +226,6 @@ void __init tomoyo_mm_init(void) TOMOYO_TRANSITION_CONTROL_INITIALIZE); } #endif - tomoyo_read_unlock(idx); } diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index bc71528ff440..fda15c1fc1c0 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -416,26 +416,21 @@ bool tomoyo_correct_path(const char *filename) */ bool tomoyo_correct_domain(const unsigned char *domainname) { - if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, - TOMOYO_ROOT_NAME_LEN)) - goto out; - domainname += TOMOYO_ROOT_NAME_LEN; - if (!*domainname) + if (!domainname || !tomoyo_domain_def(domainname)) + return false; + domainname = strchr(domainname, ' '); + if (!domainname++) return true; - if (*domainname++ != ' ') - goto out; while (1) { const unsigned char *cp = strchr(domainname, ' '); if (!cp) break; if (*domainname != '/' || !tomoyo_correct_word2(domainname, cp - domainname)) - goto out; + return false; domainname = cp + 1; } return tomoyo_correct_path(domainname); - out: - return false; } /** @@ -447,7 +442,19 @@ bool tomoyo_correct_domain(const unsigned char *domainname) */ bool tomoyo_domain_def(const unsigned char *buffer) { - return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); + const unsigned char *cp; + int len; + if (*buffer != '<') + return false; + cp = strchr(buffer, ' '); + if (!cp) + len = strlen(buffer); + else + len = cp - buffer; + if (buffer[len - 1] != '>' || + !tomoyo_correct_word2(buffer + 1, len - 2)) + return false; + return true; } /** @@ -833,22 +840,24 @@ const char *tomoyo_get_exe(void) /** * tomoyo_get_mode - Get MAC mode. * + * @ns: Pointer to "struct tomoyo_policy_namespace". * @profile: Profile number. * @index: Index number of functionality. * * Returns mode. */ -int tomoyo_get_mode(const u8 profile, const u8 index) +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; if (!tomoyo_policy_loaded) return TOMOYO_CONFIG_DISABLED; - mode = tomoyo_profile(profile)->config[index]; + mode = tomoyo_profile(ns, profile)->config[index]; if (mode == TOMOYO_CONFIG_USE_DEFAULT) - mode = tomoyo_profile(profile)->config[category]; + mode = tomoyo_profile(ns, profile)->config[category]; if (mode == TOMOYO_CONFIG_USE_DEFAULT) - mode = tomoyo_profile(profile)->default_config; + mode = tomoyo_profile(ns, profile)->default_config; return mode & 3; } @@ -872,25 +881,10 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r, profile = domain->profile; r->profile = profile; r->type = index; - r->mode = tomoyo_get_mode(profile, index); + r->mode = tomoyo_get_mode(domain->ns, profile, index); return r->mode; } -/** - * tomoyo_last_word - Get last component of a line. - * - * @line: A line. - * - * Returns the last word of a line. - */ -const char *tomoyo_last_word(const char *name) -{ - const char *cp = strrchr(name, ' '); - if (cp) - return cp + 1; - return name; -} - /** * tomoyo_domain_quota_is_ok - Check for domain's quota. * @@ -939,7 +933,7 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) if (perm & (1 << i)) count++; } - if (count < tomoyo_profile(domain->profile)-> + if (count < tomoyo_profile(domain->ns, domain->profile)-> pref[TOMOYO_PREF_MAX_LEARNING_ENTRY]) return true; if (!domain->quota_warned) {