diff options
Diffstat (limited to 'security/tomoyo/common.c')
| -rw-r--r-- | security/tomoyo/common.c | 430 |
1 files changed, 310 insertions, 120 deletions
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 283862aebdc8..0f78898bce09 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * security/tomoyo/common.c * @@ -7,6 +8,7 @@ #include <linux/uaccess.h> #include <linux/slab.h> #include <linux/security.h> +#include <linux/string_helpers.h> #include "common.h" /* String table for operation mode. */ @@ -174,16 +176,6 @@ static bool tomoyo_manage_by_non_root; /* Utility functions. */ /** - * tomoyo_yesno - Return "yes" or "no". - * - * @value: Bool value. - */ -const char *tomoyo_yesno(const unsigned int value) -{ - return value ? "yes" : "no"; -} - -/** * tomoyo_addprintf - strncat()-like-snprintf(). * * @buffer: Buffer to write to. Must be '\0'-terminated. @@ -192,10 +184,12 @@ const char *tomoyo_yesno(const unsigned int value) * * Returns nothing. */ +__printf(3, 4) static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...) { va_list args; const int pos = strlen(buffer); + va_start(args, fmt); vsnprintf(buffer + pos, len - pos - 1, fmt, args); va_end(args); @@ -213,6 +207,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head) while (head->r.w_pos) { const char *w = head->r.w[0]; size_t len = strlen(w); + if (len) { if (len > head->read_user_buf_avail) len = head->read_user_buf_avail; @@ -278,6 +273,7 @@ static void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, size_t len; size_t pos = head->r.avail; int size = head->readbuf_size - pos; + if (size <= 0) return; va_start(args, fmt); @@ -343,13 +339,14 @@ static bool tomoyo_namespace_enabled; 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 = 20110903; + ns->profile_version = 20150505; tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list); list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list); } @@ -432,6 +429,7 @@ static void tomoyo_print_number_union_nospace u8 min_type = ptr->value_type[0]; const u8 max_type = ptr->value_type[1]; char buffer[128]; + buffer[0] = '\0'; for (i = 0; i < 2; i++) { switch (min_type) { @@ -486,12 +484,13 @@ static struct tomoyo_profile *tomoyo_assign_profile { struct tomoyo_profile *ptr; struct tomoyo_profile *entry; + if (profile >= TOMOYO_MAX_PROFILES) return NULL; ptr = ns->profile_ptr[profile]; if (ptr) return ptr; - entry = kzalloc(sizeof(*entry), GFP_NOFS); + entry = kzalloc(sizeof(*entry), GFP_NOFS | __GFP_NOWARN); if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; ptr = ns->profile_ptr[profile]; @@ -529,6 +528,7 @@ struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns, { static struct tomoyo_profile tomoyo_null_profile; struct tomoyo_profile *ptr = ns->profile_ptr[profile]; + if (!ptr) ptr = &tomoyo_null_profile; return ptr; @@ -545,6 +545,7 @@ struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns, static s8 tomoyo_find_yesno(const char *string, const char *find) { const char *cp = strstr(string, find); + if (cp) { cp += strlen(find); if (!strncmp(cp, "=yes", 4)) @@ -568,6 +569,7 @@ static void tomoyo_set_uint(unsigned int *i, const char *string, const char *find) { const char *cp = strstr(string, find); + if (cp) sscanf(cp + strlen(find), "=%u", i); } @@ -586,6 +588,7 @@ static int tomoyo_set_mode(char *name, const char *value, { u8 i; u8 config; + if (!strcmp(name, "CONFIG")) { i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; config = profile->default_config; @@ -594,10 +597,12 @@ static int tomoyo_set_mode(char *name, const char *value, for (i = 0; i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { int len = 0; + if (i < TOMOYO_MAX_MAC_INDEX) { const u8 c = tomoyo_index2category[i]; const char *category = tomoyo_category_keywords[c]; + len = strlen(category); if (strncmp(name, category, len) || name[len++] != ':' || name[len++] != ':') @@ -617,11 +622,12 @@ static int tomoyo_set_mode(char *name, const char *value, config = TOMOYO_CONFIG_USE_DEFAULT; } else { u8 mode; + for (mode = 0; mode < 4; mode++) if (strstr(value, tomoyo_mode[mode])) /* * Update lower 3 bits in order to distinguish - * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'. + * 'config' from 'TOMOYO_CONFIG_USE_DEFAULT'. */ config = (config & ~7) | mode; if (config != TOMOYO_CONFIG_USE_DEFAULT) { @@ -663,6 +669,7 @@ 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", &head->w.ns->profile_version) == 1) return 0; @@ -682,6 +689,7 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) const struct tomoyo_path_info *new_comment = tomoyo_get_name(cp); const struct tomoyo_path_info *old_comment; + if (!new_comment) return -ENOMEM; spin_lock(&lock); @@ -714,8 +722,8 @@ static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config) { tomoyo_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n", tomoyo_mode[config & 3], - tomoyo_yesno(config & TOMOYO_CONFIG_WANT_GRANT_LOG), - tomoyo_yesno(config & TOMOYO_CONFIG_WANT_REJECT_LOG)); + str_yes_no(config & TOMOYO_CONFIG_WANT_GRANT_LOG), + str_yes_no(config & TOMOYO_CONFIG_WANT_REJECT_LOG)); } /** @@ -731,6 +739,7 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) 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: @@ -759,6 +768,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 : ""); @@ -787,6 +797,7 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) + TOMOYO_MAX_MAC_CATEGORY_INDEX; head->r.bit++) { const u8 i = head->r.bit; const u8 config = profile->config[i]; + if (config == TOMOYO_CONFIG_USE_DEFAULT) continue; tomoyo_print_namespace(head); @@ -846,10 +857,10 @@ static int tomoyo_update_manager_entry(const char *manager, struct tomoyo_acl_param param = { /* .ns = &tomoyo_kernel_namespace, */ .is_delete = is_delete, - .list = &tomoyo_kernel_namespace. - policy_list[TOMOYO_ID_MANAGER], + .list = &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER], }; int error = is_delete ? -ENOENT : -ENOMEM; + if (!tomoyo_correct_domain(manager) && !tomoyo_correct_word(manager)) return -EINVAL; @@ -893,10 +904,10 @@ static void tomoyo_read_manager(struct tomoyo_io_buffer *head) { if (head->r.eof) return; - list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace. - 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) continue; if (!tomoyo_flush(head)) @@ -921,7 +932,7 @@ static bool tomoyo_manager(void) const char *exe; const struct task_struct *task = current; const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname; - bool found = false; + bool found = IS_ENABLED(CONFIG_SECURITY_TOMOYO_INSECURE_BUILTIN_SETTING); if (!tomoyo_policy_loaded) return true; @@ -932,8 +943,8 @@ static bool tomoyo_manager(void) exe = tomoyo_get_exe(); if (!exe) return false; - list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. - policy_list[TOMOYO_ID_MANAGER], head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace.policy_list[TOMOYO_ID_MANAGER], head.list, + srcu_read_lock_held(&tomoyo_ss)) { if (!ptr->head.is_deleted && (!tomoyo_pathcmp(domainname, ptr->manager) || !strcmp(exe, ptr->manager->name))) { @@ -944,9 +955,10 @@ static bool tomoyo_manager(void) if (!found) { /* Reduce error messages. */ static pid_t last_pid; const pid_t pid = current->pid; + if (last_pid != pid) { - printk(KERN_WARNING "%s ( %s ) is not permitted to " - "update policies.\n", domainname->name, exe); + pr_warn("%s ( %s ) is not permitted to update policies.\n", + domainname->name, exe); last_pid = pid; } } @@ -973,19 +985,21 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, unsigned int pid; struct tomoyo_domain_info *domain = NULL; bool global_pid = false; + 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; + rcu_read_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); + domain = tomoyo_task(p)->domain_info; rcu_read_unlock(); } else if (!strncmp(data, "domain=", 7)) { if (tomoyo_domain_def(data + 7)) @@ -1003,7 +1017,7 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, if (domain) head->r.domain = &domain->list; else - head->r.eof = 1; + head->r.eof = true; tomoyo_io_printf(head, "# select %s\n", data); if (domain && domain->is_deleted) tomoyo_io_printf(head, "# This is a deleted domain.\n"); @@ -1019,10 +1033,11 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, * 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_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; } @@ -1038,11 +1053,13 @@ static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a, 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, @@ -1071,7 +1088,8 @@ static int tomoyo_delete_domain(char *domainname) if (mutex_lock_interruptible(&tomoyo_policy_lock)) return -EINTR; /* Is there an active domain? */ - list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list, + srcu_read_lock_held(&tomoyo_ss)) { /* Never delete tomoyo_kernel_domain */ if (domain == &tomoyo_kernel_domain) continue; @@ -1109,7 +1127,7 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns, }; static const struct { const char *keyword; - int (*write) (struct tomoyo_acl_param *); + int (*write)(struct tomoyo_acl_param *param); } tomoyo_callback[5] = { { "file ", tomoyo_write_file }, { "network inet ", tomoyo_write_inet_network }, @@ -1150,9 +1168,11 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head) struct tomoyo_domain_info *domain = head->w.domain; const bool is_delete = head->w.is_delete; bool is_select = !is_delete && tomoyo_str_starts(&data, "select "); - unsigned int profile; + unsigned int idx; + if (*data == '<') { int ret = 0; + domain = NULL; if (is_delete) ret = tomoyo_delete_domain(data); @@ -1166,23 +1186,27 @@ static int tomoyo_write_domain(struct tomoyo_io_buffer *head) if (!domain) return -EINVAL; ns = domain->ns; - if (sscanf(data, "use_profile %u", &profile) == 1 - && profile < TOMOYO_MAX_PROFILES) { - if (!tomoyo_policy_loaded || ns->profile_ptr[profile]) - domain->profile = (u8) profile; + if (sscanf(data, "use_profile %u", &idx) == 1 + && idx < TOMOYO_MAX_PROFILES) { + if (!tomoyo_policy_loaded || ns->profile_ptr[idx]) + if (!is_delete) + domain->profile = (u8) idx; return 0; } - if (sscanf(data, "use_group %u\n", &profile) == 1 - && profile < TOMOYO_MAX_ACL_GROUPS) { + if (sscanf(data, "use_group %u\n", &idx) == 1 + && idx < TOMOYO_MAX_ACL_GROUPS) { if (!is_delete) - domain->group = (u8) profile; + set_bit(idx, domain->group); + else + clear_bit(idx, domain->group); return 0; } - for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) { - const char *cp = tomoyo_dif[profile]; + for (idx = 0; idx < TOMOYO_MAX_DOMAIN_INFO_FLAGS; idx++) { + const char *cp = tomoyo_dif[idx]; + if (strncmp(data, cp, strlen(cp) - 1)) continue; - domain->flags[profile] = !is_delete; + domain->flags[idx] = !is_delete; return 0; } return tomoyo_write_domain2(ns, &domain->acl_info_list, data, @@ -1208,7 +1232,7 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, tomoyo_set_space(head); tomoyo_set_string(head, cond->transit->name); } - /* fall through */ + fallthrough; case 1: { const u16 condc = cond->condc; @@ -1224,9 +1248,11 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, const struct tomoyo_envp *envp = (typeof(envp)) (argv + cond->argc); u16 skip; + for (skip = 0; skip < head->r.cond_index; skip++) { const u8 left = condp->left; const u8 right = condp->right; + condp++; switch (left) { case TOMOYO_ARGV_ENTRY: @@ -1252,6 +1278,7 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, const u8 match = condp->equals; const u8 left = condp->left; const u8 right = condp->right; + if (!tomoyo_flush(head)) return false; condp++; @@ -1261,8 +1288,7 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, case TOMOYO_ARGV_ENTRY: tomoyo_io_printf(head, "exec.argv[%lu]%s=\"", - argv->index, argv-> - is_not ? "!" : ""); + argv->index, argv->is_not ? "!" : ""); tomoyo_set_string(head, argv->value->name); tomoyo_set_string(head, "\""); @@ -1273,12 +1299,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, "exec.envp[\""); tomoyo_set_string(head, envp->name->name); - tomoyo_io_printf(head, "\"]%s=", envp-> - is_not ? "!" : ""); + tomoyo_io_printf(head, "\"]%s=", envp->is_not ? "!" : ""); if (envp->value) { tomoyo_set_string(head, "\""); - tomoyo_set_string(head, envp-> - value->name); + tomoyo_set_string(head, envp->value->name); tomoyo_set_string(head, "\""); } else { tomoyo_set_string(head, @@ -1313,17 +1337,17 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, } } head->r.cond_step++; - /* fall through */ + fallthrough; case 2: if (!tomoyo_flush(head)) break; head->r.cond_step++; - /* fall through */ + fallthrough; case 3: if (cond->grant_log != TOMOYO_GRANTLOG_AUTO) tomoyo_io_printf(head, " grant_log=%s", - tomoyo_yesno(cond->grant_log == - TOMOYO_GRANTLOG_YES)); + str_yes_no(cond->grant_log == + TOMOYO_GRANTLOG_YES)); tomoyo_set_lf(head); return true; } @@ -1374,6 +1398,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, struct tomoyo_path_acl *ptr = container_of(acl, typeof(*ptr), head); const u16 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; @@ -1394,6 +1419,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, } 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); @@ -1403,6 +1429,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, struct tomoyo_path2_acl *ptr = container_of(acl, typeof(*ptr), head); const u8 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; @@ -1423,6 +1450,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, struct tomoyo_path_number_acl *ptr = container_of(acl, typeof(*ptr), head); const u8 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; @@ -1443,6 +1471,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, struct tomoyo_mkdev_acl *ptr = container_of(acl, typeof(*ptr), head); const u8 perm = ptr->perm; + for (bit = 0; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; @@ -1489,6 +1518,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, ->name); } else { char buf[128]; + tomoyo_print_ip(buf, sizeof(buf), &ptr->address); tomoyo_io_printf(head, "%s", buf); } @@ -1518,6 +1548,7 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { struct tomoyo_mount_acl *ptr = container_of(acl, typeof(*ptr), head); + tomoyo_set_group(head, "file mount"); tomoyo_print_name_union(head, &ptr->dev_name); tomoyo_print_name_union(head, &ptr->dir_name); @@ -1561,6 +1592,7 @@ static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head, list_for_each_cookie(head->r.acl, list) { struct tomoyo_acl_info *ptr = list_entry(head->r.acl, typeof(*ptr), list); + if (!tomoyo_print_entry(head, ptr)) return false; } @@ -1582,8 +1614,9 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head) list_for_each_cookie(head->r.domain, &tomoyo_domain_list) { struct tomoyo_domain_info *domain = list_entry(head->r.domain, typeof(*domain), list); + u8 i; + switch (head->r.step) { - u8 i; case 0: if (domain->is_deleted && !head->r.print_this_domain_only) @@ -1593,22 +1626,33 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head) tomoyo_set_lf(head); tomoyo_io_printf(head, "use_profile %u\n", domain->profile); - tomoyo_io_printf(head, "use_group %u\n", - domain->group); for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++) if (domain->flags[i]) tomoyo_set_string(head, tomoyo_dif[i]); + head->r.index = 0; head->r.step++; - tomoyo_set_lf(head); - /* fall through */ + fallthrough; case 1: + while (head->r.index < TOMOYO_MAX_ACL_GROUPS) { + i = head->r.index++; + if (!test_bit(i, domain->group)) + continue; + tomoyo_io_printf(head, "use_group %u\n", i); + if (!tomoyo_flush(head)) + return; + } + head->r.index = 0; + head->r.step++; + tomoyo_set_lf(head); + fallthrough; + case 2: if (!tomoyo_read_domain2(head, &domain->acl_info_list)) return; head->r.step++; if (!tomoyo_set_lf(head)) return; - /* fall through */ - case 2: + fallthrough; + case 3: head->r.step = 0; if (head->r.print_this_domain_only) goto done; @@ -1659,14 +1703,15 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head) head->r.eof = true; if (tomoyo_str_starts(&buf, "global-pid ")) global_pid = true; - pid = (unsigned int) simple_strtoul(buf, NULL, 10); + if (kstrtouint(buf, 10, &pid)) + return; rcu_read_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); + domain = tomoyo_task(p)->domain_info; rcu_read_unlock(); if (!domain) return; @@ -1709,6 +1754,7 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head) .data = head->write_buf, }; u8 i; + if (tomoyo_str_starts(¶m.data, "aggregator ")) return tomoyo_write_aggregator(¶m); for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++) @@ -1720,6 +1766,7 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head) if (tomoyo_str_starts(¶m.data, "acl_group ")) { unsigned int group; char *data; + group = simple_strtoul(param.data, &data, 10); if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ') return tomoyo_write_domain2 @@ -1744,12 +1791,15 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int 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) { struct tomoyo_acl_head *ptr = list_entry(head->r.acl, typeof(*ptr), list); + if (ptr->is_deleted) continue; if (!tomoyo_flush(head)) @@ -1769,10 +1819,10 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) 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); @@ -1800,6 +1850,7 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int 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); @@ -1812,6 +1863,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]); @@ -1827,6 +1879,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, @@ -1856,6 +1909,7 @@ 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 && @@ -1919,6 +1973,7 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); static int tomoyo_truncate(char *str) { char *start = str; + while (*(unsigned char *) str > (unsigned char) ' ') str++; *str = '\0'; @@ -1926,6 +1981,114 @@ static int tomoyo_truncate(char *str) } /** + * tomoyo_numscan - sscanf() which stores the length of a decimal integer value. + * + * @str: String to scan. + * @head: Leading string that must start with. + * @width: Pointer to "int" for storing length of a decimal integer value after @head. + * @tail: Optional character that must match after a decimal integer value. + * + * Returns whether @str starts with @head and a decimal value follows @head. + */ +static bool tomoyo_numscan(const char *str, const char *head, int *width, const char tail) +{ + const char *cp; + const int n = strlen(head); + + if (!strncmp(str, head, n)) { + cp = str + n; + while (*cp && *cp >= '0' && *cp <= '9') + cp++; + if (*cp == tail || !tail) { + *width = cp - (str + n); + return *width != 0; + } + } + *width = 0; + return 0; +} + +/** + * tomoyo_patternize_path - Make patterns for file path. Used by learning mode. + * + * @buffer: Destination buffer. + * @len: Size of @buffer. + * @entry: Original line. + * + * Returns nothing. + */ +static void tomoyo_patternize_path(char *buffer, const int len, char *entry) +{ + int width; + char *cp = entry; + + /* Nothing to do if this line is not for "file" related entry. */ + if (strncmp(entry, "file ", 5)) + goto flush; + /* + * Nothing to do if there is no colon in this line, for this rewriting + * applies to only filesystems where numeric values in the path are volatile. + */ + cp = strchr(entry + 5, ':'); + if (!cp) { + cp = entry; + goto flush; + } + /* Flush e.g. "file ioctl" part. */ + while (*cp != ' ') + cp--; + *cp++ = '\0'; + tomoyo_addprintf(buffer, len, "%s ", entry); + /* e.g. file ioctl pipe:[$INO] $CMD */ + if (tomoyo_numscan(cp, "pipe:[", &width, ']')) { + cp += width + 7; + tomoyo_addprintf(buffer, len, "pipe:[\\$]"); + goto flush; + } + /* e.g. file ioctl socket:[$INO] $CMD */ + if (tomoyo_numscan(cp, "socket:[", &width, ']')) { + cp += width + 9; + tomoyo_addprintf(buffer, len, "socket:[\\$]"); + goto flush; + } + if (!strncmp(cp, "proc:/self", 10)) { + /* e.g. file read proc:/self/task/$TID/fdinfo/$FD */ + cp += 10; + tomoyo_addprintf(buffer, len, "proc:/self"); + } else if (tomoyo_numscan(cp, "proc:/", &width, 0)) { + /* e.g. file read proc:/$PID/task/$TID/fdinfo/$FD */ + /* + * Don't patternize $PID part if $PID == 1, for several + * programs access only files in /proc/1/ directory. + */ + cp += width + 6; + if (width == 1 && *(cp - 1) == '1') + tomoyo_addprintf(buffer, len, "proc:/1"); + else + tomoyo_addprintf(buffer, len, "proc:/\\$"); + } else { + goto flush; + } + /* Patternize $TID part if "/task/" follows. */ + if (tomoyo_numscan(cp, "/task/", &width, 0)) { + cp += width + 6; + tomoyo_addprintf(buffer, len, "/task/\\$"); + } + /* Patternize $FD part if "/fd/" or "/fdinfo/" follows. */ + if (tomoyo_numscan(cp, "/fd/", &width, 0)) { + cp += width + 4; + tomoyo_addprintf(buffer, len, "/fd/\\$"); + } else if (tomoyo_numscan(cp, "/fdinfo/", &width, 0)) { + cp += width + 8; + tomoyo_addprintf(buffer, len, "/fdinfo/\\$"); + } +flush: + /* Flush remaining part if any. */ + if (*cp) + tomoyo_addprintf(buffer, len, "%s", cp); +} + +/** * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode. * * @domain: Pointer to "struct tomoyo_domain_info". @@ -1941,13 +2104,15 @@ static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header) char *symlink = NULL; char *cp = strchr(header, '\n'); int len; + if (!cp) return; cp = strchr(cp + 1, '\n'); if (!cp) return; *cp++ = '\0'; - len = strlen(cp) + 1; + /* Reserve some space for potentially using patterns. */ + len = strlen(cp) + 16; /* strstr() will return NULL if ordering is wrong. */ if (*cp == 'f') { argv0 = strstr(header, " argv[]={ \""); @@ -1964,10 +2129,10 @@ static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header) if (symlink) len += tomoyo_truncate(symlink + 1) + 1; } - buffer = kmalloc(len, GFP_NOFS); + buffer = kmalloc(len, GFP_NOFS | __GFP_ZERO); if (!buffer) return; - snprintf(buffer, len - 1, "%s", cp); + tomoyo_patternize_path(buffer, len, cp); if (realpath) tomoyo_addprintf(buffer, len, " exec.%s", realpath); if (argv0) @@ -2000,8 +2165,9 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) static unsigned int tomoyo_serial; struct tomoyo_query entry = { }; bool quota_exceeded = false; + va_start(args, fmt); - len = vsnprintf((char *) &len, 1, fmt, args) + 1; + len = vsnprintf(NULL, 0, fmt, args) + 1; va_end(args); /* Write /sys/kernel/security/tomoyo/audit. */ va_start(args, fmt); @@ -2023,7 +2189,7 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) /* Check max_learning_entry parameter. */ if (tomoyo_domain_quota_is_ok(r)) break; - /* fall through */ + fallthrough; default: return 0; } @@ -2038,7 +2204,7 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) tomoyo_add_entry(r->domain, entry.query); goto out; } - len = tomoyo_round2(entry.query_len); + len = kmalloc_size_roundup(entry.query_len); entry.domain = r->domain; spin_lock(&tomoyo_query_list_lock); if (tomoyo_memory_quota[TOMOYO_MEMORY_QUERY] && @@ -2061,8 +2227,7 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) (tomoyo_answer_wait, entry.answer || !atomic_read(&tomoyo_query_observers), HZ)) break; - else - entry.timer++; + entry.timer++; } spin_lock(&tomoyo_query_list_lock); list_del(&entry.list); @@ -2098,6 +2263,7 @@ static struct tomoyo_domain_info *tomoyo_find_domain_by_qid { struct tomoyo_query *ptr; struct tomoyo_domain_info *domain = NULL; + spin_lock(&tomoyo_query_list_lock); list_for_each_entry(ptr, &tomoyo_query_list, list) { if (ptr->serial != serial) @@ -2115,17 +2281,17 @@ static struct tomoyo_domain_info *tomoyo_find_domain_by_qid * @file: Pointer to "struct file". * @wait: Pointer to "poll_table". * - * Returns POLLIN | POLLRDNORM when ready to read, 0 otherwise. + * Returns EPOLLIN | EPOLLRDNORM when ready to read, 0 otherwise. * * Waits for access requests which violated policy in enforcing mode. */ -static unsigned int tomoyo_poll_query(struct file *file, poll_table *wait) +static __poll_t tomoyo_poll_query(struct file *file, poll_table *wait) { if (!list_empty(&tomoyo_query_list)) - return POLLIN | POLLRDNORM; + return EPOLLIN | EPOLLRDNORM; poll_wait(file, &tomoyo_query_wait, wait); if (!list_empty(&tomoyo_query_list)) - return POLLIN | POLLRDNORM; + return EPOLLIN | EPOLLRDNORM; return 0; } @@ -2140,15 +2306,15 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) unsigned int pos = 0; size_t len = 0; char *buf; + if (head->r.w_pos) return; - if (head->read_buf) { - kfree(head->read_buf); - head->read_buf = NULL; - } + kfree(head->read_buf); + head->read_buf = NULL; spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); + if (pos++ != head->r.query_index) continue; len = ptr->query_len; @@ -2166,6 +2332,7 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); + if (pos++ != head->r.query_index) continue; /* @@ -2200,9 +2367,11 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) struct list_head *tmp; unsigned int serial; unsigned int answer; + spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); + ptr->timer = 0; } spin_unlock(&tomoyo_query_list_lock); @@ -2211,6 +2380,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); + if (ptr->serial != serial) continue; ptr->answer = answer; @@ -2233,7 +2403,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.5.0"); + tomoyo_io_printf(head, "2.6.0"); head->r.eof = true; } } @@ -2253,10 +2423,10 @@ static const char * const tomoyo_memory_headers[TOMOYO_MAX_MEMORY_STAT] = { [TOMOYO_MEMORY_QUERY] = "query message:", }; -/* Timestamp counter for last updated. */ -static unsigned int tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT]; /* Counter for number of updates. */ -static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT]; +static atomic_t tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT]; +/* Timestamp counter for last updated. */ +static time64_t tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT]; /** * tomoyo_update_stat - Update statistic counters. @@ -2267,13 +2437,8 @@ static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT]; */ void tomoyo_update_stat(const u8 index) { - struct timeval tv; - do_gettimeofday(&tv); - /* - * I don't use atomic operations because race condition is not fatal. - */ - tomoyo_stat_updated[index]++; - tomoyo_stat_modified[index] = tv.tv_sec; + atomic_inc(&tomoyo_stat_updated[index]); + tomoyo_stat_modified[index] = ktime_get_real_seconds(); } /** @@ -2287,17 +2452,18 @@ static void tomoyo_read_stat(struct tomoyo_io_buffer *head) { u8 i; unsigned int total = 0; + if (head->r.eof) return; for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) { tomoyo_io_printf(head, "Policy %-30s %10u", tomoyo_policy_headers[i], - tomoyo_stat_updated[i]); + atomic_read(&tomoyo_stat_updated[i])); if (tomoyo_stat_modified[i]) { struct tomoyo_time stamp; + tomoyo_convert_time(tomoyo_stat_modified[i], &stamp); - tomoyo_io_printf(head, " (Last: %04u/%02u/%02u " - "%02u:%02u:%02u)", + tomoyo_io_printf(head, " (Last: %04u/%02u/%02u %02u:%02u:%02u)", stamp.year, stamp.month, stamp.day, stamp.hour, stamp.min, stamp.sec); } @@ -2305,6 +2471,7 @@ static void tomoyo_read_stat(struct tomoyo_io_buffer *head) } for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) { unsigned int used = tomoyo_memory_used[i]; + total += used; tomoyo_io_printf(head, "Memory used by %-22s %10u", tomoyo_memory_headers[i], used); @@ -2329,6 +2496,7 @@ static int tomoyo_write_stat(struct tomoyo_io_buffer *head) { char *data = head->write_buf; u8 i; + if (tomoyo_str_starts(&data, "Memory used by ")) for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) if (tomoyo_str_starts(&data, tomoyo_memory_headers[i])) @@ -2451,15 +2619,16 @@ int tomoyo_open_control(const u8 type, struct file *file) * @file: Pointer to "struct file". * @wait: Pointer to "poll_table". Maybe NULL. * - * Returns POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM if ready to read/write, - * POLLOUT | POLLWRNORM otherwise. + * Returns EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM if ready to read/write, + * EPOLLOUT | EPOLLWRNORM otherwise. */ -unsigned int tomoyo_poll_control(struct file *file, poll_table *wait) +__poll_t tomoyo_poll_control(struct file *file, poll_table *wait) { struct tomoyo_io_buffer *head = file->private_data; + if (head->poll) - return head->poll(file, wait) | POLLOUT | POLLWRNORM; - return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; + return head->poll(file, wait) | EPOLLOUT | EPOLLWRNORM; + return EPOLLIN | EPOLLRDNORM | EPOLLOUT | EPOLLWRNORM; } /** @@ -2472,6 +2641,7 @@ unsigned int tomoyo_poll_control(struct file *file, poll_table *wait) 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; @@ -2505,7 +2675,7 @@ static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head) * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. * * @head: Pointer to "struct tomoyo_io_buffer". - * @buffer: Poiner to buffer to write to. + * @buffer: Pointer to buffer to write to. * @buffer_len: Size of @buffer. * * Returns bytes read on success, negative value otherwise. @@ -2517,7 +2687,7 @@ ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, int idx; if (!head->read) - return -ENOSYS; + return -EINVAL; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; head->read_user_buf = buffer; @@ -2539,7 +2709,7 @@ ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, /** * tomoyo_parse_policy - Parse a policy line. * - * @head: Poiter to "struct tomoyo_io_buffer". + * @head: Pointer to "struct tomoyo_io_buffer". * @line: Line to parse. * * Returns 0 on success, negative value otherwise. @@ -2557,6 +2727,7 @@ static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line) head->type == TOMOYO_PROFILE) { if (*line == '<') { char *cp = strchr(line, ' '); + if (cp) { *cp++ = '\0'; head->w.ns = tomoyo_assign_namespace(line); @@ -2587,22 +2758,24 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, { int error = buffer_len; size_t avail_len = buffer_len; - char *cp0 = head->write_buf; + char *cp0; int idx; + if (!head->write) - return -ENOSYS; - if (!access_ok(VERIFY_READ, buffer, buffer_len)) - return -EFAULT; + return -EINVAL; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; + cp0 = head->write_buf; head->read_user_buf_avail = 0; idx = tomoyo_read_lock(); /* Read a line and dispatch it to the policy handler. */ while (avail_len > 0) { char c; + if (head->w.avail >= head->writebuf_size - 1) { const int len = head->writebuf_size * 2; - char *cp = kzalloc(len, GFP_NOFS); + char *cp = kzalloc(len, GFP_NOFS | __GFP_NOWARN); + if (!cp) { error = -ENOMEM; break; @@ -2639,13 +2812,13 @@ ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, case TOMOYO_DOMAINPOLICY: if (tomoyo_select_domain(head, cp0)) continue; - /* fall through */ + fallthrough; case TOMOYO_EXCEPTIONPOLICY: if (!strcmp(cp0, "select transition_only")) { head->r.print_transition_related_only = true; continue; } - /* fall through */ + fallthrough; default: if (!tomoyo_manager()) { error = -EPERM; @@ -2701,30 +2874,33 @@ 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.5.0\n"); - list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { + pr_info("TOMOYO: 2.6.0\n"); + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list, + srcu_read_lock_held(&tomoyo_ss)) { const u8 profile = domain->profile; - const struct tomoyo_policy_namespace *ns = domain->ns; - if (ns->profile_version != 20110903) - printk(KERN_ERR - "Profile version %u is not supported.\n", + struct tomoyo_policy_namespace *ns = domain->ns; + + if (ns->profile_version == 20110903) { + pr_info_once("Converting profile version from %u to %u.\n", + 20110903, 20150505); + ns->profile_version = 20150505; + } + if (ns->profile_version != 20150505) + pr_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", + pr_err("Profile %u (used by '%s') is not defined.\n", profile, domain->domainname->name); else continue; - printk(KERN_ERR - "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.5/ " - "for more information.\n"); + pr_err("Userland tools for TOMOYO 2.6 must be installed and policy must be initialized.\n"); + pr_err("Please see https://tomoyo.sourceforge.net/2.6/ for more information.\n"); panic("STOP!"); } tomoyo_read_unlock(idx); - printk(KERN_INFO "Mandatory Access Control activated.\n"); + pr_info("Mandatory Access Control activated.\n"); } /** @@ -2734,6 +2910,16 @@ void tomoyo_check_profile(void) */ void __init tomoyo_load_builtin_policy(void) { +#ifdef CONFIG_SECURITY_TOMOYO_INSECURE_BUILTIN_SETTING + static char tomoyo_builtin_profile[] __initdata = + "PROFILE_VERSION=20150505\n" + "0-CONFIG={ mode=learning grant_log=no reject_log=yes }\n"; + static char tomoyo_builtin_exception_policy[] __initdata = + "aggregator proc:/self/exe /proc/self/exe\n"; + static char tomoyo_builtin_domain_policy[] __initdata = ""; + static char tomoyo_builtin_manager[] __initdata = ""; + static char tomoyo_builtin_stat[] __initdata = ""; +#else /* * This include file is manually created and contains built-in policy * named "tomoyo_builtin_profile", "tomoyo_builtin_exception_policy", @@ -2741,11 +2927,14 @@ void __init tomoyo_load_builtin_policy(void) * "tomoyo_builtin_stat" in the form of "static char [] __initdata". */ #include "builtin-policy.h" +#endif u8 i; const int idx = tomoyo_read_lock(); + for (i = 0; i < 5; i++) { struct tomoyo_io_buffer head = { }; char *start = ""; + switch (i) { case 0: start = tomoyo_builtin_profile; @@ -2775,6 +2964,7 @@ void __init tomoyo_load_builtin_policy(void) } while (1) { char *end = strchr(start, '\n'); + if (!end) break; *end = '\0'; |
