From babcd37821fba57048b30151969d28303f2a8b6b Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 18 May 2010 12:11:25 -0700 Subject: selinux: remove all rcu head initializations Remove all rcu head inits. We don't care about the RCU head state before passing it to call_rcu() anyway. Only leave the "on_stack" variants so debugobjects can keep track of objects on stack. Signed-off-by: Mathieu Desnoyers Signed-off-by: Paul E. McKenney Cc: Stephen Smalley Reviewed-by: James Morris Cc: Eric Paris Signed-off-by: James Morris --- security/selinux/avc.c | 1 - security/selinux/netnode.c | 2 -- 2 files changed, 3 deletions(-) diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 7f1a304712a..3662b0f15ec 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -288,7 +288,6 @@ static struct avc_node *avc_alloc_node(void) if (!node) goto out; - INIT_RCU_HEAD(&node->rhead); INIT_HLIST_NODE(&node->list); avc_cache_stats_incr(allocations); diff --git a/security/selinux/netnode.c b/security/selinux/netnode.c index dc92792271f..65ebfe954f8 100644 --- a/security/selinux/netnode.c +++ b/security/selinux/netnode.c @@ -183,8 +183,6 @@ static void sel_netnode_insert(struct sel_netnode *node) BUG(); } - INIT_RCU_HEAD(&node->rcu); - /* we need to impose a limit on the growth of the hash table so check * this bucket to make sure it is within the specified bounds */ list_add_rcu(&node->list, &sel_netnode_hash[idx].list); -- cgit v1.2.3-70-g09d2 From 4c3e9e2ded48bcf696a45945ea7d25bb15b873fd Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 17 May 2010 10:06:58 +0900 Subject: TOMOYO: Add numeric values grouping support. This patch adds numeric values grouping support, which is useful for grouping numeric values such as file's UID, DAC's mode, ioctl()'s cmd number. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/Makefile | 2 +- security/tomoyo/common.c | 161 +++++++++++++++++++++++++++++++++++++ security/tomoyo/common.h | 61 ++++++++++++++ security/tomoyo/file.c | 14 ++++ security/tomoyo/gc.c | 41 ++++++++++ security/tomoyo/number_group.c | 176 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 454 insertions(+), 1 deletion(-) create mode 100644 security/tomoyo/number_group.c diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index 4fb39030f6b..4d1b5af4f1f 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile @@ -1 +1 @@ -obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o +obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o number_group.o diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index b5dbdc9ff73..d82c2978b1b 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -118,6 +118,159 @@ static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head, return tomoyo_io_printf(head, " %s", ptr->filename->name); } +/** + * tomoyo_parse_ulong - Parse an "unsigned long" value. + * + * @result: Pointer to "unsigned long". + * @str: Pointer to string to parse. + * + * Returns value type on success, 0 otherwise. + * + * The @src is updated to point the first character after the value + * on success. + */ +u8 tomoyo_parse_ulong(unsigned long *result, char **str) +{ + const char *cp = *str; + char *ep; + int base = 10; + if (*cp == '0') { + char c = *(cp + 1); + if (c == 'x' || c == 'X') { + base = 16; + cp += 2; + } else if (c >= '0' && c <= '7') { + base = 8; + cp++; + } + } + *result = simple_strtoul(cp, &ep, base); + if (cp == ep) + return 0; + *str = ep; + switch (base) { + case 16: + return TOMOYO_VALUE_TYPE_HEXADECIMAL; + case 8: + return TOMOYO_VALUE_TYPE_OCTAL; + default: + return TOMOYO_VALUE_TYPE_DECIMAL; + } +} + +/** + * tomoyo_print_ulong - Print an "unsigned long" value. + * + * @buffer: Pointer to buffer. + * @buffer_len: Size of @buffer. + * @value: An "unsigned long" value. + * @type: Type of @value. + * + * Returns nothing. + */ +void tomoyo_print_ulong(char *buffer, const int buffer_len, + const unsigned long value, const u8 type) +{ + if (type == TOMOYO_VALUE_TYPE_DECIMAL) + snprintf(buffer, buffer_len, "%lu", value); + else if (type == TOMOYO_VALUE_TYPE_OCTAL) + snprintf(buffer, buffer_len, "0%lo", value); + else if (type == TOMOYO_VALUE_TYPE_HEXADECIMAL) + snprintf(buffer, buffer_len, "0x%lX", value); + else + snprintf(buffer, buffer_len, "type(%u)", type); +} + +/** + * tomoyo_print_number_union - Print a tomoyo_number_union. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_number_union". + * + * Returns true on success, false otherwise. + */ +bool tomoyo_print_number_union(struct tomoyo_io_buffer *head, + const struct tomoyo_number_union *ptr) +{ + unsigned long min; + unsigned long max; + u8 min_type; + u8 max_type; + if (!tomoyo_io_printf(head, " ")) + return false; + if (ptr->is_group) + return tomoyo_io_printf(head, "@%s", + ptr->group->group_name->name); + min_type = ptr->min_type; + max_type = ptr->max_type; + min = ptr->values[0]; + max = ptr->values[1]; + switch (min_type) { + case TOMOYO_VALUE_TYPE_HEXADECIMAL: + if (!tomoyo_io_printf(head, "0x%lX", min)) + return false; + break; + case TOMOYO_VALUE_TYPE_OCTAL: + if (!tomoyo_io_printf(head, "0%lo", min)) + return false; + break; + default: + if (!tomoyo_io_printf(head, "%lu", min)) + return false; + break; + } + if (min == max && min_type == max_type) + return true; + switch (max_type) { + case TOMOYO_VALUE_TYPE_HEXADECIMAL: + return tomoyo_io_printf(head, "-0x%lX", max); + case TOMOYO_VALUE_TYPE_OCTAL: + return tomoyo_io_printf(head, "-0%lo", max); + default: + return tomoyo_io_printf(head, "-%lu", max); + } +} + +/** + * tomoyo_parse_number_union - Parse a tomoyo_number_union. + * + * @data: Number or number range or number group. + * @ptr: Pointer to "struct tomoyo_number_union". + * + * Returns true on success, false otherwise. + */ +bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) +{ + u8 type; + unsigned long v; + memset(num, 0, sizeof(*num)); + if (data[0] == '@') { + if (!tomoyo_is_correct_path(data, 0, 0, 0)) + return false; + num->group = tomoyo_get_number_group(data + 1); + num->is_group = true; + return num->group != NULL; + } + type = tomoyo_parse_ulong(&v, &data); + if (!type) + return false; + num->values[0] = v; + num->min_type = type; + if (!*data) { + num->values[1] = v; + num->max_type = type; + return true; + } + if (*data++ != '-') + return false; + type = tomoyo_parse_ulong(&v, &data); + if (!type || *data) + return false; + num->values[1] = v; + num->max_type = type; + return true; +} + /** * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value. * @@ -1750,6 +1903,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) return tomoyo_write_no_rewrite_policy(data, is_delete); if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_PATH_GROUP)) return tomoyo_write_path_group_policy(data, is_delete); + if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NUMBER_GROUP)) + return tomoyo_write_number_group_policy(data, is_delete); return -EINVAL; } @@ -1812,6 +1967,12 @@ static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) head->read_var2 = NULL; head->read_step = 10; case 10: + if (!tomoyo_read_number_group_policy(head)) + break; + head->read_var1 = NULL; + head->read_var2 = NULL; + head->read_step = 11; + case 11: head->read_eof = true; break; default: diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 9f1ae5e3ba5..33d3072f9bb 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -55,6 +55,7 @@ struct linux_binprm; #define TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN "no_initialize_domain " #define TOMOYO_KEYWORD_NO_KEEP_DOMAIN "no_keep_domain " #define TOMOYO_KEYWORD_PATH_GROUP "path_group " +#define TOMOYO_KEYWORD_NUMBER_GROUP "number_group " #define TOMOYO_KEYWORD_SELECT "select " #define TOMOYO_KEYWORD_USE_PROFILE "use_profile " #define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read" @@ -62,6 +63,12 @@ struct linux_binprm; #define TOMOYO_ROOT_NAME "" #define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1) +/* Value type definition. */ +#define TOMOYO_VALUE_TYPE_INVALID 0 +#define TOMOYO_VALUE_TYPE_DECIMAL 1 +#define TOMOYO_VALUE_TYPE_OCTAL 2 +#define TOMOYO_VALUE_TYPE_HEXADECIMAL 3 + /* Index numbers for Access Controls. */ enum tomoyo_mac_index { TOMOYO_MAC_FOR_FILE, /* domain_policy.conf */ @@ -211,6 +218,14 @@ struct tomoyo_name_union { u8 is_group; }; +struct tomoyo_number_union { + unsigned long values[2]; + struct tomoyo_number_group *group; + u8 min_type; + u8 max_type; + u8 is_group; +}; + /* Structure for "path_group" directive. */ struct tomoyo_path_group { struct list_head list; @@ -219,6 +234,14 @@ struct tomoyo_path_group { atomic_t users; }; +/* Structure for "number_group" directive. */ +struct tomoyo_number_group { + struct list_head list; + const struct tomoyo_path_info *group_name; + struct list_head member_list; + atomic_t users; +}; + /* Structure for "path_group" directive. */ struct tomoyo_path_group_member { struct list_head list; @@ -226,6 +249,13 @@ struct tomoyo_path_group_member { const struct tomoyo_path_info *member_name; }; +/* Structure for "number_group" directive. */ +struct tomoyo_number_group_member { + struct list_head list; + bool is_deleted; + struct tomoyo_number_union number; +}; + /* * tomoyo_acl_info is a structure which is used for holding * @@ -554,9 +584,18 @@ bool tomoyo_parse_name_union(const char *filename, bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, const struct tomoyo_path_group *group, const bool may_use_pattern); +/* Check whether the given value matches the given number_group. */ +bool tomoyo_number_matches_group(const unsigned long min, + const unsigned long max, + const struct tomoyo_number_group *group); /* Check whether the given filename matches the given pattern. */ bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, const struct tomoyo_path_info *pattern); + +bool tomoyo_print_number_union(struct tomoyo_io_buffer *head, + const struct tomoyo_number_union *ptr); +bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num); + /* Read "alias" entry in exception policy. */ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head); /* @@ -570,6 +609,8 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head); bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head); /* Read "path_group" entry in exception policy. */ bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head); +/* Read "number_group" entry in exception policy. */ +bool tomoyo_read_number_group_policy(struct tomoyo_io_buffer *head); /* Read "allow_read" entry in exception policy. */ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head); /* Read "deny_rewrite" entry in exception policy. */ @@ -614,6 +655,8 @@ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete); int tomoyo_write_pattern_policy(char *data, const bool is_delete); /* Create "path_group" entry in exception policy. */ int tomoyo_write_path_group_policy(char *data, const bool is_delete); +/* Create "number_group" entry in exception policy. */ +int tomoyo_write_number_group_policy(char *data, const bool is_delete); /* Find a domain by the given name. */ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); /* Find or create a domain by the given name. */ @@ -623,6 +666,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * /* Allocate memory for "struct tomoyo_path_group". */ struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name); +struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name); /* Check mode for specified functionality. */ unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, @@ -632,6 +676,8 @@ void tomoyo_fill_path_info(struct tomoyo_path_info *ptr); /* Run policy loader when /sbin/init starts. */ void tomoyo_load_policy(const char *filename); +void tomoyo_put_number_union(struct tomoyo_number_union *ptr); + /* Convert binary string to ascii string. */ int tomoyo_encode(char *buffer, int buflen, const char *str); @@ -697,6 +743,7 @@ extern struct srcu_struct tomoyo_ss; extern struct list_head tomoyo_domain_list; extern struct list_head tomoyo_path_group_list; +extern struct list_head tomoyo_number_group_list; extern struct list_head tomoyo_domain_initializer_list; extern struct list_head tomoyo_domain_keeper_list; extern struct list_head tomoyo_alias_list; @@ -773,6 +820,12 @@ static inline void tomoyo_put_path_group(struct tomoyo_path_group *group) atomic_dec(&group->users); } +static inline void tomoyo_put_number_group(struct tomoyo_number_group *group) +{ + if (group) + atomic_dec(&group->users); +} + static inline struct tomoyo_domain_info *tomoyo_domain(void) { return current_cred()->security; @@ -797,6 +850,14 @@ static inline bool tomoyo_is_same_name_union p1->is_group == p2->is_group; } +static inline bool tomoyo_is_same_number_union +(const struct tomoyo_number_union *p1, const struct tomoyo_number_union *p2) +{ + return p1->values[0] == p2->values[0] && p1->values[1] == p2->values[1] + && p1->group == p2->group && p1->min_type == p2->min_type && + p1->max_type == p2->max_type && p1->is_group == p2->is_group; +} + static inline bool tomoyo_is_same_path_acl(const struct tomoyo_path_acl *p1, const struct tomoyo_path_acl *p2) { diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 1c6f8238ec4..2dffe073091 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -76,6 +76,20 @@ static bool tomoyo_compare_name_union_pattern(const struct tomoyo_path_info return false; } +void tomoyo_put_number_union(struct tomoyo_number_union *ptr) +{ + if (ptr && ptr->is_group) + tomoyo_put_number_group(ptr->group); +} + +bool tomoyo_compare_number_union(const unsigned long value, + const struct tomoyo_number_union *ptr) +{ + if (ptr->is_group) + return tomoyo_number_matches_group(value, value, ptr->group); + return value >= ptr->values[0] && value <= ptr->values[1]; +} + /** * tomoyo_path2keyword - Get the name of single path operation. * diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index b9cc71b0431..6a48197f6ce 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -14,6 +14,8 @@ enum tomoyo_gc_id { TOMOYO_ID_PATH_GROUP, TOMOYO_ID_PATH_GROUP_MEMBER, + TOMOYO_ID_NUMBER_GROUP, + TOMOYO_ID_NUMBER_GROUP_MEMBER, TOMOYO_ID_DOMAIN_INITIALIZER, TOMOYO_ID_DOMAIN_KEEPER, TOMOYO_ID_ALIAS, @@ -162,6 +164,16 @@ static void tomoyo_del_path_group(struct tomoyo_path_group *group) tomoyo_put_name(group->group_name); } +static void tomoyo_del_number_group_member(struct tomoyo_number_group_member + *member) +{ +} + +static void tomoyo_del_number_group(struct tomoyo_number_group *group) +{ + tomoyo_put_name(group->group_name); +} + static void tomoyo_collect_entry(void) { if (mutex_lock_interruptible(&tomoyo_policy_lock)) @@ -329,6 +341,29 @@ static void tomoyo_collect_entry(void) break; } } + { + struct tomoyo_number_group *group; + list_for_each_entry_rcu(group, &tomoyo_number_group_list, list) { + struct tomoyo_number_group_member *member; + list_for_each_entry_rcu(member, &group->member_list, + list) { + if (!member->is_deleted) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_NUMBER_GROUP_MEMBER, + member)) + list_del_rcu(&member->list); + else + break; + } + if (!list_empty(&group->member_list) || + atomic_read(&group->users)) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_NUMBER_GROUP, group)) + list_del_rcu(&group->list); + else + break; + } + } mutex_unlock(&tomoyo_policy_lock); } @@ -376,6 +411,12 @@ static void tomoyo_kfree_entry(void) case TOMOYO_ID_PATH_GROUP: tomoyo_del_path_group(p->element); break; + case TOMOYO_ID_NUMBER_GROUP_MEMBER: + tomoyo_del_number_group_member(p->element); + break; + case TOMOYO_ID_NUMBER_GROUP: + tomoyo_del_number_group(p->element); + break; default: printk(KERN_WARNING "Unknown type\n"); break; diff --git a/security/tomoyo/number_group.c b/security/tomoyo/number_group.c new file mode 100644 index 00000000000..c49792e09e8 --- /dev/null +++ b/security/tomoyo/number_group.c @@ -0,0 +1,176 @@ +/* + * security/tomoyo/number_group.c + * + * Copyright (C) 2005-2009 NTT DATA CORPORATION + */ + +#include +#include "common.h" + +/* The list for "struct tomoyo_number_group". */ +LIST_HEAD(tomoyo_number_group_list); + +/** + * tomoyo_get_number_group - Allocate memory for "struct tomoyo_number_group". + * + * @group_name: The name of number group. + * + * Returns pointer to "struct tomoyo_number_group" on success, + * NULL otherwise. + */ +struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name) +{ + struct tomoyo_number_group *entry = NULL; + struct tomoyo_number_group *group = NULL; + const struct tomoyo_path_info *saved_group_name; + int error = -ENOMEM; + if (!tomoyo_is_correct_path(group_name, 0, 0, 0) || + !group_name[0]) + return NULL; + saved_group_name = tomoyo_get_name(group_name); + if (!saved_group_name) + return NULL; + entry = kzalloc(sizeof(*entry), GFP_NOFS); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + list_for_each_entry_rcu(group, &tomoyo_number_group_list, list) { + if (saved_group_name != group->group_name) + continue; + atomic_inc(&group->users); + error = 0; + break; + } + if (error && tomoyo_memory_ok(entry)) { + INIT_LIST_HEAD(&entry->member_list); + entry->group_name = saved_group_name; + saved_group_name = NULL; + atomic_set(&entry->users, 1); + list_add_tail_rcu(&entry->list, &tomoyo_number_group_list); + group = entry; + entry = NULL; + error = 0; + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_name(saved_group_name); + kfree(entry); + return !error ? group : NULL; +} + +/** + * tomoyo_write_number_group_policy - Write "struct tomoyo_number_group" list. + * + * @data: String to parse. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, nagative value otherwise. + */ +int tomoyo_write_number_group_policy(char *data, const bool is_delete) +{ + struct tomoyo_number_group *group; + struct tomoyo_number_group_member e = { }; + struct tomoyo_number_group_member *member; + int error = is_delete ? -ENOENT : -ENOMEM; + char *w[2]; + if (!tomoyo_tokenize(data, w, sizeof(w))) + return -EINVAL; + if (!tomoyo_parse_number_union(w[1], &e.number)) + return -EINVAL; + if (e.number.is_group || e.number.values[0] > e.number.values[1]) { + tomoyo_put_number_union(&e.number); + return -EINVAL; + } + group = tomoyo_get_number_group(w[0]); + if (!group) + return -ENOMEM; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + list_for_each_entry_rcu(member, &group->member_list, list) { + if (memcmp(&member->number, &e.number, sizeof(e.number))) + continue; + member->is_deleted = is_delete; + error = 0; + break; + } + if (!is_delete && error) { + struct tomoyo_number_group_member *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->list, &group->member_list); + error = 0; + } + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_number_group(group); + return error; +} + +/** + * tomoyo_read_number_group_policy - Read "struct tomoyo_number_group" list. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_read_number_group_policy(struct tomoyo_io_buffer *head) +{ + struct list_head *gpos; + struct list_head *mpos; + list_for_each_cookie(gpos, head->read_var1, &tomoyo_number_group_list) { + struct tomoyo_number_group *group; + const char *name; + group = list_entry(gpos, struct tomoyo_number_group, list); + name = group->group_name->name; + list_for_each_cookie(mpos, head->read_var2, + &group->member_list) { + int pos; + const struct tomoyo_number_group_member *member + = list_entry(mpos, + struct tomoyo_number_group_member, + list); + if (member->is_deleted) + continue; + pos = head->read_avail; + if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_NUMBER_GROUP + "%s", name) || + !tomoyo_print_number_union(head, &member->number) || + !tomoyo_io_printf(head, "\n")) { + head->read_avail = pos; + return false; + } + } + } + return true; +} + +/** + * tomoyo_number_matches_group - Check whether the given number matches members of the given number group. + * + * @min: Min number. + * @max: Max number. + * @group: Pointer to "struct tomoyo_number_group". + * + * Returns true if @min and @max partially overlaps @group, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_number_matches_group(const unsigned long min, + const unsigned long max, + const struct tomoyo_number_group *group) +{ + struct tomoyo_number_group_member *member; + bool matched = false; + list_for_each_entry_rcu(member, &group->member_list, list) { + if (member->is_deleted) + continue; + if (min > member->number.values[1] || + max < member->number.values[0]) + continue; + matched = true; + break; + } + return matched; +} -- cgit v1.2.3-70-g09d2 From cb0abe6a5b58499bd4bc1403f4987af9ead0642c Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 17 May 2010 10:08:05 +0900 Subject: TOMOYO: Use structure for passing common arguments. Use "struct tomoyo_request_info" instead of passing individual arguments. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 22 +--- security/tomoyo/common.h | 27 +++- security/tomoyo/domain.c | 2 +- security/tomoyo/file.c | 315 ++++++++++++++++++++--------------------------- security/tomoyo/tomoyo.c | 2 +- 5 files changed, 162 insertions(+), 206 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index d82c2978b1b..34d65871096 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -984,21 +984,6 @@ static const char *tomoyo_get_exe(void) return cp; } -/** - * tomoyo_get_msg - Get warning message. - * - * @is_enforce: Is it enforcing mode? - * - * Returns "ERROR" or "WARNING". - */ -const char *tomoyo_get_msg(const bool is_enforce) -{ - if (is_enforce) - return "ERROR"; - else - return "WARNING"; -} - /** * tomoyo_check_flags - Check mode for specified functionality. * @@ -1040,17 +1025,20 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain) /** * tomoyo_domain_quota_is_ok - Check for domain's quota. * - * @domain: Pointer to "struct tomoyo_domain_info". + * @r: Pointer to "struct tomoyo_request_info". * * Returns true if the domain is not exceeded quota, false otherwise. * * Caller holds tomoyo_read_lock(). */ -bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain) +bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) { unsigned int count = 0; + struct tomoyo_domain_info *domain = r->domain; struct tomoyo_acl_info *ptr; + if (r->mode != TOMOYO_CONFIG_LEARNING) + return false; if (!domain) return true; list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 33d3072f9bb..91e2bcfd56e 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -44,6 +44,13 @@ struct linux_binprm; /* Profile number is an integer between 0 and 255. */ #define TOMOYO_MAX_PROFILES 256 +enum tomoyo_mode_index { + TOMOYO_CONFIG_DISABLED, + TOMOYO_CONFIG_LEARNING, + TOMOYO_CONFIG_PERMISSIVE, + TOMOYO_CONFIG_ENFORCING +}; + /* Keywords for ACLs. */ #define TOMOYO_KEYWORD_ALIAS "alias " #define TOMOYO_KEYWORD_ALLOW_READ "allow_read " @@ -152,6 +159,17 @@ struct tomoyo_page_buffer { char buffer[4096]; }; +/* + * tomoyo_request_info is a structure which is used for holding + * + * (1) Domain information of current process. + * (2) Access control mode of the profile. + */ +struct tomoyo_request_info { + struct tomoyo_domain_info *domain; + u8 mode; /* One of tomoyo_mode_index . */ +}; + /* * tomoyo_path_info is a structure which is used for holding a string data * used by TOMOYO. @@ -332,8 +350,8 @@ struct tomoyo_domain_info { * "allow_read", "allow_write", "allow_create", "allow_unlink", "allow_mkdir", * "allow_rmdir", "allow_mkfifo", "allow_mksock", "allow_mkblock", * "allow_mkchar", "allow_truncate", "allow_symlink", "allow_rewrite", - * "allow_chmod", "allow_chown", "allow_chgrp", "allow_chroot", "allow_mount" - * and "allow_unmount". + * "allow_ioctl", "allow_chmod", "allow_chown", "allow_chgrp", "allow_chroot", + * "allow_mount" and "allow_unmount". */ struct tomoyo_path_acl { struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */ @@ -567,7 +585,7 @@ struct tomoyo_policy_manager_entry { bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr); /* Check whether the domain has too many ACL entries to hold. */ -bool tomoyo_domain_quota_is_ok(struct tomoyo_domain_info * const domain); +bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r); /* Transactional sprintf() for policy dump. */ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); @@ -623,8 +641,6 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain); const char *tomoyo_path22keyword(const u8 operation); /* Get the last component of the given domainname. */ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain); -/* Get warning message. */ -const char *tomoyo_get_msg(const bool is_enforce); /* Convert single path operation to operation name. */ const char *tomoyo_path2keyword(const u8 operation); /* Create "alias" entry in exception policy. */ @@ -723,7 +739,6 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, int tomoyo_path_perm(const u8 operation, struct path *path); int tomoyo_path2_perm(const u8 operation, struct path *path1, struct path *path2); -int tomoyo_check_rewrite_permission(struct file *filp); int tomoyo_find_next_domain(struct linux_binprm *bprm); /* Drop refcount on tomoyo_name_union. */ diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index cd8ba444676..afdf26128bf 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -691,7 +691,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) char *real_program_name = NULL; char *symlink_program_name = NULL; const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE); - const bool is_enforce = (mode == 3); + const bool is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); int retval = -ENOMEM; struct tomoyo_path_info r; /* real name */ struct tomoyo_path_info s; /* symlink name */ diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 2dffe073091..f1d2adfd33b 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -90,6 +90,61 @@ bool tomoyo_compare_number_union(const unsigned long value, return value >= ptr->values[0] && value <= ptr->values[1]; } +/** + * tomoyo_init_request_info - Initialize "struct tomoyo_request_info" members. + * + * @r: Pointer to "struct tomoyo_request_info" to initialize. + * @domain: Pointer to "struct tomoyo_domain_info". NULL for tomoyo_domain(). + * + * Returns mode. + */ +static int tomoyo_init_request_info(struct tomoyo_request_info *r, + struct tomoyo_domain_info *domain) +{ + memset(r, 0, sizeof(*r)); + if (!domain) + domain = tomoyo_domain(); + r->domain = domain; + r->mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); + return r->mode; +} + +static void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); +/** + * tomoyo_warn_log - Print warning or error message on console. + * + * @r: Pointer to "struct tomoyo_request_info". + * @fmt: The printf()'s format string, followed by parameters. + */ +static void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) +{ + int len = PAGE_SIZE; + va_list args; + char *buffer; + if (!tomoyo_verbose_mode(r->domain)) + return; + while (1) { + int len2; + buffer = kmalloc(len, GFP_NOFS); + if (!buffer) + return; + va_start(args, fmt); + len2 = vsnprintf(buffer, len - 1, fmt, args); + va_end(args); + if (len2 <= len - 1) { + buffer[len2] = '\0'; + break; + } + len = len2 + 1; + kfree(buffer); + } + printk(KERN_WARNING "TOMOYO-%s: Access %s denied for %s\n", + r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", + buffer, tomoyo_get_last_name(r->domain)); + kfree(buffer); +} + /** * tomoyo_path2keyword - Get the name of single path operation. * @@ -652,9 +707,9 @@ static int tomoyo_update_file_acl(const char *filename, u8 perm, } /** - * tomoyo_path_acl2 - Check permission for single path operation. + * tomoyo_path_acl - Check permission for single path operation. * - * @domain: Pointer to "struct tomoyo_domain_info". + * @r: Pointer to "struct tomoyo_request_info". * @filename: Filename to check. * @perm: Permission. * @may_use_pattern: True if patterned ACL is permitted. @@ -663,10 +718,11 @@ static int tomoyo_update_file_acl(const char *filename, u8 perm, * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_path_acl2(const struct tomoyo_domain_info *domain, - const struct tomoyo_path_info *filename, - const u32 perm, const bool may_use_pattern) +static int tomoyo_path_acl(const struct tomoyo_request_info *r, + const struct tomoyo_path_info *filename, + const u32 perm, const bool may_use_pattern) { + struct tomoyo_domain_info *domain = r->domain; struct tomoyo_acl_info *ptr; int error = -EPERM; @@ -692,89 +748,56 @@ static int tomoyo_path_acl2(const struct tomoyo_domain_info *domain, } /** - * tomoyo_check_file_acl - Check permission for opening files. + * tomoyo_file_perm - Check permission for opening files. * - * @domain: Pointer to "struct tomoyo_domain_info". + * @r: Pointer to "struct tomoyo_request_info". * @filename: Filename to check. - * @operation: Mode ("read" or "write" or "read/write" or "execute"). - * - * Returns 0 on success, -EPERM otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_check_file_acl(const struct tomoyo_domain_info *domain, - const struct tomoyo_path_info *filename, - const u8 operation) -{ - u32 perm = 0; - - if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE)) - return 0; - if (operation == 6) - perm = 1 << TOMOYO_TYPE_READ_WRITE; - else if (operation == 4) - perm = 1 << TOMOYO_TYPE_READ; - else if (operation == 2) - perm = 1 << TOMOYO_TYPE_WRITE; - else if (operation == 1) - perm = 1 << TOMOYO_TYPE_EXECUTE; - else - BUG(); - return tomoyo_path_acl2(domain, filename, perm, operation != 1); -} - -/** - * tomoyo_check_file_perm2 - Check permission for opening files. - * - * @domain: Pointer to "struct tomoyo_domain_info". - * @filename: Filename to check. - * @perm: Mode ("read" or "write" or "read/write" or "execute"). - * @operation: Operation name passed used for verbose mode. - * @mode: Access control mode. + * @mode: Mode ("read" or "write" or "read/write" or "execute"). * * Returns 0 on success, negative value otherwise. * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_check_file_perm2(struct tomoyo_domain_info * const domain, - const struct tomoyo_path_info *filename, - const u8 perm, const char *operation, - const u8 mode) +static int tomoyo_file_perm(struct tomoyo_request_info *r, + const struct tomoyo_path_info *filename, + const u8 mode) { - const bool is_enforce = (mode == 3); const char *msg = ""; int error = 0; + u32 perm = 0; if (!filename) return 0; - error = tomoyo_check_file_acl(domain, filename, perm); - if (error && perm == 4 && !domain->ignore_global_allow_read - && tomoyo_is_globally_readable_file(filename)) - error = 0; - if (perm == 6) + + if (mode == 6) { msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE); - else if (perm == 4) + perm = 1 << TOMOYO_TYPE_READ_WRITE; + } else if (mode == 4) { msg = tomoyo_path2keyword(TOMOYO_TYPE_READ); - else if (perm == 2) + perm = 1 << TOMOYO_TYPE_READ; + } else if (mode == 2) { msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE); - else if (perm == 1) + perm = 1 << TOMOYO_TYPE_WRITE; + } else if (mode == 1) { msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE); - else + perm = 1 << TOMOYO_TYPE_EXECUTE; + } else BUG(); + error = tomoyo_path_acl(r, filename, perm, mode != 1); + if (error && mode == 4 && !r->domain->ignore_global_allow_read + && tomoyo_is_globally_readable_file(filename)) + error = 0; if (!error) return 0; - if (tomoyo_verbose_mode(domain)) - printk(KERN_WARNING "TOMOYO-%s: Access '%s(%s) %s' denied " - "for %s\n", tomoyo_get_msg(is_enforce), msg, operation, - filename->name, tomoyo_get_last_name(domain)); - if (is_enforce) + tomoyo_warn_log(r, "%s %s", msg, filename->name); + if (r->mode == TOMOYO_CONFIG_ENFORCING) return error; - if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) { + if (tomoyo_domain_quota_is_ok(r)) { /* Don't use patterns for execute permission. */ - const struct tomoyo_path_info *patterned_file = (perm != 1) ? + const struct tomoyo_path_info *patterned_file = (mode != 1) ? tomoyo_get_file_pattern(filename) : filename; - tomoyo_update_file_acl(patterned_file->name, perm, - domain, false); + tomoyo_update_file_acl(patterned_file->name, mode, + r->domain, false); } return 0; } @@ -965,29 +988,10 @@ static int tomoyo_update_path2_acl(const u8 type, const char *filename1, return error; } -/** - * tomoyo_path_acl - Check permission for single path operation. - * - * @domain: Pointer to "struct tomoyo_domain_info". - * @type: Type of operation. - * @filename: Filename to check. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_path_acl(struct tomoyo_domain_info *domain, const u8 type, - const struct tomoyo_path_info *filename) -{ - if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE)) - return 0; - return tomoyo_path_acl2(domain, filename, 1 << type, 1); -} - /** * tomoyo_path2_acl - Check permission for double path operation. * - * @domain: Pointer to "struct tomoyo_domain_info". + * @r: Pointer to "struct tomoyo_request_info". * @type: Type of operation. * @filename1: First filename to check. * @filename2: Second filename to check. @@ -996,17 +1000,15 @@ static int tomoyo_path_acl(struct tomoyo_domain_info *domain, const u8 type, * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_path2_acl(const struct tomoyo_domain_info *domain, - const u8 type, +static int tomoyo_path2_acl(const struct tomoyo_request_info *r, const u8 type, const struct tomoyo_path_info *filename1, const struct tomoyo_path_info *filename2) { + const struct tomoyo_domain_info *domain = r->domain; struct tomoyo_acl_info *ptr; const u8 perm = 1 << type; int error = -EPERM; - if (!tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE)) - return 0; list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { struct tomoyo_path2_acl *acl; if (ptr->type != TOMOYO_TYPE_PATH2_ACL) @@ -1025,42 +1027,32 @@ static int tomoyo_path2_acl(const struct tomoyo_domain_info *domain, } /** - * tomoyo_path_permission2 - Check permission for single path operation. + * tomoyo_path_permission - Check permission for single path operation. * - * @domain: Pointer to "struct tomoyo_domain_info". + * @r: Pointer to "struct tomoyo_request_info". * @operation: Type of operation. * @filename: Filename to check. - * @mode: Access control mode. * * Returns 0 on success, negative value otherwise. * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_path_permission2(struct tomoyo_domain_info *const domain, - u8 operation, - const struct tomoyo_path_info *filename, - const u8 mode) +static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, + const struct tomoyo_path_info *filename) { - const char *msg; int error; - const bool is_enforce = (mode == 3); - if (!mode) - return 0; next: - error = tomoyo_path_acl(domain, operation, filename); - msg = tomoyo_path2keyword(operation); + error = tomoyo_path_acl(r, filename, 1 << operation, 1); if (!error) goto ok; - if (tomoyo_verbose_mode(domain)) - printk(KERN_WARNING "TOMOYO-%s: Access '%s %s' denied for %s\n", - tomoyo_get_msg(is_enforce), msg, filename->name, - tomoyo_get_last_name(domain)); - if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) { + tomoyo_warn_log(r, "%s %s", tomoyo_path2keyword(operation), + filename->name); + if (tomoyo_domain_quota_is_ok(r)) { const char *name = tomoyo_get_file_pattern(filename)->name; - tomoyo_update_path_acl(operation, name, domain, false); + tomoyo_update_path_acl(operation, name, r->domain, false); } - if (!is_enforce) + if (r->mode != TOMOYO_CONFIG_ENFORCING) error = 0; ok: /* @@ -1089,11 +1081,11 @@ static int tomoyo_path_permission2(struct tomoyo_domain_info *const domain, int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, const struct tomoyo_path_info *filename) { - const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); + struct tomoyo_request_info r; - if (!mode) + if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED) return 0; - return tomoyo_check_file_perm2(domain, filename, 1, "do_execve", mode); + return tomoyo_file_perm(&r, filename, 1); } /** @@ -1111,11 +1103,11 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, const u8 acc_mode = ACC_MODE(flag); int error = -ENOMEM; struct tomoyo_path_info *buf; - const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); - const bool is_enforce = (mode == 3); + struct tomoyo_request_info r; int idx; - if (!mode || !path->mnt) + if (tomoyo_init_request_info(&r, domain) == TOMOYO_CONFIG_DISABLED || + !path->mnt) return 0; if (acc_mode == 0) return 0; @@ -1138,25 +1130,22 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, if ((acc_mode & MAY_WRITE) && ((flag & O_TRUNC) || !(flag & O_APPEND)) && (tomoyo_is_no_rewrite_file(buf))) { - error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE, - buf, mode); + error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, buf); } if (!error) - error = tomoyo_check_file_perm2(domain, buf, acc_mode, "open", - mode); + error = tomoyo_file_perm(&r, buf, acc_mode); if (!error && (flag & O_TRUNC)) - error = tomoyo_path_permission2(domain, TOMOYO_TYPE_TRUNCATE, - buf, mode); + error = tomoyo_path_permission(&r, TOMOYO_TYPE_TRUNCATE, buf); out: kfree(buf); tomoyo_read_unlock(idx); - if (!is_enforce) + if (r.mode != TOMOYO_CONFIG_ENFORCING) error = 0; return error; } /** - * tomoyo_path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate", "symlink", "ioctl", "chmod", "chown", "chgrp", "chroot", "mount" and "unmount". + * tomoyo_path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate", "symlink", "rewrite", "ioctl", "chmod", "chown", "chgrp", "chroot", "mount" and "unmount". * * @operation: Type of operation. * @path: Pointer to "struct path". @@ -1167,18 +1156,23 @@ int tomoyo_path_perm(const u8 operation, struct path *path) { int error = -ENOMEM; struct tomoyo_path_info *buf; - struct tomoyo_domain_info *domain = tomoyo_domain(); - const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); - const bool is_enforce = (mode == 3); + struct tomoyo_request_info r; int idx; - if (!mode || !path->mnt) + if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || + !path->mnt) return 0; idx = tomoyo_read_lock(); buf = tomoyo_get_path(path); if (!buf) goto out; switch (operation) { + case TOMOYO_TYPE_REWRITE: + if (!tomoyo_is_no_rewrite_file(buf)) { + error = 0; + goto out; + } + break; case TOMOYO_TYPE_MKDIR: case TOMOYO_TYPE_RMDIR: case TOMOYO_TYPE_CHROOT: @@ -1190,47 +1184,11 @@ int tomoyo_path_perm(const u8 operation, struct path *path) tomoyo_fill_path_info(buf); } } - error = tomoyo_path_permission2(domain, operation, buf, mode); - out: - kfree(buf); - tomoyo_read_unlock(idx); - if (!is_enforce) - error = 0; - return error; -} - -/** - * tomoyo_check_rewrite_permission - Check permission for "rewrite". - * - * @filp: Pointer to "struct file". - * - * Returns 0 on success, negative value otherwise. - */ -int tomoyo_check_rewrite_permission(struct file *filp) -{ - int error = -ENOMEM; - struct tomoyo_domain_info *domain = tomoyo_domain(); - const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); - const bool is_enforce = (mode == 3); - struct tomoyo_path_info *buf; - int idx; - - if (!mode || !filp->f_path.mnt) - return 0; - - idx = tomoyo_read_lock(); - buf = tomoyo_get_path(&filp->f_path); - if (!buf) - goto out; - if (!tomoyo_is_no_rewrite_file(buf)) { - error = 0; - goto out; - } - error = tomoyo_path_permission2(domain, TOMOYO_TYPE_REWRITE, buf, mode); + error = tomoyo_path_permission(&r, operation, buf); out: kfree(buf); tomoyo_read_unlock(idx); - if (!is_enforce) + if (r.mode != TOMOYO_CONFIG_ENFORCING) error = 0; return error; } @@ -1248,14 +1206,13 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, struct path *path2) { int error = -ENOMEM; - struct tomoyo_path_info *buf1, *buf2; - struct tomoyo_domain_info *domain = tomoyo_domain(); - const u8 mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); - const bool is_enforce = (mode == 3); - const char *msg; + struct tomoyo_path_info *buf1; + struct tomoyo_path_info *buf2; + struct tomoyo_request_info r; int idx; - if (!mode || !path1->mnt || !path2->mnt) + if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || + !path1->mnt || !path2->mnt) return 0; idx = tomoyo_read_lock(); buf1 = tomoyo_get_path(path1); @@ -1278,26 +1235,22 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, } } } - error = tomoyo_path2_acl(domain, operation, buf1, buf2); - msg = tomoyo_path22keyword(operation); + error = tomoyo_path2_acl(&r, operation, buf1, buf2); if (!error) goto out; - if (tomoyo_verbose_mode(domain)) - printk(KERN_WARNING "TOMOYO-%s: Access '%s %s %s' " - "denied for %s\n", tomoyo_get_msg(is_enforce), - msg, buf1->name, buf2->name, - tomoyo_get_last_name(domain)); - if (mode == 1 && tomoyo_domain_quota_is_ok(domain)) { + tomoyo_warn_log(&r, "%s %s %s", tomoyo_path22keyword(operation), + buf1->name, buf2->name); + if (tomoyo_domain_quota_is_ok(&r)) { const char *name1 = tomoyo_get_file_pattern(buf1)->name; const char *name2 = tomoyo_get_file_pattern(buf2)->name; - tomoyo_update_path2_acl(operation, name1, name2, domain, + tomoyo_update_path2_acl(operation, name1, name2, r.domain, false); } out: kfree(buf1); kfree(buf2); tomoyo_read_unlock(idx); - if (!is_enforce) + if (r.mode != TOMOYO_CONFIG_ENFORCING) error = 0; return error; } diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index dedd97d0c16..4120f5a0e1b 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -173,7 +173,7 @@ static int tomoyo_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg) { if (cmd == F_SETFL && ((arg ^ file->f_flags) & O_APPEND)) - return tomoyo_check_rewrite_permission(file); + return tomoyo_path_perm(TOMOYO_TYPE_REWRITE, &file->f_path); return 0; } -- cgit v1.2.3-70-g09d2 From a1f9bb6a375a8dbf7797ffbd6739c46b338a77f7 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 17 May 2010 10:09:15 +0900 Subject: TOMOYO: Split file access control functions by type of parameters. Check numeric parameters for operations that deal them (e.g. chmod/chown/ioctl). Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 105 ++++++++- security/tomoyo/common.h | 126 ++++++++-- security/tomoyo/file.c | 588 ++++++++++++++++++++++++++++++++++++++++------- security/tomoyo/gc.c | 35 ++- security/tomoyo/tomoyo.c | 21 +- 5 files changed, 752 insertions(+), 123 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 34d65871096..0706b175fdb 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1043,12 +1043,11 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) return true; list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { switch (ptr->type) { - struct tomoyo_path_acl *acl; - u32 perm; + u16 perm; u8 i; case TOMOYO_TYPE_PATH_ACL: - acl = container_of(ptr, struct tomoyo_path_acl, head); - perm = acl->perm | (((u32) acl->perm_high) << 16); + perm = container_of(ptr, struct tomoyo_path_acl, head) + ->perm; for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) if (perm & (1 << i)) count++; @@ -1062,6 +1061,20 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) if (perm & (1 << i)) count++; break; + case TOMOYO_TYPE_PATH_NUMBER_ACL: + perm = container_of(ptr, struct tomoyo_path_number_acl, + head)->perm; + for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++) + if (perm & (1 << i)) + count++; + break; + case TOMOYO_TYPE_PATH_NUMBER3_ACL: + perm = container_of(ptr, struct tomoyo_path_number3_acl, + head)->perm; + for (i = 0; i < TOMOYO_MAX_PATH_NUMBER3_OPERATION; i++) + if (perm & (1 << i)) + count++; + break; } } if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) @@ -1579,7 +1592,7 @@ static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head, { int pos; u8 bit; - const u32 perm = ptr->perm | (((u32) ptr->perm_high) << 16); + const u16 perm = ptr->perm; for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { if (!(perm & (1 << bit))) @@ -1637,6 +1650,76 @@ static bool tomoyo_print_path2_acl(struct tomoyo_io_buffer *head, return false; } +/** + * tomoyo_print_path_number_acl - Print a path_number ACL entry. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_path_number_acl". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_print_path_number_acl(struct tomoyo_io_buffer *head, + struct tomoyo_path_number_acl *ptr) +{ + int pos; + u8 bit; + const u8 perm = ptr->perm; + for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; + bit++) { + if (!(perm & (1 << bit))) + continue; + pos = head->read_avail; + if (!tomoyo_io_printf(head, "allow_%s", + tomoyo_path_number2keyword(bit)) || + !tomoyo_print_name_union(head, &ptr->name) || + !tomoyo_print_number_union(head, &ptr->number) || + !tomoyo_io_printf(head, "\n")) + goto out; + } + head->read_bit = 0; + return true; + out: + head->read_bit = bit; + head->read_avail = pos; + return false; +} + +/** + * tomoyo_print_path_number3_acl - Print a path_number3 ACL entry. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_path_number3_acl". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_print_path_number3_acl(struct tomoyo_io_buffer *head, + struct tomoyo_path_number3_acl *ptr) +{ + int pos; + u8 bit; + const u16 perm = ptr->perm; + for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_NUMBER3_OPERATION; + bit++) { + if (!(perm & (1 << bit))) + continue; + pos = head->read_avail; + if (!tomoyo_io_printf(head, "allow_%s", + tomoyo_path_number32keyword(bit)) || + !tomoyo_print_name_union(head, &ptr->name) || + !tomoyo_print_number_union(head, &ptr->mode) || + !tomoyo_print_number_union(head, &ptr->major) || + !tomoyo_print_number_union(head, &ptr->minor) || + !tomoyo_io_printf(head, "\n")) + goto out; + } + head->read_bit = 0; + return true; + out: + head->read_bit = bit; + head->read_avail = pos; + return false; +} + /** * tomoyo_print_entry - Print an ACL entry. * @@ -1660,6 +1743,18 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, = container_of(ptr, struct tomoyo_path2_acl, head); return tomoyo_print_path2_acl(head, acl); } + if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) { + struct tomoyo_path_number_acl *acl + = container_of(ptr, struct tomoyo_path_number_acl, + head); + return tomoyo_print_path_number_acl(head, acl); + } + if (acl_type == TOMOYO_TYPE_PATH_NUMBER3_ACL) { + struct tomoyo_path_number3_acl *acl + = container_of(ptr, struct tomoyo_path_number3_acl, + head); + return tomoyo_print_path_number3_acl(head, acl); + } BUG(); /* This must not happen. */ return false; } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 91e2bcfd56e..565a1c11da5 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -88,17 +88,21 @@ enum tomoyo_mac_index { enum tomoyo_acl_entry_type_index { TOMOYO_TYPE_PATH_ACL, TOMOYO_TYPE_PATH2_ACL, + TOMOYO_TYPE_PATH_NUMBER_ACL, + TOMOYO_TYPE_PATH_NUMBER3_ACL, }; /* Index numbers for File Controls. */ /* - * TYPE_READ_WRITE_ACL is special. TYPE_READ_WRITE_ACL is automatically set - * if both TYPE_READ_ACL and TYPE_WRITE_ACL are set. Both TYPE_READ_ACL and - * TYPE_WRITE_ACL are automatically set if TYPE_READ_WRITE_ACL is set. - * TYPE_READ_WRITE_ACL is automatically cleared if either TYPE_READ_ACL or - * TYPE_WRITE_ACL is cleared. Both TYPE_READ_ACL and TYPE_WRITE_ACL are - * automatically cleared if TYPE_READ_WRITE_ACL is cleared. + * TOMOYO_TYPE_READ_WRITE is special. TOMOYO_TYPE_READ_WRITE is automatically + * set if both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are set. + * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically set if + * TOMOYO_TYPE_READ_WRITE is set. + * TOMOYO_TYPE_READ_WRITE is automatically cleared if either TOMOYO_TYPE_READ + * or TOMOYO_TYPE_WRITE is cleared. + * Both TOMOYO_TYPE_READ and TOMOYO_TYPE_WRITE are automatically cleared if + * TOMOYO_TYPE_READ_WRITE is cleared. */ enum tomoyo_path_acl_index { @@ -106,27 +110,23 @@ enum tomoyo_path_acl_index { TOMOYO_TYPE_EXECUTE, TOMOYO_TYPE_READ, TOMOYO_TYPE_WRITE, - TOMOYO_TYPE_CREATE, TOMOYO_TYPE_UNLINK, - TOMOYO_TYPE_MKDIR, TOMOYO_TYPE_RMDIR, - TOMOYO_TYPE_MKFIFO, - TOMOYO_TYPE_MKSOCK, - TOMOYO_TYPE_MKBLOCK, - TOMOYO_TYPE_MKCHAR, TOMOYO_TYPE_TRUNCATE, TOMOYO_TYPE_SYMLINK, TOMOYO_TYPE_REWRITE, - TOMOYO_TYPE_IOCTL, - TOMOYO_TYPE_CHMOD, - TOMOYO_TYPE_CHOWN, - TOMOYO_TYPE_CHGRP, TOMOYO_TYPE_CHROOT, TOMOYO_TYPE_MOUNT, TOMOYO_TYPE_UMOUNT, TOMOYO_MAX_PATH_OPERATION }; +enum tomoyo_path_number3_acl_index { + TOMOYO_TYPE_MKBLOCK, + TOMOYO_TYPE_MKCHAR, + TOMOYO_MAX_PATH_NUMBER3_OPERATION +}; + enum tomoyo_path2_acl_index { TOMOYO_TYPE_LINK, TOMOYO_TYPE_RENAME, @@ -134,6 +134,18 @@ enum tomoyo_path2_acl_index { TOMOYO_MAX_PATH2_OPERATION }; +enum tomoyo_path_number_acl_index { + TOMOYO_TYPE_CREATE, + TOMOYO_TYPE_MKDIR, + TOMOYO_TYPE_MKFIFO, + TOMOYO_TYPE_MKSOCK, + TOMOYO_TYPE_IOCTL, + TOMOYO_TYPE_CHMOD, + TOMOYO_TYPE_CHOWN, + TOMOYO_TYPE_CHGRP, + TOMOYO_MAX_PATH_NUMBER_OPERATION +}; + enum tomoyo_securityfs_interface_index { TOMOYO_DOMAINPOLICY, TOMOYO_EXCEPTIONPOLICY, @@ -347,19 +359,61 @@ struct tomoyo_domain_info { * (3) "name" is the pathname. * * Directives held by this structure are "allow_read/write", "allow_execute", - * "allow_read", "allow_write", "allow_create", "allow_unlink", "allow_mkdir", - * "allow_rmdir", "allow_mkfifo", "allow_mksock", "allow_mkblock", - * "allow_mkchar", "allow_truncate", "allow_symlink", "allow_rewrite", - * "allow_ioctl", "allow_chmod", "allow_chown", "allow_chgrp", "allow_chroot", + * "allow_read", "allow_write", "allow_unlink", "allow_rmdir", + * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_chroot", * "allow_mount" and "allow_unmount". */ struct tomoyo_path_acl { struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */ - u8 perm_high; u16 perm; struct tomoyo_name_union name; }; +/* + * tomoyo_path_number_acl is a structure which is used for holding an + * entry with one pathname and one number operation. + * It has following fields. + * + * (1) "head" which is a "struct tomoyo_acl_info". + * (2) "perm" which is a bitmask of permitted operations. + * (3) "name" is the pathname. + * (4) "number" is the numeric value. + * + * Directives held by this structure are "allow_create", "allow_mkdir", + * "allow_ioctl", "allow_mkfifo", "allow_mksock", "allow_chmod", "allow_chown" + * and "allow_chgrp". + * + */ +struct tomoyo_path_number_acl { + struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER_ACL */ + u8 perm; + struct tomoyo_name_union name; + struct tomoyo_number_union number; +}; + +/* + * tomoyo_path_number3_acl is a structure which is used for holding an + * entry with one pathname and three numbers operation. + * It has following fields. + * + * (1) "head" which is a "struct tomoyo_acl_info". + * (2) "perm" which is a bitmask of permitted operations. + * (3) "mode" is the create mode. + * (4) "major" is the major number of device node. + * (5) "minor" is the minor number of device node. + * + * Directives held by this structure are "allow_mkchar", "allow_mkblock". + * + */ +struct tomoyo_path_number3_acl { + struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER3_ACL */ + u8 perm; + struct tomoyo_name_union name; + struct tomoyo_number_union mode; + struct tomoyo_number_union major; + struct tomoyo_number_union minor; +}; + /* * tomoyo_path2_acl is a structure which is used for holding an * entry with two pathnames operation (i.e. link(), rename() and pivot_root()). @@ -639,6 +693,8 @@ bool tomoyo_tokenize(char *buffer, char *w[], size_t size); bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain); /* Convert double path operation to operation name. */ const char *tomoyo_path22keyword(const u8 operation); +const char *tomoyo_path_number2keyword(const u8 operation); +const char *tomoyo_path_number32keyword(const u8 operation); /* Get the last component of the given domainname. */ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain); /* Convert single path operation to operation name. */ @@ -736,11 +792,18 @@ int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, const struct tomoyo_path_info *filename); int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct path *path, const int flag); +int tomoyo_path_number_perm(const u8 operation, struct path *path, + unsigned long number); +int tomoyo_path_number3_perm(const u8 operation, struct path *path, + const unsigned int mode, unsigned int dev); int tomoyo_path_perm(const u8 operation, struct path *path); int tomoyo_path2_perm(const u8 operation, struct path *path1, struct path *path2); int tomoyo_find_next_domain(struct linux_binprm *bprm); +void tomoyo_print_ulong(char *buffer, const int buffer_len, + const unsigned long value, const u8 type); + /* Drop refcount on tomoyo_name_union. */ void tomoyo_put_name_union(struct tomoyo_name_union *ptr); @@ -880,6 +943,18 @@ static inline bool tomoyo_is_same_path_acl(const struct tomoyo_path_acl *p1, tomoyo_is_same_name_union(&p1->name, &p2->name); } +static inline bool tomoyo_is_same_path_number3_acl +(const struct tomoyo_path_number3_acl *p1, + const struct tomoyo_path_number3_acl *p2) +{ + return tomoyo_is_same_acl_head(&p1->head, &p2->head) + && tomoyo_is_same_name_union(&p1->name, &p2->name) + && tomoyo_is_same_number_union(&p1->mode, &p2->mode) + && tomoyo_is_same_number_union(&p1->major, &p2->major) + && tomoyo_is_same_number_union(&p1->minor, &p2->minor); +} + + static inline bool tomoyo_is_same_path2_acl(const struct tomoyo_path2_acl *p1, const struct tomoyo_path2_acl *p2) { @@ -888,6 +963,15 @@ static inline bool tomoyo_is_same_path2_acl(const struct tomoyo_path2_acl *p1, tomoyo_is_same_name_union(&p1->name2, &p2->name2); } +static inline bool tomoyo_is_same_path_number_acl +(const struct tomoyo_path_number_acl *p1, + const struct tomoyo_path_number_acl *p2) +{ + return tomoyo_is_same_acl_head(&p1->head, &p2->head) + && tomoyo_is_same_name_union(&p1->name, &p2->name) + && tomoyo_is_same_number_union(&p1->number, &p2->number); +} + static inline bool tomoyo_is_same_domain_initializer_entry (const struct tomoyo_domain_initializer_entry *p1, const struct tomoyo_domain_initializer_entry *p2) diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index f1d2adfd33b..727cc723f87 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -12,39 +12,49 @@ #include "common.h" #include -/* Keyword array for single path operations. */ +/* Keyword array for operations with one pathname. */ static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { [TOMOYO_TYPE_READ_WRITE] = "read/write", [TOMOYO_TYPE_EXECUTE] = "execute", [TOMOYO_TYPE_READ] = "read", [TOMOYO_TYPE_WRITE] = "write", - [TOMOYO_TYPE_CREATE] = "create", [TOMOYO_TYPE_UNLINK] = "unlink", - [TOMOYO_TYPE_MKDIR] = "mkdir", [TOMOYO_TYPE_RMDIR] = "rmdir", - [TOMOYO_TYPE_MKFIFO] = "mkfifo", - [TOMOYO_TYPE_MKSOCK] = "mksock", - [TOMOYO_TYPE_MKBLOCK] = "mkblock", - [TOMOYO_TYPE_MKCHAR] = "mkchar", [TOMOYO_TYPE_TRUNCATE] = "truncate", [TOMOYO_TYPE_SYMLINK] = "symlink", [TOMOYO_TYPE_REWRITE] = "rewrite", - [TOMOYO_TYPE_IOCTL] = "ioctl", - [TOMOYO_TYPE_CHMOD] = "chmod", - [TOMOYO_TYPE_CHOWN] = "chown", - [TOMOYO_TYPE_CHGRP] = "chgrp", [TOMOYO_TYPE_CHROOT] = "chroot", [TOMOYO_TYPE_MOUNT] = "mount", [TOMOYO_TYPE_UMOUNT] = "unmount", }; -/* Keyword array for double path operations. */ +/* Keyword array for operations with one pathname and three numbers. */ +static const char *tomoyo_path_number3_keyword +[TOMOYO_MAX_PATH_NUMBER3_OPERATION] = { + [TOMOYO_TYPE_MKBLOCK] = "mkblock", + [TOMOYO_TYPE_MKCHAR] = "mkchar", +}; + +/* Keyword array for operations with two pathnames. */ static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = { - [TOMOYO_TYPE_LINK] = "link", - [TOMOYO_TYPE_RENAME] = "rename", + [TOMOYO_TYPE_LINK] = "link", + [TOMOYO_TYPE_RENAME] = "rename", [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root", }; +/* Keyword array for operations with one pathname and one number. */ +static const char *tomoyo_path_number_keyword +[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { + [TOMOYO_TYPE_CREATE] = "create", + [TOMOYO_TYPE_MKDIR] = "mkdir", + [TOMOYO_TYPE_MKFIFO] = "mkfifo", + [TOMOYO_TYPE_MKSOCK] = "mksock", + [TOMOYO_TYPE_IOCTL] = "ioctl", + [TOMOYO_TYPE_CHMOD] = "chmod", + [TOMOYO_TYPE_CHOWN] = "chown", + [TOMOYO_TYPE_CHGRP] = "chgrp", +}; + void tomoyo_put_name_union(struct tomoyo_name_union *ptr) { if (!ptr) @@ -158,6 +168,19 @@ const char *tomoyo_path2keyword(const u8 operation) ? tomoyo_path_keyword[operation] : NULL; } +/** + * tomoyo_path_number32keyword - Get the name of path/number/number/number operations. + * + * @operation: Type of operation. + * + * Returns the name of path/number/number/number operation. + */ +const char *tomoyo_path_number32keyword(const u8 operation) +{ + return (operation < TOMOYO_MAX_PATH_NUMBER3_OPERATION) + ? tomoyo_path_number3_keyword[operation] : NULL; +} + /** * tomoyo_path22keyword - Get the name of double path operation. * @@ -171,6 +194,19 @@ const char *tomoyo_path22keyword(const u8 operation) ? tomoyo_path2_keyword[operation] : NULL; } +/** + * tomoyo_path_number2keyword - Get the name of path/number operations. + * + * @operation: Type of operation. + * + * Returns the name of path/number operation. + */ +const char *tomoyo_path_number2keyword(const u8 operation) +{ + return (operation < TOMOYO_MAX_PATH_NUMBER_OPERATION) + ? tomoyo_path_number_keyword[operation] : NULL; +} + /** * tomoyo_strendswith - Check whether the token ends with the given token. * @@ -665,8 +701,8 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) /** * tomoyo_update_file_acl - Update file's read/write/execute ACL. * - * @filename: Filename. * @perm: Permission (between 1 to 7). + * @filename: Filename. * @domain: Pointer to "struct tomoyo_domain_info". * @is_delete: True if it is a delete request. * @@ -679,7 +715,7 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_update_file_acl(const char *filename, u8 perm, +static int tomoyo_update_file_acl(u8 perm, const char *filename, struct tomoyo_domain_info * const domain, const bool is_delete) { @@ -731,14 +767,8 @@ static int tomoyo_path_acl(const struct tomoyo_request_info *r, if (ptr->type != TOMOYO_TYPE_PATH_ACL) continue; acl = container_of(ptr, struct tomoyo_path_acl, head); - if (perm <= 0xFFFF) { - if (!(acl->perm & perm)) - continue; - } else { - if (!(acl->perm_high & (perm >> 16))) - continue; - } - if (!tomoyo_compare_name_union_pattern(filename, &acl->name, + if (!(acl->perm & perm) || + !tomoyo_compare_name_union_pattern(filename, &acl->name, may_use_pattern)) continue; error = 0; @@ -796,60 +826,12 @@ static int tomoyo_file_perm(struct tomoyo_request_info *r, /* Don't use patterns for execute permission. */ const struct tomoyo_path_info *patterned_file = (mode != 1) ? tomoyo_get_file_pattern(filename) : filename; - tomoyo_update_file_acl(patterned_file->name, mode, - r->domain, false); + tomoyo_update_file_acl(mode, patterned_file->name, r->domain, + false); } return 0; } -/** - * tomoyo_write_file_policy - Update file related list. - * - * @data: String to parse. - * @domain: Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, - const bool is_delete) -{ - char *filename = strchr(data, ' '); - char *filename2; - unsigned int perm; - u8 type; - - if (!filename) - return -EINVAL; - *filename++ = '\0'; - if (sscanf(data, "%u", &perm) == 1) - return tomoyo_update_file_acl(filename, (u8) perm, domain, - is_delete); - if (strncmp(data, "allow_", 6)) - goto out; - data += 6; - for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) { - if (strcmp(data, tomoyo_path_keyword[type])) - continue; - return tomoyo_update_path_acl(type, filename, domain, - is_delete); - } - filename2 = strchr(filename, ' '); - if (!filename2) - goto out; - *filename2++ = '\0'; - for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) { - if (strcmp(data, tomoyo_path2_keyword[type])) - continue; - return tomoyo_update_path2_acl(type, filename, filename2, - domain, is_delete); - } - out: - return -EINVAL; -} - /** * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list. * @@ -866,13 +848,12 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename, struct tomoyo_domain_info *const domain, const bool is_delete) { - static const u32 tomoyo_rw_mask = + static const u16 tomoyo_rw_mask = (1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE); - const u32 perm = 1 << type; + const u16 perm = 1 << type; struct tomoyo_acl_info *ptr; struct tomoyo_path_acl e = { .head.type = TOMOYO_TYPE_PATH_ACL, - .perm_high = perm >> 16, .perm = perm }; int error = is_delete ? -ENOENT : -ENOMEM; @@ -891,19 +872,13 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename, if (!tomoyo_is_same_path_acl(acl, &e)) continue; if (is_delete) { - if (perm <= 0xFFFF) - acl->perm &= ~perm; - else - acl->perm_high &= ~(perm >> 16); + acl->perm &= ~perm; if ((acl->perm & tomoyo_rw_mask) != tomoyo_rw_mask) acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE); else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE))) acl->perm &= ~tomoyo_rw_mask; } else { - if (perm <= 0xFFFF) - acl->perm |= perm; - else - acl->perm_high |= (perm >> 16); + acl->perm |= perm; if ((acl->perm & tomoyo_rw_mask) == tomoyo_rw_mask) acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE; else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE)) @@ -927,6 +902,71 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename, return error; } +/** + * tomoyo_update_path_number3_acl - Update "struct tomoyo_path_number3_acl" list. + * + * @type: Type of operation. + * @filename: Filename. + * @mode: Create mode. + * @major: Device major number. + * @minor: Device minor number. + * @domain: Pointer to "struct tomoyo_domain_info". + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +static inline int tomoyo_update_path_number3_acl(const u8 type, + const char *filename, + char *mode, + char *major, char *minor, + struct tomoyo_domain_info * + const domain, + const bool is_delete) +{ + const u8 perm = 1 << type; + struct tomoyo_acl_info *ptr; + struct tomoyo_path_number3_acl e = { + .head.type = TOMOYO_TYPE_PATH_NUMBER3_ACL, + .perm = perm + }; + int error = is_delete ? -ENOENT : -ENOMEM; + if (!tomoyo_parse_name_union(filename, &e.name) || + !tomoyo_parse_number_union(mode, &e.mode) || + !tomoyo_parse_number_union(major, &e.major) || + !tomoyo_parse_number_union(minor, &e.minor)) + goto out; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path_number3_acl *acl = + container_of(ptr, struct tomoyo_path_number3_acl, head); + if (!tomoyo_is_same_path_number3_acl(acl, &e)) + continue; + if (is_delete) + acl->perm &= ~perm; + else + acl->perm |= perm; + error = 0; + break; + } + if (!is_delete && error) { + struct tomoyo_path_number3_acl *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->head.list, + &domain->acl_info_list); + error = 0; + } + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_name_union(&e.name); + tomoyo_put_number_union(&e.mode); + tomoyo_put_number_union(&e.major); + tomoyo_put_number_union(&e.minor); + return error; +} + /** * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list. * @@ -988,6 +1028,50 @@ static int tomoyo_update_path2_acl(const u8 type, const char *filename1, return error; } +/** + * tomoyo_path_number3_acl - Check permission for path/number/number/number operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @filename: Filename to check. + * @perm: Permission. + * @mode: Create mode. + * @major: Device major number. + * @minor: Device minor number. + * + * Returns 0 on success, -EPERM otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_path_number3_acl(struct tomoyo_request_info *r, + const struct tomoyo_path_info *filename, + const u16 perm, const unsigned int mode, + const unsigned int major, + const unsigned int minor) +{ + struct tomoyo_domain_info *domain = r->domain; + struct tomoyo_acl_info *ptr; + int error = -EPERM; + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path_number3_acl *acl; + if (ptr->type != TOMOYO_TYPE_PATH_NUMBER3_ACL) + continue; + acl = container_of(ptr, struct tomoyo_path_number3_acl, head); + if (!tomoyo_compare_number_union(mode, &acl->mode)) + continue; + if (!tomoyo_compare_number_union(major, &acl->major)) + continue; + if (!tomoyo_compare_number_union(minor, &acl->minor)) + continue; + if (!(acl->perm & perm)) + continue; + if (!tomoyo_compare_name_union(filename, &acl->name)) + continue; + error = 0; + break; + } + return error; +} + /** * tomoyo_path2_acl - Check permission for double path operation. * @@ -1068,6 +1152,195 @@ static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, return error; } +/** + * tomoyo_path_number_acl - Check permission for ioctl/chmod/chown/chgrp operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @type: Operation. + * @filename: Filename to check. + * @number: Number. + * + * Returns 0 on success, -EPERM otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_path_number_acl(struct tomoyo_request_info *r, const u8 type, + const struct tomoyo_path_info *filename, + const unsigned long number) +{ + struct tomoyo_domain_info *domain = r->domain; + struct tomoyo_acl_info *ptr; + const u8 perm = 1 << type; + int error = -EPERM; + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path_number_acl *acl; + if (ptr->type != TOMOYO_TYPE_PATH_NUMBER_ACL) + continue; + acl = container_of(ptr, struct tomoyo_path_number_acl, + head); + if (!(acl->perm & perm) || + !tomoyo_compare_number_union(number, &acl->number) || + !tomoyo_compare_name_union(filename, &acl->name)) + continue; + error = 0; + break; + } + return error; +} + +/** + * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL. + * + * @type: Type of operation. + * @filename: Filename. + * @number: Number. + * @domain: Pointer to "struct tomoyo_domain_info". + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +static inline int tomoyo_update_path_number_acl(const u8 type, + const char *filename, + char *number, + struct tomoyo_domain_info * + const domain, + const bool is_delete) +{ + const u8 perm = 1 << type; + struct tomoyo_acl_info *ptr; + struct tomoyo_path_number_acl e = { + .head.type = TOMOYO_TYPE_PATH_NUMBER_ACL, + .perm = perm + }; + int error = is_delete ? -ENOENT : -ENOMEM; + if (!domain) + return -EINVAL; + if (!tomoyo_parse_name_union(filename, &e.name)) + return -EINVAL; + if (!tomoyo_parse_number_union(number, &e.number)) + goto out; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_path_number_acl *acl = + container_of(ptr, struct tomoyo_path_number_acl, head); + if (!tomoyo_is_same_path_number_acl(acl, &e)) + continue; + if (is_delete) + acl->perm &= ~perm; + else + acl->perm |= perm; + error = 0; + break; + } + if (!is_delete && error) { + struct tomoyo_path_number_acl *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->head.list, + &domain->acl_info_list); + error = 0; + } + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_name_union(&e.name); + tomoyo_put_number_union(&e.number); + return error; +} + +/** + * tomoyo_path_number_perm2 - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp". + * + * @r: Pointer to "strct tomoyo_request_info". + * @filename: Filename to check. + * @number: Number. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_path_number_perm2(struct tomoyo_request_info *r, + const u8 type, + const struct tomoyo_path_info *filename, + const unsigned long number) +{ + char buffer[64]; + int error; + u8 radix; + + if (!filename) + return 0; + switch (type) { + case TOMOYO_TYPE_CREATE: + case TOMOYO_TYPE_MKDIR: + case TOMOYO_TYPE_MKFIFO: + case TOMOYO_TYPE_MKSOCK: + case TOMOYO_TYPE_CHMOD: + radix = TOMOYO_VALUE_TYPE_OCTAL; + break; + case TOMOYO_TYPE_IOCTL: + radix = TOMOYO_VALUE_TYPE_HEXADECIMAL; + break; + default: + radix = TOMOYO_VALUE_TYPE_DECIMAL; + break; + } + tomoyo_print_ulong(buffer, sizeof(buffer), number, radix); + error = tomoyo_path_number_acl(r, type, filename, number); + if (!error) + return 0; + tomoyo_warn_log(r, "%s %s %s", tomoyo_path_number2keyword(type), + filename->name, buffer); + if (tomoyo_domain_quota_is_ok(r)) + tomoyo_update_path_number_acl(type, + tomoyo_get_file_pattern(filename) + ->name, buffer, r->domain, false); + if (r->mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + return error; +} + +/** + * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp". + * + * @type: Type of operation. + * @path: Pointer to "struct path". + * @number: Number. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_path_number_perm(const u8 type, struct path *path, + unsigned long number) +{ + struct tomoyo_request_info r; + int error = -ENOMEM; + struct tomoyo_path_info *buf; + int idx; + + if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || + !path->mnt || !path->dentry) + return 0; + idx = tomoyo_read_lock(); + buf = tomoyo_get_path(path); + if (!buf) + goto out; + if (type == TOMOYO_TYPE_MKDIR && !buf->is_dir) { + /* + * tomoyo_get_path() reserves space for appending "/." + */ + strcat((char *) buf->name, "/"); + tomoyo_fill_path_info(buf); + } + error = tomoyo_path_number_perm2(&r, type, buf, number); + out: + kfree(buf); + tomoyo_read_unlock(idx); + if (r.mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + return error; +} + /** * tomoyo_check_exec_perm - Check permission for "execute". * @@ -1145,7 +1418,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, } /** - * tomoyo_path_perm - Check permission for "create", "unlink", "mkdir", "rmdir", "mkfifo", "mksock", "mkblock", "mkchar", "truncate", "symlink", "rewrite", "ioctl", "chmod", "chown", "chgrp", "chroot", "mount" and "unmount". + * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot", "mount" and "unmount". * * @operation: Type of operation. * @path: Pointer to "struct path". @@ -1173,7 +1446,6 @@ int tomoyo_path_perm(const u8 operation, struct path *path) goto out; } break; - case TOMOYO_TYPE_MKDIR: case TOMOYO_TYPE_RMDIR: case TOMOYO_TYPE_CHROOT: if (!buf->is_dir) { @@ -1193,6 +1465,91 @@ int tomoyo_path_perm(const u8 operation, struct path *path) return error; } +/** + * tomoyo_path_number3_perm2 - Check permission for path/number/number/number operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @operation: Type of operation. + * @filename: Filename to check. + * @mode: Create mode. + * @dev: Device number. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_path_number3_perm2(struct tomoyo_request_info *r, + const u8 operation, + const struct tomoyo_path_info *filename, + const unsigned int mode, + const unsigned int dev) +{ + int error; + const unsigned int major = MAJOR(dev); + const unsigned int minor = MINOR(dev); + + error = tomoyo_path_number3_acl(r, filename, 1 << operation, mode, + major, minor); + if (!error) + return 0; + tomoyo_warn_log(r, "%s %s 0%o %u %u", + tomoyo_path_number32keyword(operation), + filename->name, mode, major, minor); + if (tomoyo_domain_quota_is_ok(r)) { + char mode_buf[64]; + char major_buf[64]; + char minor_buf[64]; + memset(mode_buf, 0, sizeof(mode_buf)); + memset(major_buf, 0, sizeof(major_buf)); + memset(minor_buf, 0, sizeof(minor_buf)); + snprintf(mode_buf, sizeof(mode_buf) - 1, "0%o", mode); + snprintf(major_buf, sizeof(major_buf) - 1, "%u", major); + snprintf(minor_buf, sizeof(minor_buf) - 1, "%u", minor); + tomoyo_update_path_number3_acl(operation, + tomoyo_get_file_pattern(filename) + ->name, mode_buf, major_buf, + minor_buf, r->domain, false); + } + if (r->mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + return error; +} + +/** + * tomoyo_path_number3_perm - Check permission for "mkblock" and "mkchar". + * + * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK) + * @path: Pointer to "struct path". + * @mode: Create mode. + * @dev: Device number. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_path_number3_perm(const u8 operation, struct path *path, + const unsigned int mode, unsigned int dev) +{ + struct tomoyo_request_info r; + int error = -ENOMEM; + struct tomoyo_path_info *buf; + int idx; + + if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || + !path->mnt) + return 0; + idx = tomoyo_read_lock(); + error = -ENOMEM; + buf = tomoyo_get_path(path); + if (buf) { + error = tomoyo_path_number3_perm2(&r, operation, buf, mode, + new_decode_dev(dev)); + kfree(buf); + } + tomoyo_read_unlock(idx); + if (r.mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + return error; +} + /** * tomoyo_path2_perm - Check permission for "rename", "link" and "pivot_root". * @@ -1254,3 +1611,60 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, error = 0; return error; } + +/** + * tomoyo_write_file_policy - Update file related list. + * + * @data: String to parse. + * @domain: Pointer to "struct tomoyo_domain_info". + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, + const bool is_delete) +{ + char *w[5]; + u8 type; + if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) + return -EINVAL; + if (strncmp(w[0], "allow_", 6)) { + unsigned int perm; + if (sscanf(w[0], "%u", &perm) == 1) + return tomoyo_update_file_acl((u8) perm, w[1], domain, + is_delete); + goto out; + } + w[0] += 6; + for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) { + if (strcmp(w[0], tomoyo_path_keyword[type])) + continue; + return tomoyo_update_path_acl(type, w[1], domain, is_delete); + } + if (!w[2][0]) + goto out; + for (type = 0; type < TOMOYO_MAX_PATH2_OPERATION; type++) { + if (strcmp(w[0], tomoyo_path2_keyword[type])) + continue; + return tomoyo_update_path2_acl(type, w[1], w[2], domain, + is_delete); + } + for (type = 0; type < TOMOYO_MAX_PATH_NUMBER_OPERATION; type++) { + if (strcmp(w[0], tomoyo_path_number_keyword[type])) + continue; + return tomoyo_update_path_number_acl(type, w[1], w[2], domain, + is_delete); + } + if (!w[3][0] || !w[4][0]) + goto out; + for (type = 0; type < TOMOYO_MAX_PATH_NUMBER3_OPERATION; type++) { + if (strcmp(w[0], tomoyo_path_number3_keyword[type])) + continue; + return tomoyo_update_path_number3_acl(type, w[1], w[2], w[3], + w[4], domain, is_delete); + } + out: + return -EINVAL; +} diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 6a48197f6ce..78100180d23 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -106,6 +106,24 @@ static void tomoyo_del_acl(struct tomoyo_acl_info *acl) tomoyo_put_name_union(&entry->name2); } break; + case TOMOYO_TYPE_PATH_NUMBER_ACL: + { + struct tomoyo_path_number_acl *entry + = container_of(acl, typeof(*entry), head); + tomoyo_put_name_union(&entry->name); + tomoyo_put_number_union(&entry->number); + } + break; + case TOMOYO_TYPE_PATH_NUMBER3_ACL: + { + struct tomoyo_path_number3_acl *entry + = container_of(acl, typeof(*entry), head); + tomoyo_put_name_union(&entry->name); + tomoyo_put_number_union(&entry->mode); + tomoyo_put_number_union(&entry->major); + tomoyo_put_number_union(&entry->minor); + } + break; default: printk(KERN_WARNING "Unknown type\n"); break; @@ -268,10 +286,7 @@ static void tomoyo_collect_entry(void) case TOMOYO_TYPE_PATH_ACL: if (container_of(acl, struct tomoyo_path_acl, - head)->perm || - container_of(acl, - struct tomoyo_path_acl, - head)->perm_high) + head)->perm) continue; break; case TOMOYO_TYPE_PATH2_ACL: @@ -280,6 +295,18 @@ static void tomoyo_collect_entry(void) head)->perm) continue; break; + case TOMOYO_TYPE_PATH_NUMBER_ACL: + if (container_of(acl, + struct tomoyo_path_number_acl, + head)->perm) + continue; + break; + case TOMOYO_TYPE_PATH_NUMBER3_ACL: + if (container_of(acl, + struct tomoyo_path_number3_acl, + head)->perm) + continue; + break; default: continue; } diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 4120f5a0e1b..bbe00429b3f 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -112,7 +112,8 @@ static int tomoyo_path_mkdir(struct path *parent, struct dentry *dentry, int mode) { struct path path = { parent->mnt, dentry }; - return tomoyo_path_perm(TOMOYO_TYPE_MKDIR, &path); + return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path, + mode & S_IALLUGO); } static int tomoyo_path_rmdir(struct path *parent, struct dentry *dentry) @@ -133,6 +134,7 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, { struct path path = { parent->mnt, dentry }; int type = TOMOYO_TYPE_CREATE; + const unsigned int perm = mode & S_IALLUGO; switch (mode & S_IFMT) { case S_IFCHR: @@ -141,6 +143,12 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, case S_IFBLK: type = TOMOYO_TYPE_MKBLOCK; break; + default: + goto no_dev; + } + return tomoyo_path_number3_perm(type, &path, perm, dev); + no_dev: + switch (mode & S_IFMT) { case S_IFIFO: type = TOMOYO_TYPE_MKFIFO; break; @@ -148,7 +156,7 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, type = TOMOYO_TYPE_MKSOCK; break; } - return tomoyo_path_perm(type, &path); + return tomoyo_path_number_perm(type, &path, perm); } static int tomoyo_path_link(struct dentry *old_dentry, struct path *new_dir, @@ -189,23 +197,24 @@ static int tomoyo_dentry_open(struct file *f, const struct cred *cred) static int tomoyo_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - return tomoyo_path_perm(TOMOYO_TYPE_IOCTL, &file->f_path); + return tomoyo_path_number_perm(TOMOYO_TYPE_IOCTL, &file->f_path, cmd); } static int tomoyo_path_chmod(struct dentry *dentry, struct vfsmount *mnt, mode_t mode) { struct path path = { mnt, dentry }; - return tomoyo_path_perm(TOMOYO_TYPE_CHMOD, &path); + return tomoyo_path_number_perm(TOMOYO_TYPE_CHMOD, &path, + mode & S_IALLUGO); } static int tomoyo_path_chown(struct path *path, uid_t uid, gid_t gid) { int error = 0; if (uid != (uid_t) -1) - error = tomoyo_path_perm(TOMOYO_TYPE_CHOWN, path); + error = tomoyo_path_number_perm(TOMOYO_TYPE_CHOWN, path, uid); if (!error && gid != (gid_t) -1) - error = tomoyo_path_perm(TOMOYO_TYPE_CHGRP, path); + error = tomoyo_path_number_perm(TOMOYO_TYPE_CHGRP, path, gid); return error; } -- cgit v1.2.3-70-g09d2 From 2106ccd972dcd9fda7df9b181505fac1741b3508 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 17 May 2010 10:10:31 +0900 Subject: TOMOYO: Add mount restriction. mount(2) has three string and one numeric parameters. Split mount restriction code from security/tomoyo/file.c . Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/Makefile | 2 +- security/tomoyo/common.c | 35 +++++ security/tomoyo/common.h | 56 +++++++- security/tomoyo/file.c | 9 +- security/tomoyo/gc.c | 10 ++ security/tomoyo/mount.c | 366 +++++++++++++++++++++++++++++++++++++++++++++++ security/tomoyo/tomoyo.c | 2 +- 7 files changed, 469 insertions(+), 11 deletions(-) create mode 100644 security/tomoyo/mount.c diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index 4d1b5af4f1f..d7befab40ef 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile @@ -1 +1 @@ -obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o number_group.o +obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o number_group.o mount.o diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 0706b175fdb..0c6f9a5c37a 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1075,6 +1075,10 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) if (perm & (1 << i)) count++; break; + case TOMOYO_TYPE_MOUNT_ACL: + if (!container_of(ptr, struct tomoyo_mount_acl, head)-> + is_deleted) + count++; } } if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) @@ -1576,6 +1580,8 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) domain->ignore_global_allow_read = !is_delete; return 0; } + if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT)) + return tomoyo_write_mount_policy(data, domain, is_delete); return tomoyo_write_file_policy(data, domain, is_delete); } @@ -1720,6 +1726,30 @@ static bool tomoyo_print_path_number3_acl(struct tomoyo_io_buffer *head, return false; } +/** + * tomoyo_print_mount_acl - Print a mount ACL entry. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_mount_acl". + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_print_mount_acl(struct tomoyo_io_buffer *head, + struct tomoyo_mount_acl *ptr) +{ + const int pos = head->read_avail; + if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_MOUNT) || + !tomoyo_print_name_union(head, &ptr->dev_name) || + !tomoyo_print_name_union(head, &ptr->dir_name) || + !tomoyo_print_name_union(head, &ptr->fs_type) || + !tomoyo_print_number_union(head, &ptr->flags) || + !tomoyo_io_printf(head, "\n")) { + head->read_avail = pos; + return false; + } + return true; +} + /** * tomoyo_print_entry - Print an ACL entry. * @@ -1755,6 +1785,11 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, head); return tomoyo_print_path_number3_acl(head, acl); } + if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { + struct tomoyo_mount_acl *acl + = container_of(ptr, struct tomoyo_mount_acl, head); + return tomoyo_print_mount_acl(head, acl); + } BUG(); /* This must not happen. */ return false; } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 565a1c11da5..3d819b13916 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -53,6 +53,7 @@ enum tomoyo_mode_index { /* Keywords for ACLs. */ #define TOMOYO_KEYWORD_ALIAS "alias " +#define TOMOYO_KEYWORD_ALLOW_MOUNT "allow_mount " #define TOMOYO_KEYWORD_ALLOW_READ "allow_read " #define TOMOYO_KEYWORD_DELETE "delete " #define TOMOYO_KEYWORD_DENY_REWRITE "deny_rewrite " @@ -90,6 +91,7 @@ enum tomoyo_acl_entry_type_index { TOMOYO_TYPE_PATH2_ACL, TOMOYO_TYPE_PATH_NUMBER_ACL, TOMOYO_TYPE_PATH_NUMBER3_ACL, + TOMOYO_TYPE_MOUNT_ACL, }; /* Index numbers for File Controls. */ @@ -116,7 +118,6 @@ enum tomoyo_path_acl_index { TOMOYO_TYPE_SYMLINK, TOMOYO_TYPE_REWRITE, TOMOYO_TYPE_CHROOT, - TOMOYO_TYPE_MOUNT, TOMOYO_TYPE_UMOUNT, TOMOYO_MAX_PATH_OPERATION }; @@ -360,8 +361,8 @@ struct tomoyo_domain_info { * * Directives held by this structure are "allow_read/write", "allow_execute", * "allow_read", "allow_write", "allow_unlink", "allow_rmdir", - * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_chroot", - * "allow_mount" and "allow_unmount". + * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_chroot" and + * "allow_unmount". */ struct tomoyo_path_acl { struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_ACL */ @@ -434,6 +435,29 @@ struct tomoyo_path2_acl { struct tomoyo_name_union name2; }; +/* + * tomoyo_mount_acl is a structure which is used for holding an + * entry for mount operation. + * It has following fields. + * + * (1) "head" which is a "struct tomoyo_acl_info". + * (2) "is_deleted" is boolean. + * (3) "dev_name" is the device name. + * (4) "dir_name" is the mount point. + * (5) "flags" is the mount flags. + * + * Directives held by this structure are "allow_rename", "allow_link" and + * "allow_pivot_root". + */ +struct tomoyo_mount_acl { + struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MOUNT_ACL */ + bool is_deleted; + struct tomoyo_name_union dev_name; + struct tomoyo_name_union dir_name; + struct tomoyo_name_union fs_type; + struct tomoyo_number_union flags; +}; + /* * tomoyo_io_buffer is a structure which is used for reading and modifying * configuration via /sys/kernel/security/tomoyo/ interface. @@ -638,6 +662,9 @@ struct tomoyo_policy_manager_entry { /* Check whether the given name matches the given name_union. */ bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr); +/* Check whether the given number matches the given number_union. */ +bool tomoyo_compare_number_union(const unsigned long value, + const struct tomoyo_number_union *ptr); /* Check whether the domain has too many ACL entries to hold. */ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r); /* Transactional sprintf() for policy dump. */ @@ -699,6 +726,12 @@ const char *tomoyo_path_number32keyword(const u8 operation); const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain); /* Convert single path operation to operation name. */ const char *tomoyo_path2keyword(const u8 operation); +/* Fill "struct tomoyo_request_info". */ +int tomoyo_init_request_info(struct tomoyo_request_info *r, + struct tomoyo_domain_info *domain); +/* Check permission for mount operation. */ +int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, + unsigned long flags, void *data_page); /* Create "alias" entry in exception policy. */ int tomoyo_write_alias_policy(char *data, const bool is_delete); /* @@ -721,6 +754,9 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, const bool is_delete); /* Create "allow_read" entry in exception policy. */ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete); +/* Create "allow_mount" entry in domain policy. */ +int tomoyo_write_mount_policy(char *data, struct tomoyo_domain_info *domain, + const bool is_delete); /* Create "deny_rewrite" entry in exception policy. */ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete); /* Create "file_pattern" entry in exception policy. */ @@ -735,7 +771,9 @@ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * domainname, const u8 profile); - +/* Get patterned pathname. */ +const struct tomoyo_path_info * +tomoyo_get_file_pattern(const struct tomoyo_path_info *filename); /* Allocate memory for "struct tomoyo_path_group". */ struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name); struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name); @@ -972,6 +1010,16 @@ static inline bool tomoyo_is_same_path_number_acl && tomoyo_is_same_number_union(&p1->number, &p2->number); } +static inline bool tomoyo_is_same_mount_acl(const struct tomoyo_mount_acl *p1, + const struct tomoyo_mount_acl *p2) +{ + return tomoyo_is_same_acl_head(&p1->head, &p2->head) && + tomoyo_is_same_name_union(&p1->dev_name, &p2->dev_name) && + tomoyo_is_same_name_union(&p1->dir_name, &p2->dir_name) && + tomoyo_is_same_name_union(&p1->fs_type, &p2->fs_type) && + tomoyo_is_same_number_union(&p1->flags, &p2->flags); +} + static inline bool tomoyo_is_same_domain_initializer_entry (const struct tomoyo_domain_initializer_entry *p1, const struct tomoyo_domain_initializer_entry *p2) diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 727cc723f87..ae32cab8ec7 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -24,7 +24,6 @@ static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { [TOMOYO_TYPE_SYMLINK] = "symlink", [TOMOYO_TYPE_REWRITE] = "rewrite", [TOMOYO_TYPE_CHROOT] = "chroot", - [TOMOYO_TYPE_MOUNT] = "mount", [TOMOYO_TYPE_UMOUNT] = "unmount", }; @@ -108,8 +107,8 @@ bool tomoyo_compare_number_union(const unsigned long value, * * Returns mode. */ -static int tomoyo_init_request_info(struct tomoyo_request_info *r, - struct tomoyo_domain_info *domain) +int tomoyo_init_request_info(struct tomoyo_request_info *r, + struct tomoyo_domain_info *domain) { memset(r, 0, sizeof(*r)); if (!domain) @@ -487,7 +486,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, * * Caller holds tomoyo_read_lock(). */ -static const struct tomoyo_path_info * +const struct tomoyo_path_info * tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) { struct tomoyo_pattern_entry *ptr; @@ -1418,7 +1417,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, } /** - * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot", "mount" and "unmount". + * tomoyo_path_perm - Check permission for "unlink", "rmdir", "truncate", "symlink", "rewrite", "chroot" and "unmount". * * @operation: Type of operation. * @path: Pointer to "struct path". diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 78100180d23..be2d3b93553 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -124,6 +124,16 @@ static void tomoyo_del_acl(struct tomoyo_acl_info *acl) tomoyo_put_number_union(&entry->minor); } break; + case TOMOYO_TYPE_MOUNT_ACL: + { + struct tomoyo_mount_acl *entry + = container_of(acl, typeof(*entry), head); + tomoyo_put_name_union(&entry->dev_name); + tomoyo_put_name_union(&entry->dir_name); + tomoyo_put_name_union(&entry->fs_type); + tomoyo_put_number_union(&entry->flags); + } + break; default: printk(KERN_WARNING "Unknown type\n"); break; diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c new file mode 100644 index 00000000000..507be09e93a --- /dev/null +++ b/security/tomoyo/mount.c @@ -0,0 +1,366 @@ +/* + * security/tomoyo/mount.c + * + * Copyright (C) 2005-2010 NTT DATA CORPORATION + */ + +#include +#include "common.h" + +/* Keywords for mount restrictions. */ + +/* Allow to call 'mount --bind /source_dir /dest_dir' */ +#define TOMOYO_MOUNT_BIND_KEYWORD "--bind" +/* Allow to call 'mount --move /old_dir /new_dir ' */ +#define TOMOYO_MOUNT_MOVE_KEYWORD "--move" +/* Allow to call 'mount -o remount /dir ' */ +#define TOMOYO_MOUNT_REMOUNT_KEYWORD "--remount" +/* Allow to call 'mount --make-unbindable /dir' */ +#define TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD "--make-unbindable" +/* Allow to call 'mount --make-private /dir' */ +#define TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD "--make-private" +/* Allow to call 'mount --make-slave /dir' */ +#define TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD "--make-slave" +/* Allow to call 'mount --make-shared /dir' */ +#define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD "--make-shared" + +/** + * tomoyo_encode2: Encode binary string to ascii string. + * + * @str: String in binary format. + * + * Returns pointer to @str in ascii format on success, NULL otherwise. + * + * This function uses kzalloc(), so caller must kfree() if this function + * didn't return NULL. + */ +static char *tomoyo_encode2(const char *str) +{ + int len = 0; + const char *p = str; + char *cp; + char *cp0; + if (!p) + return NULL; + while (*p) { + const unsigned char c = *p++; + if (c == '\\') + len += 2; + else if (c > ' ' && c < 127) + len++; + else + len += 4; + } + len++; + /* Reserve space for appending "/". */ + cp = kzalloc(len + 10, GFP_NOFS); + if (!cp) + return NULL; + cp0 = cp; + p = str; + while (*p) { + const unsigned char c = *p++; + if (c == '\\') { + *cp++ = '\\'; + *cp++ = '\\'; + } else if (c > ' ' && c < 127) { + *cp++ = c; + } else { + *cp++ = '\\'; + *cp++ = (c >> 6) + '0'; + *cp++ = ((c >> 3) & 7) + '0'; + *cp++ = (c & 7) + '0'; + } + } + return cp0; +} + +/** + * tomoyo_mount_acl2 - Check permission for mount() operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @dev_name: Name of device file. + * @dir: Pointer to "struct path". + * @type: Name of filesystem type. + * @flags: Mount options. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, + struct path *dir, char *type, unsigned long flags) +{ + struct path path; + struct tomoyo_acl_info *ptr; + struct file_system_type *fstype = NULL; + const char *requested_type = NULL; + const char *requested_dir_name = NULL; + const char *requested_dev_name = NULL; + struct tomoyo_path_info rtype; + struct tomoyo_path_info rdev; + struct tomoyo_path_info rdir; + int need_dev = 0; + int error = -ENOMEM; + + /* Get fstype. */ + requested_type = tomoyo_encode2(type); + if (!requested_type) + goto out; + rtype.name = requested_type; + tomoyo_fill_path_info(&rtype); + + /* Get mount point. */ + requested_dir_name = tomoyo_realpath_from_path(dir); + if (!requested_dir_name) { + error = -ENOMEM; + goto out; + } + rdir.name = requested_dir_name; + tomoyo_fill_path_info(&rdir); + + /* Compare fs name. */ + if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) { + /* dev_name is ignored. */ + } else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || + !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || + !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || + !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) { + /* dev_name is ignored. */ + } else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) || + !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) { + need_dev = -1; /* dev_name is a directory */ + } else { + fstype = get_fs_type(type); + if (!fstype) { + error = -ENODEV; + goto out; + } + if (fstype->fs_flags & FS_REQUIRES_DEV) + /* dev_name is a block device file. */ + need_dev = 1; + } + if (need_dev) { + /* Get mount point or device file. */ + if (kern_path(dev_name, LOOKUP_FOLLOW, &path)) { + error = -ENOENT; + goto out; + } + requested_dev_name = tomoyo_realpath_from_path(&path); + if (!requested_dev_name) { + error = -ENOENT; + goto out; + } + } else { + /* Map dev_name to "" if no dev_name given. */ + if (!dev_name) + dev_name = ""; + requested_dev_name = tomoyo_encode2(dev_name); + if (!requested_dev_name) { + error = -ENOMEM; + goto out; + } + } + rdev.name = requested_dev_name; + tomoyo_fill_path_info(&rdev); + list_for_each_entry_rcu(ptr, &r->domain->acl_info_list, list) { + struct tomoyo_mount_acl *acl; + if (ptr->type != TOMOYO_TYPE_MOUNT_ACL) + continue; + acl = container_of(ptr, struct tomoyo_mount_acl, head); + if (acl->is_deleted || + !tomoyo_compare_number_union(flags, &acl->flags) || + !tomoyo_compare_name_union(&rtype, &acl->fs_type) || + !tomoyo_compare_name_union(&rdir, &acl->dir_name) || + (need_dev && + !tomoyo_compare_name_union(&rdev, &acl->dev_name))) + continue; + error = 0; + break; + } + if (error) { + const char *dev = tomoyo_get_file_pattern(&rdev)->name; + const char *dir = tomoyo_get_file_pattern(&rdir)->name; + int len = strlen(dev) + strlen(dir) + strlen(requested_type) + + 64; + char *buf = kzalloc(len, GFP_NOFS); + if (buf) { + snprintf(buf, len - 1, "%s %s %s 0x%lX", + dev, dir, requested_type, flags); + tomoyo_write_mount_policy(buf, r->domain, false); + kfree(buf); + } + } + out: + kfree(requested_dev_name); + kfree(requested_dir_name); + if (fstype) + put_filesystem(fstype); + kfree(requested_type); + return error; +} + +/** + * tomoyo_mount_acl - Check permission for mount() operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @dev_name: Name of device file. + * @dir: Pointer to "struct path". + * @type: Name of filesystem type. + * @flags: Mount options. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, + struct path *dir, char *type, unsigned long flags) +{ + int error; + error = -EPERM; + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + switch (flags & (MS_REMOUNT | MS_MOVE | MS_BIND)) { + case MS_REMOUNT: + case MS_MOVE: + case MS_BIND: + case 0: + break; + default: + printk(KERN_WARNING "ERROR: " + "%s%s%sare given for single mount operation.\n", + flags & MS_REMOUNT ? "'remount' " : "", + flags & MS_MOVE ? "'move' " : "", + flags & MS_BIND ? "'bind' " : ""); + return -EINVAL; + } + switch (flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED)) { + case MS_UNBINDABLE: + case MS_PRIVATE: + case MS_SLAVE: + case MS_SHARED: + case 0: + break; + default: + printk(KERN_WARNING "ERROR: " + "%s%s%s%sare given for single mount operation.\n", + flags & MS_UNBINDABLE ? "'unbindable' " : "", + flags & MS_PRIVATE ? "'private' " : "", + flags & MS_SLAVE ? "'slave' " : "", + flags & MS_SHARED ? "'shared' " : ""); + return -EINVAL; + } + if (flags & MS_REMOUNT) + error = tomoyo_mount_acl(r, dev_name, dir, + TOMOYO_MOUNT_REMOUNT_KEYWORD, + flags & ~MS_REMOUNT); + else if (flags & MS_MOVE) + error = tomoyo_mount_acl(r, dev_name, dir, + TOMOYO_MOUNT_MOVE_KEYWORD, + flags & ~MS_MOVE); + else if (flags & MS_BIND) + error = tomoyo_mount_acl(r, dev_name, dir, + TOMOYO_MOUNT_BIND_KEYWORD, + flags & ~MS_BIND); + else if (flags & MS_UNBINDABLE) + error = tomoyo_mount_acl(r, dev_name, dir, + TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD, + flags & ~MS_UNBINDABLE); + else if (flags & MS_PRIVATE) + error = tomoyo_mount_acl(r, dev_name, dir, + TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD, + flags & ~MS_PRIVATE); + else if (flags & MS_SLAVE) + error = tomoyo_mount_acl(r, dev_name, dir, + TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD, + flags & ~MS_SLAVE); + else if (flags & MS_SHARED) + error = tomoyo_mount_acl(r, dev_name, dir, + TOMOYO_MOUNT_MAKE_SHARED_KEYWORD, + flags & ~MS_SHARED); + else + error = tomoyo_mount_acl2(r, dev_name, dir, type, flags); + if (r->mode != TOMOYO_CONFIG_ENFORCING) + error = 0; + return error; +} + +/** + * tomoyo_mount_permission - Check permission for mount() operation. + * + * @dev_name: Name of device file. + * @path: Pointer to "struct path". + * @type: Name of filesystem type. May be NULL. + * @flags: Mount options. + * @data_page: Optional data. May be NULL. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, + unsigned long flags, void *data_page) +{ + struct tomoyo_request_info r; + int error; + int idx; + + if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED) + return 0; + if (!type) + type = ""; + idx = tomoyo_read_lock(); + error = tomoyo_mount_acl(&r, dev_name, path, type, flags); + tomoyo_read_unlock(idx); + return error; +} + +/** + * tomoyo_write_mount_policy - Write "struct tomoyo_mount_acl" list. + * + * @data: String to parse. + * @domain: Pointer to "struct tomoyo_domain_info". + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_write_mount_policy(char *data, struct tomoyo_domain_info *domain, + const bool is_delete) +{ + struct tomoyo_acl_info *ptr; + struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; + int error = is_delete ? -ENOENT : -ENOMEM; + char *w[4]; + if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[3][0]) + return -EINVAL; + if (!tomoyo_parse_name_union(w[0], &e.dev_name) || + !tomoyo_parse_name_union(w[1], &e.dir_name) || + !tomoyo_parse_name_union(w[2], &e.fs_type) || + !tomoyo_parse_number_union(w[3], &e.flags)) + goto out; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + struct tomoyo_mount_acl *acl = + container_of(ptr, struct tomoyo_mount_acl, head); + if (!tomoyo_is_same_mount_acl(acl, &e)) + continue; + acl->is_deleted = is_delete; + error = 0; + break; + } + if (!is_delete && error) { + struct tomoyo_mount_acl *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->head.list, + &domain->acl_info_list); + error = 0; + } + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_name_union(&e.dev_name); + tomoyo_put_name_union(&e.dir_name); + tomoyo_put_name_union(&e.fs_type); + tomoyo_put_number_union(&e.flags); + return error; +} diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index bbe00429b3f..5d64d409b11 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -226,7 +226,7 @@ static int tomoyo_path_chroot(struct path *path) static int tomoyo_sb_mount(char *dev_name, struct path *path, char *type, unsigned long flags, void *data) { - return tomoyo_path_perm(TOMOYO_TYPE_MOUNT, path); + return tomoyo_mount_permission(dev_name, path, type, flags, data); } static int tomoyo_sb_umount(struct vfsmount *mnt, int flags) -- cgit v1.2.3-70-g09d2 From 17fcfbd9d45b57f38d40e31f9d28db53f4af5c88 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 17 May 2010 10:11:36 +0900 Subject: TOMOYO: Add interactive enforcing mode. Since the behavior of the system is restricted by policy, we may need to update policy when you update packages. We need to update policy in the following cases. * The pathname of files has changed. * The dependency of files has changed. * The access permissions required has increased. The ideal way to update policy is to rebuild from the scratch using learning mode. But it is not desirable to change from enforcing mode to other mode if the system has once entered in production state. Suppose MAC could support per-application enforcing mode, the MAC becomes useless if an application that is not running in enforcing mode was cracked. For example, the whole system becomes vulnerable if only HTTP server application is running in learning mode to rebuild policy for the application. So, in TOMOYO Linux, updating policy is done while the system is running in enforcing mode. This patch implements "interactive enforcing mode" which allows administrators to judge whether to accept policy violation in enforcing mode or not. A demo movie is available at http://www.youtube.com/watch?v=b9q1Jo25LPA . Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 426 ++++++++++++++++++++++++++++++++++++++++++++- security/tomoyo/common.h | 28 ++- security/tomoyo/domain.c | 47 +++-- security/tomoyo/file.c | 140 +++++++-------- security/tomoyo/mount.c | 24 ++- security/tomoyo/realpath.c | 19 +- 6 files changed, 564 insertions(+), 120 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 0c6f9a5c37a..ee46aaa3566 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -74,6 +74,8 @@ static int tomoyo_read_control(struct file *file, char __user *buffer, /* Write operation for /sys/kernel/security/tomoyo/ interface. */ static int tomoyo_write_control(struct file *file, const char __user *buffer, const int buffer_len); +/* Check whether the domain has too many ACL entries to hold. */ +static bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r); /** * tomoyo_parse_name_union - Parse a tomoyo_name_union. @@ -1031,7 +1033,7 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain) * * Caller holds tomoyo_read_lock(). */ -bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) +static bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) { unsigned int count = 0; struct tomoyo_domain_info *domain = r->domain; @@ -1530,6 +1532,24 @@ static int tomoyo_delete_domain(char *domainname) return 0; } +/** + * tomoyo_write_domain_policy2 - Write domain policy. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_write_domain_policy2(char *data, + struct tomoyo_domain_info *domain, + const bool is_delete) +{ + if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT)) + return tomoyo_write_mount_policy(data, domain, is_delete); + return tomoyo_write_file_policy(data, domain, is_delete); +} + /** * tomoyo_write_domain_policy - Write domain policy. * @@ -1580,9 +1600,7 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) domain->ignore_global_allow_read = !is_delete; return 0; } - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT)) - return tomoyo_write_mount_policy(data, domain, is_delete); - return tomoyo_write_file_policy(data, domain, is_delete); + return tomoyo_write_domain_policy2(data, domain, is_delete); } /** @@ -2185,6 +2203,357 @@ void tomoyo_load_policy(const char *filename) } } +/** + * tomoyo_print_header - Get header line of audit log. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns string representation. + * + * This function uses kmalloc(), so caller must kfree() if this function + * didn't return NULL. + */ +static char *tomoyo_print_header(struct tomoyo_request_info *r) +{ + static const char *tomoyo_mode_4[4] = { + "disabled", "learning", "permissive", "enforcing" + }; + struct timeval tv; + const pid_t gpid = task_pid_nr(current); + static const int tomoyo_buffer_len = 4096; + char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS); + if (!buffer) + return NULL; + do_gettimeofday(&tv); + snprintf(buffer, tomoyo_buffer_len - 1, + "#timestamp=%lu profile=%u mode=%s (global-pid=%u)" + " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u" + " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }", + tv.tv_sec, r->profile, tomoyo_mode_4[r->mode], gpid, + (pid_t) sys_getpid(), (pid_t) sys_getppid(), + current_uid(), current_gid(), current_euid(), + current_egid(), current_suid(), current_sgid(), + current_fsuid(), current_fsgid()); + return buffer; +} + +/** + * tomoyo_init_audit_log - Allocate buffer for audit logs. + * + * @len: Required size. + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns pointer to allocated memory. + * + * The @len is updated to add the header lines' size on success. + * + * This function uses kzalloc(), so caller must kfree() if this function + * didn't return NULL. + */ +static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r) +{ + char *buf = NULL; + const char *header; + const char *domainname; + if (!r->domain) + r->domain = tomoyo_domain(); + domainname = r->domain->domainname->name; + header = tomoyo_print_header(r); + if (!header) + return NULL; + *len += strlen(domainname) + strlen(header) + 10; + buf = kzalloc(*len, GFP_NOFS); + if (buf) + snprintf(buf, (*len) - 1, "%s\n%s\n", header, domainname); + kfree(header); + return buf; +} + +/* Wait queue for tomoyo_query_list. */ +static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait); + +/* Lock for manipulating tomoyo_query_list. */ +static DEFINE_SPINLOCK(tomoyo_query_list_lock); + +/* Structure for query. */ +struct tomoyo_query_entry { + struct list_head list; + char *query; + int query_len; + unsigned int serial; + int timer; + int answer; +}; + +/* The list for "struct tomoyo_query_entry". */ +static LIST_HEAD(tomoyo_query_list); + +/* + * Number of "struct file" referring /sys/kernel/security/tomoyo/query + * interface. + */ +static atomic_t tomoyo_query_observers = ATOMIC_INIT(0); + +/** + * tomoyo_supervisor - Ask for the supervisor's decision. + * + * @r: Pointer to "struct tomoyo_request_info". + * @fmt: The printf()'s format string, followed by parameters. + * + * Returns 0 if the supervisor decided to permit the access request which + * violated the policy in enforcing mode, TOMOYO_RETRY_REQUEST if the + * supervisor decided to retry the access request which violated the policy in + * enforcing mode, 0 if it is not in enforcing mode, -EPERM otherwise. + */ +int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) +{ + va_list args; + int error = -EPERM; + int pos; + int len; + static unsigned int tomoyo_serial; + struct tomoyo_query_entry *tomoyo_query_entry = NULL; + bool quota_exceeded = false; + char *header; + switch (r->mode) { + char *buffer; + case TOMOYO_CONFIG_LEARNING: + if (!tomoyo_domain_quota_is_ok(r)) + return 0; + va_start(args, fmt); + len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4; + va_end(args); + buffer = kmalloc(len, GFP_NOFS); + if (!buffer) + return 0; + va_start(args, fmt); + vsnprintf(buffer, len - 1, fmt, args); + va_end(args); + tomoyo_normalize_line(buffer); + tomoyo_write_domain_policy2(buffer, r->domain, false); + kfree(buffer); + /* fall through */ + case TOMOYO_CONFIG_PERMISSIVE: + return 0; + } + if (!r->domain) + r->domain = tomoyo_domain(); + if (!atomic_read(&tomoyo_query_observers)) + return -EPERM; + va_start(args, fmt); + len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32; + va_end(args); + header = tomoyo_init_audit_log(&len, r); + if (!header) + goto out; + tomoyo_query_entry = kzalloc(sizeof(*tomoyo_query_entry), GFP_NOFS); + if (!tomoyo_query_entry) + goto out; + tomoyo_query_entry->query = kzalloc(len, GFP_NOFS); + if (!tomoyo_query_entry->query) + goto out; + len = ksize(tomoyo_query_entry->query); + INIT_LIST_HEAD(&tomoyo_query_entry->list); + spin_lock(&tomoyo_query_list_lock); + if (tomoyo_quota_for_query && tomoyo_query_memory_size + len + + sizeof(*tomoyo_query_entry) >= tomoyo_quota_for_query) { + quota_exceeded = true; + } else { + tomoyo_query_memory_size += len + sizeof(*tomoyo_query_entry); + tomoyo_query_entry->serial = tomoyo_serial++; + } + spin_unlock(&tomoyo_query_list_lock); + if (quota_exceeded) + goto out; + pos = snprintf(tomoyo_query_entry->query, len - 1, "Q%u-%hu\n%s", + tomoyo_query_entry->serial, r->retry, header); + kfree(header); + header = NULL; + va_start(args, fmt); + vsnprintf(tomoyo_query_entry->query + pos, len - 1 - pos, fmt, args); + tomoyo_query_entry->query_len = strlen(tomoyo_query_entry->query) + 1; + va_end(args); + spin_lock(&tomoyo_query_list_lock); + list_add_tail(&tomoyo_query_entry->list, &tomoyo_query_list); + spin_unlock(&tomoyo_query_list_lock); + /* Give 10 seconds for supervisor's opinion. */ + for (tomoyo_query_entry->timer = 0; + atomic_read(&tomoyo_query_observers) && tomoyo_query_entry->timer < 100; + tomoyo_query_entry->timer++) { + wake_up(&tomoyo_query_wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(HZ / 10); + if (tomoyo_query_entry->answer) + break; + } + spin_lock(&tomoyo_query_list_lock); + list_del(&tomoyo_query_entry->list); + tomoyo_query_memory_size -= len + sizeof(*tomoyo_query_entry); + spin_unlock(&tomoyo_query_list_lock); + switch (tomoyo_query_entry->answer) { + case 3: /* Asked to retry by administrator. */ + error = TOMOYO_RETRY_REQUEST; + r->retry++; + break; + case 1: + /* Granted by administrator. */ + error = 0; + break; + case 0: + /* Timed out. */ + break; + default: + /* Rejected by administrator. */ + break; + } + out: + if (tomoyo_query_entry) + kfree(tomoyo_query_entry->query); + kfree(tomoyo_query_entry); + kfree(header); + return error; +} + +/** + * tomoyo_poll_query - poll() for /sys/kernel/security/tomoyo/query. + * + * @file: Pointer to "struct file". + * @wait: Pointer to "poll_table". + * + * Returns POLLIN | POLLRDNORM when ready to read, 0 otherwise. + * + * Waits for access requests which violated policy in enforcing mode. + */ +static int tomoyo_poll_query(struct file *file, poll_table *wait) +{ + struct list_head *tmp; + bool found = false; + u8 i; + for (i = 0; i < 2; i++) { + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, + list); + if (ptr->answer) + continue; + found = true; + break; + } + spin_unlock(&tomoyo_query_list_lock); + if (found) + return POLLIN | POLLRDNORM; + if (i) + break; + poll_wait(file, &tomoyo_query_wait, wait); + } + return 0; +} + +/** + * tomoyo_read_query - Read access requests which violated policy in enforcing mode. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0. + */ +static int tomoyo_read_query(struct tomoyo_io_buffer *head) +{ + struct list_head *tmp; + int pos = 0; + int len = 0; + char *buf; + if (head->read_avail) + return 0; + if (head->read_buf) { + kfree(head->read_buf); + head->read_buf = NULL; + head->readbuf_size = 0; + } + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, list); + if (ptr->answer) + continue; + if (pos++ != head->read_step) + continue; + len = ptr->query_len; + break; + } + spin_unlock(&tomoyo_query_list_lock); + if (!len) { + head->read_step = 0; + return 0; + } + buf = kzalloc(len, GFP_NOFS); + if (!buf) + return 0; + pos = 0; + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, list); + if (ptr->answer) + continue; + if (pos++ != head->read_step) + continue; + /* + * Some query can be skipped because tomoyo_query_list + * can change, but I don't care. + */ + if (len == ptr->query_len) + memmove(buf, ptr->query, len); + break; + } + spin_unlock(&tomoyo_query_list_lock); + if (buf[0]) { + head->read_avail = len; + head->readbuf_size = head->read_avail; + head->read_buf = buf; + head->read_step++; + } else { + kfree(buf); + } + return 0; +} + +/** + * tomoyo_write_answer - Write the supervisor's decision. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0 on success, -EINVAL otherwise. + */ +static int tomoyo_write_answer(struct tomoyo_io_buffer *head) +{ + char *data = head->write_buf; + 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_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, list); + ptr->timer = 0; + } + spin_unlock(&tomoyo_query_list_lock); + if (sscanf(data, "A%u=%u", &serial, &answer) != 2) + return -EINVAL; + spin_lock(&tomoyo_query_list_lock); + list_for_each(tmp, &tomoyo_query_list) { + struct tomoyo_query_entry *ptr + = list_entry(tmp, struct tomoyo_query_entry, list); + if (ptr->serial != serial) + continue; + if (!ptr->answer) + ptr->answer = answer; + break; + } + spin_unlock(&tomoyo_query_list_lock); + return 0; +} + /** * tomoyo_read_version: Get version. * @@ -2239,6 +2608,7 @@ static int tomoyo_open_control(const u8 type, struct file *file) if (!head) return -ENOMEM; mutex_init(&head->io_sem); + head->type = type; switch (type) { case TOMOYO_DOMAINPOLICY: /* /sys/kernel/security/tomoyo/domain_policy */ @@ -2280,6 +2650,11 @@ static int tomoyo_open_control(const u8 type, struct file *file) head->write = tomoyo_write_profile; head->read = tomoyo_read_profile; break; + case TOMOYO_QUERY: /* /sys/kernel/security/tomoyo/query */ + head->poll = tomoyo_poll_query; + head->write = tomoyo_write_answer; + head->read = tomoyo_read_query; + break; case TOMOYO_MANAGER: /* /sys/kernel/security/tomoyo/manager */ head->write = tomoyo_write_manager_policy; @@ -2292,7 +2667,9 @@ static int tomoyo_open_control(const u8 type, struct file *file) * for reading. */ head->read = NULL; - } else { + head->poll = NULL; + } else if (!head->poll) { + /* Don't allocate read_buf for poll() access. */ if (!head->readbuf_size) head->readbuf_size = 4096 * 2; head->read_buf = kzalloc(head->readbuf_size, GFP_NOFS); @@ -2316,7 +2693,8 @@ static int tomoyo_open_control(const u8 type, struct file *file) return -ENOMEM; } } - head->reader_idx = tomoyo_read_lock(); + if (type != TOMOYO_QUERY) + head->reader_idx = tomoyo_read_lock(); file->private_data = head; /* * Call the handler now if the file is @@ -2327,9 +2705,34 @@ static int tomoyo_open_control(const u8 type, struct file *file) */ if (type == TOMOYO_SELFDOMAIN) tomoyo_read_control(file, NULL, 0); + /* + * If the file is /sys/kernel/security/tomoyo/query , increment the + * observer counter. + * The obserber counter is used by tomoyo_supervisor() to see if + * there is some process monitoring /sys/kernel/security/tomoyo/query. + */ + else if (type == TOMOYO_QUERY) + atomic_inc(&tomoyo_query_observers); return 0; } +/** + * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface. + * + * @file: Pointer to "struct file". + * @wait: Pointer to "poll_table". + * + * Waits for read readiness. + * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd . + */ +int tomoyo_poll_control(struct file *file, poll_table *wait) +{ + struct tomoyo_io_buffer *head = file->private_data; + if (!head->poll) + return -ENOSYS; + return head->poll(file, wait); +} + /** * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. * @@ -2443,7 +2846,14 @@ static int tomoyo_close_control(struct file *file) struct tomoyo_io_buffer *head = file->private_data; const bool is_write = !!head->write_buf; - tomoyo_read_unlock(head->reader_idx); + /* + * If the file is /sys/kernel/security/tomoyo/query , decrement the + * observer counter. + */ + if (head->type == TOMOYO_QUERY) + atomic_dec(&tomoyo_query_observers); + else + tomoyo_read_unlock(head->reader_idx); /* Release memory used for policy I/O. */ kfree(head->read_buf); head->read_buf = NULL; @@ -2562,6 +2972,8 @@ static int __init tomoyo_initerface_init(void) return 0; tomoyo_dir = securityfs_create_dir("tomoyo", NULL); + tomoyo_create_entry("query", 0600, tomoyo_dir, + TOMOYO_QUERY); tomoyo_create_entry("domain_policy", 0600, tomoyo_dir, TOMOYO_DOMAINPOLICY); tomoyo_create_entry("exception_policy", 0600, tomoyo_dir, diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 3d819b13916..dc5f98f52f6 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -20,6 +20,7 @@ #include #include #include +#include struct linux_binprm; /********** Constants definitions. **********/ @@ -156,9 +157,12 @@ enum tomoyo_securityfs_interface_index { TOMOYO_SELFDOMAIN, TOMOYO_VERSION, TOMOYO_PROFILE, + TOMOYO_QUERY, TOMOYO_MANAGER }; +#define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */ + /********** Structure definitions. **********/ /* @@ -176,10 +180,14 @@ struct tomoyo_page_buffer { * tomoyo_request_info is a structure which is used for holding * * (1) Domain information of current process. - * (2) Access control mode of the profile. + * (2) How many retries are made for this request. + * (3) Profile number used for this request. + * (4) Access control mode of the profile. */ struct tomoyo_request_info { struct tomoyo_domain_info *domain; + u8 retry; + u8 profile; u8 mode; /* One of tomoyo_mode_index . */ }; @@ -484,6 +492,7 @@ struct tomoyo_mount_acl { struct tomoyo_io_buffer { int (*read) (struct tomoyo_io_buffer *); int (*write) (struct tomoyo_io_buffer *); + int (*poll) (struct file *file, poll_table *wait); /* Exclusive lock for this structure. */ struct mutex io_sem; /* Index returned by tomoyo_read_lock(). */ @@ -514,6 +523,8 @@ struct tomoyo_io_buffer { int write_avail; /* Size of write buffer. */ int writebuf_size; + /* Type of this interface. */ + u8 type; }; /* @@ -659,14 +670,15 @@ struct tomoyo_policy_manager_entry { /********** Function prototypes. **********/ +extern asmlinkage long sys_getpid(void); +extern asmlinkage long sys_getppid(void); + /* Check whether the given name matches the given name_union. */ bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr); /* Check whether the given number matches the given number_union. */ bool tomoyo_compare_number_union(const unsigned long value, const struct tomoyo_number_union *ptr); -/* Check whether the domain has too many ACL entries to hold. */ -bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r); /* Transactional sprintf() for policy dump. */ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); @@ -763,6 +775,8 @@ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete); int tomoyo_write_pattern_policy(char *data, const bool is_delete); /* Create "path_group" entry in exception policy. */ int tomoyo_write_path_group_policy(char *data, const bool is_delete); +int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); /* Create "number_group" entry in exception policy. */ int tomoyo_write_number_group_policy(char *data, const bool is_delete); /* Find a domain by the given name. */ @@ -771,9 +785,6 @@ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * domainname, const u8 profile); -/* Get patterned pathname. */ -const struct tomoyo_path_info * -tomoyo_get_file_pattern(const struct tomoyo_path_info *filename); /* Allocate memory for "struct tomoyo_path_group". */ struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name); struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name); @@ -807,6 +818,8 @@ char *tomoyo_realpath(const char *pathname); char *tomoyo_realpath_nofollow(const char *pathname); /* Same with tomoyo_realpath() except that the pathname is already solved. */ char *tomoyo_realpath_from_path(struct path *path); +/* Get patterned pathname. */ +const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename); /* Check memory quota. */ bool tomoyo_memory_ok(void *ptr); @@ -878,6 +891,9 @@ extern bool tomoyo_policy_loaded; /* The kernel's domain. */ extern struct tomoyo_domain_info tomoyo_kernel_domain; +extern unsigned int tomoyo_quota_for_query; +extern unsigned int tomoyo_query_memory_size; + /********** Inlined functions. **********/ static inline int tomoyo_read_lock(void) diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index afdf26128bf..7e242d27da5 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -678,6 +678,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * */ int tomoyo_find_next_domain(struct linux_binprm *bprm) { + struct tomoyo_request_info r; /* * This function assumes that the size of buffer returned by * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN. @@ -693,11 +694,12 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE); const bool is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); int retval = -ENOMEM; - struct tomoyo_path_info r; /* real name */ - struct tomoyo_path_info s; /* symlink name */ - struct tomoyo_path_info l; /* last name */ + struct tomoyo_path_info rn; /* real name */ + struct tomoyo_path_info sn; /* symlink name */ + struct tomoyo_path_info ln; /* last name */ static bool initialized; + tomoyo_init_request_info(&r, NULL); if (!tmp) goto out; @@ -713,6 +715,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) initialized = true; } + retry: /* Get tomoyo_realpath of program. */ retval = -ENOENT; /* I hope tomoyo_realpath() won't fail with -ENOMEM. */ @@ -724,37 +727,39 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) if (!symlink_program_name) goto out; - r.name = real_program_name; - tomoyo_fill_path_info(&r); - s.name = symlink_program_name; - tomoyo_fill_path_info(&s); - l.name = tomoyo_get_last_name(old_domain); - tomoyo_fill_path_info(&l); + rn.name = real_program_name; + tomoyo_fill_path_info(&rn); + sn.name = symlink_program_name; + tomoyo_fill_path_info(&sn); + ln.name = tomoyo_get_last_name(old_domain); + tomoyo_fill_path_info(&ln); /* Check 'alias' directive. */ - if (tomoyo_pathcmp(&r, &s)) { + if (tomoyo_pathcmp(&rn, &sn)) { struct tomoyo_alias_entry *ptr; /* Is this program allowed to be called via symbolic links? */ list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { if (ptr->is_deleted || - tomoyo_pathcmp(&r, ptr->original_name) || - tomoyo_pathcmp(&s, ptr->aliased_name)) + tomoyo_pathcmp(&rn, ptr->original_name) || + tomoyo_pathcmp(&sn, ptr->aliased_name)) continue; memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN); strncpy(real_program_name, ptr->aliased_name->name, TOMOYO_MAX_PATHNAME_LEN - 1); - tomoyo_fill_path_info(&r); + tomoyo_fill_path_info(&rn); break; } } /* Check execute permission. */ - retval = tomoyo_check_exec_perm(old_domain, &r); + retval = tomoyo_check_exec_perm(old_domain, &rn); + if (retval == TOMOYO_RETRY_REQUEST) + goto retry; if (retval < 0) goto out; new_domain_name = tmp->buffer; - if (tomoyo_is_domain_initializer(old_domain->domainname, &r, &l)) { + if (tomoyo_is_domain_initializer(old_domain->domainname, &rn, &ln)) { /* Transit to the child of tomoyo_kernel_domain domain. */ snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1, TOMOYO_ROOT_NAME " " "%s", real_program_name); @@ -766,7 +771,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) * initializers because they might start before /sbin/init. */ domain = old_domain; - } else if (tomoyo_is_domain_keeper(old_domain->domainname, &r, &l)) { + } else if (tomoyo_is_domain_keeper(old_domain->domainname, &rn, &ln)) { /* Keep current domain. */ domain = old_domain; } else { @@ -779,8 +784,14 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) domain = tomoyo_find_domain(new_domain_name); if (domain) goto done; - if (is_enforce) - goto done; + if (is_enforce) { + int error = tomoyo_supervisor(&r, "# wants to create domain\n" + "%s\n", new_domain_name); + if (error == TOMOYO_RETRY_REQUEST) + goto retry; + if (error < 0) + goto done; + } domain = tomoyo_find_or_assign_new_domain(new_domain_name, old_domain->profile); done: diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index ae32cab8ec7..c629cb4e2c6 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -478,7 +478,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, } /** - * tomoyo_get_file_pattern - Get patterned pathname. + * tomoyo_file_pattern - Get patterned pathname. * * @filename: The filename to find patterned pathname. * @@ -486,8 +486,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, * * Caller holds tomoyo_read_lock(). */ -const struct tomoyo_path_info * -tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) +const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename) { struct tomoyo_pattern_entry *ptr; const struct tomoyo_path_info *pattern = NULL; @@ -507,7 +506,7 @@ tomoyo_get_file_pattern(const struct tomoyo_path_info *filename) } if (pattern) filename = pattern; - return filename; + return filename->name; } /** @@ -812,23 +811,25 @@ static int tomoyo_file_perm(struct tomoyo_request_info *r, perm = 1 << TOMOYO_TYPE_EXECUTE; } else BUG(); - error = tomoyo_path_acl(r, filename, perm, mode != 1); - if (error && mode == 4 && !r->domain->ignore_global_allow_read - && tomoyo_is_globally_readable_file(filename)) + do { + error = tomoyo_path_acl(r, filename, perm, mode != 1); + if (error && mode == 4 && !r->domain->ignore_global_allow_read + && tomoyo_is_globally_readable_file(filename)) + error = 0; + if (!error) + break; + tomoyo_warn_log(r, "%s %s", msg, filename->name); + error = tomoyo_supervisor(r, "allow_%s %s\n", msg, + mode == 1 ? filename->name : + tomoyo_file_pattern(filename)); + /* + * Do not retry for execute request, for alias may have + * changed. + */ + } while (error == TOMOYO_RETRY_REQUEST && mode != 1); + if (r->mode != TOMOYO_CONFIG_ENFORCING) error = 0; - if (!error) - return 0; - tomoyo_warn_log(r, "%s %s", msg, filename->name); - if (r->mode == TOMOYO_CONFIG_ENFORCING) - return error; - if (tomoyo_domain_quota_is_ok(r)) { - /* Don't use patterns for execute permission. */ - const struct tomoyo_path_info *patterned_file = (mode != 1) ? - tomoyo_get_file_pattern(filename) : filename; - tomoyo_update_file_acl(mode, patterned_file->name, r->domain, - false); - } - return 0; + return error; } /** @@ -1123,21 +1124,21 @@ static int tomoyo_path2_acl(const struct tomoyo_request_info *r, const u8 type, static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, const struct tomoyo_path_info *filename) { + const char *msg; int error; next: - error = tomoyo_path_acl(r, filename, 1 << operation, 1); - if (!error) - goto ok; - tomoyo_warn_log(r, "%s %s", tomoyo_path2keyword(operation), - filename->name); - if (tomoyo_domain_quota_is_ok(r)) { - const char *name = tomoyo_get_file_pattern(filename)->name; - tomoyo_update_path_acl(operation, name, r->domain, false); - } + do { + error = tomoyo_path_acl(r, filename, 1 << operation, 1); + if (!error) + break; + msg = tomoyo_path2keyword(operation); + tomoyo_warn_log(r, "%s %s", msg, filename->name); + error = tomoyo_supervisor(r, "allow_%s %s\n", msg, + tomoyo_file_pattern(filename)); + } while (error == TOMOYO_RETRY_REQUEST); if (r->mode != TOMOYO_CONFIG_ENFORCING) error = 0; - ok: /* * Since "allow_truncate" doesn't imply "allow_rewrite" permission, * we need to check "allow_rewrite" permission if the filename is @@ -1267,6 +1268,7 @@ static int tomoyo_path_number_perm2(struct tomoyo_request_info *r, char buffer[64]; int error; u8 radix; + const char *msg; if (!filename) return 0; @@ -1286,15 +1288,16 @@ static int tomoyo_path_number_perm2(struct tomoyo_request_info *r, break; } tomoyo_print_ulong(buffer, sizeof(buffer), number, radix); - error = tomoyo_path_number_acl(r, type, filename, number); - if (!error) - return 0; - tomoyo_warn_log(r, "%s %s %s", tomoyo_path_number2keyword(type), - filename->name, buffer); - if (tomoyo_domain_quota_is_ok(r)) - tomoyo_update_path_number_acl(type, - tomoyo_get_file_pattern(filename) - ->name, buffer, r->domain, false); + do { + error = tomoyo_path_number_acl(r, type, filename, number); + if (!error) + break; + msg = tomoyo_path_number2keyword(type); + tomoyo_warn_log(r, "%s %s %s", msg, filename->name, buffer); + error = tomoyo_supervisor(r, "allow_%s %s %s\n", msg, + tomoyo_file_pattern(filename), + buffer); + } while (error == TOMOYO_RETRY_REQUEST); if (r->mode != TOMOYO_CONFIG_ENFORCING) error = 0; return error; @@ -1484,32 +1487,23 @@ static int tomoyo_path_number3_perm2(struct tomoyo_request_info *r, const unsigned int dev) { int error; + const char *msg; const unsigned int major = MAJOR(dev); const unsigned int minor = MINOR(dev); - error = tomoyo_path_number3_acl(r, filename, 1 << operation, mode, - major, minor); - if (!error) - return 0; - tomoyo_warn_log(r, "%s %s 0%o %u %u", - tomoyo_path_number32keyword(operation), - filename->name, mode, major, minor); - if (tomoyo_domain_quota_is_ok(r)) { - char mode_buf[64]; - char major_buf[64]; - char minor_buf[64]; - memset(mode_buf, 0, sizeof(mode_buf)); - memset(major_buf, 0, sizeof(major_buf)); - memset(minor_buf, 0, sizeof(minor_buf)); - snprintf(mode_buf, sizeof(mode_buf) - 1, "0%o", mode); - snprintf(major_buf, sizeof(major_buf) - 1, "%u", major); - snprintf(minor_buf, sizeof(minor_buf) - 1, "%u", minor); - tomoyo_update_path_number3_acl(operation, - tomoyo_get_file_pattern(filename) - ->name, mode_buf, major_buf, - minor_buf, r->domain, false); - } - if (r->mode != TOMOYO_CONFIG_ENFORCING) + do { + error = tomoyo_path_number3_acl(r, filename, 1 << operation, + mode, major, minor); + if (!error) + break; + msg = tomoyo_path_number32keyword(operation); + tomoyo_warn_log(r, "%s %s 0%o %u %u", msg, filename->name, + mode, major, minor); + error = tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", msg, + tomoyo_file_pattern(filename), mode, + major, minor); + } while (error == TOMOYO_RETRY_REQUEST); + if (r->mode != TOMOYO_CONFIG_ENFORCING) error = 0; return error; } @@ -1562,6 +1556,7 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, struct path *path2) { int error = -ENOMEM; + const char *msg; struct tomoyo_path_info *buf1; struct tomoyo_path_info *buf2; struct tomoyo_request_info r; @@ -1591,17 +1586,16 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, } } } - error = tomoyo_path2_acl(&r, operation, buf1, buf2); - if (!error) - goto out; - tomoyo_warn_log(&r, "%s %s %s", tomoyo_path22keyword(operation), - buf1->name, buf2->name); - if (tomoyo_domain_quota_is_ok(&r)) { - const char *name1 = tomoyo_get_file_pattern(buf1)->name; - const char *name2 = tomoyo_get_file_pattern(buf2)->name; - tomoyo_update_path2_acl(operation, name1, name2, r.domain, - false); - } + do { + error = tomoyo_path2_acl(&r, operation, buf1, buf2); + if (!error) + break; + msg = tomoyo_path22keyword(operation); + tomoyo_warn_log(&r, "%s %s %s", msg, buf1->name, buf2->name); + error = tomoyo_supervisor(&r, "allow_%s %s %s\n", msg, + tomoyo_file_pattern(buf1), + tomoyo_file_pattern(buf2)); + } while (error == TOMOYO_RETRY_REQUEST); out: kfree(buf1); kfree(buf2); diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 507be09e93a..aeac619f787 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -178,19 +178,12 @@ static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, error = 0; break; } - if (error) { - const char *dev = tomoyo_get_file_pattern(&rdev)->name; - const char *dir = tomoyo_get_file_pattern(&rdir)->name; - int len = strlen(dev) + strlen(dir) + strlen(requested_type) - + 64; - char *buf = kzalloc(len, GFP_NOFS); - if (buf) { - snprintf(buf, len - 1, "%s %s %s 0x%lX", - dev, dir, requested_type, flags); - tomoyo_write_mount_policy(buf, r->domain, false); - kfree(buf); - } - } + if (error) + error = tomoyo_supervisor(r, TOMOYO_KEYWORD_ALLOW_MOUNT + "%s %s %s 0x%lX\n", + tomoyo_file_pattern(&rdev), + tomoyo_file_pattern(&rdir), + requested_type, flags); out: kfree(requested_dev_name); kfree(requested_dir_name); @@ -279,7 +272,10 @@ static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD, flags & ~MS_SHARED); else - error = tomoyo_mount_acl2(r, dev_name, dir, type, flags); + do { + error = tomoyo_mount_acl2(r, dev_name, dir, type, + flags); + } while (error == TOMOYO_RETRY_REQUEST); if (r->mode != TOMOYO_CONFIG_ENFORCING) error = 0; return error; diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index d1b96f01962..3ceb1724c92 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -333,6 +333,9 @@ void __init tomoyo_realpath_init(void) panic("Can't register tomoyo_kernel_domain"); } +unsigned int tomoyo_quota_for_query; +unsigned int tomoyo_query_memory_size; + /** * tomoyo_read_memory_counter - Check for memory usage in bytes. * @@ -345,6 +348,7 @@ int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) if (!head->read_eof) { const unsigned int policy = atomic_read(&tomoyo_policy_memory_size); + const unsigned int query = tomoyo_query_memory_size; char buffer[64]; memset(buffer, 0, sizeof(buffer)); @@ -354,8 +358,17 @@ int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) tomoyo_quota_for_policy); else buffer[0] = '\0'; - tomoyo_io_printf(head, "Policy: %10u%s\n", policy, buffer); - tomoyo_io_printf(head, "Total: %10u\n", policy); + tomoyo_io_printf(head, "Policy: %10u%s\n", policy, + buffer); + if (tomoyo_quota_for_query) + snprintf(buffer, sizeof(buffer) - 1, + " (Quota: %10u)", + tomoyo_quota_for_query); + else + buffer[0] = '\0'; + tomoyo_io_printf(head, "Query lists: %10u%s\n", query, + buffer); + tomoyo_io_printf(head, "Total: %10u\n", policy + query); head->read_eof = true; } return 0; @@ -375,5 +388,7 @@ int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) if (sscanf(data, "Policy: %u", &size) == 1) tomoyo_quota_for_policy = size; + else if (sscanf(data, "Query lists: %u", &size) == 1) + tomoyo_quota_for_query = size; return 0; } -- cgit v1.2.3-70-g09d2 From c3ef1500ec833890275172c7d063333404b64d60 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 17 May 2010 10:12:46 +0900 Subject: TOMOYO: Split files into some pieces. security/tomoyo/common.c became too large to read. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/Makefile | 2 +- security/tomoyo/common.c | 1143 +-------------------------------------- security/tomoyo/common.h | 29 +- security/tomoyo/domain.c | 20 +- security/tomoyo/file.c | 62 +-- security/tomoyo/load_policy.c | 81 +++ security/tomoyo/memory.c | 236 ++++++++ security/tomoyo/realpath.c | 214 +------- security/tomoyo/securityfs_if.c | 140 +++++ security/tomoyo/tomoyo.c | 7 +- security/tomoyo/util.c | 951 ++++++++++++++++++++++++++++++++ 11 files changed, 1469 insertions(+), 1416 deletions(-) create mode 100644 security/tomoyo/load_policy.c create mode 100644 security/tomoyo/memory.c create mode 100644 security/tomoyo/securityfs_if.c create mode 100644 security/tomoyo/util.c diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index d7befab40ef..3aa6f076948 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile @@ -1 +1 @@ -obj-y = common.o realpath.o tomoyo.o domain.o file.o gc.o path_group.o number_group.o mount.o +obj-y = common.o domain.o file.o gc.o load_policy.o memory.o mount.o number_group.o path_group.o realpath.o securityfs_if.o tomoyo.o util.o diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index ee46aaa3566..57ddfc5d9c5 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -3,10 +3,7 @@ * * Common functions for TOMOYO. * - * Copyright (C) 2005-2009 NTT DATA CORPORATION - * - * Version: 2.2.0 2009/04/01 - * + * Copyright (C) 2005-2010 NTT DATA CORPORATION */ #include @@ -15,12 +12,6 @@ #include #include "common.h" -/* Lock for protecting policy. */ -DEFINE_MUTEX(tomoyo_policy_lock); - -/* Has loading policy done? */ -bool tomoyo_policy_loaded; - /* String table for functionality that takes 4 modes. */ static const char *tomoyo_mode_4[4] = { "disabled", "learning", "permissive", "enforcing" @@ -64,42 +55,6 @@ static bool tomoyo_manage_by_non_root; /* Utility functions. */ -/* Open operation for /sys/kernel/security/tomoyo/ interface. */ -static int tomoyo_open_control(const u8 type, struct file *file); -/* Close /sys/kernel/security/tomoyo/ interface. */ -static int tomoyo_close_control(struct file *file); -/* Read operation for /sys/kernel/security/tomoyo/ interface. */ -static int tomoyo_read_control(struct file *file, char __user *buffer, - const int buffer_len); -/* Write operation for /sys/kernel/security/tomoyo/ interface. */ -static int tomoyo_write_control(struct file *file, const char __user *buffer, - const int buffer_len); -/* Check whether the domain has too many ACL entries to hold. */ -static bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r); - -/** - * tomoyo_parse_name_union - Parse a tomoyo_name_union. - * - * @filename: Name or name group. - * @ptr: Pointer to "struct tomoyo_name_union". - * - * Returns true on success, false otherwise. - */ -bool tomoyo_parse_name_union(const char *filename, - struct tomoyo_name_union *ptr) -{ - if (!tomoyo_is_correct_path(filename, 0, 0, 0)) - return false; - if (filename[0] == '@') { - ptr->group = tomoyo_get_path_group(filename + 1); - ptr->is_group = true; - return ptr->group != NULL; - } - ptr->filename = tomoyo_get_name(filename); - ptr->is_group = false; - return ptr->filename != NULL; -} - /** * tomoyo_print_name_union - Print a tomoyo_name_union. * @@ -120,69 +75,6 @@ static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head, return tomoyo_io_printf(head, " %s", ptr->filename->name); } -/** - * tomoyo_parse_ulong - Parse an "unsigned long" value. - * - * @result: Pointer to "unsigned long". - * @str: Pointer to string to parse. - * - * Returns value type on success, 0 otherwise. - * - * The @src is updated to point the first character after the value - * on success. - */ -u8 tomoyo_parse_ulong(unsigned long *result, char **str) -{ - const char *cp = *str; - char *ep; - int base = 10; - if (*cp == '0') { - char c = *(cp + 1); - if (c == 'x' || c == 'X') { - base = 16; - cp += 2; - } else if (c >= '0' && c <= '7') { - base = 8; - cp++; - } - } - *result = simple_strtoul(cp, &ep, base); - if (cp == ep) - return 0; - *str = ep; - switch (base) { - case 16: - return TOMOYO_VALUE_TYPE_HEXADECIMAL; - case 8: - return TOMOYO_VALUE_TYPE_OCTAL; - default: - return TOMOYO_VALUE_TYPE_DECIMAL; - } -} - -/** - * tomoyo_print_ulong - Print an "unsigned long" value. - * - * @buffer: Pointer to buffer. - * @buffer_len: Size of @buffer. - * @value: An "unsigned long" value. - * @type: Type of @value. - * - * Returns nothing. - */ -void tomoyo_print_ulong(char *buffer, const int buffer_len, - const unsigned long value, const u8 type) -{ - if (type == TOMOYO_VALUE_TYPE_DECIMAL) - snprintf(buffer, buffer_len, "%lu", value); - else if (type == TOMOYO_VALUE_TYPE_OCTAL) - snprintf(buffer, buffer_len, "0%lo", value); - else if (type == TOMOYO_VALUE_TYPE_HEXADECIMAL) - snprintf(buffer, buffer_len, "0x%lX", value); - else - snprintf(buffer, buffer_len, "type(%u)", type); -} - /** * tomoyo_print_number_union - Print a tomoyo_number_union. * @@ -233,704 +125,6 @@ bool tomoyo_print_number_union(struct tomoyo_io_buffer *head, } } -/** - * tomoyo_parse_number_union - Parse a tomoyo_number_union. - * - * @data: Number or number range or number group. - * @ptr: Pointer to "struct tomoyo_number_union". - * - * Returns true on success, false otherwise. - */ -bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) -{ - u8 type; - unsigned long v; - memset(num, 0, sizeof(*num)); - if (data[0] == '@') { - if (!tomoyo_is_correct_path(data, 0, 0, 0)) - return false; - num->group = tomoyo_get_number_group(data + 1); - num->is_group = true; - return num->group != NULL; - } - type = tomoyo_parse_ulong(&v, &data); - if (!type) - return false; - num->values[0] = v; - num->min_type = type; - if (!*data) { - num->values[1] = v; - num->max_type = type; - return true; - } - if (*data++ != '-') - return false; - type = tomoyo_parse_ulong(&v, &data); - if (!type || *data) - return false; - num->values[1] = v; - num->max_type = type; - return true; -} - -/** - * tomoyo_is_byte_range - Check whether the string isa \ooo style octal value. - * - * @str: Pointer to the string. - * - * Returns true if @str is a \ooo style octal value, false otherwise. - * - * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF. - * This function verifies that \ooo is in valid range. - */ -static inline bool tomoyo_is_byte_range(const char *str) -{ - return *str >= '0' && *str++ <= '3' && - *str >= '0' && *str++ <= '7' && - *str >= '0' && *str <= '7'; -} - -/** - * tomoyo_is_alphabet_char - Check whether the character is an alphabet. - * - * @c: The character to check. - * - * Returns true if @c is an alphabet character, false otherwise. - */ -static inline bool tomoyo_is_alphabet_char(const char c) -{ - return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); -} - -/** - * tomoyo_make_byte - Make byte value from three octal characters. - * - * @c1: The first character. - * @c2: The second character. - * @c3: The third character. - * - * Returns byte value. - */ -static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3) -{ - return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); -} - -/** - * tomoyo_str_starts - Check whether the given string starts with the given keyword. - * - * @src: Pointer to pointer to the string. - * @find: Pointer to the keyword. - * - * Returns true if @src starts with @find, false otherwise. - * - * The @src is updated to point the first character after the @find - * if @src starts with @find. - */ -static bool tomoyo_str_starts(char **src, const char *find) -{ - const int len = strlen(find); - char *tmp = *src; - - if (strncmp(tmp, find, len)) - return false; - tmp += len; - *src = tmp; - return true; -} - -/** - * tomoyo_normalize_line - Format string. - * - * @buffer: The line to normalize. - * - * Leading and trailing whitespaces are removed. - * Multiple whitespaces are packed into single space. - * - * Returns nothing. - */ -static void tomoyo_normalize_line(unsigned char *buffer) -{ - unsigned char *sp = buffer; - unsigned char *dp = buffer; - bool first = true; - - while (tomoyo_is_invalid(*sp)) - sp++; - while (*sp) { - if (!first) - *dp++ = ' '; - first = false; - while (tomoyo_is_valid(*sp)) - *dp++ = *sp++; - while (tomoyo_is_invalid(*sp)) - sp++; - } - *dp = '\0'; -} - -/** - * tomoyo_tokenize - Tokenize string. - * - * @buffer: The line to tokenize. - * @w: Pointer to "char *". - * @size: Sizeof @w . - * - * Returns true on success, false otherwise. - */ -bool tomoyo_tokenize(char *buffer, char *w[], size_t size) -{ - int count = size / sizeof(char *); - int i; - for (i = 0; i < count; i++) - w[i] = ""; - for (i = 0; i < count; i++) { - char *cp = strchr(buffer, ' '); - if (cp) - *cp = '\0'; - w[i] = buffer; - if (!cp) - break; - buffer = cp + 1; - } - return i < count || !*buffer; -} - -/** - * tomoyo_is_correct_path - Validate a pathname. - * @filename: The pathname to check. - * @start_type: Should the pathname start with '/'? - * 1 = must / -1 = must not / 0 = don't care - * @pattern_type: Can the pathname contain a wildcard? - * 1 = must / -1 = must not / 0 = don't care - * @end_type: Should the pathname end with '/'? - * 1 = must / -1 = must not / 0 = don't care - * - * Check whether the given filename follows the naming rules. - * Returns true if @filename follows the naming rules, false otherwise. - */ -bool tomoyo_is_correct_path(const char *filename, const s8 start_type, - const s8 pattern_type, const s8 end_type) -{ - const char *const start = filename; - bool in_repetition = false; - bool contains_pattern = false; - unsigned char c; - unsigned char d; - unsigned char e; - - if (!filename) - goto out; - c = *filename; - if (start_type == 1) { /* Must start with '/' */ - if (c != '/') - goto out; - } else if (start_type == -1) { /* Must not start with '/' */ - if (c == '/') - goto out; - } - if (c) - c = *(filename + strlen(filename) - 1); - if (end_type == 1) { /* Must end with '/' */ - if (c != '/') - goto out; - } else if (end_type == -1) { /* Must not end with '/' */ - if (c == '/') - goto out; - } - while (1) { - c = *filename++; - if (!c) - break; - if (c == '\\') { - c = *filename++; - switch (c) { - case '\\': /* "\\" */ - continue; - case '$': /* "\$" */ - case '+': /* "\+" */ - case '?': /* "\?" */ - case '*': /* "\*" */ - case '@': /* "\@" */ - case 'x': /* "\x" */ - case 'X': /* "\X" */ - case 'a': /* "\a" */ - case 'A': /* "\A" */ - case '-': /* "\-" */ - if (pattern_type == -1) - break; /* Must not contain pattern */ - contains_pattern = true; - continue; - case '{': /* "/\{" */ - if (filename - 3 < start || - *(filename - 3) != '/') - break; - if (pattern_type == -1) - break; /* Must not contain pattern */ - contains_pattern = true; - in_repetition = true; - continue; - case '}': /* "\}/" */ - if (*filename != '/') - break; - if (!in_repetition) - break; - in_repetition = false; - continue; - case '0': /* "\ooo" */ - case '1': - case '2': - case '3': - d = *filename++; - if (d < '0' || d > '7') - break; - e = *filename++; - if (e < '0' || e > '7') - break; - c = tomoyo_make_byte(c, d, e); - if (tomoyo_is_invalid(c)) - continue; /* pattern is not \000 */ - } - goto out; - } else if (in_repetition && c == '/') { - goto out; - } else if (tomoyo_is_invalid(c)) { - goto out; - } - } - if (pattern_type == 1) { /* Must contain pattern */ - if (!contains_pattern) - goto out; - } - if (in_repetition) - goto out; - return true; - out: - return false; -} - -/** - * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules. - * @domainname: The domainname to check. - * - * Returns true if @domainname follows the naming rules, false otherwise. - */ -bool tomoyo_is_correct_domain(const unsigned char *domainname) -{ - unsigned char c; - unsigned char d; - unsigned char e; - - if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, - TOMOYO_ROOT_NAME_LEN)) - goto out; - domainname += TOMOYO_ROOT_NAME_LEN; - if (!*domainname) - return true; - do { - if (*domainname++ != ' ') - goto out; - if (*domainname++ != '/') - goto out; - while ((c = *domainname) != '\0' && c != ' ') { - domainname++; - if (c == '\\') { - c = *domainname++; - switch ((c)) { - case '\\': /* "\\" */ - continue; - case '0': /* "\ooo" */ - case '1': - case '2': - case '3': - d = *domainname++; - if (d < '0' || d > '7') - break; - e = *domainname++; - if (e < '0' || e > '7') - break; - c = tomoyo_make_byte(c, d, e); - if (tomoyo_is_invalid(c)) - /* pattern is not \000 */ - continue; - } - goto out; - } else if (tomoyo_is_invalid(c)) { - goto out; - } - } - } while (*domainname); - return true; - out: - return false; -} - -/** - * tomoyo_is_domain_def - Check whether the given token can be a domainname. - * - * @buffer: The token to check. - * - * Returns true if @buffer possibly be a domainname, false otherwise. - */ -bool tomoyo_is_domain_def(const unsigned char *buffer) -{ - return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); -} - -/** - * tomoyo_find_domain - Find a domain by the given name. - * - * @domainname: The domainname to find. - * - * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) -{ - struct tomoyo_domain_info *domain; - struct tomoyo_path_info name; - - name.name = domainname; - tomoyo_fill_path_info(&name); - list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { - if (!domain->is_deleted && - !tomoyo_pathcmp(&name, domain->domainname)) - return domain; - } - return NULL; -} - -/** - * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token. - * - * @filename: The string to evaluate. - * - * Returns the initial length without a pattern in @filename. - */ -static int tomoyo_const_part_length(const char *filename) -{ - char c; - int len = 0; - - if (!filename) - return 0; - while ((c = *filename++) != '\0') { - if (c != '\\') { - len++; - continue; - } - c = *filename++; - switch (c) { - case '\\': /* "\\" */ - len += 2; - continue; - case '0': /* "\ooo" */ - case '1': - case '2': - case '3': - c = *filename++; - if (c < '0' || c > '7') - break; - c = *filename++; - if (c < '0' || c > '7') - break; - len += 4; - continue; - } - break; - } - return len; -} - -/** - * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members. - * - * @ptr: Pointer to "struct tomoyo_path_info" to fill in. - * - * The caller sets "struct tomoyo_path_info"->name. - */ -void tomoyo_fill_path_info(struct tomoyo_path_info *ptr) -{ - const char *name = ptr->name; - const int len = strlen(name); - - ptr->const_len = tomoyo_const_part_length(name); - ptr->is_dir = len && (name[len - 1] == '/'); - ptr->is_patterned = (ptr->const_len < len); - ptr->hash = full_name_hash(name, len); -} - -/** - * tomoyo_file_matches_pattern2 - Pattern matching without '/' character - * and "\-" pattern. - * - * @filename: The start of string to check. - * @filename_end: The end of string to check. - * @pattern: The start of pattern to compare. - * @pattern_end: The end of pattern to compare. - * - * Returns true if @filename matches @pattern, false otherwise. - */ -static bool tomoyo_file_matches_pattern2(const char *filename, - const char *filename_end, - const char *pattern, - const char *pattern_end) -{ - while (filename < filename_end && pattern < pattern_end) { - char c; - if (*pattern != '\\') { - if (*filename++ != *pattern++) - return false; - continue; - } - c = *filename; - pattern++; - switch (*pattern) { - int i; - int j; - case '?': - if (c == '/') { - return false; - } else if (c == '\\') { - if (filename[1] == '\\') - filename++; - else if (tomoyo_is_byte_range(filename + 1)) - filename += 3; - else - return false; - } - break; - case '\\': - if (c != '\\') - return false; - if (*++filename != '\\') - return false; - break; - case '+': - if (!isdigit(c)) - return false; - break; - case 'x': - if (!isxdigit(c)) - return false; - break; - case 'a': - if (!tomoyo_is_alphabet_char(c)) - return false; - break; - case '0': - case '1': - case '2': - case '3': - if (c == '\\' && tomoyo_is_byte_range(filename + 1) - && strncmp(filename + 1, pattern, 3) == 0) { - filename += 3; - pattern += 2; - break; - } - return false; /* Not matched. */ - case '*': - case '@': - for (i = 0; i <= filename_end - filename; i++) { - if (tomoyo_file_matches_pattern2( - filename + i, filename_end, - pattern + 1, pattern_end)) - return true; - c = filename[i]; - if (c == '.' && *pattern == '@') - break; - if (c != '\\') - continue; - if (filename[i + 1] == '\\') - i++; - else if (tomoyo_is_byte_range(filename + i + 1)) - i += 3; - else - break; /* Bad pattern. */ - } - return false; /* Not matched. */ - default: - j = 0; - c = *pattern; - if (c == '$') { - while (isdigit(filename[j])) - j++; - } else if (c == 'X') { - while (isxdigit(filename[j])) - j++; - } else if (c == 'A') { - while (tomoyo_is_alphabet_char(filename[j])) - j++; - } - for (i = 1; i <= j; i++) { - if (tomoyo_file_matches_pattern2( - filename + i, filename_end, - pattern + 1, pattern_end)) - return true; - } - return false; /* Not matched or bad pattern. */ - } - filename++; - pattern++; - } - while (*pattern == '\\' && - (*(pattern + 1) == '*' || *(pattern + 1) == '@')) - pattern += 2; - return filename == filename_end && pattern == pattern_end; -} - -/** - * tomoyo_file_matches_pattern - Pattern matching without without '/' character. - * - * @filename: The start of string to check. - * @filename_end: The end of string to check. - * @pattern: The start of pattern to compare. - * @pattern_end: The end of pattern to compare. - * - * Returns true if @filename matches @pattern, false otherwise. - */ -static bool tomoyo_file_matches_pattern(const char *filename, - const char *filename_end, - const char *pattern, - const char *pattern_end) -{ - const char *pattern_start = pattern; - bool first = true; - bool result; - - while (pattern < pattern_end - 1) { - /* Split at "\-" pattern. */ - if (*pattern++ != '\\' || *pattern++ != '-') - continue; - result = tomoyo_file_matches_pattern2(filename, - filename_end, - pattern_start, - pattern - 2); - if (first) - result = !result; - if (result) - return false; - first = false; - pattern_start = pattern; - } - result = tomoyo_file_matches_pattern2(filename, filename_end, - pattern_start, pattern_end); - return first ? result : !result; -} - -/** - * tomoyo_path_matches_pattern2 - Do pathname pattern matching. - * - * @f: The start of string to check. - * @p: The start of pattern to compare. - * - * Returns true if @f matches @p, false otherwise. - */ -static bool tomoyo_path_matches_pattern2(const char *f, const char *p) -{ - const char *f_delimiter; - const char *p_delimiter; - - while (*f && *p) { - f_delimiter = strchr(f, '/'); - if (!f_delimiter) - f_delimiter = f + strlen(f); - p_delimiter = strchr(p, '/'); - if (!p_delimiter) - p_delimiter = p + strlen(p); - if (*p == '\\' && *(p + 1) == '{') - goto recursive; - if (!tomoyo_file_matches_pattern(f, f_delimiter, p, - p_delimiter)) - return false; - f = f_delimiter; - if (*f) - f++; - p = p_delimiter; - if (*p) - p++; - } - /* Ignore trailing "\*" and "\@" in @pattern. */ - while (*p == '\\' && - (*(p + 1) == '*' || *(p + 1) == '@')) - p += 2; - return !*f && !*p; - recursive: - /* - * The "\{" pattern is permitted only after '/' character. - * This guarantees that below "*(p - 1)" is safe. - * Also, the "\}" pattern is permitted only before '/' character - * so that "\{" + "\}" pair will not break the "\-" operator. - */ - if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || - *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') - return false; /* Bad pattern. */ - do { - /* Compare current component with pattern. */ - if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2, - p_delimiter - 2)) - break; - /* Proceed to next component. */ - f = f_delimiter; - if (!*f) - break; - f++; - /* Continue comparison. */ - if (tomoyo_path_matches_pattern2(f, p_delimiter + 1)) - return true; - f_delimiter = strchr(f, '/'); - } while (f_delimiter); - return false; /* Not matched. */ -} - -/** - * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern. - * - * @filename: The filename to check. - * @pattern: The pattern to compare. - * - * Returns true if matches, false otherwise. - * - * The following patterns are available. - * \\ \ itself. - * \ooo Octal representation of a byte. - * \* Zero or more repetitions of characters other than '/'. - * \@ Zero or more repetitions of characters other than '/' or '.'. - * \? 1 byte character other than '/'. - * \$ One or more repetitions of decimal digits. - * \+ 1 decimal digit. - * \X One or more repetitions of hexadecimal digits. - * \x 1 hexadecimal digit. - * \A One or more repetitions of alphabet characters. - * \a 1 alphabet character. - * - * \- Subtraction operator. - * - * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ - * /dir/dir/dir/ ). - */ -bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, - const struct tomoyo_path_info *pattern) -{ - const char *f = filename->name; - const char *p = pattern->name; - const int len = pattern->const_len; - - /* If @pattern doesn't contain pattern, I can use strcmp(). */ - if (!pattern->is_patterned) - return !tomoyo_pathcmp(filename, pattern); - /* Don't compare directory and non-directory. */ - if (filename->is_dir != pattern->is_dir) - return false; - /* Compare the initial length without patterns. */ - if (strncmp(f, p, len)) - return false; - f += len; - p += len; - return tomoyo_path_matches_pattern2(f, p); -} - /** * tomoyo_io_printf - Transactional printf() to "struct tomoyo_io_buffer" structure. * @@ -959,33 +153,6 @@ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) return true; } -/** - * tomoyo_get_exe - Get tomoyo_realpath() of current process. - * - * Returns the tomoyo_realpath() of current process on success, NULL otherwise. - * - * This function uses kzalloc(), so the caller must call kfree() - * if this function didn't return NULL. - */ -static const char *tomoyo_get_exe(void) -{ - struct mm_struct *mm = current->mm; - struct vm_area_struct *vma; - const char *cp = NULL; - - if (!mm) - return NULL; - down_read(&mm->mmap_sem); - for (vma = mm->mmap; vma; vma = vma->vm_next) { - if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { - cp = tomoyo_realpath_from_path(&vma->vm_file->f_path); - break; - } - } - up_read(&mm->mmap_sem); - return cp; -} - /** * tomoyo_check_flags - Check mode for specified functionality. * @@ -1024,76 +191,6 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain) return tomoyo_check_flags(domain, TOMOYO_VERBOSE) != 0; } -/** - * tomoyo_domain_quota_is_ok - Check for domain's quota. - * - * @r: Pointer to "struct tomoyo_request_info". - * - * Returns true if the domain is not exceeded quota, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) -{ - unsigned int count = 0; - struct tomoyo_domain_info *domain = r->domain; - struct tomoyo_acl_info *ptr; - - if (r->mode != TOMOYO_CONFIG_LEARNING) - return false; - if (!domain) - return true; - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { - switch (ptr->type) { - u16 perm; - u8 i; - case TOMOYO_TYPE_PATH_ACL: - perm = container_of(ptr, struct tomoyo_path_acl, head) - ->perm; - for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) - if (perm & (1 << i)) - count++; - if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) - count -= 2; - break; - case TOMOYO_TYPE_PATH2_ACL: - perm = container_of(ptr, struct tomoyo_path2_acl, head) - ->perm; - for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++) - if (perm & (1 << i)) - count++; - break; - case TOMOYO_TYPE_PATH_NUMBER_ACL: - perm = container_of(ptr, struct tomoyo_path_number_acl, - head)->perm; - for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++) - if (perm & (1 << i)) - count++; - break; - case TOMOYO_TYPE_PATH_NUMBER3_ACL: - perm = container_of(ptr, struct tomoyo_path_number3_acl, - head)->perm; - for (i = 0; i < TOMOYO_MAX_PATH_NUMBER3_OPERATION; i++) - if (perm & (1 << i)) - count++; - break; - case TOMOYO_TYPE_MOUNT_ACL: - if (!container_of(ptr, struct tomoyo_mount_acl, head)-> - is_deleted) - count++; - } - } - if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) - return true; - if (!domain->quota_warned) { - domain->quota_warned = true; - printk(KERN_WARNING "TOMOYO-WARNING: " - "Domain '%s' has so many ACLs to hold. " - "Stopped learning mode.\n", domain->domainname->name); - } - return false; -} - /** * tomoyo_find_or_assign_new_profile - Create a new profile. * @@ -2118,91 +1215,6 @@ static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) return 0; } -/* path to policy loader */ -static const char *tomoyo_loader = "/sbin/tomoyo-init"; - -/** - * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists. - * - * Returns true if /sbin/tomoyo-init exists, false otherwise. - */ -static bool tomoyo_policy_loader_exists(void) -{ - /* - * Don't activate MAC if the policy loader doesn't exist. - * If the initrd includes /sbin/init but real-root-dev has not - * mounted on / yet, activating MAC will block the system since - * policies are not loaded yet. - * Thus, let do_execve() call this function everytime. - */ - struct path path; - - if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) { - printk(KERN_INFO "Not activating Mandatory Access Control now " - "since %s doesn't exist.\n", tomoyo_loader); - return false; - } - path_put(&path); - return true; -} - -/** - * tomoyo_load_policy - Run external policy loader to load policy. - * - * @filename: The program about to start. - * - * This function checks whether @filename is /sbin/init , and if so - * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init - * and then continues invocation of /sbin/init. - * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and - * writes to /sys/kernel/security/tomoyo/ interfaces. - * - * Returns nothing. - */ -void tomoyo_load_policy(const char *filename) -{ - char *argv[2]; - char *envp[3]; - - if (tomoyo_policy_loaded) - return; - /* - * Check filename is /sbin/init or /sbin/tomoyo-start. - * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't - * be passed. - * You can create /sbin/tomoyo-start by - * "ln -s /bin/true /sbin/tomoyo-start". - */ - if (strcmp(filename, "/sbin/init") && - strcmp(filename, "/sbin/tomoyo-start")) - return; - if (!tomoyo_policy_loader_exists()) - return; - - printk(KERN_INFO "Calling %s to load policy. Please wait.\n", - tomoyo_loader); - argv[0] = (char *) tomoyo_loader; - argv[1] = NULL; - envp[0] = "HOME=/"; - envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; - envp[2] = NULL; - call_usermodehelper(argv[0], argv, envp, 1); - - printk(KERN_INFO "TOMOYO: 2.2.0 2009/04/01\n"); - printk(KERN_INFO "Mandatory Access Control activated.\n"); - tomoyo_policy_loaded = true; - { /* Check all profiles currently assigned to domains are defined. */ - struct tomoyo_domain_info *domain; - list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { - const u8 profile = domain->profile; - if (tomoyo_profile_ptr[profile]) - continue; - panic("Profile %u (used by '%s') not defined.\n", - profile, domain->domainname->name); - } - } -} - /** * tomoyo_print_header - Get header line of audit log. * @@ -2601,7 +1613,7 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) * * Caller acquires tomoyo_read_lock(). */ -static int tomoyo_open_control(const u8 type, struct file *file) +int tomoyo_open_control(const u8 type, struct file *file) { struct tomoyo_io_buffer *head = kzalloc(sizeof(*head), GFP_NOFS); @@ -2744,8 +1756,8 @@ int tomoyo_poll_control(struct file *file, poll_table *wait) * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_read_control(struct file *file, char __user *buffer, - const int buffer_len) +int tomoyo_read_control(struct file *file, char __user *buffer, + const int buffer_len) { int len = 0; struct tomoyo_io_buffer *head = file->private_data; @@ -2789,8 +1801,8 @@ static int tomoyo_read_control(struct file *file, char __user *buffer, * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_write_control(struct file *file, const char __user *buffer, - const int buffer_len) +int tomoyo_write_control(struct file *file, const char __user *buffer, + const int buffer_len) { struct tomoyo_io_buffer *head = file->private_data; int error = buffer_len; @@ -2841,7 +1853,7 @@ static int tomoyo_write_control(struct file *file, const char __user *buffer, * * Caller looses tomoyo_read_lock(). */ -static int tomoyo_close_control(struct file *file) +int tomoyo_close_control(struct file *file) { struct tomoyo_io_buffer *head = file->private_data; const bool is_write = !!head->write_buf; @@ -2868,131 +1880,22 @@ static int tomoyo_close_control(struct file *file) } /** - * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. - * - * @inode: Pointer to "struct inode". - * @file: Pointer to "struct file". - * - * Returns 0 on success, negative value otherwise. + * tomoyo_check_profile - Check all profiles currently assigned to domains are defined. */ -static int tomoyo_open(struct inode *inode, struct file *file) +void tomoyo_check_profile(void) { - const int key = ((u8 *) file->f_path.dentry->d_inode->i_private) - - ((u8 *) NULL); - return tomoyo_open_control(key, file); -} - -/** - * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface. - * - * @inode: Pointer to "struct inode". - * @file: Pointer to "struct file". - * - * Returns 0 on success, negative value otherwise. - */ -static int tomoyo_release(struct inode *inode, struct file *file) -{ - return tomoyo_close_control(file); -} - -/** - * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface. - * - * @file: Pointer to "struct file". - * @buf: Pointer to buffer. - * @count: Size of @buf. - * @ppos: Unused. - * - * Returns bytes read on success, negative value otherwise. - */ -static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count, - loff_t *ppos) -{ - return tomoyo_read_control(file, buf, count); -} - -/** - * tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface. - * - * @file: Pointer to "struct file". - * @buf: Pointer to buffer. - * @count: Size of @buf. - * @ppos: Unused. - * - * Returns @count on success, negative value otherwise. - */ -static ssize_t tomoyo_write(struct file *file, const char __user *buf, - size_t count, loff_t *ppos) -{ - return tomoyo_write_control(file, buf, count); -} - -/* - * tomoyo_operations is a "struct file_operations" which is used for handling - * /sys/kernel/security/tomoyo/ interface. - * - * Some files under /sys/kernel/security/tomoyo/ directory accept open(O_RDWR). - * See tomoyo_io_buffer for internals. - */ -static const struct file_operations tomoyo_operations = { - .open = tomoyo_open, - .release = tomoyo_release, - .read = tomoyo_read, - .write = tomoyo_write, -}; - -/** - * tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory. - * - * @name: The name of the interface file. - * @mode: The permission of the interface file. - * @parent: The parent directory. - * @key: Type of interface. - * - * Returns nothing. - */ -static void __init tomoyo_create_entry(const char *name, const mode_t mode, - struct dentry *parent, const u8 key) -{ - securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key, - &tomoyo_operations); -} - -/** - * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface. - * - * Returns 0. - */ -static int __init tomoyo_initerface_init(void) -{ - struct dentry *tomoyo_dir; - - /* Don't create securityfs entries unless registered. */ - if (current_cred()->security != &tomoyo_kernel_domain) - return 0; - - tomoyo_dir = securityfs_create_dir("tomoyo", NULL); - tomoyo_create_entry("query", 0600, tomoyo_dir, - TOMOYO_QUERY); - tomoyo_create_entry("domain_policy", 0600, tomoyo_dir, - TOMOYO_DOMAINPOLICY); - tomoyo_create_entry("exception_policy", 0600, tomoyo_dir, - TOMOYO_EXCEPTIONPOLICY); - tomoyo_create_entry("self_domain", 0400, tomoyo_dir, - TOMOYO_SELFDOMAIN); - tomoyo_create_entry(".domain_status", 0600, tomoyo_dir, - TOMOYO_DOMAIN_STATUS); - tomoyo_create_entry(".process_status", 0600, tomoyo_dir, - TOMOYO_PROCESS_STATUS); - tomoyo_create_entry("meminfo", 0600, tomoyo_dir, - TOMOYO_MEMINFO); - tomoyo_create_entry("profile", 0600, tomoyo_dir, - TOMOYO_PROFILE); - tomoyo_create_entry("manager", 0600, tomoyo_dir, - TOMOYO_MANAGER); - tomoyo_create_entry("version", 0400, tomoyo_dir, - TOMOYO_VERSION); - return 0; + struct tomoyo_domain_info *domain; + const int idx = tomoyo_read_lock(); + tomoyo_policy_loaded = true; + /* Check all profiles currently assigned to domains are defined. */ + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { + const u8 profile = domain->profile; + if (tomoyo_profile_ptr[profile]) + continue; + panic("Profile %u (used by '%s') not defined.\n", + profile, domain->domainname->name); + } + tomoyo_read_unlock(idx); + printk(KERN_INFO "TOMOYO: 2.2.0 2009/04/01\n"); + printk(KERN_INFO "Mandatory Access Control activated.\n"); } - -fs_initcall(tomoyo_initerface_init); diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index dc5f98f52f6..be03e4a21db 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -673,6 +673,31 @@ struct tomoyo_policy_manager_entry { extern asmlinkage long sys_getpid(void); extern asmlinkage long sys_getppid(void); +/* Check whether the given string starts with the given keyword. */ +bool tomoyo_str_starts(char **src, const char *find); +/* Get tomoyo_realpath() of current process. */ +const char *tomoyo_get_exe(void); +/* Format string. */ +void tomoyo_normalize_line(unsigned char *buffer); +/* Print warning or error message on console. */ +void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) + __attribute__ ((format(printf, 2, 3))); +/* Check all profiles currently assigned to domains are defined. */ +void tomoyo_check_profile(void); +/* Open operation for /sys/kernel/security/tomoyo/ interface. */ +int tomoyo_open_control(const u8 type, struct file *file); +/* Close /sys/kernel/security/tomoyo/ interface. */ +int tomoyo_close_control(struct file *file); +/* Read operation for /sys/kernel/security/tomoyo/ interface. */ +int tomoyo_read_control(struct file *file, char __user *buffer, + const int buffer_len); +/* Write operation for /sys/kernel/security/tomoyo/ interface. */ +int tomoyo_write_control(struct file *file, const char __user *buffer, + const int buffer_len); +/* Check whether the domain has too many ACL entries to hold. */ +bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r); +/* Print out of memory warning message. */ +void tomoyo_warn_oom(const char *function); /* Check whether the given name matches the given name_union. */ bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr); @@ -837,8 +862,8 @@ int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head); /* Set memory quota. */ int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head); -/* Initialize realpath related code. */ -void __init tomoyo_realpath_init(void); +/* Initialize mm related code. */ +void __init tomoyo_mm_init(void); int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, const struct tomoyo_path_info *filename); int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 7e242d27da5..08428bc082d 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -1,12 +1,9 @@ /* * security/tomoyo/domain.c * - * Implementation of the Domain-Based Mandatory Access Control. - * - * Copyright (C) 2005-2009 NTT DATA CORPORATION - * - * Version: 2.2.0 2009/04/01 + * Domain transition functions for TOMOYO. * + * Copyright (C) 2005-2010 NTT DATA CORPORATION */ #include "common.h" @@ -697,24 +694,11 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) struct tomoyo_path_info rn; /* real name */ struct tomoyo_path_info sn; /* symlink name */ struct tomoyo_path_info ln; /* last name */ - static bool initialized; tomoyo_init_request_info(&r, NULL); if (!tmp) goto out; - if (!initialized) { - /* - * Built-in initializers. This is needed because policies are - * not loaded until starting /sbin/init. - */ - tomoyo_update_domain_initializer_entry(NULL, "/sbin/hotplug", - false, false); - tomoyo_update_domain_initializer_entry(NULL, "/sbin/modprobe", - false, false); - initialized = true; - } - retry: /* Get tomoyo_realpath of program. */ retval = -ENOENT; diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index c629cb4e2c6..c13806937dc 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -1,12 +1,9 @@ /* * security/tomoyo/file.c * - * Implementation of the Domain-Based Mandatory Access Control. - * - * Copyright (C) 2005-2009 NTT DATA CORPORATION - * - * Version: 2.2.0 2009/04/01 + * Pathname restriction functions. * + * Copyright (C) 2005-2010 NTT DATA CORPORATION */ #include "common.h" @@ -99,61 +96,6 @@ bool tomoyo_compare_number_union(const unsigned long value, return value >= ptr->values[0] && value <= ptr->values[1]; } -/** - * tomoyo_init_request_info - Initialize "struct tomoyo_request_info" members. - * - * @r: Pointer to "struct tomoyo_request_info" to initialize. - * @domain: Pointer to "struct tomoyo_domain_info". NULL for tomoyo_domain(). - * - * Returns mode. - */ -int tomoyo_init_request_info(struct tomoyo_request_info *r, - struct tomoyo_domain_info *domain) -{ - memset(r, 0, sizeof(*r)); - if (!domain) - domain = tomoyo_domain(); - r->domain = domain; - r->mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); - return r->mode; -} - -static void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) - __attribute__ ((format(printf, 2, 3))); -/** - * tomoyo_warn_log - Print warning or error message on console. - * - * @r: Pointer to "struct tomoyo_request_info". - * @fmt: The printf()'s format string, followed by parameters. - */ -static void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) -{ - int len = PAGE_SIZE; - va_list args; - char *buffer; - if (!tomoyo_verbose_mode(r->domain)) - return; - while (1) { - int len2; - buffer = kmalloc(len, GFP_NOFS); - if (!buffer) - return; - va_start(args, fmt); - len2 = vsnprintf(buffer, len - 1, fmt, args); - va_end(args); - if (len2 <= len - 1) { - buffer[len2] = '\0'; - break; - } - len = len2 + 1; - kfree(buffer); - } - printk(KERN_WARNING "TOMOYO-%s: Access %s denied for %s\n", - r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", - buffer, tomoyo_get_last_name(r->domain)); - kfree(buffer); -} - /** * tomoyo_path2keyword - Get the name of single path operation. * diff --git a/security/tomoyo/load_policy.c b/security/tomoyo/load_policy.c new file mode 100644 index 00000000000..bbada7ca1b9 --- /dev/null +++ b/security/tomoyo/load_policy.c @@ -0,0 +1,81 @@ +/* + * security/tomoyo/load_policy.c + * + * Policy loader launcher for TOMOYO. + * + * Copyright (C) 2005-2010 NTT DATA CORPORATION + */ + +#include "common.h" + +/* path to policy loader */ +static const char *tomoyo_loader = "/sbin/tomoyo-init"; + +/** + * tomoyo_policy_loader_exists - Check whether /sbin/tomoyo-init exists. + * + * Returns true if /sbin/tomoyo-init exists, false otherwise. + */ +static bool tomoyo_policy_loader_exists(void) +{ + /* + * Don't activate MAC if the policy loader doesn't exist. + * If the initrd includes /sbin/init but real-root-dev has not + * mounted on / yet, activating MAC will block the system since + * policies are not loaded yet. + * Thus, let do_execve() call this function everytime. + */ + struct path path; + + if (kern_path(tomoyo_loader, LOOKUP_FOLLOW, &path)) { + printk(KERN_INFO "Not activating Mandatory Access Control now " + "since %s doesn't exist.\n", tomoyo_loader); + return false; + } + path_put(&path); + return true; +} + +/** + * tomoyo_load_policy - Run external policy loader to load policy. + * + * @filename: The program about to start. + * + * This function checks whether @filename is /sbin/init , and if so + * invoke /sbin/tomoyo-init and wait for the termination of /sbin/tomoyo-init + * and then continues invocation of /sbin/init. + * /sbin/tomoyo-init reads policy files in /etc/tomoyo/ directory and + * writes to /sys/kernel/security/tomoyo/ interfaces. + * + * Returns nothing. + */ +void tomoyo_load_policy(const char *filename) +{ + char *argv[2]; + char *envp[3]; + + if (tomoyo_policy_loaded) + return; + /* + * Check filename is /sbin/init or /sbin/tomoyo-start. + * /sbin/tomoyo-start is a dummy filename in case where /sbin/init can't + * be passed. + * You can create /sbin/tomoyo-start by + * "ln -s /bin/true /sbin/tomoyo-start". + */ + if (strcmp(filename, "/sbin/init") && + strcmp(filename, "/sbin/tomoyo-start")) + return; + if (!tomoyo_policy_loader_exists()) + return; + + printk(KERN_INFO "Calling %s to load policy. Please wait.\n", + tomoyo_loader); + argv[0] = (char *) tomoyo_loader; + argv[1] = NULL; + envp[0] = "HOME=/"; + envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + envp[2] = NULL; + call_usermodehelper(argv[0], argv, envp, 1); + tomoyo_check_profile(); +} diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c new file mode 100644 index 00000000000..8fb73ff5cb6 --- /dev/null +++ b/security/tomoyo/memory.c @@ -0,0 +1,236 @@ +/* + * security/tomoyo/memory.c + * + * Memory management functions for TOMOYO. + * + * Copyright (C) 2005-2010 NTT DATA CORPORATION + */ + +#include +#include +#include "common.h" + +/** + * tomoyo_warn_oom - Print out of memory warning message. + * + * @function: Function's name. + */ +void tomoyo_warn_oom(const char *function) +{ + /* Reduce error messages. */ + static pid_t tomoyo_last_pid; + const pid_t pid = current->pid; + if (tomoyo_last_pid != pid) { + printk(KERN_WARNING "ERROR: Out of memory at %s.\n", + function); + tomoyo_last_pid = pid; + } + if (!tomoyo_policy_loaded) + panic("MAC Initialization failed.\n"); +} + +/* Memory allocated for policy. */ +static atomic_t tomoyo_policy_memory_size; +/* Quota for holding policy. */ +static unsigned int tomoyo_quota_for_policy; + +/** + * tomoyo_memory_ok - Check memory quota. + * + * @ptr: Pointer to allocated memory. + * + * Returns true on success, false otherwise. + * + * Returns true if @ptr is not NULL and quota not exceeded, false otherwise. + */ +bool tomoyo_memory_ok(void *ptr) +{ + size_t s = ptr ? ksize(ptr) : 0; + atomic_add(s, &tomoyo_policy_memory_size); + if (ptr && (!tomoyo_quota_for_policy || + atomic_read(&tomoyo_policy_memory_size) + <= tomoyo_quota_for_policy)) { + memset(ptr, 0, s); + return true; + } + atomic_sub(s, &tomoyo_policy_memory_size); + tomoyo_warn_oom(__func__); + return false; +} + +/** + * tomoyo_commit_ok - Check memory quota. + * + * @data: Data to copy from. + * @size: Size in byte. + * + * Returns pointer to allocated memory on success, NULL otherwise. + * @data is zero-cleared on success. + */ +void *tomoyo_commit_ok(void *data, const unsigned int size) +{ + void *ptr = kzalloc(size, GFP_NOFS); + if (tomoyo_memory_ok(ptr)) { + memmove(ptr, data, size); + memset(data, 0, size); + return ptr; + } + return NULL; +} + +/** + * tomoyo_memory_free - Free memory for elements. + * + * @ptr: Pointer to allocated memory. + */ +void tomoyo_memory_free(void *ptr) +{ + atomic_sub(ksize(ptr), &tomoyo_policy_memory_size); + kfree(ptr); +} + +/* + * tomoyo_name_list is used for holding string data used by TOMOYO. + * Since same string data is likely used for multiple times (e.g. + * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of + * "const struct tomoyo_path_info *". + */ +struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; + +/** + * tomoyo_get_name - Allocate permanent memory for string data. + * + * @name: The string to store into the permernent memory. + * + * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. + */ +const struct tomoyo_path_info *tomoyo_get_name(const char *name) +{ + struct tomoyo_name_entry *ptr; + unsigned int hash; + int len; + int allocated_len; + struct list_head *head; + + if (!name) + return NULL; + len = strlen(name) + 1; + hash = full_name_hash((const unsigned char *) name, len - 1); + head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)]; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + return NULL; + list_for_each_entry(ptr, head, list) { + if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) + continue; + atomic_inc(&ptr->users); + goto out; + } + ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS); + allocated_len = ptr ? ksize(ptr) : 0; + if (!ptr || (tomoyo_quota_for_policy && + atomic_read(&tomoyo_policy_memory_size) + allocated_len + > tomoyo_quota_for_policy)) { + kfree(ptr); + ptr = NULL; + tomoyo_warn_oom(__func__); + goto out; + } + atomic_add(allocated_len, &tomoyo_policy_memory_size); + ptr->entry.name = ((char *) ptr) + sizeof(*ptr); + memmove((char *) ptr->entry.name, name, len); + atomic_set(&ptr->users, 1); + tomoyo_fill_path_info(&ptr->entry); + list_add_tail(&ptr->list, head); + out: + mutex_unlock(&tomoyo_policy_lock); + return ptr ? &ptr->entry : NULL; +} + +/** + * tomoyo_mm_init - Initialize mm related code. + */ +void __init tomoyo_mm_init(void) +{ + int idx; + + BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX); + for (idx = 0; idx < TOMOYO_MAX_HASH; idx++) + INIT_LIST_HEAD(&tomoyo_name_list[idx]); + INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); + tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_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"); + { + /* Load built-in policy. */ + tomoyo_write_domain_initializer_policy("/sbin/hotplug", + false, false); + tomoyo_write_domain_initializer_policy("/sbin/modprobe", + false, false); + } + tomoyo_read_unlock(idx); +} + + +/* Memory allocated for query lists. */ +unsigned int tomoyo_query_memory_size; +/* Quota for holding query lists. */ +unsigned int tomoyo_quota_for_query; + +/** + * tomoyo_read_memory_counter - Check for memory usage in bytes. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns memory usage. + */ +int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) +{ + if (!head->read_eof) { + const unsigned int policy + = atomic_read(&tomoyo_policy_memory_size); + const unsigned int query = tomoyo_query_memory_size; + char buffer[64]; + + memset(buffer, 0, sizeof(buffer)); + if (tomoyo_quota_for_policy) + snprintf(buffer, sizeof(buffer) - 1, + " (Quota: %10u)", + tomoyo_quota_for_policy); + else + buffer[0] = '\0'; + tomoyo_io_printf(head, "Policy: %10u%s\n", policy, + buffer); + if (tomoyo_quota_for_query) + snprintf(buffer, sizeof(buffer) - 1, + " (Quota: %10u)", + tomoyo_quota_for_query); + else + buffer[0] = '\0'; + tomoyo_io_printf(head, "Query lists: %10u%s\n", query, + buffer); + tomoyo_io_printf(head, "Total: %10u\n", policy + query); + head->read_eof = true; + } + return 0; +} + +/** + * tomoyo_write_memory_quota - Set memory quota. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0. + */ +int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) +{ + char *data = head->write_buf; + unsigned int size; + + if (sscanf(data, "Policy: %u", &size) == 1) + tomoyo_quota_for_policy = size; + else if (sscanf(data, "Query lists: %u", &size) == 1) + tomoyo_quota_for_query = size; + return 0; +} diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 3ceb1724c92..1fd685a94ad 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -1,19 +1,15 @@ /* * security/tomoyo/realpath.c * - * Get the canonicalized absolute pathnames. The basis for TOMOYO. - * - * Copyright (C) 2005-2009 NTT DATA CORPORATION - * - * Version: 2.2.0 2009/04/01 + * Pathname calculation functions for TOMOYO. * + * Copyright (C) 2005-2010 NTT DATA CORPORATION */ #include #include #include #include -#include #include #include #include "common.h" @@ -123,7 +119,7 @@ int tomoyo_realpath_from_path2(struct path *path, char *newname, } } if (error) - printk(KERN_WARNING "tomoyo_realpath: Pathname too long.\n"); + tomoyo_warn_oom(__func__); return error; } @@ -141,6 +137,7 @@ char *tomoyo_realpath_from_path(struct path *path) { char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_NOFS); + BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX); BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer) <= TOMOYO_MAX_PATHNAME_LEN - 1); if (!buf) @@ -189,206 +186,3 @@ char *tomoyo_realpath_nofollow(const char *pathname) } return NULL; } - -/* Memory allocated for non-string data. */ -static atomic_t tomoyo_policy_memory_size; -/* Quota for holding policy. */ -static unsigned int tomoyo_quota_for_policy; - -/** - * tomoyo_memory_ok - Check memory quota. - * - * @ptr: Pointer to allocated memory. - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_policy_lock. - * Memory pointed by @ptr will be zeroed on success. - */ -bool tomoyo_memory_ok(void *ptr) -{ - int allocated_len = ptr ? ksize(ptr) : 0; - atomic_add(allocated_len, &tomoyo_policy_memory_size); - if (ptr && (!tomoyo_quota_for_policy || - atomic_read(&tomoyo_policy_memory_size) - <= tomoyo_quota_for_policy)) { - memset(ptr, 0, allocated_len); - return true; - } - printk(KERN_WARNING "ERROR: Out of memory " - "for tomoyo_alloc_element().\n"); - if (!tomoyo_policy_loaded) - panic("MAC Initialization failed.\n"); - return false; -} - -/** - * tomoyo_commit_ok - Check memory quota. - * - * @data: Data to copy from. - * @size: Size in byte. - * - * Returns pointer to allocated memory on success, NULL otherwise. - */ -void *tomoyo_commit_ok(void *data, const unsigned int size) -{ - void *ptr = kzalloc(size, GFP_NOFS); - if (tomoyo_memory_ok(ptr)) { - memmove(ptr, data, size); - memset(data, 0, size); - return ptr; - } - return NULL; -} - -/** - * tomoyo_memory_free - Free memory for elements. - * - * @ptr: Pointer to allocated memory. - */ -void tomoyo_memory_free(void *ptr) -{ - atomic_sub(ksize(ptr), &tomoyo_policy_memory_size); - kfree(ptr); -} - -/* - * tomoyo_name_list is used for holding string data used by TOMOYO. - * Since same string data is likely used for multiple times (e.g. - * "/lib/libc-2.5.so"), TOMOYO shares string data in the form of - * "const struct tomoyo_path_info *". - */ -struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; - -/** - * tomoyo_get_name - Allocate permanent memory for string data. - * - * @name: The string to store into the permernent memory. - * - * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. - */ -const struct tomoyo_path_info *tomoyo_get_name(const char *name) -{ - struct tomoyo_name_entry *ptr; - unsigned int hash; - int len; - int allocated_len; - struct list_head *head; - - if (!name) - return NULL; - len = strlen(name) + 1; - hash = full_name_hash((const unsigned char *) name, len - 1); - head = &tomoyo_name_list[hash_long(hash, TOMOYO_HASH_BITS)]; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - return NULL; - list_for_each_entry(ptr, head, list) { - if (hash != ptr->entry.hash || strcmp(name, ptr->entry.name)) - continue; - atomic_inc(&ptr->users); - goto out; - } - ptr = kzalloc(sizeof(*ptr) + len, GFP_NOFS); - allocated_len = ptr ? ksize(ptr) : 0; - if (!ptr || (tomoyo_quota_for_policy && - atomic_read(&tomoyo_policy_memory_size) + allocated_len - > tomoyo_quota_for_policy)) { - kfree(ptr); - printk(KERN_WARNING "ERROR: Out of memory " - "for tomoyo_get_name().\n"); - if (!tomoyo_policy_loaded) - panic("MAC Initialization failed.\n"); - ptr = NULL; - goto out; - } - atomic_add(allocated_len, &tomoyo_policy_memory_size); - ptr->entry.name = ((char *) ptr) + sizeof(*ptr); - memmove((char *) ptr->entry.name, name, len); - atomic_set(&ptr->users, 1); - tomoyo_fill_path_info(&ptr->entry); - list_add_tail(&ptr->list, head); - out: - mutex_unlock(&tomoyo_policy_lock); - return ptr ? &ptr->entry : NULL; -} - -/** - * tomoyo_realpath_init - Initialize realpath related code. - */ -void __init tomoyo_realpath_init(void) -{ - int i; - - BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX); - for (i = 0; i < TOMOYO_MAX_HASH; i++) - INIT_LIST_HEAD(&tomoyo_name_list[i]); - INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); - tomoyo_kernel_domain.domainname = tomoyo_get_name(TOMOYO_ROOT_NAME); - /* - * tomoyo_read_lock() is not needed because this function is - * called before the first "delete" request. - */ - list_add_tail_rcu(&tomoyo_kernel_domain.list, &tomoyo_domain_list); - if (tomoyo_find_domain(TOMOYO_ROOT_NAME) != &tomoyo_kernel_domain) - panic("Can't register tomoyo_kernel_domain"); -} - -unsigned int tomoyo_quota_for_query; -unsigned int tomoyo_query_memory_size; - -/** - * tomoyo_read_memory_counter - Check for memory usage in bytes. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns memory usage. - */ -int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) -{ - if (!head->read_eof) { - const unsigned int policy - = atomic_read(&tomoyo_policy_memory_size); - const unsigned int query = tomoyo_query_memory_size; - char buffer[64]; - - memset(buffer, 0, sizeof(buffer)); - if (tomoyo_quota_for_policy) - snprintf(buffer, sizeof(buffer) - 1, - " (Quota: %10u)", - tomoyo_quota_for_policy); - else - buffer[0] = '\0'; - tomoyo_io_printf(head, "Policy: %10u%s\n", policy, - buffer); - if (tomoyo_quota_for_query) - snprintf(buffer, sizeof(buffer) - 1, - " (Quota: %10u)", - tomoyo_quota_for_query); - else - buffer[0] = '\0'; - tomoyo_io_printf(head, "Query lists: %10u%s\n", query, - buffer); - tomoyo_io_printf(head, "Total: %10u\n", policy + query); - head->read_eof = true; - } - return 0; -} - -/** - * tomoyo_write_memory_quota - Set memory quota. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns 0. - */ -int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head) -{ - char *data = head->write_buf; - unsigned int size; - - if (sscanf(data, "Policy: %u", &size) == 1) - tomoyo_quota_for_policy = size; - else if (sscanf(data, "Query lists: %u", &size) == 1) - tomoyo_quota_for_query = size; - return 0; -} diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c new file mode 100644 index 00000000000..5eb53510c4a --- /dev/null +++ b/security/tomoyo/securityfs_if.c @@ -0,0 +1,140 @@ +/* + * security/tomoyo/common.c + * + * Securityfs interface for TOMOYO. + * + * Copyright (C) 2005-2010 NTT DATA CORPORATION + */ + +#include +#include "common.h" + +/** + * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. + * + * @inode: Pointer to "struct inode". + * @file: Pointer to "struct file". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_open(struct inode *inode, struct file *file) +{ + const int key = ((u8 *) file->f_path.dentry->d_inode->i_private) + - ((u8 *) NULL); + return tomoyo_open_control(key, file); +} + +/** + * tomoyo_release - close() for /sys/kernel/security/tomoyo/ interface. + * + * @inode: Pointer to "struct inode". + * @file: Pointer to "struct file". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_release(struct inode *inode, struct file *file) +{ + return tomoyo_close_control(file); +} + +/** + * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface. + * + * @file: Pointer to "struct file". + * @buf: Pointer to buffer. + * @count: Size of @buf. + * @ppos: Unused. + * + * Returns bytes read on success, negative value otherwise. + */ +static ssize_t tomoyo_read(struct file *file, char __user *buf, size_t count, + loff_t *ppos) +{ + return tomoyo_read_control(file, buf, count); +} + +/** + * tomoyo_write - write() for /sys/kernel/security/tomoyo/ interface. + * + * @file: Pointer to "struct file". + * @buf: Pointer to buffer. + * @count: Size of @buf. + * @ppos: Unused. + * + * Returns @count on success, negative value otherwise. + */ +static ssize_t tomoyo_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + return tomoyo_write_control(file, buf, count); +} + +/* + * tomoyo_operations is a "struct file_operations" which is used for handling + * /sys/kernel/security/tomoyo/ interface. + * + * Some files under /sys/kernel/security/tomoyo/ directory accept open(O_RDWR). + * See tomoyo_io_buffer for internals. + */ +static const struct file_operations tomoyo_operations = { + .open = tomoyo_open, + .release = tomoyo_release, + .read = tomoyo_read, + .write = tomoyo_write, +}; + +/** + * tomoyo_create_entry - Create interface files under /sys/kernel/security/tomoyo/ directory. + * + * @name: The name of the interface file. + * @mode: The permission of the interface file. + * @parent: The parent directory. + * @key: Type of interface. + * + * Returns nothing. + */ +static void __init tomoyo_create_entry(const char *name, const mode_t mode, + struct dentry *parent, const u8 key) +{ + securityfs_create_file(name, mode, parent, ((u8 *) NULL) + key, + &tomoyo_operations); +} + +/** + * tomoyo_initerface_init - Initialize /sys/kernel/security/tomoyo/ interface. + * + * Returns 0. + */ +static int __init tomoyo_initerface_init(void) +{ + struct dentry *tomoyo_dir; + + /* Don't create securityfs entries unless registered. */ + if (current_cred()->security != &tomoyo_kernel_domain) + return 0; + + tomoyo_dir = securityfs_create_dir("tomoyo", NULL); + tomoyo_create_entry("query", 0600, tomoyo_dir, + TOMOYO_QUERY); + tomoyo_create_entry("domain_policy", 0600, tomoyo_dir, + TOMOYO_DOMAINPOLICY); + tomoyo_create_entry("exception_policy", 0600, tomoyo_dir, + TOMOYO_EXCEPTIONPOLICY); + tomoyo_create_entry("self_domain", 0400, tomoyo_dir, + TOMOYO_SELFDOMAIN); + tomoyo_create_entry(".domain_status", 0600, tomoyo_dir, + TOMOYO_DOMAIN_STATUS); + tomoyo_create_entry(".process_status", 0600, tomoyo_dir, + TOMOYO_PROCESS_STATUS); + tomoyo_create_entry("meminfo", 0600, tomoyo_dir, + TOMOYO_MEMINFO); + tomoyo_create_entry("profile", 0600, tomoyo_dir, + TOMOYO_PROFILE); + tomoyo_create_entry("manager", 0600, tomoyo_dir, + TOMOYO_MANAGER); + tomoyo_create_entry("version", 0400, tomoyo_dir, + TOMOYO_VERSION); + return 0; +} + +fs_initcall(tomoyo_initerface_init); diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 5d64d409b11..57d442e7339 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -3,10 +3,7 @@ * * LSM hooks for TOMOYO Linux. * - * Copyright (C) 2005-2009 NTT DATA CORPORATION - * - * Version: 2.2.0 2009/04/01 - * + * Copyright (C) 2005-2010 NTT DATA CORPORATION */ #include @@ -286,7 +283,7 @@ static int __init tomoyo_init(void) panic("Failure registering TOMOYO Linux"); printk(KERN_INFO "TOMOYO Linux initialized\n"); cred->security = &tomoyo_kernel_domain; - tomoyo_realpath_init(); + tomoyo_mm_init(); return 0; } diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c new file mode 100644 index 00000000000..7b023f5e131 --- /dev/null +++ b/security/tomoyo/util.c @@ -0,0 +1,951 @@ +/* + * security/tomoyo/util.c + * + * Utility functions for TOMOYO. + * + * Copyright (C) 2005-2010 NTT DATA CORPORATION + */ + +#include +#include "common.h" + +/* Lock for protecting policy. */ +DEFINE_MUTEX(tomoyo_policy_lock); + +/* Has /sbin/init started? */ +bool tomoyo_policy_loaded; + +/** + * tomoyo_parse_ulong - Parse an "unsigned long" value. + * + * @result: Pointer to "unsigned long". + * @str: Pointer to string to parse. + * + * Returns value type on success, 0 otherwise. + * + * The @src is updated to point the first character after the value + * on success. + */ +u8 tomoyo_parse_ulong(unsigned long *result, char **str) +{ + const char *cp = *str; + char *ep; + int base = 10; + if (*cp == '0') { + char c = *(cp + 1); + if (c == 'x' || c == 'X') { + base = 16; + cp += 2; + } else if (c >= '0' && c <= '7') { + base = 8; + cp++; + } + } + *result = simple_strtoul(cp, &ep, base); + if (cp == ep) + return 0; + *str = ep; + switch (base) { + case 16: + return TOMOYO_VALUE_TYPE_HEXADECIMAL; + case 8: + return TOMOYO_VALUE_TYPE_OCTAL; + default: + return TOMOYO_VALUE_TYPE_DECIMAL; + } +} + +/** + * tomoyo_print_ulong - Print an "unsigned long" value. + * + * @buffer: Pointer to buffer. + * @buffer_len: Size of @buffer. + * @value: An "unsigned long" value. + * @type: Type of @value. + * + * Returns nothing. + */ +void tomoyo_print_ulong(char *buffer, const int buffer_len, + const unsigned long value, const u8 type) +{ + if (type == TOMOYO_VALUE_TYPE_DECIMAL) + snprintf(buffer, buffer_len, "%lu", value); + else if (type == TOMOYO_VALUE_TYPE_OCTAL) + snprintf(buffer, buffer_len, "0%lo", value); + else if (type == TOMOYO_VALUE_TYPE_HEXADECIMAL) + snprintf(buffer, buffer_len, "0x%lX", value); + else + snprintf(buffer, buffer_len, "type(%u)", type); +} + +/** + * tomoyo_parse_name_union - Parse a tomoyo_name_union. + * + * @filename: Name or name group. + * @ptr: Pointer to "struct tomoyo_name_union". + * + * Returns true on success, false otherwise. + */ +bool tomoyo_parse_name_union(const char *filename, + struct tomoyo_name_union *ptr) +{ + if (!tomoyo_is_correct_path(filename, 0, 0, 0)) + return false; + if (filename[0] == '@') { + ptr->group = tomoyo_get_path_group(filename + 1); + ptr->is_group = true; + return ptr->group != NULL; + } + ptr->filename = tomoyo_get_name(filename); + ptr->is_group = false; + return ptr->filename != NULL; +} + +/** + * tomoyo_parse_number_union - Parse a tomoyo_number_union. + * + * @data: Number or number range or number group. + * @ptr: Pointer to "struct tomoyo_number_union". + * + * Returns true on success, false otherwise. + */ +bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) +{ + u8 type; + unsigned long v; + memset(num, 0, sizeof(*num)); + if (data[0] == '@') { + if (!tomoyo_is_correct_path(data, 0, 0, 0)) + return false; + num->group = tomoyo_get_number_group(data + 1); + num->is_group = true; + return num->group != NULL; + } + type = tomoyo_parse_ulong(&v, &data); + if (!type) + return false; + num->values[0] = v; + num->min_type = type; + if (!*data) { + num->values[1] = v; + num->max_type = type; + return true; + } + if (*data++ != '-') + return false; + type = tomoyo_parse_ulong(&v, &data); + if (!type || *data) + return false; + num->values[1] = v; + num->max_type = type; + return true; +} + +/** + * tomoyo_is_byte_range - Check whether the string is a \ooo style octal value. + * + * @str: Pointer to the string. + * + * Returns true if @str is a \ooo style octal value, false otherwise. + * + * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF. + * This function verifies that \ooo is in valid range. + */ +static inline bool tomoyo_is_byte_range(const char *str) +{ + return *str >= '0' && *str++ <= '3' && + *str >= '0' && *str++ <= '7' && + *str >= '0' && *str <= '7'; +} + +/** + * tomoyo_is_alphabet_char - Check whether the character is an alphabet. + * + * @c: The character to check. + * + * Returns true if @c is an alphabet character, false otherwise. + */ +static inline bool tomoyo_is_alphabet_char(const char c) +{ + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); +} + +/** + * tomoyo_make_byte - Make byte value from three octal characters. + * + * @c1: The first character. + * @c2: The second character. + * @c3: The third character. + * + * Returns byte value. + */ +static inline u8 tomoyo_make_byte(const u8 c1, const u8 c2, const u8 c3) +{ + return ((c1 - '0') << 6) + ((c2 - '0') << 3) + (c3 - '0'); +} + +/** + * tomoyo_str_starts - Check whether the given string starts with the given keyword. + * + * @src: Pointer to pointer to the string. + * @find: Pointer to the keyword. + * + * Returns true if @src starts with @find, false otherwise. + * + * The @src is updated to point the first character after the @find + * if @src starts with @find. + */ +bool tomoyo_str_starts(char **src, const char *find) +{ + const int len = strlen(find); + char *tmp = *src; + + if (strncmp(tmp, find, len)) + return false; + tmp += len; + *src = tmp; + return true; +} + +/** + * tomoyo_normalize_line - Format string. + * + * @buffer: The line to normalize. + * + * Leading and trailing whitespaces are removed. + * Multiple whitespaces are packed into single space. + * + * Returns nothing. + */ +void tomoyo_normalize_line(unsigned char *buffer) +{ + unsigned char *sp = buffer; + unsigned char *dp = buffer; + bool first = true; + + while (tomoyo_is_invalid(*sp)) + sp++; + while (*sp) { + if (!first) + *dp++ = ' '; + first = false; + while (tomoyo_is_valid(*sp)) + *dp++ = *sp++; + while (tomoyo_is_invalid(*sp)) + sp++; + } + *dp = '\0'; +} + +/** + * tomoyo_tokenize - Tokenize string. + * + * @buffer: The line to tokenize. + * @w: Pointer to "char *". + * @size: Sizeof @w . + * + * Returns true on success, false otherwise. + */ +bool tomoyo_tokenize(char *buffer, char *w[], size_t size) +{ + int count = size / sizeof(char *); + int i; + for (i = 0; i < count; i++) + w[i] = ""; + for (i = 0; i < count; i++) { + char *cp = strchr(buffer, ' '); + if (cp) + *cp = '\0'; + w[i] = buffer; + if (!cp) + break; + buffer = cp + 1; + } + return i < count || !*buffer; +} + +/** + * tomoyo_is_correct_path - Validate a pathname. + * + * @filename: The pathname to check. + * @start_type: Should the pathname start with '/'? + * 1 = must / -1 = must not / 0 = don't care + * @pattern_type: Can the pathname contain a wildcard? + * 1 = must / -1 = must not / 0 = don't care + * @end_type: Should the pathname end with '/'? + * 1 = must / -1 = must not / 0 = don't care + * + * Check whether the given filename follows the naming rules. + * Returns true if @filename follows the naming rules, false otherwise. + */ +bool tomoyo_is_correct_path(const char *filename, const s8 start_type, + const s8 pattern_type, const s8 end_type) +{ + const char *const start = filename; + bool in_repetition = false; + bool contains_pattern = false; + unsigned char c; + unsigned char d; + unsigned char e; + + if (!filename) + goto out; + c = *filename; + if (start_type == 1) { /* Must start with '/' */ + if (c != '/') + goto out; + } else if (start_type == -1) { /* Must not start with '/' */ + if (c == '/') + goto out; + } + if (c) + c = *(filename + strlen(filename) - 1); + if (end_type == 1) { /* Must end with '/' */ + if (c != '/') + goto out; + } else if (end_type == -1) { /* Must not end with '/' */ + if (c == '/') + goto out; + } + while (1) { + c = *filename++; + if (!c) + break; + if (c == '\\') { + c = *filename++; + switch (c) { + case '\\': /* "\\" */ + continue; + case '$': /* "\$" */ + case '+': /* "\+" */ + case '?': /* "\?" */ + case '*': /* "\*" */ + case '@': /* "\@" */ + case 'x': /* "\x" */ + case 'X': /* "\X" */ + case 'a': /* "\a" */ + case 'A': /* "\A" */ + case '-': /* "\-" */ + if (pattern_type == -1) + break; /* Must not contain pattern */ + contains_pattern = true; + continue; + case '{': /* "/\{" */ + if (filename - 3 < start || + *(filename - 3) != '/') + break; + if (pattern_type == -1) + break; /* Must not contain pattern */ + contains_pattern = true; + in_repetition = true; + continue; + case '}': /* "\}/" */ + if (*filename != '/') + break; + if (!in_repetition) + break; + in_repetition = false; + continue; + case '0': /* "\ooo" */ + case '1': + case '2': + case '3': + d = *filename++; + if (d < '0' || d > '7') + break; + e = *filename++; + if (e < '0' || e > '7') + break; + c = tomoyo_make_byte(c, d, e); + if (tomoyo_is_invalid(c)) + continue; /* pattern is not \000 */ + } + goto out; + } else if (in_repetition && c == '/') { + goto out; + } else if (tomoyo_is_invalid(c)) { + goto out; + } + } + if (pattern_type == 1) { /* Must contain pattern */ + if (!contains_pattern) + goto out; + } + if (in_repetition) + goto out; + return true; + out: + return false; +} + +/** + * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules. + * + * @domainname: The domainname to check. + * + * Returns true if @domainname follows the naming rules, false otherwise. + */ +bool tomoyo_is_correct_domain(const unsigned char *domainname) +{ + unsigned char c; + unsigned char d; + unsigned char e; + + if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, + TOMOYO_ROOT_NAME_LEN)) + goto out; + domainname += TOMOYO_ROOT_NAME_LEN; + if (!*domainname) + return true; + do { + if (*domainname++ != ' ') + goto out; + if (*domainname++ != '/') + goto out; + while ((c = *domainname) != '\0' && c != ' ') { + domainname++; + if (c == '\\') { + c = *domainname++; + switch ((c)) { + case '\\': /* "\\" */ + continue; + case '0': /* "\ooo" */ + case '1': + case '2': + case '3': + d = *domainname++; + if (d < '0' || d > '7') + break; + e = *domainname++; + if (e < '0' || e > '7') + break; + c = tomoyo_make_byte(c, d, e); + if (tomoyo_is_invalid(c)) + /* pattern is not \000 */ + continue; + } + goto out; + } else if (tomoyo_is_invalid(c)) { + goto out; + } + } + } while (*domainname); + return true; + out: + return false; +} + +/** + * tomoyo_is_domain_def - Check whether the given token can be a domainname. + * + * @buffer: The token to check. + * + * Returns true if @buffer possibly be a domainname, false otherwise. + */ +bool tomoyo_is_domain_def(const unsigned char *buffer) +{ + return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); +} + +/** + * tomoyo_find_domain - Find a domain by the given name. + * + * @domainname: The domainname to find. + * + * Returns pointer to "struct tomoyo_domain_info" if found, NULL otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname) +{ + struct tomoyo_domain_info *domain; + struct tomoyo_path_info name; + + name.name = domainname; + tomoyo_fill_path_info(&name); + list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { + if (!domain->is_deleted && + !tomoyo_pathcmp(&name, domain->domainname)) + return domain; + } + return NULL; +} + +/** + * tomoyo_const_part_length - Evaluate the initial length without a pattern in a token. + * + * @filename: The string to evaluate. + * + * Returns the initial length without a pattern in @filename. + */ +static int tomoyo_const_part_length(const char *filename) +{ + char c; + int len = 0; + + if (!filename) + return 0; + while ((c = *filename++) != '\0') { + if (c != '\\') { + len++; + continue; + } + c = *filename++; + switch (c) { + case '\\': /* "\\" */ + len += 2; + continue; + case '0': /* "\ooo" */ + case '1': + case '2': + case '3': + c = *filename++; + if (c < '0' || c > '7') + break; + c = *filename++; + if (c < '0' || c > '7') + break; + len += 4; + continue; + } + break; + } + return len; +} + +/** + * tomoyo_fill_path_info - Fill in "struct tomoyo_path_info" members. + * + * @ptr: Pointer to "struct tomoyo_path_info" to fill in. + * + * The caller sets "struct tomoyo_path_info"->name. + */ +void tomoyo_fill_path_info(struct tomoyo_path_info *ptr) +{ + const char *name = ptr->name; + const int len = strlen(name); + + ptr->const_len = tomoyo_const_part_length(name); + ptr->is_dir = len && (name[len - 1] == '/'); + ptr->is_patterned = (ptr->const_len < len); + ptr->hash = full_name_hash(name, len); +} + +/** + * tomoyo_file_matches_pattern2 - Pattern matching without '/' character and "\-" pattern. + * + * @filename: The start of string to check. + * @filename_end: The end of string to check. + * @pattern: The start of pattern to compare. + * @pattern_end: The end of pattern to compare. + * + * Returns true if @filename matches @pattern, false otherwise. + */ +static bool tomoyo_file_matches_pattern2(const char *filename, + const char *filename_end, + const char *pattern, + const char *pattern_end) +{ + while (filename < filename_end && pattern < pattern_end) { + char c; + if (*pattern != '\\') { + if (*filename++ != *pattern++) + return false; + continue; + } + c = *filename; + pattern++; + switch (*pattern) { + int i; + int j; + case '?': + if (c == '/') { + return false; + } else if (c == '\\') { + if (filename[1] == '\\') + filename++; + else if (tomoyo_is_byte_range(filename + 1)) + filename += 3; + else + return false; + } + break; + case '\\': + if (c != '\\') + return false; + if (*++filename != '\\') + return false; + break; + case '+': + if (!isdigit(c)) + return false; + break; + case 'x': + if (!isxdigit(c)) + return false; + break; + case 'a': + if (!tomoyo_is_alphabet_char(c)) + return false; + break; + case '0': + case '1': + case '2': + case '3': + if (c == '\\' && tomoyo_is_byte_range(filename + 1) + && strncmp(filename + 1, pattern, 3) == 0) { + filename += 3; + pattern += 2; + break; + } + return false; /* Not matched. */ + case '*': + case '@': + for (i = 0; i <= filename_end - filename; i++) { + if (tomoyo_file_matches_pattern2( + filename + i, filename_end, + pattern + 1, pattern_end)) + return true; + c = filename[i]; + if (c == '.' && *pattern == '@') + break; + if (c != '\\') + continue; + if (filename[i + 1] == '\\') + i++; + else if (tomoyo_is_byte_range(filename + i + 1)) + i += 3; + else + break; /* Bad pattern. */ + } + return false; /* Not matched. */ + default: + j = 0; + c = *pattern; + if (c == '$') { + while (isdigit(filename[j])) + j++; + } else if (c == 'X') { + while (isxdigit(filename[j])) + j++; + } else if (c == 'A') { + while (tomoyo_is_alphabet_char(filename[j])) + j++; + } + for (i = 1; i <= j; i++) { + if (tomoyo_file_matches_pattern2( + filename + i, filename_end, + pattern + 1, pattern_end)) + return true; + } + return false; /* Not matched or bad pattern. */ + } + filename++; + pattern++; + } + while (*pattern == '\\' && + (*(pattern + 1) == '*' || *(pattern + 1) == '@')) + pattern += 2; + return filename == filename_end && pattern == pattern_end; +} + +/** + * tomoyo_file_matches_pattern - Pattern matching without '/' character. + * + * @filename: The start of string to check. + * @filename_end: The end of string to check. + * @pattern: The start of pattern to compare. + * @pattern_end: The end of pattern to compare. + * + * Returns true if @filename matches @pattern, false otherwise. + */ +static bool tomoyo_file_matches_pattern(const char *filename, + const char *filename_end, + const char *pattern, + const char *pattern_end) +{ + const char *pattern_start = pattern; + bool first = true; + bool result; + + while (pattern < pattern_end - 1) { + /* Split at "\-" pattern. */ + if (*pattern++ != '\\' || *pattern++ != '-') + continue; + result = tomoyo_file_matches_pattern2(filename, + filename_end, + pattern_start, + pattern - 2); + if (first) + result = !result; + if (result) + return false; + first = false; + pattern_start = pattern; + } + result = tomoyo_file_matches_pattern2(filename, filename_end, + pattern_start, pattern_end); + return first ? result : !result; +} + +/** + * tomoyo_path_matches_pattern2 - Do pathname pattern matching. + * + * @f: The start of string to check. + * @p: The start of pattern to compare. + * + * Returns true if @f matches @p, false otherwise. + */ +static bool tomoyo_path_matches_pattern2(const char *f, const char *p) +{ + const char *f_delimiter; + const char *p_delimiter; + + while (*f && *p) { + f_delimiter = strchr(f, '/'); + if (!f_delimiter) + f_delimiter = f + strlen(f); + p_delimiter = strchr(p, '/'); + if (!p_delimiter) + p_delimiter = p + strlen(p); + if (*p == '\\' && *(p + 1) == '{') + goto recursive; + if (!tomoyo_file_matches_pattern(f, f_delimiter, p, + p_delimiter)) + return false; + f = f_delimiter; + if (*f) + f++; + p = p_delimiter; + if (*p) + p++; + } + /* Ignore trailing "\*" and "\@" in @pattern. */ + while (*p == '\\' && + (*(p + 1) == '*' || *(p + 1) == '@')) + p += 2; + return !*f && !*p; + recursive: + /* + * The "\{" pattern is permitted only after '/' character. + * This guarantees that below "*(p - 1)" is safe. + * Also, the "\}" pattern is permitted only before '/' character + * so that "\{" + "\}" pair will not break the "\-" operator. + */ + if (*(p - 1) != '/' || p_delimiter <= p + 3 || *p_delimiter != '/' || + *(p_delimiter - 1) != '}' || *(p_delimiter - 2) != '\\') + return false; /* Bad pattern. */ + do { + /* Compare current component with pattern. */ + if (!tomoyo_file_matches_pattern(f, f_delimiter, p + 2, + p_delimiter - 2)) + break; + /* Proceed to next component. */ + f = f_delimiter; + if (!*f) + break; + f++; + /* Continue comparison. */ + if (tomoyo_path_matches_pattern2(f, p_delimiter + 1)) + return true; + f_delimiter = strchr(f, '/'); + } while (f_delimiter); + return false; /* Not matched. */ +} + +/** + * tomoyo_path_matches_pattern - Check whether the given filename matches the given pattern. + * + * @filename: The filename to check. + * @pattern: The pattern to compare. + * + * Returns true if matches, false otherwise. + * + * The following patterns are available. + * \\ \ itself. + * \ooo Octal representation of a byte. + * \* Zero or more repetitions of characters other than '/'. + * \@ Zero or more repetitions of characters other than '/' or '.'. + * \? 1 byte character other than '/'. + * \$ One or more repetitions of decimal digits. + * \+ 1 decimal digit. + * \X One or more repetitions of hexadecimal digits. + * \x 1 hexadecimal digit. + * \A One or more repetitions of alphabet characters. + * \a 1 alphabet character. + * + * \- Subtraction operator. + * + * /\{dir\}/ '/' + 'One or more repetitions of dir/' (e.g. /dir/ /dir/dir/ + * /dir/dir/dir/ ). + */ +bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, + const struct tomoyo_path_info *pattern) +{ + const char *f = filename->name; + const char *p = pattern->name; + const int len = pattern->const_len; + + /* If @pattern doesn't contain pattern, I can use strcmp(). */ + if (!pattern->is_patterned) + return !tomoyo_pathcmp(filename, pattern); + /* Don't compare directory and non-directory. */ + if (filename->is_dir != pattern->is_dir) + return false; + /* Compare the initial length without patterns. */ + if (strncmp(f, p, len)) + return false; + f += len; + p += len; + return tomoyo_path_matches_pattern2(f, p); +} + +/** + * tomoyo_get_exe - Get tomoyo_realpath() of current process. + * + * Returns the tomoyo_realpath() of current process on success, NULL otherwise. + * + * This function uses kzalloc(), so the caller must call kfree() + * if this function didn't return NULL. + */ +const char *tomoyo_get_exe(void) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + const char *cp = NULL; + + if (!mm) + return NULL; + down_read(&mm->mmap_sem); + for (vma = mm->mmap; vma; vma = vma->vm_next) { + if ((vma->vm_flags & VM_EXECUTABLE) && vma->vm_file) { + cp = tomoyo_realpath_from_path(&vma->vm_file->f_path); + break; + } + } + up_read(&mm->mmap_sem); + return cp; +} + +/** + * tomoyo_init_request_info - Initialize "struct tomoyo_request_info" members. + * + * @r: Pointer to "struct tomoyo_request_info" to initialize. + * @domain: Pointer to "struct tomoyo_domain_info". NULL for tomoyo_domain(). + * + * Returns mode. + */ +int tomoyo_init_request_info(struct tomoyo_request_info *r, + struct tomoyo_domain_info *domain) +{ + memset(r, 0, sizeof(*r)); + if (!domain) + domain = tomoyo_domain(); + r->domain = domain; + r->mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); + return r->mode; +} + +/** + * tomoyo_warn_log - Print warning or error message on console. + * + * @r: Pointer to "struct tomoyo_request_info". + * @fmt: The printf()'s format string, followed by parameters. + */ +void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) +{ + int len = PAGE_SIZE; + va_list args; + char *buffer; + if (!tomoyo_verbose_mode(r->domain)) + return; + while (1) { + int len2; + buffer = kmalloc(len, GFP_NOFS); + if (!buffer) + return; + va_start(args, fmt); + len2 = vsnprintf(buffer, len - 1, fmt, args); + va_end(args); + if (len2 <= len - 1) { + buffer[len2] = '\0'; + break; + } + len = len2 + 1; + kfree(buffer); + } + printk(KERN_WARNING "TOMOYO-%s: Access %s denied for %s\n", + r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", + buffer, tomoyo_get_last_name(r->domain)); + kfree(buffer); +} + +/** + * tomoyo_domain_quota_is_ok - Check for domain's quota. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns true if the domain is not exceeded quota, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) +{ + unsigned int count = 0; + struct tomoyo_domain_info *domain = r->domain; + struct tomoyo_acl_info *ptr; + + if (r->mode != TOMOYO_CONFIG_LEARNING) + return false; + if (!domain) + return true; + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + switch (ptr->type) { + u16 perm; + u8 i; + case TOMOYO_TYPE_PATH_ACL: + perm = container_of(ptr, struct tomoyo_path_acl, head) + ->perm; + for (i = 0; i < TOMOYO_MAX_PATH_OPERATION; i++) + if (perm & (1 << i)) + count++; + if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) + count -= 2; + break; + case TOMOYO_TYPE_PATH2_ACL: + perm = container_of(ptr, struct tomoyo_path2_acl, head) + ->perm; + for (i = 0; i < TOMOYO_MAX_PATH2_OPERATION; i++) + if (perm & (1 << i)) + count++; + break; + case TOMOYO_TYPE_PATH_NUMBER_ACL: + perm = container_of(ptr, struct tomoyo_path_number_acl, + head)->perm; + for (i = 0; i < TOMOYO_MAX_PATH_NUMBER_OPERATION; i++) + if (perm & (1 << i)) + count++; + break; + case TOMOYO_TYPE_PATH_NUMBER3_ACL: + perm = container_of(ptr, struct tomoyo_path_number3_acl, + head)->perm; + for (i = 0; i < TOMOYO_MAX_PATH_NUMBER3_OPERATION; i++) + if (perm & (1 << i)) + count++; + break; + case TOMOYO_TYPE_MOUNT_ACL: + if (!container_of(ptr, struct tomoyo_mount_acl, head)-> + is_deleted) + count++; + } + } + if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) + return true; + if (!domain->quota_warned) { + domain->quota_warned = true; + printk(KERN_WARNING "TOMOYO-WARNING: " + "Domain '%s' has so many ACLs to hold. " + "Stopped learning mode.\n", domain->domainname->name); + } + return false; +} -- cgit v1.2.3-70-g09d2 From 3e62cbb8436f6c0cb799c8b7f106de7f662a7b8d Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 1 Jun 2010 09:14:04 +0200 Subject: smack: opt_dentry is never null in in smack_d_instantiate() This patch removes some unneeded code for if opt_dentry is null because that can never happen. The function dereferences "opt_dentry" earlier when it checks "if (opt_dentry->d_parent == opt_dentry) {". That code was added in 2008. This function called from security_d_instantiate(). I checked all the places which call security_d_instantiate() and dentry is always non-null. I also checked the selinux version of this hook and there is a comment which says that dentry should be non-null if called from d_instantiate(). Signed-off-by: Dan Carpenter Acked-by: Casey Schaufler Signed-off-by: James Morris --- security/smack/smack_lsm.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 0f2fc480fc6..07abc9ce72f 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2191,7 +2191,7 @@ static void smack_ipc_getsecid(struct kern_ipc_perm *ipp, u32 *secid) /** * smack_d_instantiate - Make sure the blob is correct on an inode - * @opt_dentry: unused + * @opt_dentry: dentry where inode will be attached * @inode: the object * * Set the inode's security blob if it hasn't been done already. @@ -2310,20 +2310,10 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) /* * Get the dentry for xattr. */ - if (opt_dentry == NULL) { - dp = d_find_alias(inode); - if (dp == NULL) - break; - } else { - dp = dget(opt_dentry); - if (dp == NULL) - break; - } - + dp = dget(opt_dentry); fetched = smk_fetch(inode, dp); if (fetched != NULL) final = fetched; - dput(dp); break; } -- cgit v1.2.3-70-g09d2 From ea0d3ab239fba48d6e998b19c28d78f765963007 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 2 Jun 2010 13:24:43 +0900 Subject: LSM: Remove unused arguments from security_path_truncate(). When commit be6d3e56a6b9b3a4ee44a0685e39e595073c6f0d "introduce new LSM hooks where vfsmount is available." was proposed, regarding security_path_truncate(), only "struct file *" argument (which AppArmor wanted to use) was removed. But length and time_attrs arguments are not used by TOMOYO nor AppArmor. Thus, let's remove these arguments. Signed-off-by: Tetsuo Handa Acked-by: Nick Piggin Signed-off-by: James Morris --- fs/namei.c | 3 +-- fs/open.c | 5 ++--- include/linux/security.h | 11 +++-------- security/capability.c | 3 +-- security/security.c | 5 ++--- security/tomoyo/tomoyo.c | 3 +-- 6 files changed, 10 insertions(+), 20 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 868d0cb9d47..fe34c2b879f 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1484,8 +1484,7 @@ static int handle_truncate(struct path *path) */ error = locks_verify_locked(inode); if (!error) - error = security_path_truncate(path, 0, - ATTR_MTIME|ATTR_CTIME|ATTR_OPEN); + error = security_path_truncate(path); if (!error) { error = do_truncate(path->dentry, 0, ATTR_MTIME|ATTR_CTIME|ATTR_OPEN, diff --git a/fs/open.c b/fs/open.c index 5463266db9e..a54ed85209c 100644 --- a/fs/open.c +++ b/fs/open.c @@ -110,7 +110,7 @@ static long do_sys_truncate(const char __user *pathname, loff_t length) error = locks_verify_truncate(inode, NULL, length); if (!error) - error = security_path_truncate(&path, length, 0); + error = security_path_truncate(&path); if (!error) error = do_truncate(path.dentry, length, 0, NULL); @@ -165,8 +165,7 @@ static long do_sys_ftruncate(unsigned int fd, loff_t length, int small) error = locks_verify_truncate(inode, file, length); if (!error) - error = security_path_truncate(&file->f_path, length, - ATTR_MTIME|ATTR_CTIME); + error = security_path_truncate(&file->f_path); if (!error) error = do_truncate(dentry, length, ATTR_MTIME|ATTR_CTIME, file); out_putf: diff --git a/include/linux/security.h b/include/linux/security.h index 0c881917046..723a93df756 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -470,8 +470,6 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @path_truncate: * Check permission before truncating a file. * @path contains the path structure for the file. - * @length is the new length of the file. - * @time_attrs is the flags passed to do_truncate(). * Return 0 if permission is granted. * @inode_getattr: * Check permission before obtaining file attributes. @@ -1412,8 +1410,7 @@ struct security_operations { int (*path_rmdir) (struct path *dir, struct dentry *dentry); int (*path_mknod) (struct path *dir, struct dentry *dentry, int mode, unsigned int dev); - int (*path_truncate) (struct path *path, loff_t length, - unsigned int time_attrs); + int (*path_truncate) (struct path *path); int (*path_symlink) (struct path *dir, struct dentry *dentry, const char *old_name); int (*path_link) (struct dentry *old_dentry, struct path *new_dir, @@ -2806,8 +2803,7 @@ int security_path_mkdir(struct path *dir, struct dentry *dentry, int mode); int security_path_rmdir(struct path *dir, struct dentry *dentry); int security_path_mknod(struct path *dir, struct dentry *dentry, int mode, unsigned int dev); -int security_path_truncate(struct path *path, loff_t length, - unsigned int time_attrs); +int security_path_truncate(struct path *path); int security_path_symlink(struct path *dir, struct dentry *dentry, const char *old_name); int security_path_link(struct dentry *old_dentry, struct path *new_dir, @@ -2841,8 +2837,7 @@ static inline int security_path_mknod(struct path *dir, struct dentry *dentry, return 0; } -static inline int security_path_truncate(struct path *path, loff_t length, - unsigned int time_attrs) +static inline int security_path_truncate(struct path *path) { return 0; } diff --git a/security/capability.c b/security/capability.c index 8168e3ecd5b..4aeb699da1b 100644 --- a/security/capability.c +++ b/security/capability.c @@ -268,8 +268,7 @@ static int cap_path_rename(struct path *old_path, struct dentry *old_dentry, return 0; } -static int cap_path_truncate(struct path *path, loff_t length, - unsigned int time_attrs) +static int cap_path_truncate(struct path *path) { return 0; } diff --git a/security/security.c b/security/security.c index 351942a4ca0..e8c87b8601b 100644 --- a/security/security.c +++ b/security/security.c @@ -417,12 +417,11 @@ int security_path_rename(struct path *old_dir, struct dentry *old_dentry, new_dentry); } -int security_path_truncate(struct path *path, loff_t length, - unsigned int time_attrs) +int security_path_truncate(struct path *path) { if (unlikely(IS_PRIVATE(path->dentry->d_inode))) return 0; - return security_ops->path_truncate(path, length, time_attrs); + return security_ops->path_truncate(path); } int security_path_chmod(struct dentry *dentry, struct vfsmount *mnt, diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 57d442e7339..7be732cadd4 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -93,8 +93,7 @@ static int tomoyo_bprm_check_security(struct linux_binprm *bprm) return tomoyo_check_open_permission(domain, &bprm->file->f_path, O_RDONLY); } -static int tomoyo_path_truncate(struct path *path, loff_t length, - unsigned int time_attrs) +static int tomoyo_path_truncate(struct path *path) { return tomoyo_path_perm(TOMOYO_TYPE_TRUNCATE, path); } -- cgit v1.2.3-70-g09d2 From 9b244373da3eab671da6c5125482121528a9ebf3 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 3 Jun 2010 20:35:53 +0900 Subject: TOMOYO: Several fixes for TOMOYO's management programs. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 25 ++++++++++++++++++++----- security/tomoyo/common.h | 2 ++ security/tomoyo/path_group.c | 2 +- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 57ddfc5d9c5..98e3639db99 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -366,7 +366,7 @@ static int tomoyo_read_profile(struct tomoyo_io_buffer *head) * * or * - * # echo '/usr/lib/ccs/editpolicy' > /sys/kernel/security/tomoyo/manager + * # echo '/usr/sbin/tomoyo-editpolicy' > /sys/kernel/security/tomoyo/manager * (if you want to specify by a program's location) * * and is deleted by @@ -376,7 +376,7 @@ static int tomoyo_read_profile(struct tomoyo_io_buffer *head) * * or * - * # echo 'delete /usr/lib/ccs/editpolicy' > \ + * # echo 'delete /usr/sbin/tomoyo-editpolicy' > \ * /sys/kernel/security/tomoyo/manager * * and all entries are retrieved by @@ -556,12 +556,17 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, { unsigned int pid; struct tomoyo_domain_info *domain = NULL; + bool global_pid = false; - if (sscanf(data, "pid=%u", &pid) == 1) { + if (sscanf(data, "pid=%u", &pid) == 1 || + (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { struct task_struct *p; rcu_read_lock(); read_lock(&tasklist_lock); - p = find_task_by_vpid(pid); + if (global_pid) + p = find_task_by_pid_ns(pid, &init_pid_ns); + else + p = find_task_by_vpid(pid); if (p) domain = tomoyo_real_domain(p); read_unlock(&tasklist_lock); @@ -697,6 +702,14 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) domain->ignore_global_allow_read = !is_delete; return 0; } + if (!strcmp(data, TOMOYO_KEYWORD_QUOTA_EXCEEDED)) { + domain->quota_warned = !is_delete; + return 0; + } + if (!strcmp(data, TOMOYO_KEYWORD_TRANSITION_FAILED)) { + domain->transition_failed = !is_delete; + return 0; + } return tomoyo_write_domain_policy2(data, domain, is_delete); } @@ -853,6 +866,8 @@ static bool tomoyo_print_mount_acl(struct tomoyo_io_buffer *head, struct tomoyo_mount_acl *ptr) { const int pos = head->read_avail; + if (ptr->is_deleted) + return true; if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_MOUNT) || !tomoyo_print_name_union(head, &ptr->dev_name) || !tomoyo_print_name_union(head, &ptr->dir_name) || @@ -993,7 +1008,7 @@ tail_mark: * This is equivalent to doing * * ( echo "select " $domainname; echo "use_profile " $profile ) | - * /usr/lib/ccs/loadpolicy -d + * /usr/sbin/tomoyo-loadpolicy -d * * Caller holds tomoyo_read_lock(). */ diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index be03e4a21db..6270a530c4d 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -68,6 +68,8 @@ enum tomoyo_mode_index { #define TOMOYO_KEYWORD_SELECT "select " #define TOMOYO_KEYWORD_USE_PROFILE "use_profile " #define TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "ignore_global_allow_read" +#define TOMOYO_KEYWORD_QUOTA_EXCEEDED "quota_exceeded" +#define TOMOYO_KEYWORD_TRANSITION_FAILED "transition_failed" /* A domain definition starts with . */ #define TOMOYO_ROOT_NAME "" #define TOMOYO_ROOT_NAME_LEN (sizeof(TOMOYO_ROOT_NAME) - 1) diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c index c988041c8e1..636025e26b0 100644 --- a/security/tomoyo/path_group.c +++ b/security/tomoyo/path_group.c @@ -6,7 +6,7 @@ #include #include "common.h" -/* The list for "struct ccs_path_group". */ +/* The list for "struct tomoyo_path_group". */ LIST_HEAD(tomoyo_path_group_list); /** -- cgit v1.2.3-70-g09d2 From c8c57e842720d8cc92ac8607f2d1c16d92314573 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 3 Jun 2010 20:36:43 +0900 Subject: TOMOYO: Support longer pathname. Allow pathnames longer than 4000 bytes. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.h | 48 +--------- security/tomoyo/domain.c | 72 +++++++-------- security/tomoyo/file.c | 136 ++++++++++++---------------- security/tomoyo/memory.c | 1 - security/tomoyo/mount.c | 55 +----------- security/tomoyo/realpath.c | 214 ++++++++++++++++++++++++--------------------- 6 files changed, 208 insertions(+), 318 deletions(-) diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 6270a530c4d..f4a8aa244af 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -33,14 +33,7 @@ struct linux_binprm; #define TOMOYO_HASH_BITS 8 #define TOMOYO_MAX_HASH (1u<domainname->name; const char *original_name = bprm->filename; - char *new_domain_name = NULL; - char *real_program_name = NULL; - char *symlink_program_name = NULL; const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE); const bool is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); int retval = -ENOMEM; - struct tomoyo_path_info rn; /* real name */ - struct tomoyo_path_info sn; /* symlink name */ + bool need_kfree = false; + struct tomoyo_path_info rn = { }; /* real name */ + struct tomoyo_path_info sn = { }; /* symlink name */ struct tomoyo_path_info ln; /* last name */ + ln.name = tomoyo_get_last_name(old_domain); + tomoyo_fill_path_info(&ln); tomoyo_init_request_info(&r, NULL); if (!tmp) goto out; retry: + if (need_kfree) { + kfree(rn.name); + need_kfree = false; + } /* Get tomoyo_realpath of program. */ retval = -ENOENT; - /* I hope tomoyo_realpath() won't fail with -ENOMEM. */ - real_program_name = tomoyo_realpath(original_name); - if (!real_program_name) + rn.name = tomoyo_realpath(original_name); + if (!rn.name) goto out; + tomoyo_fill_path_info(&rn); + need_kfree = true; + /* Get tomoyo_realpath of symbolic link. */ - symlink_program_name = tomoyo_realpath_nofollow(original_name); - if (!symlink_program_name) + sn.name = tomoyo_realpath_nofollow(original_name); + if (!sn.name) goto out; - - rn.name = real_program_name; - tomoyo_fill_path_info(&rn); - sn.name = symlink_program_name; tomoyo_fill_path_info(&sn); - ln.name = tomoyo_get_last_name(old_domain); - tomoyo_fill_path_info(&ln); /* Check 'alias' directive. */ if (tomoyo_pathcmp(&rn, &sn)) { @@ -727,10 +723,10 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) tomoyo_pathcmp(&rn, ptr->original_name) || tomoyo_pathcmp(&sn, ptr->aliased_name)) continue; - memset(real_program_name, 0, TOMOYO_MAX_PATHNAME_LEN); - strncpy(real_program_name, ptr->aliased_name->name, - TOMOYO_MAX_PATHNAME_LEN - 1); - tomoyo_fill_path_info(&rn); + kfree(rn.name); + need_kfree = false; + /* This is OK because it is read only. */ + rn = *ptr->aliased_name; break; } } @@ -742,11 +738,10 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) if (retval < 0) goto out; - new_domain_name = tmp->buffer; if (tomoyo_is_domain_initializer(old_domain->domainname, &rn, &ln)) { /* Transit to the child of tomoyo_kernel_domain domain. */ - snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1, - TOMOYO_ROOT_NAME " " "%s", real_program_name); + snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, + TOMOYO_ROOT_NAME " " "%s", rn.name); } else if (old_domain == &tomoyo_kernel_domain && !tomoyo_policy_loaded) { /* @@ -760,29 +755,27 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) domain = old_domain; } else { /* Normal domain transition. */ - snprintf(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1, - "%s %s", old_domain_name, real_program_name); + snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, + "%s %s", old_domain_name, rn.name); } - if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN) + if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10) goto done; - domain = tomoyo_find_domain(new_domain_name); + domain = tomoyo_find_domain(tmp); if (domain) goto done; if (is_enforce) { int error = tomoyo_supervisor(&r, "# wants to create domain\n" - "%s\n", new_domain_name); + "%s\n", tmp); if (error == TOMOYO_RETRY_REQUEST) goto retry; if (error < 0) goto done; } - domain = tomoyo_find_or_assign_new_domain(new_domain_name, - old_domain->profile); + domain = tomoyo_find_or_assign_new_domain(tmp, old_domain->profile); done: if (domain) goto out; - printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", - new_domain_name); + printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n", tmp); if (is_enforce) retval = -EPERM; else @@ -793,8 +786,9 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) /* Update reference count on "struct tomoyo_domain_info". */ atomic_inc(&domain->users); bprm->cred->security = domain; - kfree(real_program_name); - kfree(symlink_program_name); + if (need_kfree) + kfree(rn.name); + kfree(sn.name); kfree(tmp); return retval; } diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index c13806937dc..cef685415df 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -148,6 +148,17 @@ const char *tomoyo_path_number2keyword(const u8 operation) ? tomoyo_path_number_keyword[operation] : NULL; } +static void tomoyo_add_slash(struct tomoyo_path_info *buf) +{ + if (buf->is_dir) + return; + /* + * This is OK because tomoyo_encode() reserves space for appending "/". + */ + strcat((char *) buf->name, "/"); + tomoyo_fill_path_info(buf); +} + /** * tomoyo_strendswith - Check whether the token ends with the given token. * @@ -167,30 +178,21 @@ static bool tomoyo_strendswith(const char *name, const char *tail) } /** - * tomoyo_get_path - Get realpath. + * tomoyo_get_realpath - Get realpath. * + * @buf: Pointer to "struct tomoyo_path_info". * @path: Pointer to "struct path". * - * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise. + * Returns true on success, false otherwise. */ -static struct tomoyo_path_info *tomoyo_get_path(struct path *path) +static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path) { - int error; - struct tomoyo_path_info_with_data *buf = kzalloc(sizeof(*buf), - GFP_NOFS); - - if (!buf) - return NULL; - /* Reserve one byte for appending "/". */ - error = tomoyo_realpath_from_path2(path, buf->body, - sizeof(buf->body) - 2); - if (!error) { - buf->head.name = buf->body; - tomoyo_fill_path_info(&buf->head); - return &buf->head; + buf->name = tomoyo_realpath_from_path(path); + if (buf->name) { + tomoyo_fill_path_info(buf); + return true; } - kfree(buf); - return NULL; + return false; } static int tomoyo_update_path2_acl(const u8 type, const char *filename1, @@ -1259,26 +1261,20 @@ int tomoyo_path_number_perm(const u8 type, struct path *path, { struct tomoyo_request_info r; int error = -ENOMEM; - struct tomoyo_path_info *buf; + struct tomoyo_path_info buf; int idx; if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry) return 0; idx = tomoyo_read_lock(); - buf = tomoyo_get_path(path); - if (!buf) + if (!tomoyo_get_realpath(&buf, path)) goto out; - if (type == TOMOYO_TYPE_MKDIR && !buf->is_dir) { - /* - * tomoyo_get_path() reserves space for appending "/." - */ - strcat((char *) buf->name, "/"); - tomoyo_fill_path_info(buf); - } - error = tomoyo_path_number_perm2(&r, type, buf, number); + if (type == TOMOYO_TYPE_MKDIR) + tomoyo_add_slash(&buf); + error = tomoyo_path_number_perm2(&r, type, &buf, number); out: - kfree(buf); + kfree(buf.name); tomoyo_read_unlock(idx); if (r.mode != TOMOYO_CONFIG_ENFORCING) error = 0; @@ -1319,7 +1315,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, { const u8 acc_mode = ACC_MODE(flag); int error = -ENOMEM; - struct tomoyo_path_info *buf; + struct tomoyo_path_info buf; struct tomoyo_request_info r; int idx; @@ -1335,8 +1331,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, */ return 0; idx = tomoyo_read_lock(); - buf = tomoyo_get_path(path); - if (!buf) + if (!tomoyo_get_realpath(&buf, path)) goto out; error = 0; /* @@ -1346,15 +1341,15 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, */ if ((acc_mode & MAY_WRITE) && ((flag & O_TRUNC) || !(flag & O_APPEND)) && - (tomoyo_is_no_rewrite_file(buf))) { - error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, buf); + (tomoyo_is_no_rewrite_file(&buf))) { + error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, &buf); } if (!error) - error = tomoyo_file_perm(&r, buf, acc_mode); + error = tomoyo_file_perm(&r, &buf, acc_mode); if (!error && (flag & O_TRUNC)) - error = tomoyo_path_permission(&r, TOMOYO_TYPE_TRUNCATE, buf); + error = tomoyo_path_permission(&r, TOMOYO_TYPE_TRUNCATE, &buf); out: - kfree(buf); + kfree(buf.name); tomoyo_read_unlock(idx); if (r.mode != TOMOYO_CONFIG_ENFORCING) error = 0; @@ -1372,7 +1367,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, int tomoyo_path_perm(const u8 operation, struct path *path) { int error = -ENOMEM; - struct tomoyo_path_info *buf; + struct tomoyo_path_info buf; struct tomoyo_request_info r; int idx; @@ -1380,29 +1375,23 @@ int tomoyo_path_perm(const u8 operation, struct path *path) !path->mnt) return 0; idx = tomoyo_read_lock(); - buf = tomoyo_get_path(path); - if (!buf) + if (!tomoyo_get_realpath(&buf, path)) goto out; switch (operation) { case TOMOYO_TYPE_REWRITE: - if (!tomoyo_is_no_rewrite_file(buf)) { + if (!tomoyo_is_no_rewrite_file(&buf)) { error = 0; goto out; } break; case TOMOYO_TYPE_RMDIR: case TOMOYO_TYPE_CHROOT: - if (!buf->is_dir) { - /* - * tomoyo_get_path() reserves space for appending "/." - */ - strcat((char *) buf->name, "/"); - tomoyo_fill_path_info(buf); - } + tomoyo_add_slash(&buf); + break; } - error = tomoyo_path_permission(&r, operation, buf); + error = tomoyo_path_permission(&r, operation, &buf); out: - kfree(buf); + kfree(buf.name); tomoyo_read_unlock(idx); if (r.mode != TOMOYO_CONFIG_ENFORCING) error = 0; @@ -1465,7 +1454,7 @@ int tomoyo_path_number3_perm(const u8 operation, struct path *path, { struct tomoyo_request_info r; int error = -ENOMEM; - struct tomoyo_path_info *buf; + struct tomoyo_path_info buf; int idx; if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || @@ -1473,11 +1462,10 @@ int tomoyo_path_number3_perm(const u8 operation, struct path *path, return 0; idx = tomoyo_read_lock(); error = -ENOMEM; - buf = tomoyo_get_path(path); - if (buf) { - error = tomoyo_path_number3_perm2(&r, operation, buf, mode, + if (tomoyo_get_realpath(&buf, path)) { + error = tomoyo_path_number3_perm2(&r, operation, &buf, mode, new_decode_dev(dev)); - kfree(buf); + kfree(buf.name); } tomoyo_read_unlock(idx); if (r.mode != TOMOYO_CONFIG_ENFORCING) @@ -1499,48 +1487,40 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, { int error = -ENOMEM; const char *msg; - struct tomoyo_path_info *buf1; - struct tomoyo_path_info *buf2; + struct tomoyo_path_info buf1; + struct tomoyo_path_info buf2; struct tomoyo_request_info r; int idx; if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || !path1->mnt || !path2->mnt) return 0; + buf1.name = NULL; + buf2.name = NULL; idx = tomoyo_read_lock(); - buf1 = tomoyo_get_path(path1); - buf2 = tomoyo_get_path(path2); - if (!buf1 || !buf2) + if (!tomoyo_get_realpath(&buf1, path1) || + !tomoyo_get_realpath(&buf2, path2)) goto out; { struct dentry *dentry = path1->dentry; if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) { - /* - * tomoyo_get_path() reserves space for appending "/." - */ - if (!buf1->is_dir) { - strcat((char *) buf1->name, "/"); - tomoyo_fill_path_info(buf1); - } - if (!buf2->is_dir) { - strcat((char *) buf2->name, "/"); - tomoyo_fill_path_info(buf2); - } + tomoyo_add_slash(&buf1); + tomoyo_add_slash(&buf2); } } do { - error = tomoyo_path2_acl(&r, operation, buf1, buf2); + error = tomoyo_path2_acl(&r, operation, &buf1, &buf2); if (!error) break; msg = tomoyo_path22keyword(operation); - tomoyo_warn_log(&r, "%s %s %s", msg, buf1->name, buf2->name); + tomoyo_warn_log(&r, "%s %s %s", msg, buf1.name, buf2.name); error = tomoyo_supervisor(&r, "allow_%s %s %s\n", msg, - tomoyo_file_pattern(buf1), - tomoyo_file_pattern(buf2)); + tomoyo_file_pattern(&buf1), + tomoyo_file_pattern(&buf2)); } while (error == TOMOYO_RETRY_REQUEST); out: - kfree(buf1); - kfree(buf2); + kfree(buf1.name); + kfree(buf2.name); tomoyo_read_unlock(idx); if (r.mode != TOMOYO_CONFIG_ENFORCING) error = 0; diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 8fb73ff5cb6..4809febc1ac 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -153,7 +153,6 @@ void __init tomoyo_mm_init(void) { int idx; - BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX); for (idx = 0; idx < TOMOYO_MAX_HASH; idx++) INIT_LIST_HEAD(&tomoyo_name_list[idx]); INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index aeac619f787..7c1c7fdd368 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -24,57 +24,6 @@ /* Allow to call 'mount --make-shared /dir' */ #define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD "--make-shared" -/** - * tomoyo_encode2: Encode binary string to ascii string. - * - * @str: String in binary format. - * - * Returns pointer to @str in ascii format on success, NULL otherwise. - * - * This function uses kzalloc(), so caller must kfree() if this function - * didn't return NULL. - */ -static char *tomoyo_encode2(const char *str) -{ - int len = 0; - const char *p = str; - char *cp; - char *cp0; - if (!p) - return NULL; - while (*p) { - const unsigned char c = *p++; - if (c == '\\') - len += 2; - else if (c > ' ' && c < 127) - len++; - else - len += 4; - } - len++; - /* Reserve space for appending "/". */ - cp = kzalloc(len + 10, GFP_NOFS); - if (!cp) - return NULL; - cp0 = cp; - p = str; - while (*p) { - const unsigned char c = *p++; - if (c == '\\') { - *cp++ = '\\'; - *cp++ = '\\'; - } else if (c > ' ' && c < 127) { - *cp++ = c; - } else { - *cp++ = '\\'; - *cp++ = (c >> 6) + '0'; - *cp++ = ((c >> 3) & 7) + '0'; - *cp++ = (c & 7) + '0'; - } - } - return cp0; -} - /** * tomoyo_mount_acl2 - Check permission for mount() operation. * @@ -104,7 +53,7 @@ static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, int error = -ENOMEM; /* Get fstype. */ - requested_type = tomoyo_encode2(type); + requested_type = tomoyo_encode(type); if (!requested_type) goto out; rtype.name = requested_type; @@ -155,7 +104,7 @@ static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, /* Map dev_name to "" if no dev_name given. */ if (!dev_name) dev_name = ""; - requested_dev_name = tomoyo_encode2(dev_name); + requested_dev_name = tomoyo_encode(dev_name); if (!requested_dev_name) { error = -ENOMEM; goto out; diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 1fd685a94ad..153fa23a05c 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -12,141 +12,153 @@ #include #include #include +#include #include "common.h" /** * tomoyo_encode: Convert binary string to ascii string. * - * @buffer: Buffer for ASCII string. - * @buflen: Size of @buffer. - * @str: Binary string. + * @str: String in binary format. * - * Returns 0 on success, -ENOMEM otherwise. + * Returns pointer to @str in ascii format on success, NULL otherwise. + * + * This function uses kzalloc(), so caller must kfree() if this function + * didn't return NULL. */ -int tomoyo_encode(char *buffer, int buflen, const char *str) +char *tomoyo_encode(const char *str) { - while (1) { - const unsigned char c = *(unsigned char *) str++; + int len = 0; + const char *p = str; + char *cp; + char *cp0; - if (tomoyo_is_valid(c)) { - if (--buflen <= 0) - break; - *buffer++ = (char) c; - if (c != '\\') - continue; - if (--buflen <= 0) - break; - *buffer++ = (char) c; - continue; - } - if (!c) { - if (--buflen <= 0) - break; - *buffer = '\0'; - return 0; + if (!p) + return NULL; + while (*p) { + const unsigned char c = *p++; + if (c == '\\') + len += 2; + else if (c > ' ' && c < 127) + len++; + else + len += 4; + } + len++; + /* Reserve space for appending "/". */ + cp = kzalloc(len + 10, GFP_NOFS); + if (!cp) + return NULL; + cp0 = cp; + p = str; + while (*p) { + const unsigned char c = *p++; + + if (c == '\\') { + *cp++ = '\\'; + *cp++ = '\\'; + } else if (c > ' ' && c < 127) { + *cp++ = c; + } else { + *cp++ = '\\'; + *cp++ = (c >> 6) + '0'; + *cp++ = ((c >> 3) & 7) + '0'; + *cp++ = (c & 7) + '0'; } - buflen -= 4; - if (buflen <= 0) - break; - *buffer++ = '\\'; - *buffer++ = (c >> 6) + '0'; - *buffer++ = ((c >> 3) & 7) + '0'; - *buffer++ = (c & 7) + '0'; } - return -ENOMEM; + return cp0; } /** - * tomoyo_realpath_from_path2 - Returns realpath(3) of the given dentry but ignores chroot'ed root. + * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. * - * @path: Pointer to "struct path". - * @newname: Pointer to buffer to return value in. - * @newname_len: Size of @newname. + * @path: Pointer to "struct path". * - * Returns 0 on success, negative value otherwise. + * Returns the realpath of the given @path on success, NULL otherwise. * * If dentry is a directory, trailing '/' is appended. * Characters out of 0x20 < c < 0x7F range are converted to * \ooo style octal string. * Character \ is converted to \\ string. + * + * These functions use kzalloc(), so the caller must call kfree() + * if these functions didn't return NULL. */ -int tomoyo_realpath_from_path2(struct path *path, char *newname, - int newname_len) +char *tomoyo_realpath_from_path(struct path *path) { - int error = -ENOMEM; + char *buf = NULL; + char *name = NULL; + unsigned int buf_len = PAGE_SIZE / 2; struct dentry *dentry = path->dentry; - char *sp; - - if (!dentry || !path->mnt || !newname || newname_len <= 2048) - return -EINVAL; - if (dentry->d_op && dentry->d_op->d_dname) { + bool is_dir; + if (!dentry) + return NULL; + is_dir = dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode); + while (1) { + struct path ns_root = { .mnt = NULL, .dentry = NULL }; + char *pos; + buf_len <<= 1; + kfree(buf); + buf = kmalloc(buf_len, GFP_NOFS); + if (!buf) + break; + /* Get better name for socket. */ + if (dentry->d_sb && dentry->d_sb->s_magic == SOCKFS_MAGIC) { + struct inode *inode = dentry->d_inode; + struct socket *sock = inode ? SOCKET_I(inode) : NULL; + struct sock *sk = sock ? sock->sk : NULL; + if (sk) { + snprintf(buf, buf_len - 1, "socket:[family=%u:" + "type=%u:protocol=%u]", sk->sk_family, + sk->sk_type, sk->sk_protocol); + } else { + snprintf(buf, buf_len - 1, "socket:[unknown]"); + } + name = tomoyo_encode(buf); + break; + } /* For "socket:[\$]" and "pipe:[\$]". */ - static const int offset = 1536; - sp = dentry->d_op->d_dname(dentry, newname + offset, - newname_len - offset); - } else { - struct path ns_root = {.mnt = NULL, .dentry = NULL}; - + if (dentry->d_op && dentry->d_op->d_dname) { + pos = dentry->d_op->d_dname(dentry, buf, buf_len - 1); + if (IS_ERR(pos)) + continue; + name = tomoyo_encode(pos); + break; + } + /* If we don't have a vfsmount, we can't calculate. */ + if (!path->mnt) + break; spin_lock(&dcache_lock); /* go to whatever namespace root we are under */ - sp = __d_path(path, &ns_root, newname, newname_len); + pos = __d_path(path, &ns_root, buf, buf_len); spin_unlock(&dcache_lock); /* Prepend "/proc" prefix if using internal proc vfs mount. */ - if (!IS_ERR(sp) && (path->mnt->mnt_flags & MNT_INTERNAL) && + if (!IS_ERR(pos) && (path->mnt->mnt_flags & MNT_INTERNAL) && (path->mnt->mnt_sb->s_magic == PROC_SUPER_MAGIC)) { - sp -= 5; - if (sp >= newname) - memcpy(sp, "/proc", 5); + pos -= 5; + if (pos >= buf) + memcpy(pos, "/proc", 5); else - sp = ERR_PTR(-ENOMEM); - } - } - if (IS_ERR(sp)) - error = PTR_ERR(sp); - else - error = tomoyo_encode(newname, sp - newname, sp); - /* Append trailing '/' if dentry is a directory. */ - if (!error && dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode) - && *newname) { - sp = newname + strlen(newname); - if (*(sp - 1) != '/') { - if (sp < newname + newname_len - 4) { - *sp++ = '/'; - *sp = '\0'; - } else { - error = -ENOMEM; - } + pos = ERR_PTR(-ENOMEM); } + if (IS_ERR(pos)) + continue; + name = tomoyo_encode(pos); + break; } - if (error) - tomoyo_warn_oom(__func__); - return error; -} - -/** - * tomoyo_realpath_from_path - Returns realpath(3) of the given pathname but ignores chroot'ed root. - * - * @path: Pointer to "struct path". - * - * Returns the realpath of the given @path on success, NULL otherwise. - * - * These functions use kzalloc(), so the caller must call kfree() - * if these functions didn't return NULL. - */ -char *tomoyo_realpath_from_path(struct path *path) -{ - char *buf = kzalloc(sizeof(struct tomoyo_page_buffer), GFP_NOFS); - - BUILD_BUG_ON(TOMOYO_MAX_PATHNAME_LEN > PATH_MAX); - BUILD_BUG_ON(sizeof(struct tomoyo_page_buffer) - <= TOMOYO_MAX_PATHNAME_LEN - 1); - if (!buf) - return NULL; - if (tomoyo_realpath_from_path2(path, buf, - TOMOYO_MAX_PATHNAME_LEN - 1) == 0) - return buf; kfree(buf); - return NULL; + if (!name) + tomoyo_warn_oom(__func__); + else if (is_dir && *name) { + /* Append trailing '/' if dentry is a directory. */ + char *pos = name + strlen(name) - 1; + if (*pos != '/') + /* + * This is OK because tomoyo_encode() reserves space + * for appending "/". + */ + *++pos = '/'; + } + return name; } /** -- cgit v1.2.3-70-g09d2 From 3f629636320dfa65804779a3fc333f3147f3b064 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 3 Jun 2010 20:37:26 +0900 Subject: TOMOYO: Allow wildcard for execute permission. Some applications create and execute programs dynamically. We need to accept wildcard for execute permission because such programs contain random suffix in their filenames. This patch loosens up regulation of string parameters. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 2 +- security/tomoyo/common.h | 7 +- security/tomoyo/domain.c | 21 +++--- security/tomoyo/file.c | 37 +++------- security/tomoyo/number_group.c | 3 +- security/tomoyo/path_group.c | 17 ++--- security/tomoyo/util.c | 154 ++++++++++++++++------------------------- 7 files changed, 90 insertions(+), 151 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 98e3639db99..3f94011c641 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -407,7 +407,7 @@ static int tomoyo_update_manager_entry(const char *manager, return -EINVAL; e.is_domain = true; } else { - if (!tomoyo_is_correct_path(manager, 1, -1, -1)) + if (!tomoyo_is_correct_path(manager)) return -EINVAL; } e.manager = tomoyo_get_name(manager); diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index f4a8aa244af..d1b8d791bff 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -672,16 +672,15 @@ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) /* Check whether the domainname is correct. */ bool tomoyo_is_correct_domain(const unsigned char *domainname); /* Check whether the token is correct. */ -bool tomoyo_is_correct_path(const char *filename, const s8 start_type, - const s8 pattern_type, const s8 end_type); +bool tomoyo_is_correct_path(const char *filename); +bool tomoyo_is_correct_word(const char *string); /* Check whether the token can be a domainname. */ bool tomoyo_is_domain_def(const unsigned char *buffer); bool tomoyo_parse_name_union(const char *filename, struct tomoyo_name_union *ptr); /* Check whether the given filename matches the given path_group. */ bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, - const struct tomoyo_path_group *group, - const bool may_use_pattern); + const struct tomoyo_path_group *group); /* Check whether the given value matches the given number_group. */ bool tomoyo_number_matches_group(const unsigned long min, const unsigned long max, diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 7b8693e29a1..50f6e797217 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -131,11 +131,11 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname, struct tomoyo_domain_initializer_entry e = { .is_not = is_not }; int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(program, 1, -1, -1)) - return -EINVAL; /* No patterns allowed. */ + if (!tomoyo_is_correct_path(program)) + return -EINVAL; if (domainname) { if (!tomoyo_is_domain_def(domainname) && - tomoyo_is_correct_path(domainname, 1, -1, -1)) + tomoyo_is_correct_path(domainname)) e.is_last_name = true; else if (!tomoyo_is_correct_domain(domainname)) return -EINVAL; @@ -342,12 +342,12 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname, int error = is_delete ? -ENOENT : -ENOMEM; if (!tomoyo_is_domain_def(domainname) && - tomoyo_is_correct_path(domainname, 1, -1, -1)) + tomoyo_is_correct_path(domainname)) e.is_last_name = true; else if (!tomoyo_is_correct_domain(domainname)) return -EINVAL; if (program) { - if (!tomoyo_is_correct_path(program, 1, -1, -1)) + if (!tomoyo_is_correct_path(program)) return -EINVAL; e.program = tomoyo_get_name(program); if (!e.program) @@ -533,13 +533,14 @@ static int tomoyo_update_alias_entry(const char *original_name, struct tomoyo_alias_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(original_name, 1, -1, -1) || - !tomoyo_is_correct_path(aliased_name, 1, -1, -1)) - return -EINVAL; /* No patterns allowed. */ + if (!tomoyo_is_correct_path(original_name) || + !tomoyo_is_correct_path(aliased_name)) + return -EINVAL; e.original_name = tomoyo_get_name(original_name); e.aliased_name = tomoyo_get_name(aliased_name); - if (!e.original_name || !e.aliased_name) - goto out; + if (!e.original_name || !e.aliased_name || + e.original_name->is_patterned || e.aliased_name->is_patterned) + goto out; /* No patterns allowed. */ if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index cef685415df..83fa17a1113 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -65,23 +65,10 @@ bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr) { if (ptr->is_group) - return tomoyo_path_matches_group(name, ptr->group, 1); + return tomoyo_path_matches_group(name, ptr->group); return tomoyo_path_matches_pattern(name, ptr->filename); } -static bool tomoyo_compare_name_union_pattern(const struct tomoyo_path_info - *name, - const struct tomoyo_name_union - *ptr, const bool may_use_pattern) -{ - if (ptr->is_group) - return tomoyo_path_matches_group(name, ptr->group, - may_use_pattern); - if (may_use_pattern || !ptr->filename->is_patterned) - return tomoyo_path_matches_pattern(name, ptr->filename); - return false; -} - void tomoyo_put_number_union(struct tomoyo_number_union *ptr) { if (ptr && ptr->is_group) @@ -247,7 +234,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename, struct tomoyo_globally_readable_file_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(filename, 1, 0, -1)) + if (!tomoyo_is_correct_word(filename)) return -EINVAL; e.filename = tomoyo_get_name(filename); if (!e.filename) @@ -391,13 +378,14 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, const bool is_delete) { struct tomoyo_pattern_entry *ptr; - struct tomoyo_pattern_entry e = { .pattern = tomoyo_get_name(pattern) }; + struct tomoyo_pattern_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; + if (!tomoyo_is_correct_word(pattern)) + return -EINVAL; + e.pattern = tomoyo_get_name(pattern); if (!e.pattern) return error; - if (!e.pattern->is_patterned) - goto out; if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { @@ -543,7 +531,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern, struct tomoyo_no_rewrite_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(pattern, 0, 0, 0)) + if (!tomoyo_is_correct_word(pattern)) return -EINVAL; e.pattern = tomoyo_get_name(pattern); if (!e.pattern) @@ -690,7 +678,6 @@ static int tomoyo_update_file_acl(u8 perm, const char *filename, * @r: Pointer to "struct tomoyo_request_info". * @filename: Filename to check. * @perm: Permission. - * @may_use_pattern: True if patterned ACL is permitted. * * Returns 0 on success, -EPERM otherwise. * @@ -698,7 +685,7 @@ static int tomoyo_update_file_acl(u8 perm, const char *filename, */ static int tomoyo_path_acl(const struct tomoyo_request_info *r, const struct tomoyo_path_info *filename, - const u32 perm, const bool may_use_pattern) + const u32 perm) { struct tomoyo_domain_info *domain = r->domain; struct tomoyo_acl_info *ptr; @@ -710,8 +697,7 @@ static int tomoyo_path_acl(const struct tomoyo_request_info *r, continue; acl = container_of(ptr, struct tomoyo_path_acl, head); if (!(acl->perm & perm) || - !tomoyo_compare_name_union_pattern(filename, &acl->name, - may_use_pattern)) + !tomoyo_compare_name_union(filename, &acl->name)) continue; error = 0; break; @@ -756,7 +742,7 @@ static int tomoyo_file_perm(struct tomoyo_request_info *r, } else BUG(); do { - error = tomoyo_path_acl(r, filename, perm, mode != 1); + error = tomoyo_path_acl(r, filename, perm); if (error && mode == 4 && !r->domain->ignore_global_allow_read && tomoyo_is_globally_readable_file(filename)) error = 0; @@ -764,7 +750,6 @@ static int tomoyo_file_perm(struct tomoyo_request_info *r, break; tomoyo_warn_log(r, "%s %s", msg, filename->name); error = tomoyo_supervisor(r, "allow_%s %s\n", msg, - mode == 1 ? filename->name : tomoyo_file_pattern(filename)); /* * Do not retry for execute request, for alias may have @@ -1073,7 +1058,7 @@ static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, next: do { - error = tomoyo_path_acl(r, filename, 1 << operation, 1); + error = tomoyo_path_acl(r, filename, 1 << operation); if (!error) break; msg = tomoyo_path2keyword(operation); diff --git a/security/tomoyo/number_group.c b/security/tomoyo/number_group.c index c49792e09e8..8d6ef8f006f 100644 --- a/security/tomoyo/number_group.c +++ b/security/tomoyo/number_group.c @@ -24,8 +24,7 @@ struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name) struct tomoyo_number_group *group = NULL; const struct tomoyo_path_info *saved_group_name; int error = -ENOMEM; - if (!tomoyo_is_correct_path(group_name, 0, 0, 0) || - !group_name[0]) + if (!tomoyo_is_correct_word(group_name)) return NULL; saved_group_name = tomoyo_get_name(group_name); if (!saved_group_name) diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c index 636025e26b0..07e4f782367 100644 --- a/security/tomoyo/path_group.c +++ b/security/tomoyo/path_group.c @@ -22,8 +22,7 @@ struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name) struct tomoyo_path_group *group = NULL; const struct tomoyo_path_info *saved_group_name; int error = -ENOMEM; - if (!tomoyo_is_correct_path(group_name, 0, 0, 0) || - !group_name[0]) + if (!tomoyo_is_correct_word(group_name)) return NULL; saved_group_name = tomoyo_get_name(group_name); if (!saved_group_name) @@ -141,29 +140,21 @@ bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head) * * @pathname: The name of pathname. * @group: Pointer to "struct tomoyo_path_group". - * @may_use_pattern: True if wild card is permitted. * * Returns true if @pathname matches pathnames in @group, false otherwise. * * Caller holds tomoyo_read_lock(). */ bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, - const struct tomoyo_path_group *group, - const bool may_use_pattern) + const struct tomoyo_path_group *group) { struct tomoyo_path_group_member *member; bool matched = false; list_for_each_entry_rcu(member, &group->member_list, list) { if (member->is_deleted) continue; - if (!member->member_name->is_patterned) { - if (tomoyo_pathcmp(pathname, member->member_name)) - continue; - } else if (may_use_pattern) { - if (!tomoyo_path_matches_pattern(pathname, - member->member_name)) - continue; - } else + if (!tomoyo_path_matches_pattern(pathname, + member->member_name)) continue; matched = true; break; diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 7b023f5e131..592b76a2bce 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -89,7 +89,7 @@ void tomoyo_print_ulong(char *buffer, const int buffer_len, bool tomoyo_parse_name_union(const char *filename, struct tomoyo_name_union *ptr) { - if (!tomoyo_is_correct_path(filename, 0, 0, 0)) + if (!tomoyo_is_correct_word(filename)) return false; if (filename[0] == '@') { ptr->group = tomoyo_get_path_group(filename + 1); @@ -115,7 +115,7 @@ bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) unsigned long v; memset(num, 0, sizeof(*num)); if (data[0] == '@') { - if (!tomoyo_is_correct_path(data, 0, 0, 0)) + if (!tomoyo_is_correct_word(data)) return false; num->group = tomoyo_get_number_group(data + 1); num->is_group = true; @@ -265,54 +265,29 @@ bool tomoyo_tokenize(char *buffer, char *w[], size_t size) } /** - * tomoyo_is_correct_path - Validate a pathname. + * tomoyo_is_correct_word2 - Validate a string. * - * @filename: The pathname to check. - * @start_type: Should the pathname start with '/'? - * 1 = must / -1 = must not / 0 = don't care - * @pattern_type: Can the pathname contain a wildcard? - * 1 = must / -1 = must not / 0 = don't care - * @end_type: Should the pathname end with '/'? - * 1 = must / -1 = must not / 0 = don't care + * @string: The string to check. May be non-'\0'-terminated. + * @len: Length of @string. * - * Check whether the given filename follows the naming rules. - * Returns true if @filename follows the naming rules, false otherwise. + * Check whether the given string follows the naming rules. + * Returns true if @string follows the naming rules, false otherwise. */ -bool tomoyo_is_correct_path(const char *filename, const s8 start_type, - const s8 pattern_type, const s8 end_type) +static bool tomoyo_is_correct_word2(const char *string, size_t len) { - const char *const start = filename; + const char *const start = string; bool in_repetition = false; - bool contains_pattern = false; unsigned char c; unsigned char d; unsigned char e; - - if (!filename) + if (!len) goto out; - c = *filename; - if (start_type == 1) { /* Must start with '/' */ - if (c != '/') - goto out; - } else if (start_type == -1) { /* Must not start with '/' */ - if (c == '/') - goto out; - } - if (c) - c = *(filename + strlen(filename) - 1); - if (end_type == 1) { /* Must end with '/' */ - if (c != '/') - goto out; - } else if (end_type == -1) { /* Must not end with '/' */ - if (c == '/') - goto out; - } - while (1) { - c = *filename++; - if (!c) - break; + while (len--) { + c = *string++; if (c == '\\') { - c = *filename++; + if (!len--) + goto out; + c = *string++; switch (c) { case '\\': /* "\\" */ continue; @@ -326,21 +301,14 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, case 'a': /* "\a" */ case 'A': /* "\A" */ case '-': /* "\-" */ - if (pattern_type == -1) - break; /* Must not contain pattern */ - contains_pattern = true; continue; case '{': /* "/\{" */ - if (filename - 3 < start || - *(filename - 3) != '/') + if (string - 3 < start || *(string - 3) != '/') break; - if (pattern_type == -1) - break; /* Must not contain pattern */ - contains_pattern = true; in_repetition = true; continue; case '}': /* "\}/" */ - if (*filename != '/') + if (*string != '/') break; if (!in_repetition) break; @@ -350,11 +318,11 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, case '1': case '2': case '3': - d = *filename++; - if (d < '0' || d > '7') + if (!len-- || !len--) break; - e = *filename++; - if (e < '0' || e > '7') + d = *string++; + e = *string++; + if (d < '0' || d > '7' || e < '0' || e > '7') break; c = tomoyo_make_byte(c, d, e); if (tomoyo_is_invalid(c)) @@ -367,10 +335,6 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, goto out; } } - if (pattern_type == 1) { /* Must contain pattern */ - if (!contains_pattern) - goto out; - } if (in_repetition) goto out; return true; @@ -378,59 +342,59 @@ bool tomoyo_is_correct_path(const char *filename, const s8 start_type, return false; } +/** + * tomoyo_is_correct_word - Validate a string. + * + * @string: The string to check. + * + * Check whether the given string follows the naming rules. + * Returns true if @string follows the naming rules, false otherwise. + */ +bool tomoyo_is_correct_word(const char *string) +{ + return tomoyo_is_correct_word2(string, strlen(string)); +} + +/** + * tomoyo_is_correct_path - Validate a pathname. + * + * @filename: The pathname to check. + * + * Check whether the given pathname follows the naming rules. + * Returns true if @filename follows the naming rules, false otherwise. + */ +bool tomoyo_is_correct_path(const char *filename) +{ + return *filename == '/' && tomoyo_is_correct_word(filename); +} + /** * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules. * - * @domainname: The domainname to check. + * @domainname: The domainname to check. * * Returns true if @domainname follows the naming rules, false otherwise. */ bool tomoyo_is_correct_domain(const unsigned char *domainname) { - unsigned char c; - unsigned char d; - unsigned char e; - if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN)) goto out; domainname += TOMOYO_ROOT_NAME_LEN; if (!*domainname) return true; - do { - if (*domainname++ != ' ') - goto out; - if (*domainname++ != '/') + if (*domainname++ != ' ') + goto out; + while (1) { + const unsigned char *cp = strchr(domainname, ' '); + if (!cp) + break; + if (*domainname != '/' || + !tomoyo_is_correct_word2(domainname, cp - domainname - 1)) goto out; - while ((c = *domainname) != '\0' && c != ' ') { - domainname++; - if (c == '\\') { - c = *domainname++; - switch ((c)) { - case '\\': /* "\\" */ - continue; - case '0': /* "\ooo" */ - case '1': - case '2': - case '3': - d = *domainname++; - if (d < '0' || d > '7') - break; - e = *domainname++; - if (e < '0' || e > '7') - break; - c = tomoyo_make_byte(c, d, e); - if (tomoyo_is_invalid(c)) - /* pattern is not \000 */ - continue; - } - goto out; - } else if (tomoyo_is_invalid(c)) { - goto out; - } - } - } while (*domainname); - return true; + domainname = cp + 1; + } + return tomoyo_is_correct_path(domainname); out: return false; } -- cgit v1.2.3-70-g09d2 From 1084307ca097745ed6e40a192329b133a49271ac Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 3 Jun 2010 20:38:03 +0900 Subject: TOMOYO: Add pathname aggregation support. This patch allows users to aggregate programs which provide similar functionality (e.g. /usr/bin/vi and /usr/bin/emacs ). Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 4 ++ security/tomoyo/common.h | 32 +++++++++++ security/tomoyo/domain.c | 147 +++++++++++++++++++++++++++++++++++++++++++++++ security/tomoyo/gc.c | 21 +++++++ 4 files changed, 204 insertions(+) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 3f94011c641..bdf1ed7ca45 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1141,6 +1141,8 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN)) return tomoyo_write_domain_initializer_policy(data, true, is_delete); + if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_AGGREGATOR)) + return tomoyo_write_aggregator_policy(data, is_delete); if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALIAS)) return tomoyo_write_alias_policy(data, is_delete); if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_READ)) @@ -1196,6 +1198,8 @@ static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) head->read_var2 = NULL; head->read_step = 6; case 6: + if (!tomoyo_read_aggregator_policy(head)) + break; head->read_var2 = NULL; head->read_step = 7; case 7: diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index d1b8d791bff..54db39aa339 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -46,6 +46,7 @@ enum tomoyo_mode_index { }; /* Keywords for ACLs. */ +#define TOMOYO_KEYWORD_AGGREGATOR "aggregator " #define TOMOYO_KEYWORD_ALIAS "alias " #define TOMOYO_KEYWORD_ALLOW_MOUNT "allow_mount " #define TOMOYO_KEYWORD_ALLOW_READ "allow_read " @@ -592,6 +593,24 @@ struct tomoyo_domain_keeper_entry { bool is_last_name; }; +/* + * tomoyo_aggregator_entry is a structure which is used for holding + * "aggregator" entries. + * It has following fields. + * + * (1) "list" which is linked to tomoyo_aggregator_list . + * (2) "original_name" which is originally requested name. + * (3) "aggregated_name" which is name to rewrite. + * (4) "is_deleted" is a bool which is true if marked as deleted, false + * otherwise. + */ +struct tomoyo_aggregator_entry { + struct list_head list; + const struct tomoyo_path_info *original_name; + const struct tomoyo_path_info *aggregated_name; + bool is_deleted; +}; + /* * tomoyo_alias_entry is a structure which is used for holding "alias" entries. * It has following fields. @@ -693,6 +712,8 @@ bool tomoyo_print_number_union(struct tomoyo_io_buffer *head, const struct tomoyo_number_union *ptr); bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num); +/* Read "aggregator" entry in exception policy. */ +bool tomoyo_read_aggregator_policy(struct tomoyo_io_buffer *head); /* Read "alias" entry in exception policy. */ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head); /* @@ -730,6 +751,8 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r, /* Check permission for mount operation. */ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, unsigned long flags, void *data_page); +/* Create "aggregator" entry in exception policy. */ +int tomoyo_write_aggregator_policy(char *data, const bool is_delete); /* Create "alias" entry in exception policy. */ int tomoyo_write_alias_policy(char *data, const bool is_delete); /* @@ -857,6 +880,7 @@ extern struct list_head tomoyo_path_group_list; extern struct list_head tomoyo_number_group_list; extern struct list_head tomoyo_domain_initializer_list; extern struct list_head tomoyo_domain_keeper_list; +extern struct list_head tomoyo_aggregator_list; extern struct list_head tomoyo_alias_list; extern struct list_head tomoyo_globally_readable_list; extern struct list_head tomoyo_pattern_list; @@ -1036,6 +1060,14 @@ static inline bool tomoyo_is_same_domain_keeper_entry && p1->program == p2->program; } +static inline bool tomoyo_is_same_aggregator_entry +(const struct tomoyo_aggregator_entry *p1, + const struct tomoyo_aggregator_entry *p2) +{ + return p1->original_name == p2->original_name && + p1->aggregated_name == p2->aggregated_name; +} + static inline bool tomoyo_is_same_alias_entry (const struct tomoyo_alias_entry *p1, const struct tomoyo_alias_entry *p2) { diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 50f6e797217..a07ca6dc1a0 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -482,6 +482,136 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname, return flag; } +/* + * tomoyo_aggregator_list is used for holding list of rewrite table for + * execve() request. Some programs provides similar functionality. This keyword + * allows users to aggregate such programs. + * + * Entries are added by + * + * # echo 'aggregator /usr/bin/vi /./editor' > \ + * /sys/kernel/security/tomoyo/exception_policy + * # echo 'aggregator /usr/bin/emacs /./editor' > \ + * /sys/kernel/security/tomoyo/exception_policy + * + * and are deleted by + * + * # echo 'delete aggregator /usr/bin/vi /./editor' > \ + * /sys/kernel/security/tomoyo/exception_policy + * # echo 'delete aggregator /usr/bin/emacs /./editor' > \ + * /sys/kernel/security/tomoyo/exception_policy + * + * and all entries are retrieved by + * + * # grep ^aggregator /sys/kernel/security/tomoyo/exception_policy + * + * In the example above, if /usr/bin/vi or /usr/bin/emacs are executed, + * permission is checked for /./editor and domainname which the current process + * will belong to after execve() succeeds is calculated using /./editor . + */ +LIST_HEAD(tomoyo_aggregator_list); + +/** + * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator_entry" list. + * + * @original_name: The original program's name. + * @aggregated_name: The program name to use. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_update_aggregator_entry(const char *original_name, + const char *aggregated_name, + const bool is_delete) +{ + struct tomoyo_aggregator_entry *ptr; + struct tomoyo_aggregator_entry e = { }; + int error = is_delete ? -ENOENT : -ENOMEM; + + if (!tomoyo_is_correct_path(original_name) || + !tomoyo_is_correct_path(aggregated_name)) + return -EINVAL; + e.original_name = tomoyo_get_name(original_name); + e.aggregated_name = tomoyo_get_name(aggregated_name); + if (!e.original_name || !e.aggregated_name || + e.aggregated_name->is_patterned) /* No patterns allowed. */ + goto out; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, list) { + if (!tomoyo_is_same_aggregator_entry(ptr, &e)) + continue; + ptr->is_deleted = is_delete; + error = 0; + break; + } + if (!is_delete && error) { + struct tomoyo_aggregator_entry *entry = + tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + list_add_tail_rcu(&entry->list, + &tomoyo_aggregator_list); + error = 0; + } + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_name(e.original_name); + tomoyo_put_name(e.aggregated_name); + return error; +} + +/** + * tomoyo_read_aggregator_policy - Read "struct tomoyo_aggregator_entry" list. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_read_aggregator_policy(struct tomoyo_io_buffer *head) +{ + struct list_head *pos; + bool done = true; + + list_for_each_cookie(pos, head->read_var2, &tomoyo_aggregator_list) { + struct tomoyo_aggregator_entry *ptr; + + ptr = list_entry(pos, struct tomoyo_aggregator_entry, list); + if (ptr->is_deleted) + continue; + done = tomoyo_io_printf(head, TOMOYO_KEYWORD_AGGREGATOR + "%s %s\n", ptr->original_name->name, + ptr->aggregated_name->name); + if (!done) + break; + } + return done; +} + +/** + * tomoyo_write_aggregator_policy - Write "struct tomoyo_aggregator_entry" list. + * + * @data: String to parse. + * @is_delete: True if it is a delete request. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_write_aggregator_policy(char *data, const bool is_delete) +{ + char *cp = strchr(data, ' '); + + if (!cp) + return -EINVAL; + *cp++ = '\0'; + return tomoyo_update_aggregator_entry(data, cp, is_delete); +} + /* * tomoyo_alias_list is used for holding list of symlink's pathnames which are * allowed to be passed to an execve() request. Normally, the domainname which @@ -732,6 +862,23 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) } } + /* Check 'aggregator' directive. */ + { + struct tomoyo_aggregator_entry *ptr; + list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, list) { + if (ptr->is_deleted || + !tomoyo_path_matches_pattern(&rn, + ptr->original_name)) + continue; + if (need_kfree) + kfree(rn.name); + need_kfree = false; + /* This is OK because it is read only. */ + rn = *ptr->aggregated_name; + break; + } + } + /* Check execute permission. */ retval = tomoyo_check_exec_perm(old_domain, &rn); if (retval == TOMOYO_RETRY_REQUEST) diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index be2d3b93553..8a31f0c628b 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -18,6 +18,7 @@ enum tomoyo_gc_id { TOMOYO_ID_NUMBER_GROUP_MEMBER, TOMOYO_ID_DOMAIN_INITIALIZER, TOMOYO_ID_DOMAIN_KEEPER, + TOMOYO_ID_AGGREGATOR, TOMOYO_ID_ALIAS, TOMOYO_ID_GLOBALLY_READABLE, TOMOYO_ID_PATTERN, @@ -77,6 +78,12 @@ static void tomoyo_del_domain_keeper(struct tomoyo_domain_keeper_entry *ptr) tomoyo_put_name(ptr->program); } +static void tomoyo_del_aggregator(struct tomoyo_aggregator_entry *ptr) +{ + tomoyo_put_name(ptr->original_name); + tomoyo_put_name(ptr->aggregated_name); +} + static void tomoyo_del_alias(struct tomoyo_alias_entry *ptr) { tomoyo_put_name(ptr->original_name); @@ -263,6 +270,17 @@ static void tomoyo_collect_entry(void) break; } } + { + struct tomoyo_aggregator_entry *ptr; + list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, list) { + if (!ptr->is_deleted) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_AGGREGATOR, ptr)) + list_del_rcu(&ptr->list); + else + break; + } + } { struct tomoyo_alias_entry *ptr; list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { @@ -417,6 +435,9 @@ static void tomoyo_kfree_entry(void) case TOMOYO_ID_DOMAIN_KEEPER: tomoyo_del_domain_keeper(p->element); break; + case TOMOYO_ID_AGGREGATOR: + tomoyo_del_aggregator(p->element); + break; case TOMOYO_ID_ALIAS: tomoyo_del_alias(p->element); break; -- cgit v1.2.3-70-g09d2 From 57c2590fb7fd38bd52708ff2716a577d0c2b3c5a Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 3 Jun 2010 20:38:44 +0900 Subject: TOMOYO: Update profile structure. This patch allows users to change access control mode for per-operation basis. This feature comes from non LSM version of TOMOYO which is designed for permitting users to use SELinux and TOMOYO at the same time. SELinux does not care filename in a directory whereas TOMOYO does. Change of filename can change how the file is used. For example, renaming index.txt to .htaccess will change how the file is used. Thus, letting SELinux to enforce read()/write()/mmap() etc. restriction and letting TOMOYO to enforce rename() restriction is an example usage of this feature. What is unfortunate for me is that currently LSM does not allow users to use SELinux and LSM version of TOMOYO at the same time... Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 431 ++++++++++++++++++++++++++++------------------- security/tomoyo/common.h | 68 ++++++-- security/tomoyo/domain.c | 9 +- security/tomoyo/file.c | 131 +++++++++----- security/tomoyo/mount.c | 3 +- security/tomoyo/util.c | 92 +++++++--- 6 files changed, 486 insertions(+), 248 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index bdf1ed7ca45..811adb5e9fe 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -9,52 +9,73 @@ #include #include #include -#include #include "common.h" +static struct tomoyo_profile tomoyo_default_profile = { + .learning = &tomoyo_default_profile.preference, + .permissive = &tomoyo_default_profile.preference, + .enforcing = &tomoyo_default_profile.preference, + .preference.enforcing_verbose = true, + .preference.learning_max_entry = 2048, + .preference.learning_verbose = false, + .preference.permissive_verbose = true +}; + +/* 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 functionality that takes 4 modes. */ static const char *tomoyo_mode_4[4] = { "disabled", "learning", "permissive", "enforcing" }; -/* String table for functionality that takes 2 modes. */ -static const char *tomoyo_mode_2[4] = { - "disabled", "enabled", "enabled", "enabled" -}; -/* - * tomoyo_control_array is a static data which contains - * - * (1) functionality name used by /sys/kernel/security/tomoyo/profile . - * (2) initial values for "struct tomoyo_profile". - * (3) max values for "struct tomoyo_profile". - */ -static struct { - const char *keyword; - unsigned int current_value; - const unsigned int max_value; -} tomoyo_control_array[TOMOYO_MAX_CONTROL_INDEX] = { - [TOMOYO_MAC_FOR_FILE] = { "MAC_FOR_FILE", 0, 3 }, - [TOMOYO_MAX_ACCEPT_ENTRY] = { "MAX_ACCEPT_ENTRY", 2048, INT_MAX }, - [TOMOYO_VERBOSE] = { "TOMOYO_VERBOSE", 1, 1 }, +/* String table for /sys/kernel/security/tomoyo/profile */ +static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { + [TOMOYO_MAC_FILE_EXECUTE] = "file::execute", + [TOMOYO_MAC_FILE_OPEN] = "file::open", + [TOMOYO_MAC_FILE_CREATE] = "file::create", + [TOMOYO_MAC_FILE_UNLINK] = "file::unlink", + [TOMOYO_MAC_FILE_MKDIR] = "file::mkdir", + [TOMOYO_MAC_FILE_RMDIR] = "file::rmdir", + [TOMOYO_MAC_FILE_MKFIFO] = "file::mkfifo", + [TOMOYO_MAC_FILE_MKSOCK] = "file::mksock", + [TOMOYO_MAC_FILE_TRUNCATE] = "file::truncate", + [TOMOYO_MAC_FILE_SYMLINK] = "file::symlink", + [TOMOYO_MAC_FILE_REWRITE] = "file::rewrite", + [TOMOYO_MAC_FILE_MKBLOCK] = "file::mkblock", + [TOMOYO_MAC_FILE_MKCHAR] = "file::mkchar", + [TOMOYO_MAC_FILE_LINK] = "file::link", + [TOMOYO_MAC_FILE_RENAME] = "file::rename", + [TOMOYO_MAC_FILE_CHMOD] = "file::chmod", + [TOMOYO_MAC_FILE_CHOWN] = "file::chown", + [TOMOYO_MAC_FILE_CHGRP] = "file::chgrp", + [TOMOYO_MAC_FILE_IOCTL] = "file::ioctl", + [TOMOYO_MAC_FILE_CHROOT] = "file::chroot", + [TOMOYO_MAC_FILE_MOUNT] = "file::mount", + [TOMOYO_MAC_FILE_UMOUNT] = "file::umount", + [TOMOYO_MAC_FILE_PIVOT_ROOT] = "file::pivot_root", + [TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", }; -/* - * tomoyo_profile is a structure which is used for holding the mode of access - * controls. TOMOYO has 4 modes: disabled, learning, permissive, enforcing. - * An administrator can define up to 256 profiles. - * The ->profile of "struct tomoyo_domain_info" is used for remembering - * the profile's number (0 - 255) assigned to that domain. - */ -static struct tomoyo_profile { - unsigned int value[TOMOYO_MAX_CONTROL_INDEX]; - const struct tomoyo_path_info *comment; -} *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; - /* Permit policy management by non-root user? */ static bool tomoyo_manage_by_non_root; /* Utility functions. */ +/** + * tomoyo_yesno - Return "yes" or "no". + * + * @value: Bool value. + */ +static const char *tomoyo_yesno(const unsigned int value) +{ + return value ? "yes" : "no"; +} + /** * tomoyo_print_name_union - Print a tomoyo_name_union. * @@ -153,44 +174,6 @@ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) return true; } -/** - * tomoyo_check_flags - Check mode for specified functionality. - * - * @domain: Pointer to "struct tomoyo_domain_info". - * @index: The functionality to check mode. - * - * TOMOYO checks only process context. - * This code disables TOMOYO's enforcement in case the function is called from - * interrupt context. - */ -unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, - const u8 index) -{ - const u8 profile = domain->profile; - - if (WARN_ON(in_interrupt())) - return 0; - return tomoyo_policy_loaded && index < TOMOYO_MAX_CONTROL_INDEX -#if TOMOYO_MAX_PROFILES != 256 - && profile < TOMOYO_MAX_PROFILES -#endif - && tomoyo_profile_ptr[profile] ? - tomoyo_profile_ptr[profile]->value[index] : 0; -} - -/** - * tomoyo_verbose_mode - Check whether TOMOYO is verbose mode. - * - * @domain: Pointer to "struct tomoyo_domain_info". - * - * Returns true if domain policy violation warning should be printed to - * console. - */ -bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain) -{ - return tomoyo_check_flags(domain, TOMOYO_VERBOSE) != 0; -} - /** * tomoyo_find_or_assign_new_profile - Create a new profile. * @@ -198,36 +181,56 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain) * * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise. */ -static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned - int profile) +static struct tomoyo_profile *tomoyo_find_or_assign_new_profile +(const unsigned int profile) { - struct tomoyo_profile *ptr = NULL; - int i; - + struct tomoyo_profile *ptr; + struct tomoyo_profile *entry; if (profile >= TOMOYO_MAX_PROFILES) return NULL; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - return NULL; ptr = tomoyo_profile_ptr[profile]; if (ptr) - goto ok; - ptr = kmalloc(sizeof(*ptr), GFP_NOFS); - if (!tomoyo_memory_ok(ptr)) { - kfree(ptr); - ptr = NULL; - goto ok; + return ptr; + entry = kzalloc(sizeof(*entry), GFP_NOFS); + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + ptr = tomoyo_profile_ptr[profile]; + if (!ptr && tomoyo_memory_ok(entry)) { + ptr = entry; + ptr->learning = &tomoyo_default_profile.preference; + ptr->permissive = &tomoyo_default_profile.preference; + ptr->enforcing = &tomoyo_default_profile.preference; + ptr->default_config = TOMOYO_CONFIG_DISABLED; + memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT, + sizeof(ptr->config)); + mb(); /* Avoid out-of-order execution. */ + tomoyo_profile_ptr[profile] = ptr; + entry = NULL; } - for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) - ptr->value[i] = tomoyo_control_array[i].current_value; - mb(); /* Avoid out-of-order execution. */ - tomoyo_profile_ptr[profile] = ptr; - ok: mutex_unlock(&tomoyo_policy_lock); + out: + kfree(entry); return ptr; } /** - * tomoyo_write_profile - Write to profile table. + * tomoyo_profile - Find a profile. + * + * @profile: Profile number to find. + * + * Returns pointer to "struct tomoyo_profile". + */ +struct tomoyo_profile *tomoyo_profile(const u8 profile) +{ + struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile]; + if (!tomoyo_policy_loaded) + return &tomoyo_default_profile; + BUG_ON(!ptr); + return ptr; +} + +/** + * tomoyo_write_profile - Write profile table. * * @head: Pointer to "struct tomoyo_io_buffer". * @@ -237,64 +240,116 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) { char *data = head->write_buf; unsigned int i; - unsigned int value; + int value; + int mode; + u8 config; + bool use_default = false; char *cp; struct tomoyo_profile *profile; - unsigned long num; - - cp = strchr(data, '-'); - if (cp) - *cp = '\0'; - if (strict_strtoul(data, 10, &num)) - return -EINVAL; - if (cp) + if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1) + return 0; + i = simple_strtoul(data, &cp, 10); + if (data == cp) { + profile = &tomoyo_default_profile; + } else { + if (*cp != '-') + return -EINVAL; data = cp + 1; - profile = tomoyo_find_or_assign_new_profile(num); - if (!profile) - return -EINVAL; + profile = tomoyo_find_or_assign_new_profile(i); + if (!profile) + return -EINVAL; + } cp = strchr(data, '='); if (!cp) return -EINVAL; - *cp = '\0'; + *cp++ = '\0'; + if (profile != &tomoyo_default_profile) + use_default = strstr(cp, "use_default") != NULL; + if (strstr(cp, "verbose=yes")) + value = 1; + else if (strstr(cp, "verbose=no")) + value = 0; + else + value = -1; + if (!strcmp(data, "PREFERENCE::enforcing")) { + if (use_default) { + profile->enforcing = &tomoyo_default_profile.preference; + return 0; + } + profile->enforcing = &profile->preference; + if (value >= 0) + profile->preference.enforcing_verbose = value; + return 0; + } + if (!strcmp(data, "PREFERENCE::permissive")) { + if (use_default) { + profile->permissive = &tomoyo_default_profile.preference; + return 0; + } + profile->permissive = &profile->preference; + if (value >= 0) + profile->preference.permissive_verbose = value; + return 0; + } + if (!strcmp(data, "PREFERENCE::learning")) { + char *cp2; + if (use_default) { + profile->learning = &tomoyo_default_profile.preference; + return 0; + } + profile->learning = &profile->preference; + if (value >= 0) + profile->preference.learning_verbose = value; + cp2 = strstr(cp, "max_entry="); + if (cp2) + sscanf(cp2 + 10, "%u", + &profile->preference.learning_max_entry); + return 0; + } + if (profile == &tomoyo_default_profile) + return -EINVAL; if (!strcmp(data, "COMMENT")) { const struct tomoyo_path_info *old_comment = profile->comment; - profile->comment = tomoyo_get_name(cp + 1); + profile->comment = tomoyo_get_name(cp); tomoyo_put_name(old_comment); return 0; } - for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) { - if (strcmp(data, tomoyo_control_array[i].keyword)) - continue; - if (sscanf(cp + 1, "%u", &value) != 1) { - int j; - const char **modes; - switch (i) { - case TOMOYO_VERBOSE: - modes = tomoyo_mode_2; - break; - default: - modes = tomoyo_mode_4; - break; - } - for (j = 0; j < 4; j++) { - if (strcmp(cp + 1, modes[j])) - continue; - value = j; - break; - } - if (j == 4) - return -EINVAL; - } else if (value > tomoyo_control_array[i].max_value) { - value = tomoyo_control_array[i].max_value; + if (!strcmp(data, "CONFIG")) { + i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; + config = profile->default_config; + } else if (tomoyo_str_starts(&data, "CONFIG::")) { + config = 0; + for (i = 0; i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { + if (strcmp(data, tomoyo_mac_keywords[i])) + continue; + config = profile->config[i]; + break; } - profile->value[i] = value; - return 0; + if (i == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) + return -EINVAL; + } else { + return -EINVAL; } - return -EINVAL; + if (use_default) { + config = TOMOYO_CONFIG_USE_DEFAULT; + } else { + for (mode = 3; mode >= 0; mode--) + if (strstr(cp, tomoyo_mode_4[mode])) + /* + * Update lower 3 bits in order to distinguish + * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'. + */ + config = (config & ~7) | mode; + } + if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) + profile->config[i] = config; + else if (config != TOMOYO_CONFIG_USE_DEFAULT) + profile->default_config = config; + return 0; } /** - * tomoyo_read_profile - Read from profile table. + * tomoyo_read_profile - Read profile table. * * @head: Pointer to "struct tomoyo_io_buffer". * @@ -302,53 +357,82 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) */ static int tomoyo_read_profile(struct tomoyo_io_buffer *head) { - static const int total = TOMOYO_MAX_CONTROL_INDEX + 1; - int step; - + int index; if (head->read_eof) return 0; - for (step = head->read_step; step < TOMOYO_MAX_PROFILES * total; - step++) { - const u8 index = step / total; - u8 type = step % total; + if (head->read_bit) + goto body; + tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903"); + tomoyo_io_printf(head, "PREFERENCE::learning={ verbose=%s " + "max_entry=%u }\n", + tomoyo_yesno(tomoyo_default_profile.preference. + learning_verbose), + tomoyo_default_profile.preference.learning_max_entry); + tomoyo_io_printf(head, "PREFERENCE::permissive={ verbose=%s }\n", + tomoyo_yesno(tomoyo_default_profile.preference. + permissive_verbose)); + tomoyo_io_printf(head, "PREFERENCE::enforcing={ verbose=%s }\n", + tomoyo_yesno(tomoyo_default_profile.preference. + enforcing_verbose)); + head->read_bit = 1; + body: + for (index = head->read_step; index < TOMOYO_MAX_PROFILES; index++) { + bool done; + u8 config; + int i; + int pos; const struct tomoyo_profile *profile = tomoyo_profile_ptr[index]; - head->read_step = step; + const struct tomoyo_path_info *comment; + head->read_step = index; if (!profile) continue; - if (!type) { /* Print profile' comment tag. */ - if (!tomoyo_io_printf(head, "%u-COMMENT=%s\n", - index, profile->comment ? - profile->comment->name : "")) - break; - continue; - } - type--; - if (type < TOMOYO_MAX_CONTROL_INDEX) { - const unsigned int value = profile->value[type]; - const char **modes = NULL; - const char *keyword - = tomoyo_control_array[type].keyword; - switch (tomoyo_control_array[type].max_value) { - case 3: - modes = tomoyo_mode_4; - break; - case 1: - modes = tomoyo_mode_2; - break; - } - if (modes) { - if (!tomoyo_io_printf(head, "%u-%s=%s\n", index, - keyword, modes[value])) - break; - } else { - if (!tomoyo_io_printf(head, "%u-%s=%u\n", index, - keyword, value)) - break; - } + pos = head->read_avail; + comment = profile->comment; + done = tomoyo_io_printf(head, "%u-COMMENT=%s\n", index, + comment ? comment->name : ""); + if (!done) + goto out; + config = profile->default_config; + if (!tomoyo_io_printf(head, "%u-CONFIG={ mode=%s }\n", index, + tomoyo_mode_4[config & 3])) + goto out; + for (i = 0; i < TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { + config = profile->config[i]; + if (config == TOMOYO_CONFIG_USE_DEFAULT) + continue; + if (!tomoyo_io_printf(head, + "%u-CONFIG::%s={ mode=%s }\n", + index, tomoyo_mac_keywords[i], + tomoyo_mode_4[config & 3])) + goto out; } + if (profile->learning != &tomoyo_default_profile.preference && + !tomoyo_io_printf(head, "%u-PREFERENCE::learning={ " + "verbose=%s max_entry=%u }\n", index, + tomoyo_yesno(profile->preference. + learning_verbose), + profile->preference.learning_max_entry)) + goto out; + if (profile->permissive != &tomoyo_default_profile.preference + && !tomoyo_io_printf(head, "%u-PREFERENCE::permissive={ " + "verbose=%s }\n", index, + tomoyo_yesno(profile->preference. + permissive_verbose))) + goto out; + if (profile->enforcing != &tomoyo_default_profile.preference && + !tomoyo_io_printf(head, "%u-PREFERENCE::enforcing={ " + "verbose=%s }\n", index, + tomoyo_yesno(profile->preference. + enforcing_verbose))) + goto out; + continue; + out: + head->read_avail = pos; + break; } - if (step == TOMOYO_MAX_PROFILES * total) + if (index == TOMOYO_MAX_PROFILES) head->read_eof = true; return 0; } @@ -1595,7 +1679,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) static int tomoyo_read_version(struct tomoyo_io_buffer *head) { if (!head->read_eof) { - tomoyo_io_printf(head, "2.2.0"); + tomoyo_io_printf(head, "2.3.0-pre"); head->read_eof = true; } return 0; @@ -1915,6 +1999,9 @@ void tomoyo_check_profile(void) profile, domain->domainname->name); } tomoyo_read_unlock(idx); - printk(KERN_INFO "TOMOYO: 2.2.0 2009/04/01\n"); + if (tomoyo_profile_version != 20090903) + panic("Profile version %u is not supported.\n", + tomoyo_profile_version); + printk(KERN_INFO "TOMOYO: 2.3.0-pre 2010/06/03\n"); printk(KERN_INFO "Mandatory Access Control activated.\n"); } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 54db39aa339..c777c594a00 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -42,7 +42,8 @@ enum tomoyo_mode_index { TOMOYO_CONFIG_DISABLED, TOMOYO_CONFIG_LEARNING, TOMOYO_CONFIG_PERMISSIVE, - TOMOYO_CONFIG_ENFORCING + TOMOYO_CONFIG_ENFORCING, + TOMOYO_CONFIG_USE_DEFAULT = 255 }; /* Keywords for ACLs. */ @@ -74,14 +75,6 @@ enum tomoyo_mode_index { #define TOMOYO_VALUE_TYPE_OCTAL 2 #define TOMOYO_VALUE_TYPE_HEXADECIMAL 3 -/* Index numbers for Access Controls. */ -enum tomoyo_mac_index { - TOMOYO_MAC_FOR_FILE, /* domain_policy.conf */ - TOMOYO_MAX_ACCEPT_ENTRY, - TOMOYO_VERBOSE, - TOMOYO_MAX_CONTROL_INDEX -}; - /* Index numbers for Access Controls. */ enum tomoyo_acl_entry_type_index { TOMOYO_TYPE_PATH_ACL, @@ -157,6 +150,38 @@ enum tomoyo_securityfs_interface_index { TOMOYO_MANAGER }; +enum tomoyo_mac_index { + TOMOYO_MAC_FILE_EXECUTE, + TOMOYO_MAC_FILE_OPEN, + TOMOYO_MAC_FILE_CREATE, + TOMOYO_MAC_FILE_UNLINK, + TOMOYO_MAC_FILE_MKDIR, + TOMOYO_MAC_FILE_RMDIR, + TOMOYO_MAC_FILE_MKFIFO, + TOMOYO_MAC_FILE_MKSOCK, + TOMOYO_MAC_FILE_TRUNCATE, + TOMOYO_MAC_FILE_SYMLINK, + TOMOYO_MAC_FILE_REWRITE, + TOMOYO_MAC_FILE_MKBLOCK, + TOMOYO_MAC_FILE_MKCHAR, + TOMOYO_MAC_FILE_LINK, + TOMOYO_MAC_FILE_RENAME, + TOMOYO_MAC_FILE_CHMOD, + TOMOYO_MAC_FILE_CHOWN, + TOMOYO_MAC_FILE_CHGRP, + TOMOYO_MAC_FILE_IOCTL, + TOMOYO_MAC_FILE_CHROOT, + TOMOYO_MAC_FILE_MOUNT, + TOMOYO_MAC_FILE_UMOUNT, + TOMOYO_MAC_FILE_PIVOT_ROOT, + TOMOYO_MAX_MAC_INDEX +}; + +enum tomoyo_mac_category_index { + TOMOYO_MAC_CATEGORY_FILE, + TOMOYO_MAX_MAC_CATEGORY_INDEX +}; + #define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */ /********** Structure definitions. **********/ @@ -174,6 +199,7 @@ struct tomoyo_request_info { u8 retry; u8 profile; u8 mode; /* One of tomoyo_mode_index . */ + u8 type; }; /* @@ -649,6 +675,23 @@ struct tomoyo_policy_manager_entry { bool is_deleted; /* True if this entry is deleted. */ }; +struct tomoyo_preference { + unsigned int learning_max_entry; + bool enforcing_verbose; + bool learning_verbose; + bool permissive_verbose; +}; + +struct tomoyo_profile { + const struct tomoyo_path_info *comment; + struct tomoyo_preference *learning; + struct tomoyo_preference *permissive; + struct tomoyo_preference *enforcing; + struct tomoyo_preference preference; + u8 default_config; + u8 config[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX]; +}; + /********** Function prototypes. **********/ extern asmlinkage long sys_getpid(void); @@ -685,6 +728,7 @@ bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, /* Check whether the given number matches the given number_union. */ 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); /* Transactional sprintf() for policy dump. */ bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); @@ -747,7 +791,8 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain); const char *tomoyo_path2keyword(const u8 operation); /* Fill "struct tomoyo_request_info". */ int tomoyo_init_request_info(struct tomoyo_request_info *r, - struct tomoyo_domain_info *domain); + struct tomoyo_domain_info *domain, + const u8 index); /* Check permission for mount operation. */ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, unsigned long flags, void *data_page); @@ -794,6 +839,7 @@ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * domainname, const u8 profile); +struct tomoyo_profile *tomoyo_profile(const u8 profile); /* Allocate memory for "struct tomoyo_path_group". */ struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name); struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name); @@ -844,7 +890,7 @@ int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head); /* Initialize mm related code. */ void __init tomoyo_mm_init(void); -int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, +int tomoyo_check_exec_perm(struct tomoyo_request_info *r, const struct tomoyo_path_info *filename); int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct path *path, const int flag); diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index a07ca6dc1a0..09ec37c12a9 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -812,8 +812,8 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) struct tomoyo_domain_info *domain = NULL; const char *old_domain_name = old_domain->domainname->name; const char *original_name = bprm->filename; - const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE); - const bool is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); + u8 mode; + bool is_enforce; int retval = -ENOMEM; bool need_kfree = false; struct tomoyo_path_info rn = { }; /* real name */ @@ -822,7 +822,8 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) ln.name = tomoyo_get_last_name(old_domain); tomoyo_fill_path_info(&ln); - tomoyo_init_request_info(&r, NULL); + mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); + is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); if (!tmp) goto out; @@ -880,7 +881,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) } /* Check execute permission. */ - retval = tomoyo_check_exec_perm(old_domain, &rn); + retval = tomoyo_check_exec_perm(&r, &rn); if (retval == TOMOYO_RETRY_REQUEST) goto retry; if (retval < 0) diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 83fa17a1113..8e51348d022 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -51,6 +51,42 @@ static const char *tomoyo_path_number_keyword [TOMOYO_TYPE_CHGRP] = "chgrp", }; +static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = { + [TOMOYO_TYPE_READ_WRITE] = TOMOYO_MAC_FILE_OPEN, + [TOMOYO_TYPE_EXECUTE] = TOMOYO_MAC_FILE_EXECUTE, + [TOMOYO_TYPE_READ] = TOMOYO_MAC_FILE_OPEN, + [TOMOYO_TYPE_WRITE] = TOMOYO_MAC_FILE_OPEN, + [TOMOYO_TYPE_UNLINK] = TOMOYO_MAC_FILE_UNLINK, + [TOMOYO_TYPE_RMDIR] = TOMOYO_MAC_FILE_RMDIR, + [TOMOYO_TYPE_TRUNCATE] = TOMOYO_MAC_FILE_TRUNCATE, + [TOMOYO_TYPE_SYMLINK] = TOMOYO_MAC_FILE_SYMLINK, + [TOMOYO_TYPE_REWRITE] = TOMOYO_MAC_FILE_REWRITE, + [TOMOYO_TYPE_CHROOT] = TOMOYO_MAC_FILE_CHROOT, + [TOMOYO_TYPE_UMOUNT] = TOMOYO_MAC_FILE_UMOUNT, +}; + +static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_PATH_NUMBER3_OPERATION] = { + [TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK, + [TOMOYO_TYPE_MKCHAR] = TOMOYO_MAC_FILE_MKCHAR, +}; + +static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = { + [TOMOYO_TYPE_LINK] = TOMOYO_MAC_FILE_LINK, + [TOMOYO_TYPE_RENAME] = TOMOYO_MAC_FILE_RENAME, + [TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT, +}; + +static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { + [TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE, + [TOMOYO_TYPE_MKDIR] = TOMOYO_MAC_FILE_MKDIR, + [TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO, + [TOMOYO_TYPE_MKSOCK] = TOMOYO_MAC_FILE_MKSOCK, + [TOMOYO_TYPE_IOCTL] = TOMOYO_MAC_FILE_IOCTL, + [TOMOYO_TYPE_CHMOD] = TOMOYO_MAC_FILE_CHMOD, + [TOMOYO_TYPE_CHOWN] = TOMOYO_MAC_FILE_CHOWN, + [TOMOYO_TYPE_CHGRP] = TOMOYO_MAC_FILE_CHGRP, +}; + void tomoyo_put_name_union(struct tomoyo_name_union *ptr) { if (!ptr) @@ -1057,6 +1093,10 @@ static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, int error; next: + r->type = tomoyo_p2mac[operation]; + r->mode = tomoyo_get_mode(r->profile, r->type); + if (r->mode == TOMOYO_CONFIG_DISABLED) + return 0; do { error = tomoyo_path_acl(r, filename, 1 << operation); if (!error) @@ -1249,8 +1289,8 @@ int tomoyo_path_number_perm(const u8 type, struct path *path, struct tomoyo_path_info buf; int idx; - if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || - !path->mnt || !path->dentry) + if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type]) + == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry) return 0; idx = tomoyo_read_lock(); if (!tomoyo_get_realpath(&buf, path)) @@ -1269,21 +1309,19 @@ int tomoyo_path_number_perm(const u8 type, struct path *path, /** * tomoyo_check_exec_perm - Check permission for "execute". * - * @domain: Pointer to "struct tomoyo_domain_info". + * @r: Pointer to "struct tomoyo_request_info". * @filename: Check permission for "execute". * * Returns 0 on success, negativevalue otherwise. * * Caller holds tomoyo_read_lock(). */ -int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain, +int tomoyo_check_exec_perm(struct tomoyo_request_info *r, const struct tomoyo_path_info *filename) { - struct tomoyo_request_info r; - - if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED) + if (r->mode == TOMOYO_CONFIG_DISABLED) return 0; - return tomoyo_file_perm(&r, filename, 1); + return tomoyo_file_perm(r, filename, 1); } /** @@ -1304,17 +1342,11 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct tomoyo_request_info r; int idx; - if (tomoyo_init_request_info(&r, domain) == TOMOYO_CONFIG_DISABLED || - !path->mnt) - return 0; - if (acc_mode == 0) - return 0; - if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)) - /* - * I don't check directories here because mkdir() and rmdir() - * don't call me. - */ + if (!path->mnt || + (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))) return 0; + buf.name = NULL; + r.mode = TOMOYO_CONFIG_DISABLED; idx = tomoyo_read_lock(); if (!tomoyo_get_realpath(&buf, path)) goto out; @@ -1324,15 +1356,26 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, * we need to check "allow_rewrite" permission when the filename is not * opened for append mode or the filename is truncated at open time. */ - if ((acc_mode & MAY_WRITE) && - ((flag & O_TRUNC) || !(flag & O_APPEND)) && - (tomoyo_is_no_rewrite_file(&buf))) { - error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, &buf); + if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND) + && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_REWRITE) + != TOMOYO_CONFIG_DISABLED) { + if (!tomoyo_get_realpath(&buf, path)) { + error = -ENOMEM; + goto out; + } + if (tomoyo_is_no_rewrite_file(&buf)) + error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, + &buf); } - if (!error) + if (!error && acc_mode && + tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN) + != TOMOYO_CONFIG_DISABLED) { + if (!buf.name && !tomoyo_get_realpath(&buf, path)) { + error = -ENOMEM; + goto out; + } error = tomoyo_file_perm(&r, &buf, acc_mode); - if (!error && (flag & O_TRUNC)) - error = tomoyo_path_permission(&r, TOMOYO_TYPE_TRUNCATE, &buf); + } out: kfree(buf.name); tomoyo_read_unlock(idx); @@ -1356,9 +1399,12 @@ int tomoyo_path_perm(const u8 operation, struct path *path) struct tomoyo_request_info r; int idx; - if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || - !path->mnt) + if (!path->mnt) + return 0; + if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation]) + == TOMOYO_CONFIG_DISABLED) return 0; + buf.name = NULL; idx = tomoyo_read_lock(); if (!tomoyo_get_realpath(&buf, path)) goto out; @@ -1371,6 +1417,7 @@ int tomoyo_path_perm(const u8 operation, struct path *path) break; case TOMOYO_TYPE_RMDIR: case TOMOYO_TYPE_CHROOT: + case TOMOYO_TYPE_UMOUNT: tomoyo_add_slash(&buf); break; } @@ -1442,8 +1489,9 @@ int tomoyo_path_number3_perm(const u8 operation, struct path *path, struct tomoyo_path_info buf; int idx; - if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || - !path->mnt) + if (!path->mnt || + tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation]) + == TOMOYO_CONFIG_DISABLED) return 0; idx = tomoyo_read_lock(); error = -ENOMEM; @@ -1477,8 +1525,9 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, struct tomoyo_request_info r; int idx; - if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED || - !path1->mnt || !path2->mnt) + if (!path1->mnt || !path2->mnt || + tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation]) + == TOMOYO_CONFIG_DISABLED) return 0; buf1.name = NULL; buf2.name = NULL; @@ -1486,13 +1535,19 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, if (!tomoyo_get_realpath(&buf1, path1) || !tomoyo_get_realpath(&buf2, path2)) goto out; - { - struct dentry *dentry = path1->dentry; - if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) { - tomoyo_add_slash(&buf1); - tomoyo_add_slash(&buf2); - } - } + switch (operation) { + struct dentry *dentry; + case TOMOYO_TYPE_RENAME: + case TOMOYO_TYPE_LINK: + dentry = path1->dentry; + if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode)) + break; + /* fall through */ + case TOMOYO_TYPE_PIVOT_ROOT: + tomoyo_add_slash(&buf1); + tomoyo_add_slash(&buf2); + break; + } do { error = tomoyo_path2_acl(&r, operation, &buf1, &buf2); if (!error) diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 7c1c7fdd368..77ee8bf4194 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -248,7 +248,8 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, int error; int idx; - if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED) + if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_MOUNT) + == TOMOYO_CONFIG_DISABLED) return 0; if (!type) type = ""; diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 592b76a2bce..307793ed607 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -791,25 +791,67 @@ const char *tomoyo_get_exe(void) return cp; } +/** + * tomoyo_get_mode - Get MAC mode. + * + * @profile: Profile number. + * @index: Index number of functionality. + * + * Returns mode. + */ +int tomoyo_get_mode(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]; + if (mode == TOMOYO_CONFIG_USE_DEFAULT) + mode = tomoyo_profile(profile)->config[category]; + if (mode == TOMOYO_CONFIG_USE_DEFAULT) + mode = tomoyo_profile(profile)->default_config; + return mode & 3; +} + /** * tomoyo_init_request_info - Initialize "struct tomoyo_request_info" members. * * @r: Pointer to "struct tomoyo_request_info" to initialize. * @domain: Pointer to "struct tomoyo_domain_info". NULL for tomoyo_domain(). + * @index: Index number of functionality. * * Returns mode. */ int tomoyo_init_request_info(struct tomoyo_request_info *r, - struct tomoyo_domain_info *domain) + struct tomoyo_domain_info *domain, const u8 index) { + u8 profile; memset(r, 0, sizeof(*r)); if (!domain) domain = tomoyo_domain(); r->domain = domain; - r->mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE); + profile = domain->profile; + r->profile = profile; + r->type = index; + r->mode = tomoyo_get_mode(profile, index); return r->mode; } +/** + * tomoyo_last_word - Get last component of a line. + * + * @line: A line. + * + * Returns the last word of a line. + */ +static const char *tomoyo_last_word(const char *name) +{ + const char *cp = strrchr(name, ' '); + if (cp) + return cp + 1; + return name; +} + /** * tomoyo_warn_log - Print warning or error message on console. * @@ -818,29 +860,34 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r, */ void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...) { - int len = PAGE_SIZE; va_list args; char *buffer; - if (!tomoyo_verbose_mode(r->domain)) - return; - while (1) { - int len2; - buffer = kmalloc(len, GFP_NOFS); - if (!buffer) + const struct tomoyo_domain_info * const domain = r->domain; + const struct tomoyo_profile *profile = tomoyo_profile(domain->profile); + switch (r->mode) { + case TOMOYO_CONFIG_ENFORCING: + if (!profile->enforcing->enforcing_verbose) return; - va_start(args, fmt); - len2 = vsnprintf(buffer, len - 1, fmt, args); - va_end(args); - if (len2 <= len - 1) { - buffer[len2] = '\0'; - break; - } - len = len2 + 1; - kfree(buffer); + break; + case TOMOYO_CONFIG_PERMISSIVE: + if (!profile->permissive->permissive_verbose) + return; + break; + case TOMOYO_CONFIG_LEARNING: + if (!profile->learning->learning_verbose) + return; + break; } - printk(KERN_WARNING "TOMOYO-%s: Access %s denied for %s\n", - r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", - buffer, tomoyo_get_last_name(r->domain)); + buffer = kmalloc(4096, GFP_NOFS); + if (!buffer) + return; + va_start(args, fmt); + vsnprintf(buffer, 4095, fmt, args); + va_end(args); + buffer[4095] = '\0'; + printk(KERN_WARNING "%s: Access %s denied for %s\n", + r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", buffer, + tomoyo_last_word(domain->domainname->name)); kfree(buffer); } @@ -903,7 +950,8 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) count++; } } - if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY)) + if (count < tomoyo_profile(domain->profile)->learning-> + learning_max_entry) return true; if (!domain->quota_warned) { domain->quota_warned = true; -- cgit v1.2.3-70-g09d2 From 9156235b3427d6f01c5c95022f72f381f07583f5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 Jun 2010 17:31:05 +0100 Subject: KEYS: Authorise keyctl_set_timeout() on a key if we have its authorisation key Authorise a process to perform keyctl_set_timeout() on an uninstantiated key if that process has the authorisation key for it. This allows the instantiator to set the timeout on a key it is instantiating - provided it does it before instantiating the key. For instance, the test upcall script provided with the keyutils package could be modified to set the expiry to an hour hence before instantiating the key: [/usr/share/keyutils/request-key-debug.sh] if [ "$3" != "neg" ] then + keyctl timeout $1 3600 keyctl instantiate $1 "Debug $3" $4 || exit 1 else Signed-off-by: David Howells Signed-off-by: James Morris --- security/keys/keyctl.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 6261745e445..639226afd0d 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1091,7 +1091,7 @@ error: long keyctl_set_timeout(key_serial_t id, unsigned timeout) { struct timespec now; - struct key *key; + struct key *key, *instkey; key_ref_t key_ref; time_t expiry; long ret; @@ -1099,10 +1099,25 @@ long keyctl_set_timeout(key_serial_t id, unsigned timeout) key_ref = lookup_user_key(id, KEY_LOOKUP_CREATE | KEY_LOOKUP_PARTIAL, KEY_SETATTR); if (IS_ERR(key_ref)) { + /* setting the timeout on a key under construction is permitted + * if we have the authorisation token handy */ + if (PTR_ERR(key_ref) == -EACCES) { + instkey = key_get_instantiation_authkey(id); + if (!IS_ERR(instkey)) { + key_put(instkey); + key_ref = lookup_user_key(id, + KEY_LOOKUP_PARTIAL, + 0); + if (!IS_ERR(key_ref)) + goto okay; + } + } + ret = PTR_ERR(key_ref); goto error; } +okay: key = key_ref_to_ptr(key_ref); /* make the changes with the locks held to prevent races */ -- cgit v1.2.3-70-g09d2 From 927942aabbbe506bf9bc70a16dc5460ecc64c148 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 11 Jun 2010 17:31:10 +0100 Subject: KEYS: Make /proc/keys check to see if a key is possessed before security check Make /proc/keys check to see if the calling process possesses each key before performing the security check. The possession check can be skipped if the key doesn't have the possessor-view permission bit set. This causes the keys a process possesses to show up in /proc/keys, even if they don't have matching user/group/other view permissions. Signed-off-by: David Howells Signed-off-by: James Morris --- security/keys/internal.h | 5 ++++ security/keys/proc.c | 20 ++++++++++++-- security/keys/process_keys.c | 64 +++++++++++++++++++++++++++++--------------- 3 files changed, 66 insertions(+), 23 deletions(-) diff --git a/security/keys/internal.h b/security/keys/internal.h index 38783dcf6c6..addb67b169f 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -114,6 +114,10 @@ extern key_ref_t keyring_search_aux(key_ref_t keyring_ref, const void *description, key_match_func_t match); +extern key_ref_t search_my_process_keyrings(struct key_type *type, + const void *description, + key_match_func_t match, + const struct cred *cred); extern key_ref_t search_process_keyrings(struct key_type *type, const void *description, key_match_func_t match, @@ -134,6 +138,7 @@ extern struct key *request_key_and_link(struct key_type *type, struct key *dest_keyring, unsigned long flags); +extern int lookup_user_key_possessed(const struct key *key, const void *target); extern key_ref_t lookup_user_key(key_serial_t id, unsigned long flags, key_perm_t perm); #define KEY_LOOKUP_CREATE 0x01 diff --git a/security/keys/proc.c b/security/keys/proc.c index 068b66ea2f1..70373966816 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -184,20 +184,36 @@ static void proc_keys_stop(struct seq_file *p, void *v) static int proc_keys_show(struct seq_file *m, void *v) { + const struct cred *cred = current_cred(); struct rb_node *_p = v; struct key *key = rb_entry(_p, struct key, serial_node); struct timespec now; unsigned long timo; + key_ref_t key_ref, skey_ref; char xbuf[12]; int rc; + key_ref = make_key_ref(key, 0); + + /* determine if the key is possessed by this process (a test we can + * skip if the key does not indicate the possessor can view it + */ + if (key->perm & KEY_POS_VIEW) { + skey_ref = search_my_process_keyrings(key->type, key, + lookup_user_key_possessed, + cred); + if (!IS_ERR(skey_ref)) { + key_ref_put(skey_ref); + key_ref = make_key_ref(key, 1); + } + } + /* check whether the current task is allowed to view the key (assuming * non-possession) * - the caller holds a spinlock, and thus the RCU read lock, making our * access to __current_cred() safe */ - rc = key_task_permission(make_key_ref(key, 0), current_cred(), - KEY_VIEW); + rc = key_task_permission(key_ref, cred, KEY_VIEW); if (rc < 0) return 0; diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 6b8e4ff4cc6..f8e7251ae2c 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -309,22 +309,19 @@ void key_fsgid_changed(struct task_struct *tsk) /*****************************************************************************/ /* - * search the process keyrings for the first matching key + * search only my process keyrings for the first matching key * - we use the supplied match function to see if the description (or other * feature of interest) matches * - we return -EAGAIN if we didn't find any matching key * - we return -ENOKEY if we found only negative matching keys */ -key_ref_t search_process_keyrings(struct key_type *type, - const void *description, - key_match_func_t match, - const struct cred *cred) +key_ref_t search_my_process_keyrings(struct key_type *type, + const void *description, + key_match_func_t match, + const struct cred *cred) { - struct request_key_auth *rka; key_ref_t key_ref, ret, err; - might_sleep(); - /* we want to return -EAGAIN or -ENOKEY if any of the keyrings were * searchable, but we failed to find a key or we found a negative key; * otherwise we want to return a sample error (probably -EACCES) if @@ -424,6 +421,36 @@ key_ref_t search_process_keyrings(struct key_type *type, } } + /* no key - decide on the error we're going to go for */ + key_ref = ret ? ret : err; + +found: + return key_ref; +} + +/*****************************************************************************/ +/* + * search the process keyrings for the first matching key + * - we use the supplied match function to see if the description (or other + * feature of interest) matches + * - we return -EAGAIN if we didn't find any matching key + * - we return -ENOKEY if we found only negative matching keys + */ +key_ref_t search_process_keyrings(struct key_type *type, + const void *description, + key_match_func_t match, + const struct cred *cred) +{ + struct request_key_auth *rka; + key_ref_t key_ref, ret = ERR_PTR(-EACCES), err; + + might_sleep(); + + key_ref = search_my_process_keyrings(type, description, match, cred); + if (!IS_ERR(key_ref)) + goto found; + err = key_ref; + /* if this process has an instantiation authorisation key, then we also * search the keyrings of the process mentioned there * - we don't permit access to request_key auth keys via this method @@ -446,24 +473,19 @@ key_ref_t search_process_keyrings(struct key_type *type, if (!IS_ERR(key_ref)) goto found; - switch (PTR_ERR(key_ref)) { - case -EAGAIN: /* no key */ - if (ret) - break; - case -ENOKEY: /* negative key */ - ret = key_ref; - break; - default: - err = key_ref; - break; - } + ret = key_ref; } else { up_read(&cred->request_key_auth->sem); } } /* no key - decide on the error we're going to go for */ - key_ref = ret ? ret : err; + if (err == ERR_PTR(-ENOKEY) || ret == ERR_PTR(-ENOKEY)) + key_ref = ERR_PTR(-ENOKEY); + else if (err == ERR_PTR(-EACCES)) + key_ref = ret; + else + key_ref = err; found: return key_ref; @@ -474,7 +496,7 @@ found: /* * see if the key we're looking at is the target key */ -static int lookup_user_key_possessed(const struct key *key, const void *target) +int lookup_user_key_possessed(const struct key *key, const void *target) { return key == target; -- cgit v1.2.3-70-g09d2 From 237ab459f12cb98eadd3fe7b85343e183a1076a4 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Sat, 12 Jun 2010 20:46:22 +0900 Subject: TOMOYO: Use callback for updating entries. Use common "struct list_head" + "bool" + "u8" structure and use common code for elements using that structure. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 4 +- security/tomoyo/common.h | 82 ++++------- security/tomoyo/domain.c | 51 +++++++ security/tomoyo/file.c | 349 +++++++++++++++++++++-------------------------- security/tomoyo/gc.c | 28 +--- security/tomoyo/mount.c | 43 +++--- security/tomoyo/util.c | 8 +- 7 files changed, 253 insertions(+), 312 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 811adb5e9fe..6556e5d27d7 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -950,8 +950,6 @@ static bool tomoyo_print_mount_acl(struct tomoyo_io_buffer *head, struct tomoyo_mount_acl *ptr) { const int pos = head->read_avail; - if (ptr->is_deleted) - return true; if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_MOUNT) || !tomoyo_print_name_union(head, &ptr->dev_name) || !tomoyo_print_name_union(head, &ptr->dir_name) || @@ -977,6 +975,8 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, { const u8 acl_type = ptr->type; + if (ptr->is_deleted) + return true; if (acl_type == TOMOYO_TYPE_PATH_ACL) { struct tomoyo_path_acl *acl = container_of(ptr, struct tomoyo_path_acl, head); diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index c777c594a00..539b9a28b73 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -112,6 +112,8 @@ enum tomoyo_path_acl_index { TOMOYO_MAX_PATH_OPERATION }; +#define TOMOYO_RW_MASK ((1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE)) + enum tomoyo_path_number3_acl_index { TOMOYO_TYPE_MKBLOCK, TOMOYO_TYPE_MKCHAR, @@ -289,17 +291,19 @@ struct tomoyo_number_group_member { * * (1) "list" which is linked to the ->acl_info_list of * "struct tomoyo_domain_info" - * (2) "type" which tells type of the entry (either - * "struct tomoyo_path_acl" or "struct tomoyo_path2_acl"). + * (2) "is_deleted" is a bool which is true if this domain is marked as + * "deleted", false otherwise. + * (3) "type" which tells type of the entry. * * Packing "struct tomoyo_acl_info" allows - * "struct tomoyo_path_acl" to embed "u8" + "u16" and - * "struct tomoyo_path2_acl" to embed "u8" - * without enlarging their structure size. + * "struct tomoyo_path_acl" to embed "u16" and "struct tomoyo_path2_acl" + * "struct tomoyo_path_number_acl" "struct tomoyo_path_number3_acl" to embed + * "u8" without enlarging their structure size. */ struct tomoyo_acl_info { struct list_head list; - u8 type; + bool is_deleted; + u8 type; /* = one of values in "enum tomoyo_acl_entry_type_index". */ } __packed; /* @@ -438,17 +442,15 @@ struct tomoyo_path2_acl { * It has following fields. * * (1) "head" which is a "struct tomoyo_acl_info". - * (2) "is_deleted" is boolean. - * (3) "dev_name" is the device name. - * (4) "dir_name" is the mount point. + * (2) "dev_name" is the device name. + * (3) "dir_name" is the mount point. + * (4) "fs_type" is the filesystem type. * (5) "flags" is the mount flags. * - * Directives held by this structure are "allow_rename", "allow_link" and - * "allow_pivot_root". + * Directive held by this structure is "allow_mount". */ struct tomoyo_mount_acl { struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MOUNT_ACL */ - bool is_deleted; struct tomoyo_name_union dev_name; struct tomoyo_name_union dir_name; struct tomoyo_name_union fs_type; @@ -914,6 +916,16 @@ void tomoyo_run_gc(void); void tomoyo_memory_free(void *ptr); +int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, + bool is_delete, struct tomoyo_domain_info *domain, + bool (*check_duplicate) (const struct tomoyo_acl_info + *, + const struct tomoyo_acl_info + *), + bool (*merge_duplicate) (struct tomoyo_acl_info *, + struct tomoyo_acl_info *, + const bool)); + /********** External variable definitions. **********/ /* Lock for GC. */ @@ -1042,52 +1054,6 @@ static inline bool tomoyo_is_same_number_union p1->max_type == p2->max_type && p1->is_group == p2->is_group; } -static inline bool tomoyo_is_same_path_acl(const struct tomoyo_path_acl *p1, - const struct tomoyo_path_acl *p2) -{ - return tomoyo_is_same_acl_head(&p1->head, &p2->head) && - tomoyo_is_same_name_union(&p1->name, &p2->name); -} - -static inline bool tomoyo_is_same_path_number3_acl -(const struct tomoyo_path_number3_acl *p1, - const struct tomoyo_path_number3_acl *p2) -{ - return tomoyo_is_same_acl_head(&p1->head, &p2->head) - && tomoyo_is_same_name_union(&p1->name, &p2->name) - && tomoyo_is_same_number_union(&p1->mode, &p2->mode) - && tomoyo_is_same_number_union(&p1->major, &p2->major) - && tomoyo_is_same_number_union(&p1->minor, &p2->minor); -} - - -static inline bool tomoyo_is_same_path2_acl(const struct tomoyo_path2_acl *p1, - const struct tomoyo_path2_acl *p2) -{ - return tomoyo_is_same_acl_head(&p1->head, &p2->head) && - tomoyo_is_same_name_union(&p1->name1, &p2->name1) && - tomoyo_is_same_name_union(&p1->name2, &p2->name2); -} - -static inline bool tomoyo_is_same_path_number_acl -(const struct tomoyo_path_number_acl *p1, - const struct tomoyo_path_number_acl *p2) -{ - return tomoyo_is_same_acl_head(&p1->head, &p2->head) - && tomoyo_is_same_name_union(&p1->name, &p2->name) - && tomoyo_is_same_number_union(&p1->number, &p2->number); -} - -static inline bool tomoyo_is_same_mount_acl(const struct tomoyo_mount_acl *p1, - const struct tomoyo_mount_acl *p2) -{ - return tomoyo_is_same_acl_head(&p1->head, &p2->head) && - tomoyo_is_same_name_union(&p1->dev_name, &p2->dev_name) && - tomoyo_is_same_name_union(&p1->dir_name, &p2->dir_name) && - tomoyo_is_same_name_union(&p1->fs_type, &p2->fs_type) && - tomoyo_is_same_number_union(&p1->flags, &p2->flags); -} - static inline bool tomoyo_is_same_domain_initializer_entry (const struct tomoyo_domain_initializer_entry *p1, const struct tomoyo_domain_initializer_entry *p2) diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 09ec37c12a9..f774e73e002 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -15,6 +15,57 @@ /* The initial domain. */ struct tomoyo_domain_info tomoyo_kernel_domain; +/** + * tomoyo_update_domain - Update an entry for domain policy. + * + * @new_entry: Pointer to "struct tomoyo_acl_info". + * @size: Size of @new_entry in bytes. + * @is_delete: True if it is a delete request. + * @domain: Pointer to "struct tomoyo_domain_info". + * @check_duplicate: Callback function to find duplicated entry. + * @merge_duplicate: Callback function to merge duplicated entry. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, + bool is_delete, struct tomoyo_domain_info *domain, + bool (*check_duplicate) (const struct tomoyo_acl_info + *, + const struct tomoyo_acl_info + *), + bool (*merge_duplicate) (struct tomoyo_acl_info *, + struct tomoyo_acl_info *, + const bool)) +{ + int error = is_delete ? -ENOENT : -ENOMEM; + struct tomoyo_acl_info *entry; + + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + return error; + list_for_each_entry_rcu(entry, &domain->acl_info_list, list) { + if (!check_duplicate(entry, new_entry)) + continue; + if (merge_duplicate) + entry->is_deleted = merge_duplicate(entry, new_entry, + is_delete); + else + entry->is_deleted = is_delete; + error = 0; + break; + } + if (error && !is_delete) { + entry = tomoyo_commit_ok(new_entry, size); + if (entry) { + list_add_tail_rcu(&entry->list, &domain->acl_info_list); + error = 0; + } + } + mutex_unlock(&tomoyo_policy_lock); + return error; +} + /* * tomoyo_domain_list is used for holding list of domains. * The ->acl_info_list of "struct tomoyo_domain_info" is used for holding diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 8e51348d022..b826058c72e 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -664,50 +664,6 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) return done; } -/** - * tomoyo_update_file_acl - Update file's read/write/execute ACL. - * - * @perm: Permission (between 1 to 7). - * @filename: Filename. - * @domain: Pointer to "struct tomoyo_domain_info". - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * This is legacy support interface for older policy syntax. - * Current policy syntax uses "allow_read/write" instead of "6", - * "allow_read" instead of "4", "allow_write" instead of "2", - * "allow_execute" instead of "1". - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_update_file_acl(u8 perm, const char *filename, - struct tomoyo_domain_info * const domain, - const bool is_delete) -{ - if (perm > 7 || !perm) { - printk(KERN_DEBUG "%s: Invalid permission '%d %s'\n", - __func__, perm, filename); - return -EINVAL; - } - if (filename[0] != '@' && tomoyo_strendswith(filename, "/")) - /* - * Only 'allow_mkdir' and 'allow_rmdir' are valid for - * directory permissions. - */ - return 0; - if (perm & 4) - tomoyo_update_path_acl(TOMOYO_TYPE_READ, filename, domain, - is_delete); - if (perm & 2) - tomoyo_update_path_acl(TOMOYO_TYPE_WRITE, filename, domain, - is_delete); - if (perm & 1) - tomoyo_update_path_acl(TOMOYO_TYPE_EXECUTE, filename, domain, - is_delete); - return 0; -} - /** * tomoyo_path_acl - Check permission for single path operation. * @@ -797,6 +753,40 @@ static int tomoyo_file_perm(struct tomoyo_request_info *r, return error; } +static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a, + const struct tomoyo_acl_info *b) +{ + const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head); + const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head); + return tomoyo_is_same_acl_head(&p1->head, &p2->head) && + tomoyo_is_same_name_union(&p1->name, &p2->name); +} + +static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a, + struct tomoyo_acl_info *b, + const bool is_delete) +{ + u16 * const a_perm = &container_of(a, struct tomoyo_path_acl, head) + ->perm; + u16 perm = *a_perm; + const u16 b_perm = container_of(b, struct tomoyo_path_acl, head)->perm; + if (is_delete) { + perm &= ~b_perm; + if ((perm & TOMOYO_RW_MASK) != TOMOYO_RW_MASK) + perm &= ~(1 << TOMOYO_TYPE_READ_WRITE); + else if (!(perm & (1 << TOMOYO_TYPE_READ_WRITE))) + perm &= ~TOMOYO_RW_MASK; + } else { + perm |= b_perm; + if ((perm & TOMOYO_RW_MASK) == TOMOYO_RW_MASK) + perm |= (1 << TOMOYO_TYPE_READ_WRITE); + else if (perm & (1 << TOMOYO_TYPE_READ_WRITE)) + perm |= TOMOYO_RW_MASK; + } + *a_perm = perm; + return !perm; +} + /** * tomoyo_update_path_acl - Update "struct tomoyo_path_acl" list. * @@ -810,63 +800,56 @@ static int tomoyo_file_perm(struct tomoyo_request_info *r, * Caller holds tomoyo_read_lock(). */ static int tomoyo_update_path_acl(const u8 type, const char *filename, - struct tomoyo_domain_info *const domain, + struct tomoyo_domain_info * const domain, const bool is_delete) { - static const u16 tomoyo_rw_mask = - (1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE); - const u16 perm = 1 << type; - struct tomoyo_acl_info *ptr; struct tomoyo_path_acl e = { .head.type = TOMOYO_TYPE_PATH_ACL, - .perm = perm + .perm = 1 << type }; - int error = is_delete ? -ENOENT : -ENOMEM; - - if (type == TOMOYO_TYPE_READ_WRITE) - e.perm |= tomoyo_rw_mask; - if (!domain) - return -EINVAL; + int error; + if (e.perm == (1 << TOMOYO_TYPE_READ_WRITE)) + e.perm |= TOMOYO_RW_MASK; if (!tomoyo_parse_name_union(filename, &e.name)) return -EINVAL; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { - struct tomoyo_path_acl *acl = - container_of(ptr, struct tomoyo_path_acl, head); - if (!tomoyo_is_same_path_acl(acl, &e)) - continue; - if (is_delete) { - acl->perm &= ~perm; - if ((acl->perm & tomoyo_rw_mask) != tomoyo_rw_mask) - acl->perm &= ~(1 << TOMOYO_TYPE_READ_WRITE); - else if (!(acl->perm & (1 << TOMOYO_TYPE_READ_WRITE))) - acl->perm &= ~tomoyo_rw_mask; - } else { - acl->perm |= perm; - if ((acl->perm & tomoyo_rw_mask) == tomoyo_rw_mask) - acl->perm |= 1 << TOMOYO_TYPE_READ_WRITE; - else if (acl->perm & (1 << TOMOYO_TYPE_READ_WRITE)) - acl->perm |= tomoyo_rw_mask; - } - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_path_acl *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &domain->acl_info_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); - out: + error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, + tomoyo_same_path_acl, + tomoyo_merge_path_acl); tomoyo_put_name_union(&e.name); return error; } +static bool tomoyo_same_path_number3_acl(const struct tomoyo_acl_info *a, + const struct tomoyo_acl_info *b) +{ + const struct tomoyo_path_number3_acl *p1 = container_of(a, typeof(*p1), + head); + const struct tomoyo_path_number3_acl *p2 = container_of(b, typeof(*p2), + head); + return tomoyo_is_same_acl_head(&p1->head, &p2->head) + && tomoyo_is_same_name_union(&p1->name, &p2->name) + && tomoyo_is_same_number_union(&p1->mode, &p2->mode) + && tomoyo_is_same_number_union(&p1->major, &p2->major) + && tomoyo_is_same_number_union(&p1->minor, &p2->minor); +} + +static bool tomoyo_merge_path_number3_acl(struct tomoyo_acl_info *a, + struct tomoyo_acl_info *b, + const bool is_delete) +{ + u8 *const a_perm = &container_of(a, struct tomoyo_path_number3_acl, + head)->perm; + u8 perm = *a_perm; + const u8 b_perm = container_of(b, struct tomoyo_path_number3_acl, head) + ->perm; + if (is_delete) + perm &= ~b_perm; + else + perm |= b_perm; + *a_perm = perm; + return !perm; +} + /** * tomoyo_update_path_number3_acl - Update "struct tomoyo_path_number3_acl" list. * @@ -879,20 +862,17 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename, * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ -static inline int tomoyo_update_path_number3_acl(const u8 type, - const char *filename, - char *mode, - char *major, char *minor, - struct tomoyo_domain_info * - const domain, - const bool is_delete) +static int tomoyo_update_path_number3_acl(const u8 type, const char *filename, + char *mode, char *major, char *minor, + struct tomoyo_domain_info * const + domain, const bool is_delete) { - const u8 perm = 1 << type; - struct tomoyo_acl_info *ptr; struct tomoyo_path_number3_acl e = { .head.type = TOMOYO_TYPE_PATH_NUMBER3_ACL, - .perm = perm + .perm = 1 << type }; int error = is_delete ? -ENOENT : -ENOMEM; if (!tomoyo_parse_name_union(filename, &e.name) || @@ -900,30 +880,9 @@ static inline int tomoyo_update_path_number3_acl(const u8 type, !tomoyo_parse_number_union(major, &e.major) || !tomoyo_parse_number_union(minor, &e.minor)) goto out; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { - struct tomoyo_path_number3_acl *acl = - container_of(ptr, struct tomoyo_path_number3_acl, head); - if (!tomoyo_is_same_path_number3_acl(acl, &e)) - continue; - if (is_delete) - acl->perm &= ~perm; - else - acl->perm |= perm; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_path_number3_acl *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &domain->acl_info_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); + error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, + tomoyo_same_path_number3_acl, + tomoyo_merge_path_number3_acl); out: tomoyo_put_name_union(&e.name); tomoyo_put_number_union(&e.mode); @@ -932,6 +891,32 @@ static inline int tomoyo_update_path_number3_acl(const u8 type, return error; } +static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a, + const struct tomoyo_acl_info *b) +{ + const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head); + const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head); + return tomoyo_is_same_acl_head(&p1->head, &p2->head) + && tomoyo_is_same_name_union(&p1->name1, &p2->name1) + && tomoyo_is_same_name_union(&p1->name2, &p2->name2); +} + +static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a, + struct tomoyo_acl_info *b, + const bool is_delete) +{ + u8 * const a_perm = &container_of(a, struct tomoyo_path2_acl, head) + ->perm; + u8 perm = *a_perm; + const u8 b_perm = container_of(b, struct tomoyo_path2_acl, head)->perm; + if (is_delete) + perm &= ~b_perm; + else + perm |= b_perm; + *a_perm = perm; + return !perm; +} + /** * tomoyo_update_path2_acl - Update "struct tomoyo_path2_acl" list. * @@ -947,46 +932,20 @@ static inline int tomoyo_update_path_number3_acl(const u8 type, */ static int tomoyo_update_path2_acl(const u8 type, const char *filename1, const char *filename2, - struct tomoyo_domain_info *const domain, + struct tomoyo_domain_info * const domain, const bool is_delete) { - const u8 perm = 1 << type; struct tomoyo_path2_acl e = { .head.type = TOMOYO_TYPE_PATH2_ACL, - .perm = perm + .perm = 1 << type }; - struct tomoyo_acl_info *ptr; int error = is_delete ? -ENOENT : -ENOMEM; - - if (!domain) - return -EINVAL; if (!tomoyo_parse_name_union(filename1, &e.name1) || !tomoyo_parse_name_union(filename2, &e.name2)) goto out; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { - struct tomoyo_path2_acl *acl = - container_of(ptr, struct tomoyo_path2_acl, head); - if (!tomoyo_is_same_path2_acl(acl, &e)) - continue; - if (is_delete) - acl->perm &= ~perm; - else - acl->perm |= perm; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_path2_acl *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &domain->acl_info_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); + error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, + tomoyo_same_path2_acl, + tomoyo_merge_path2_acl); out: tomoyo_put_name_union(&e.name1); tomoyo_put_name_union(&e.name2); @@ -1157,6 +1116,35 @@ static int tomoyo_path_number_acl(struct tomoyo_request_info *r, const u8 type, return error; } +static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a, + const struct tomoyo_acl_info *b) +{ + const struct tomoyo_path_number_acl *p1 = container_of(a, typeof(*p1), + head); + const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2), + head); + return tomoyo_is_same_acl_head(&p1->head, &p2->head) + && tomoyo_is_same_name_union(&p1->name, &p2->name) + && tomoyo_is_same_number_union(&p1->number, &p2->number); +} + +static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a, + struct tomoyo_acl_info *b, + const bool is_delete) +{ + u8 * const a_perm = &container_of(a, struct tomoyo_path_number_acl, + head)->perm; + u8 perm = *a_perm; + const u8 b_perm = container_of(b, struct tomoyo_path_number_acl, head) + ->perm; + if (is_delete) + perm &= ~b_perm; + else + perm |= b_perm; + *a_perm = perm; + return !perm; +} + /** * tomoyo_update_path_number_acl - Update ioctl/chmod/chown/chgrp ACL. * @@ -1168,50 +1156,24 @@ static int tomoyo_path_number_acl(struct tomoyo_request_info *r, const u8 type, * * Returns 0 on success, negative value otherwise. */ -static inline int tomoyo_update_path_number_acl(const u8 type, - const char *filename, - char *number, - struct tomoyo_domain_info * - const domain, - const bool is_delete) +static int tomoyo_update_path_number_acl(const u8 type, const char *filename, + char *number, + struct tomoyo_domain_info * const + domain, + const bool is_delete) { - const u8 perm = 1 << type; - struct tomoyo_acl_info *ptr; struct tomoyo_path_number_acl e = { .head.type = TOMOYO_TYPE_PATH_NUMBER_ACL, - .perm = perm + .perm = 1 << type }; int error = is_delete ? -ENOENT : -ENOMEM; - if (!domain) - return -EINVAL; if (!tomoyo_parse_name_union(filename, &e.name)) return -EINVAL; if (!tomoyo_parse_number_union(number, &e.number)) goto out; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { - struct tomoyo_path_number_acl *acl = - container_of(ptr, struct tomoyo_path_number_acl, head); - if (!tomoyo_is_same_path_number_acl(acl, &e)) - continue; - if (is_delete) - acl->perm &= ~perm; - else - acl->perm |= perm; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_path_number_acl *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &domain->acl_info_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); + error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, + tomoyo_same_path_number_acl, + tomoyo_merge_path_number_acl); out: tomoyo_put_name_union(&e.name); tomoyo_put_number_union(&e.number); @@ -1585,13 +1547,8 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, u8 type; if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) return -EINVAL; - if (strncmp(w[0], "allow_", 6)) { - unsigned int perm; - if (sscanf(w[0], "%u", &perm) == 1) - return tomoyo_update_file_acl((u8) perm, w[1], domain, - is_delete); + if (strncmp(w[0], "allow_", 6)) goto out; - } w[0] += 6; for (type = 0; type < TOMOYO_MAX_PATH_OPERATION; type++) { if (strcmp(w[0], tomoyo_path_keyword[type])) diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 8a31f0c628b..aed7ddd0de8 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -310,34 +310,8 @@ static void tomoyo_collect_entry(void) struct tomoyo_acl_info *acl; list_for_each_entry_rcu(acl, &domain->acl_info_list, list) { - switch (acl->type) { - case TOMOYO_TYPE_PATH_ACL: - if (container_of(acl, - struct tomoyo_path_acl, - head)->perm) - continue; - break; - case TOMOYO_TYPE_PATH2_ACL: - if (container_of(acl, - struct tomoyo_path2_acl, - head)->perm) - continue; - break; - case TOMOYO_TYPE_PATH_NUMBER_ACL: - if (container_of(acl, - struct tomoyo_path_number_acl, - head)->perm) - continue; - break; - case TOMOYO_TYPE_PATH_NUMBER3_ACL: - if (container_of(acl, - struct tomoyo_path_number3_acl, - head)->perm) - continue; - break; - default: + if (!acl->is_deleted) continue; - } if (tomoyo_add_to_gc(TOMOYO_ID_ACL, acl)) list_del_rcu(&acl->list); else diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 77ee8bf4194..c170b41c383 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -114,11 +114,10 @@ static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, tomoyo_fill_path_info(&rdev); list_for_each_entry_rcu(ptr, &r->domain->acl_info_list, list) { struct tomoyo_mount_acl *acl; - if (ptr->type != TOMOYO_TYPE_MOUNT_ACL) + if (ptr->is_deleted || ptr->type != TOMOYO_TYPE_MOUNT_ACL) continue; acl = container_of(ptr, struct tomoyo_mount_acl, head); - if (acl->is_deleted || - !tomoyo_compare_number_union(flags, &acl->flags) || + if (!tomoyo_compare_number_union(flags, &acl->flags) || !tomoyo_compare_name_union(&rtype, &acl->fs_type) || !tomoyo_compare_name_union(&rdir, &acl->dir_name) || (need_dev && @@ -259,6 +258,18 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, return error; } +static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a, + const struct tomoyo_acl_info *b) +{ + const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head); + const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head); + return tomoyo_is_same_acl_head(&p1->head, &p2->head) && + tomoyo_is_same_name_union(&p1->dev_name, &p2->dev_name) && + tomoyo_is_same_name_union(&p1->dir_name, &p2->dir_name) && + tomoyo_is_same_name_union(&p1->fs_type, &p2->fs_type) && + tomoyo_is_same_number_union(&p1->flags, &p2->flags); +} + /** * tomoyo_write_mount_policy - Write "struct tomoyo_mount_acl" list. * @@ -267,11 +278,12 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). */ int tomoyo_write_mount_policy(char *data, struct tomoyo_domain_info *domain, const bool is_delete) { - struct tomoyo_acl_info *ptr; struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; int error = is_delete ? -ENOENT : -ENOMEM; char *w[4]; @@ -282,27 +294,8 @@ int tomoyo_write_mount_policy(char *data, struct tomoyo_domain_info *domain, !tomoyo_parse_name_union(w[2], &e.fs_type) || !tomoyo_parse_number_union(w[3], &e.flags)) goto out; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { - struct tomoyo_mount_acl *acl = - container_of(ptr, struct tomoyo_mount_acl, head); - if (!tomoyo_is_same_mount_acl(acl, &e)) - continue; - acl->is_deleted = is_delete; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_mount_acl *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &domain->acl_info_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); + error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, + tomoyo_same_mount_acl, NULL); out: tomoyo_put_name_union(&e.dev_name); tomoyo_put_name_union(&e.dir_name); diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 307793ed607..e5931686ca3 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -911,6 +911,8 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) if (!domain) return true; list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + if (!ptr->is_deleted) + continue; switch (ptr->type) { u16 perm; u8 i; @@ -944,10 +946,8 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) if (perm & (1 << i)) count++; break; - case TOMOYO_TYPE_MOUNT_ACL: - if (!container_of(ptr, struct tomoyo_mount_acl, head)-> - is_deleted) - count++; + default: + count++; } } if (count < tomoyo_profile(domain->profile)->learning-> -- cgit v1.2.3-70-g09d2 From 82e0f001a4c1112dcff9cafa9812a33889ad9b8a Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 15 Jun 2010 09:22:42 +0900 Subject: TOMOYO: Use common structure for list element. Use common "struct list_head" + "bool" structure. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 18 +++---- security/tomoyo/common.h | 112 ++++++++++++++++++----------------------- security/tomoyo/domain.c | 62 ++++++++++++----------- security/tomoyo/file.c | 46 +++++++++-------- security/tomoyo/gc.c | 63 ++++++++++++----------- security/tomoyo/number_group.c | 15 +++--- security/tomoyo/path_group.c | 15 +++--- 7 files changed, 167 insertions(+), 164 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 6556e5d27d7..7bfad45fcd6 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -499,10 +499,10 @@ static int tomoyo_update_manager_entry(const char *manager, return -ENOMEM; if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, head.list) { if (ptr->manager != e.manager) continue; - ptr->is_deleted = is_delete; + ptr->head.is_deleted = is_delete; error = 0; break; } @@ -510,7 +510,7 @@ static int tomoyo_update_manager_entry(const char *manager, struct tomoyo_policy_manager_entry *entry = tomoyo_commit_ok(&e, sizeof(e)); if (entry) { - list_add_tail_rcu(&entry->list, + list_add_tail_rcu(&entry->head.list, &tomoyo_policy_manager_list); error = 0; } @@ -562,8 +562,8 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) &tomoyo_policy_manager_list) { struct tomoyo_policy_manager_entry *ptr; ptr = list_entry(pos, struct tomoyo_policy_manager_entry, - list); - if (ptr->is_deleted) + head.list); + if (ptr->head.is_deleted) continue; done = tomoyo_io_printf(head, "%s\n", ptr->manager->name); if (!done) @@ -593,8 +593,8 @@ static bool tomoyo_is_policy_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_manager_list, list) { - if (!ptr->is_deleted && ptr->is_domain + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, head.list) { + if (!ptr->head.is_deleted && ptr->is_domain && !tomoyo_pathcmp(domainname, ptr->manager)) { found = true; break; @@ -605,8 +605,8 @@ static bool tomoyo_is_policy_manager(void) exe = tomoyo_get_exe(); if (!exe) return false; - list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, list) { - if (!ptr->is_deleted && !ptr->is_domain + list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, head.list) { + if (!ptr->head.is_deleted && !ptr->is_domain && !strcmp(exe, ptr->manager->name)) { found = true; break; diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 539b9a28b73..0ab6e86f90a 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -188,6 +188,20 @@ enum tomoyo_mac_category_index { /********** Structure definitions. **********/ +/* + * tomoyo_acl_head is a structure which is used for holding elements not in + * domain policy. + * It has following fields. + * + * (1) "list" which is linked to tomoyo_policy_list[] . + * (2) "is_deleted" is a bool which is true if marked as deleted, false + * otherwise. + */ +struct tomoyo_acl_head { + struct list_head list; + bool is_deleted; +} __packed; + /* * tomoyo_request_info is a structure which is used for holding * @@ -274,15 +288,13 @@ struct tomoyo_number_group { /* Structure for "path_group" directive. */ struct tomoyo_path_group_member { - struct list_head list; - bool is_deleted; + struct tomoyo_acl_head head; const struct tomoyo_path_info *member_name; }; /* Structure for "number_group" directive. */ struct tomoyo_number_group_member { - struct list_head list; - bool is_deleted; + struct tomoyo_acl_head head; struct tomoyo_number_union number; }; @@ -523,15 +535,12 @@ struct tomoyo_io_buffer { * "allow_read" entries. * It has following fields. * - * (1) "list" which is linked to tomoyo_globally_readable_list . + * (1) "head" is "struct tomoyo_acl_head". * (2) "filename" is a pathname which is allowed to open(O_RDONLY). - * (3) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. */ struct tomoyo_globally_readable_file_entry { - struct list_head list; + struct tomoyo_acl_head head; const struct tomoyo_path_info *filename; - bool is_deleted; }; /* @@ -539,16 +548,13 @@ struct tomoyo_globally_readable_file_entry { * "tomoyo_pattern_list" entries. * It has following fields. * - * (1) "list" which is linked to tomoyo_pattern_list . + * (1) "head" is "struct tomoyo_acl_head". * (2) "pattern" is a pathname pattern which is used for converting pathnames * to pathname patterns during learning mode. - * (3) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. */ struct tomoyo_pattern_entry { - struct list_head list; + struct tomoyo_acl_head head; const struct tomoyo_path_info *pattern; - bool is_deleted; }; /* @@ -556,16 +562,13 @@ struct tomoyo_pattern_entry { * "deny_rewrite" entries. * It has following fields. * - * (1) "list" which is linked to tomoyo_no_rewrite_list . + * (1) "head" is "struct tomoyo_acl_head". * (2) "pattern" is a pathname which is by default not permitted to modify * already existing content. - * (3) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. */ struct tomoyo_no_rewrite_entry { - struct list_head list; + struct tomoyo_acl_head head; const struct tomoyo_path_info *pattern; - bool is_deleted; }; /* @@ -573,25 +576,22 @@ struct tomoyo_no_rewrite_entry { * "initialize_domain" and "no_initialize_domain" entries. * It has following fields. * - * (1) "list" which is linked to tomoyo_domain_initializer_list . - * (2) "domainname" which is "a domainname" or "the last component of a - * domainname". This field is NULL if "from" clause is not specified. - * (3) "program" which is a program's pathname. - * (4) "is_deleted" is a bool which is true if marked as deleted, false + * (1) "head" is "struct tomoyo_acl_head". + * (2) "is_not" is a bool which is true if "no_initialize_domain", false * otherwise. - * (5) "is_not" is a bool which is true if "no_initialize_domain", false - * otherwise. - * (6) "is_last_name" is a bool which is true if "domainname" is "the last + * (3) "is_last_name" is a bool which is true if "domainname" is "the last * component of a domainname", false otherwise. + * (4) "domainname" which is "a domainname" or "the last component of a + * domainname". This field is NULL if "from" clause is not specified. + * (5) "program" which is a program's pathname. */ struct tomoyo_domain_initializer_entry { - struct list_head list; - const struct tomoyo_path_info *domainname; /* This may be NULL */ - const struct tomoyo_path_info *program; - bool is_deleted; + struct tomoyo_acl_head head; bool is_not; /* True if this entry is "no_initialize_domain". */ /* True if the domainname is tomoyo_get_last_name(). */ bool is_last_name; + const struct tomoyo_path_info *domainname; /* This may be NULL */ + const struct tomoyo_path_info *program; }; /* @@ -599,26 +599,23 @@ struct tomoyo_domain_initializer_entry { * "keep_domain" and "no_keep_domain" entries. * It has following fields. * - * (1) "list" which is linked to tomoyo_domain_keeper_list . - * (2) "domainname" which is "a domainname" or "the last component of a - * domainname". - * (3) "program" which is a program's pathname. - * This field is NULL if "from" clause is not specified. - * (4) "is_deleted" is a bool which is true if marked as deleted, false + * (1) "head" is "struct tomoyo_acl_head". + * (2) "is_not" is a bool which is true if "no_initialize_domain", false * otherwise. - * (5) "is_not" is a bool which is true if "no_initialize_domain", false - * otherwise. - * (6) "is_last_name" is a bool which is true if "domainname" is "the last + * (3) "is_last_name" is a bool which is true if "domainname" is "the last * component of a domainname", false otherwise. + * (4) "domainname" which is "a domainname" or "the last component of a + * domainname". + * (5) "program" which is a program's pathname. + * This field is NULL if "from" clause is not specified. */ struct tomoyo_domain_keeper_entry { - struct list_head list; - const struct tomoyo_path_info *domainname; - const struct tomoyo_path_info *program; /* This may be NULL */ - bool is_deleted; + struct tomoyo_acl_head head; bool is_not; /* True if this entry is "no_keep_domain". */ /* True if the domainname is tomoyo_get_last_name(). */ bool is_last_name; + const struct tomoyo_path_info *domainname; + const struct tomoyo_path_info *program; /* This may be NULL */ }; /* @@ -626,34 +623,28 @@ struct tomoyo_domain_keeper_entry { * "aggregator" entries. * It has following fields. * - * (1) "list" which is linked to tomoyo_aggregator_list . + * (1) "head" is "struct tomoyo_acl_head". * (2) "original_name" which is originally requested name. * (3) "aggregated_name" which is name to rewrite. - * (4) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. */ struct tomoyo_aggregator_entry { - struct list_head list; + struct tomoyo_acl_head head; const struct tomoyo_path_info *original_name; const struct tomoyo_path_info *aggregated_name; - bool is_deleted; }; /* * tomoyo_alias_entry is a structure which is used for holding "alias" entries. * It has following fields. * - * (1) "list" which is linked to tomoyo_alias_list . + * (1) "head" is "struct tomoyo_acl_head". * (2) "original_name" which is a dereferenced pathname. * (3) "aliased_name" which is a symlink's pathname. - * (4) "is_deleted" is a bool which is true if marked as deleted, false - * otherwise. */ struct tomoyo_alias_entry { - struct list_head list; + struct tomoyo_acl_head head; const struct tomoyo_path_info *original_name; const struct tomoyo_path_info *aliased_name; - bool is_deleted; }; /* @@ -662,19 +653,16 @@ struct tomoyo_alias_entry { * /sys/kernel/security/tomoyo/ interface. * It has following fields. * - * (1) "list" which is linked to tomoyo_policy_manager_list . - * (2) "manager" is a domainname or a program's pathname. - * (3) "is_domain" is a bool which is true if "manager" is a domainname, false - * otherwise. - * (4) "is_deleted" is a bool which is true if marked as deleted, false + * (1) "head" is "struct tomoyo_acl_head". + * (2) "is_domain" is a bool which is true if "manager" is a domainname, false * otherwise. + * (3) "manager" is a domainname or a program's pathname. */ struct tomoyo_policy_manager_entry { - struct list_head list; + struct tomoyo_acl_head head; + bool is_domain; /* True if manager is a domainname. */ /* A path to program or a domainname. */ const struct tomoyo_path_info *manager; - bool is_domain; /* True if manager is a domainname. */ - bool is_deleted; /* True if this entry is deleted. */ }; struct tomoyo_preference { diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index f774e73e002..60297da6adc 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -199,10 +199,11 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname, goto out; if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, + head.list) { if (!tomoyo_is_same_domain_initializer_entry(ptr, &e)) continue; - ptr->is_deleted = is_delete; + ptr->head.is_deleted = is_delete; error = 0; break; } @@ -210,7 +211,7 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname, struct tomoyo_domain_initializer_entry *entry = tomoyo_commit_ok(&e, sizeof(e)); if (entry) { - list_add_tail_rcu(&entry->list, + list_add_tail_rcu(&entry->head.list, &tomoyo_domain_initializer_list); error = 0; } @@ -243,8 +244,8 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head) const char *domain = ""; struct tomoyo_domain_initializer_entry *ptr; ptr = list_entry(pos, struct tomoyo_domain_initializer_entry, - list); - if (ptr->is_deleted) + head.list); + if (ptr->head.is_deleted) continue; no = ptr->is_not ? "no_" : ""; if (ptr->domainname) { @@ -308,8 +309,9 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info * struct tomoyo_domain_initializer_entry *ptr; bool flag = false; - list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { - if (ptr->is_deleted) + list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, + head.list) { + if (ptr->head.is_deleted) continue; if (ptr->domainname) { if (!ptr->is_last_name) { @@ -409,10 +411,10 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname, goto out; if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, head.list) { if (!tomoyo_is_same_domain_keeper_entry(ptr, &e)) continue; - ptr->is_deleted = is_delete; + ptr->head.is_deleted = is_delete; error = 0; break; } @@ -420,7 +422,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname, struct tomoyo_domain_keeper_entry *entry = tomoyo_commit_ok(&e, sizeof(e)); if (entry) { - list_add_tail_rcu(&entry->list, + list_add_tail_rcu(&entry->head.list, &tomoyo_domain_keeper_list); error = 0; } @@ -475,8 +477,9 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head) const char *from = ""; const char *program = ""; - ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, list); - if (ptr->is_deleted) + ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, + head.list); + if (ptr->head.is_deleted) continue; no = ptr->is_not ? "no_" : ""; if (ptr->program) { @@ -512,8 +515,8 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname, struct tomoyo_domain_keeper_entry *ptr; bool flag = false; - list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { - if (ptr->is_deleted) + list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, head.list) { + if (ptr->head.is_deleted) continue; if (!ptr->is_last_name) { if (ptr->domainname != domainname) @@ -591,10 +594,10 @@ static int tomoyo_update_aggregator_entry(const char *original_name, goto out; if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, head.list) { if (!tomoyo_is_same_aggregator_entry(ptr, &e)) continue; - ptr->is_deleted = is_delete; + ptr->head.is_deleted = is_delete; error = 0; break; } @@ -602,7 +605,7 @@ static int tomoyo_update_aggregator_entry(const char *original_name, struct tomoyo_aggregator_entry *entry = tomoyo_commit_ok(&e, sizeof(e)); if (entry) { - list_add_tail_rcu(&entry->list, + list_add_tail_rcu(&entry->head.list, &tomoyo_aggregator_list); error = 0; } @@ -631,8 +634,9 @@ bool tomoyo_read_aggregator_policy(struct tomoyo_io_buffer *head) list_for_each_cookie(pos, head->read_var2, &tomoyo_aggregator_list) { struct tomoyo_aggregator_entry *ptr; - ptr = list_entry(pos, struct tomoyo_aggregator_entry, list); - if (ptr->is_deleted) + ptr = list_entry(pos, struct tomoyo_aggregator_entry, + head.list); + if (ptr->head.is_deleted) continue; done = tomoyo_io_printf(head, TOMOYO_KEYWORD_AGGREGATOR "%s %s\n", ptr->original_name->name, @@ -724,10 +728,10 @@ static int tomoyo_update_alias_entry(const char *original_name, goto out; /* No patterns allowed. */ if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_alias_list, head.list) { if (!tomoyo_is_same_alias_entry(ptr, &e)) continue; - ptr->is_deleted = is_delete; + ptr->head.is_deleted = is_delete; error = 0; break; } @@ -735,7 +739,8 @@ static int tomoyo_update_alias_entry(const char *original_name, struct tomoyo_alias_entry *entry = tomoyo_commit_ok(&e, sizeof(e)); if (entry) { - list_add_tail_rcu(&entry->list, &tomoyo_alias_list); + list_add_tail_rcu(&entry->head.list, + &tomoyo_alias_list); error = 0; } } @@ -763,8 +768,8 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head) list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) { struct tomoyo_alias_entry *ptr; - ptr = list_entry(pos, struct tomoyo_alias_entry, list); - if (ptr->is_deleted) + ptr = list_entry(pos, struct tomoyo_alias_entry, head.list); + if (ptr->head.is_deleted) continue; done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n", ptr->original_name->name, @@ -901,8 +906,8 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) if (tomoyo_pathcmp(&rn, &sn)) { struct tomoyo_alias_entry *ptr; /* Is this program allowed to be called via symbolic links? */ - list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { - if (ptr->is_deleted || + list_for_each_entry_rcu(ptr, &tomoyo_alias_list, head.list) { + if (ptr->head.is_deleted || tomoyo_pathcmp(&rn, ptr->original_name) || tomoyo_pathcmp(&sn, ptr->aliased_name)) continue; @@ -917,8 +922,9 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) /* Check 'aggregator' directive. */ { struct tomoyo_aggregator_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, list) { - if (ptr->is_deleted || + list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, + head.list) { + if (ptr->head.is_deleted || !tomoyo_path_matches_pattern(&rn, ptr->original_name)) continue; diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index b826058c72e..09436d11f29 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -277,10 +277,11 @@ static int tomoyo_update_globally_readable_entry(const char *filename, return -ENOMEM; if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, + head.list) { if (ptr->filename != e.filename) continue; - ptr->is_deleted = is_delete; + ptr->head.is_deleted = is_delete; error = 0; break; } @@ -288,7 +289,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename, struct tomoyo_globally_readable_file_entry *entry = tomoyo_commit_ok(&e, sizeof(e)); if (entry) { - list_add_tail_rcu(&entry->list, + list_add_tail_rcu(&entry->head.list, &tomoyo_globally_readable_list); error = 0; } @@ -314,8 +315,9 @@ static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info * struct tomoyo_globally_readable_file_entry *ptr; bool found = false; - list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, list) { - if (!ptr->is_deleted && + list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, + head.list) { + if (!ptr->head.is_deleted && tomoyo_path_matches_pattern(filename, ptr->filename)) { found = true; break; @@ -358,8 +360,8 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head) struct tomoyo_globally_readable_file_entry *ptr; ptr = list_entry(pos, struct tomoyo_globally_readable_file_entry, - list); - if (ptr->is_deleted) + head.list); + if (ptr->head.is_deleted) continue; done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n", ptr->filename->name); @@ -424,10 +426,10 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, return error; if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, head.list) { if (e.pattern != ptr->pattern) continue; - ptr->is_deleted = is_delete; + ptr->head.is_deleted = is_delete; error = 0; break; } @@ -435,7 +437,8 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, struct tomoyo_pattern_entry *entry = tomoyo_commit_ok(&e, sizeof(e)); if (entry) { - list_add_tail_rcu(&entry->list, &tomoyo_pattern_list); + list_add_tail_rcu(&entry->head.list, + &tomoyo_pattern_list); error = 0; } } @@ -459,8 +462,8 @@ const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename) struct tomoyo_pattern_entry *ptr; const struct tomoyo_path_info *pattern = NULL; - list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { - if (ptr->is_deleted) + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, head.list) { + if (ptr->head.is_deleted) continue; if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) continue; @@ -508,8 +511,8 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head) list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) { struct tomoyo_pattern_entry *ptr; - ptr = list_entry(pos, struct tomoyo_pattern_entry, list); - if (ptr->is_deleted) + ptr = list_entry(pos, struct tomoyo_pattern_entry, head.list); + if (ptr->head.is_deleted) continue; done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN "%s\n", ptr->pattern->name); @@ -574,10 +577,10 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern, return error; if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, head.list) { if (ptr->pattern != e.pattern) continue; - ptr->is_deleted = is_delete; + ptr->head.is_deleted = is_delete; error = 0; break; } @@ -585,7 +588,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern, struct tomoyo_no_rewrite_entry *entry = tomoyo_commit_ok(&e, sizeof(e)); if (entry) { - list_add_tail_rcu(&entry->list, + list_add_tail_rcu(&entry->head.list, &tomoyo_no_rewrite_list); error = 0; } @@ -611,8 +614,8 @@ static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename) struct tomoyo_no_rewrite_entry *ptr; bool found = false; - list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { - if (ptr->is_deleted) + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, head.list) { + if (ptr->head.is_deleted) continue; if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) continue; @@ -653,8 +656,9 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) { struct tomoyo_no_rewrite_entry *ptr; - ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, list); - if (ptr->is_deleted) + ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, + head.list); + if (ptr->head.is_deleted) continue; done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE "%s\n", ptr->pattern->name); diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index aed7ddd0de8..2dd9665af26 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -216,33 +216,34 @@ static void tomoyo_collect_entry(void) { struct tomoyo_globally_readable_file_entry *ptr; list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, - list) { - if (!ptr->is_deleted) + head.list) { + if (!ptr->head.is_deleted) continue; if (tomoyo_add_to_gc(TOMOYO_ID_GLOBALLY_READABLE, ptr)) - list_del_rcu(&ptr->list); + list_del_rcu(&ptr->head.list); else break; } } { struct tomoyo_pattern_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, list) { - if (!ptr->is_deleted) + list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, head.list) { + if (!ptr->head.is_deleted) continue; if (tomoyo_add_to_gc(TOMOYO_ID_PATTERN, ptr)) - list_del_rcu(&ptr->list); + list_del_rcu(&ptr->head.list); else break; } } { struct tomoyo_no_rewrite_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, list) { - if (!ptr->is_deleted) + list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, + head.list) { + if (!ptr->head.is_deleted) continue; if (tomoyo_add_to_gc(TOMOYO_ID_NO_REWRITE, ptr)) - list_del_rcu(&ptr->list); + list_del_rcu(&ptr->head.list); else break; } @@ -250,44 +251,46 @@ static void tomoyo_collect_entry(void) { struct tomoyo_domain_initializer_entry *ptr; list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, - list) { - if (!ptr->is_deleted) + head.list) { + if (!ptr->head.is_deleted) continue; if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_INITIALIZER, ptr)) - list_del_rcu(&ptr->list); + list_del_rcu(&ptr->head.list); else break; } } { struct tomoyo_domain_keeper_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { - if (!ptr->is_deleted) + list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, + head.list) { + if (!ptr->head.is_deleted) continue; if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_KEEPER, ptr)) - list_del_rcu(&ptr->list); + list_del_rcu(&ptr->head.list); else break; } } { struct tomoyo_aggregator_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, list) { - if (!ptr->is_deleted) + list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, + head.list) { + if (!ptr->head.is_deleted) continue; if (tomoyo_add_to_gc(TOMOYO_ID_AGGREGATOR, ptr)) - list_del_rcu(&ptr->list); + list_del_rcu(&ptr->head.list); else break; } } { struct tomoyo_alias_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) { - if (!ptr->is_deleted) + list_for_each_entry_rcu(ptr, &tomoyo_alias_list, head.list) { + if (!ptr->head.is_deleted) continue; if (tomoyo_add_to_gc(TOMOYO_ID_ALIAS, ptr)) - list_del_rcu(&ptr->list); + list_del_rcu(&ptr->head.list); else break; } @@ -295,11 +298,11 @@ static void tomoyo_collect_entry(void) { struct tomoyo_policy_manager_entry *ptr; list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, - list) { - if (!ptr->is_deleted) + head.list) { + if (!ptr->head.is_deleted) continue; if (tomoyo_add_to_gc(TOMOYO_ID_MANAGER, ptr)) - list_del_rcu(&ptr->list); + list_del_rcu(&ptr->head.list); else break; } @@ -352,12 +355,12 @@ static void tomoyo_collect_entry(void) list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) { struct tomoyo_path_group_member *member; list_for_each_entry_rcu(member, &group->member_list, - list) { - if (!member->is_deleted) + head.list) { + if (!member->head.is_deleted) continue; if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP_MEMBER, member)) - list_del_rcu(&member->list); + list_del_rcu(&member->head.list); else break; } @@ -375,12 +378,12 @@ static void tomoyo_collect_entry(void) list_for_each_entry_rcu(group, &tomoyo_number_group_list, list) { struct tomoyo_number_group_member *member; list_for_each_entry_rcu(member, &group->member_list, - list) { - if (!member->is_deleted) + head.list) { + if (!member->head.is_deleted) continue; if (tomoyo_add_to_gc(TOMOYO_ID_NUMBER_GROUP_MEMBER, member)) - list_del_rcu(&member->list); + list_del_rcu(&member->head.list); else break; } diff --git a/security/tomoyo/number_group.c b/security/tomoyo/number_group.c index 8d6ef8f006f..afc5b697212 100644 --- a/security/tomoyo/number_group.c +++ b/security/tomoyo/number_group.c @@ -84,10 +84,10 @@ int tomoyo_write_number_group_policy(char *data, const bool is_delete) return -ENOMEM; if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(member, &group->member_list, list) { + list_for_each_entry_rcu(member, &group->member_list, head.list) { if (memcmp(&member->number, &e.number, sizeof(e.number))) continue; - member->is_deleted = is_delete; + member->head.is_deleted = is_delete; error = 0; break; } @@ -95,7 +95,8 @@ int tomoyo_write_number_group_policy(char *data, const bool is_delete) struct tomoyo_number_group_member *entry = tomoyo_commit_ok(&e, sizeof(e)); if (entry) { - list_add_tail_rcu(&entry->list, &group->member_list); + list_add_tail_rcu(&entry->head.list, + &group->member_list); error = 0; } } @@ -129,8 +130,8 @@ bool tomoyo_read_number_group_policy(struct tomoyo_io_buffer *head) const struct tomoyo_number_group_member *member = list_entry(mpos, struct tomoyo_number_group_member, - list); - if (member->is_deleted) + head.list); + if (member->head.is_deleted) continue; pos = head->read_avail; if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_NUMBER_GROUP @@ -162,8 +163,8 @@ bool tomoyo_number_matches_group(const unsigned long min, { struct tomoyo_number_group_member *member; bool matched = false; - list_for_each_entry_rcu(member, &group->member_list, list) { - if (member->is_deleted) + list_for_each_entry_rcu(member, &group->member_list, head.list) { + if (member->head.is_deleted) continue; if (min > member->number.values[1] || max < member->number.values[0]) diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c index 07e4f782367..7838f768129 100644 --- a/security/tomoyo/path_group.c +++ b/security/tomoyo/path_group.c @@ -79,10 +79,10 @@ int tomoyo_write_path_group_policy(char *data, const bool is_delete) goto out; if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(member, &group->member_list, list) { + list_for_each_entry_rcu(member, &group->member_list, head.list) { if (member->member_name != e.member_name) continue; - member->is_deleted = is_delete; + member->head.is_deleted = is_delete; error = 0; break; } @@ -90,7 +90,8 @@ int tomoyo_write_path_group_policy(char *data, const bool is_delete) struct tomoyo_path_group_member *entry = tomoyo_commit_ok(&e, sizeof(e)); if (entry) { - list_add_tail_rcu(&entry->list, &group->member_list); + list_add_tail_rcu(&entry->head.list, + &group->member_list); error = 0; } } @@ -122,8 +123,8 @@ bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head) struct tomoyo_path_group_member *member; member = list_entry(mpos, struct tomoyo_path_group_member, - list); - if (member->is_deleted) + head.list); + if (member->head.is_deleted) continue; if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_PATH_GROUP "%s %s\n", @@ -150,8 +151,8 @@ bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, { struct tomoyo_path_group_member *member; bool matched = false; - list_for_each_entry_rcu(member, &group->member_list, list) { - if (member->is_deleted) + list_for_each_entry_rcu(member, &group->member_list, head.list) { + if (member->head.is_deleted) continue; if (!tomoyo_path_matches_pattern(pathname, member->member_name)) -- cgit v1.2.3-70-g09d2 From 36f5e1ffbf2bb951105ae4e261bcc1de3eaf510c Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 15 Jun 2010 09:23:26 +0900 Subject: TOMOYO: Use callback for updating entries. Use common code for elements using "struct list_head" + "bool" structure. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 35 +++----- security/tomoyo/common.h | 39 ++------- security/tomoyo/domain.c | 184 +++++++++++++++++++++++------------------ security/tomoyo/file.c | 107 +++++++++--------------- security/tomoyo/number_group.c | 45 ++++------ security/tomoyo/path_group.c | 32 +++---- 6 files changed, 190 insertions(+), 252 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 7bfad45fcd6..4ee47af0917 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -469,6 +469,15 @@ static int tomoyo_read_profile(struct tomoyo_io_buffer *head) */ LIST_HEAD(tomoyo_policy_manager_list); +static bool tomoyo_same_manager_entry(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + return container_of(a, struct tomoyo_policy_manager_entry, head) + ->manager == + container_of(b, struct tomoyo_policy_manager_entry, head) + ->manager; +} + /** * tomoyo_update_manager_entry - Add a manager entry. * @@ -482,9 +491,8 @@ LIST_HEAD(tomoyo_policy_manager_list); static int tomoyo_update_manager_entry(const char *manager, const bool is_delete) { - struct tomoyo_policy_manager_entry *ptr; struct tomoyo_policy_manager_entry e = { }; - int error = is_delete ? -ENOENT : -ENOMEM; + int error; if (tomoyo_is_domain_def(manager)) { if (!tomoyo_is_correct_domain(manager)) @@ -497,26 +505,9 @@ static int tomoyo_update_manager_entry(const char *manager, e.manager = tomoyo_get_name(manager); if (!e.manager) return -ENOMEM; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, head.list) { - if (ptr->manager != e.manager) - continue; - ptr->head.is_deleted = is_delete; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_policy_manager_entry *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &tomoyo_policy_manager_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); - out: + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + &tomoyo_policy_manager_list, + tomoyo_same_manager_entry); tomoyo_put_name(e.manager); return error; } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 0ab6e86f90a..c8ab7553c48 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -913,6 +913,12 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, bool (*merge_duplicate) (struct tomoyo_acl_info *, struct tomoyo_acl_info *, const bool)); +int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, + bool is_delete, struct list_head *list, + bool (*check_duplicate) (const struct tomoyo_acl_head + *, + const struct tomoyo_acl_head + *)); /********** External variable definitions. **********/ @@ -1042,39 +1048,6 @@ static inline bool tomoyo_is_same_number_union p1->max_type == p2->max_type && p1->is_group == p2->is_group; } -static inline bool tomoyo_is_same_domain_initializer_entry -(const struct tomoyo_domain_initializer_entry *p1, - const struct tomoyo_domain_initializer_entry *p2) -{ - return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name - && p1->domainname == p2->domainname - && p1->program == p2->program; -} - -static inline bool tomoyo_is_same_domain_keeper_entry -(const struct tomoyo_domain_keeper_entry *p1, - const struct tomoyo_domain_keeper_entry *p2) -{ - return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name - && p1->domainname == p2->domainname - && p1->program == p2->program; -} - -static inline bool tomoyo_is_same_aggregator_entry -(const struct tomoyo_aggregator_entry *p1, - const struct tomoyo_aggregator_entry *p2) -{ - return p1->original_name == p2->original_name && - p1->aggregated_name == p2->aggregated_name; -} - -static inline bool tomoyo_is_same_alias_entry -(const struct tomoyo_alias_entry *p1, const struct tomoyo_alias_entry *p2) -{ - return p1->original_name == p2->original_name && - p1->aliased_name == p2->aliased_name; -} - /** * list_for_each_cookie - iterate over a list with cookie. * @pos: the &struct list_head to use as a loop cursor. diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 60297da6adc..fe621af46c2 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -15,6 +15,49 @@ /* The initial domain. */ struct tomoyo_domain_info tomoyo_kernel_domain; +/** + * tomoyo_update_policy - Update an entry for exception policy. + * + * @new_entry: Pointer to "struct tomoyo_acl_info". + * @size: Size of @new_entry in bytes. + * @is_delete: True if it is a delete request. + * @list: Pointer to "struct list_head". + * @check_duplicate: Callback function to find duplicated entry. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, + bool is_delete, struct list_head *list, + bool (*check_duplicate) (const struct tomoyo_acl_head + *, + const struct tomoyo_acl_head + *)) +{ + int error = is_delete ? -ENOENT : -ENOMEM; + struct tomoyo_acl_head *entry; + + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + return -ENOMEM; + list_for_each_entry_rcu(entry, list, list) { + if (!check_duplicate(entry, new_entry)) + continue; + entry->is_deleted = is_delete; + error = 0; + break; + } + if (error && !is_delete) { + entry = tomoyo_commit_ok(new_entry, size); + if (entry) { + list_add_tail_rcu(&entry->list, list); + error = 0; + } + } + mutex_unlock(&tomoyo_policy_lock); + return error; +} + /** * tomoyo_update_domain - Update an entry for domain policy. * @@ -161,6 +204,20 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain) */ LIST_HEAD(tomoyo_domain_initializer_list); +static bool tomoyo_same_domain_initializer_entry(const struct tomoyo_acl_head * + a, + const struct tomoyo_acl_head * + b) +{ + const struct tomoyo_domain_initializer_entry *p1 = + container_of(a, typeof(*p1), head); + const struct tomoyo_domain_initializer_entry *p2 = + container_of(b, typeof(*p2), head); + return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name + && p1->domainname == p2->domainname + && p1->program == p2->program; +} + /** * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list. * @@ -178,7 +235,6 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname, const bool is_not, const bool is_delete) { - struct tomoyo_domain_initializer_entry *ptr; struct tomoyo_domain_initializer_entry e = { .is_not = is_not }; int error = is_delete ? -ENOENT : -ENOMEM; @@ -197,26 +253,9 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname, e.program = tomoyo_get_name(program); if (!e.program) goto out; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, - head.list) { - if (!tomoyo_is_same_domain_initializer_entry(ptr, &e)) - continue; - ptr->head.is_deleted = is_delete; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_domain_initializer_entry *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &tomoyo_domain_initializer_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + &tomoyo_domain_initializer_list, + tomoyo_same_domain_initializer_entry); out: tomoyo_put_name(e.domainname); tomoyo_put_name(e.program); @@ -373,6 +412,18 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info * */ LIST_HEAD(tomoyo_domain_keeper_list); +static bool tomoyo_same_domain_keeper_entry(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + const struct tomoyo_domain_keeper_entry *p1 = + container_of(a, typeof(*p1), head); + const struct tomoyo_domain_keeper_entry *p2 = + container_of(b, typeof(*p2), head); + return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name + && p1->domainname == p2->domainname + && p1->program == p2->program; +} + /** * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list. * @@ -390,7 +441,6 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname, const bool is_not, const bool is_delete) { - struct tomoyo_domain_keeper_entry *ptr; struct tomoyo_domain_keeper_entry e = { .is_not = is_not }; int error = is_delete ? -ENOENT : -ENOMEM; @@ -409,25 +459,9 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname, e.domainname = tomoyo_get_name(domainname); if (!e.domainname) goto out; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, head.list) { - if (!tomoyo_is_same_domain_keeper_entry(ptr, &e)) - continue; - ptr->head.is_deleted = is_delete; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_domain_keeper_entry *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &tomoyo_domain_keeper_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + &tomoyo_domain_keeper_list, + tomoyo_same_domain_keeper_entry); out: tomoyo_put_name(e.domainname); tomoyo_put_name(e.program); @@ -565,6 +599,17 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname, */ LIST_HEAD(tomoyo_aggregator_list); +static bool tomoyo_same_aggregator_entry(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + const struct tomoyo_aggregator_entry *p1 = container_of(a, typeof(*p1), + head); + const struct tomoyo_aggregator_entry *p2 = container_of(b, typeof(*p2), + head); + return p1->original_name == p2->original_name && + p1->aggregated_name == p2->aggregated_name; +} + /** * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator_entry" list. * @@ -580,7 +625,6 @@ static int tomoyo_update_aggregator_entry(const char *original_name, const char *aggregated_name, const bool is_delete) { - struct tomoyo_aggregator_entry *ptr; struct tomoyo_aggregator_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; @@ -592,25 +636,9 @@ static int tomoyo_update_aggregator_entry(const char *original_name, if (!e.original_name || !e.aggregated_name || e.aggregated_name->is_patterned) /* No patterns allowed. */ goto out; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, head.list) { - if (!tomoyo_is_same_aggregator_entry(ptr, &e)) - continue; - ptr->head.is_deleted = is_delete; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_aggregator_entry *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &tomoyo_aggregator_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + &tomoyo_aggregator_list, + tomoyo_same_aggregator_entry); out: tomoyo_put_name(e.original_name); tomoyo_put_name(e.aggregated_name); @@ -699,6 +727,17 @@ int tomoyo_write_aggregator_policy(char *data, const bool is_delete) */ LIST_HEAD(tomoyo_alias_list); +static bool tomoyo_same_alias_entry(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + const struct tomoyo_alias_entry *p1 = container_of(a, typeof(*p1), + head); + const struct tomoyo_alias_entry *p2 = container_of(b, typeof(*p2), + head); + return p1->original_name == p2->original_name && + p1->aliased_name == p2->aliased_name; +} + /** * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list. * @@ -714,7 +753,6 @@ static int tomoyo_update_alias_entry(const char *original_name, const char *aliased_name, const bool is_delete) { - struct tomoyo_alias_entry *ptr; struct tomoyo_alias_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; @@ -726,25 +764,9 @@ static int tomoyo_update_alias_entry(const char *original_name, if (!e.original_name || !e.aliased_name || e.original_name->is_patterned || e.aliased_name->is_patterned) goto out; /* No patterns allowed. */ - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &tomoyo_alias_list, head.list) { - if (!tomoyo_is_same_alias_entry(ptr, &e)) - continue; - ptr->head.is_deleted = is_delete; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_alias_entry *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &tomoyo_alias_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + &tomoyo_alias_list, + tomoyo_same_alias_entry); out: tomoyo_put_name(e.original_name); tomoyo_put_name(e.aliased_name); diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 09436d11f29..8015719926d 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -253,6 +253,15 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename, */ LIST_HEAD(tomoyo_globally_readable_list); +static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + return container_of(a, struct tomoyo_globally_readable_file_entry, + head)->filename == + container_of(b, struct tomoyo_globally_readable_file_entry, + head)->filename; +} + /** * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list. * @@ -266,36 +275,17 @@ LIST_HEAD(tomoyo_globally_readable_list); static int tomoyo_update_globally_readable_entry(const char *filename, const bool is_delete) { - struct tomoyo_globally_readable_file_entry *ptr; struct tomoyo_globally_readable_file_entry e = { }; - int error = is_delete ? -ENOENT : -ENOMEM; + int error; if (!tomoyo_is_correct_word(filename)) return -EINVAL; e.filename = tomoyo_get_name(filename); if (!e.filename) return -ENOMEM; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, - head.list) { - if (ptr->filename != e.filename) - continue; - ptr->head.is_deleted = is_delete; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_globally_readable_file_entry *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &tomoyo_globally_readable_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); - out: + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + &tomoyo_globally_readable_list, + tomoyo_same_globally_readable); tomoyo_put_name(e.filename); return error; } @@ -402,6 +392,13 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head) */ LIST_HEAD(tomoyo_pattern_list); +static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + return container_of(a, struct tomoyo_pattern_entry, head)->pattern == + container_of(b, struct tomoyo_pattern_entry, head)->pattern; +} + /** * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list. * @@ -415,35 +412,17 @@ LIST_HEAD(tomoyo_pattern_list); static int tomoyo_update_file_pattern_entry(const char *pattern, const bool is_delete) { - struct tomoyo_pattern_entry *ptr; struct tomoyo_pattern_entry e = { }; - int error = is_delete ? -ENOENT : -ENOMEM; + int error; if (!tomoyo_is_correct_word(pattern)) return -EINVAL; e.pattern = tomoyo_get_name(pattern); if (!e.pattern) - return error; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, head.list) { - if (e.pattern != ptr->pattern) - continue; - ptr->head.is_deleted = is_delete; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_pattern_entry *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &tomoyo_pattern_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); - out: + return -ENOMEM; + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + &tomoyo_pattern_list, + tomoyo_same_pattern); tomoyo_put_name(e.pattern); return error; } @@ -553,6 +532,14 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head) */ LIST_HEAD(tomoyo_no_rewrite_list); +static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + return container_of(a, struct tomoyo_no_rewrite_entry, head)->pattern + == container_of(b, struct tomoyo_no_rewrite_entry, head) + ->pattern; +} + /** * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list. * @@ -566,35 +553,17 @@ LIST_HEAD(tomoyo_no_rewrite_list); static int tomoyo_update_no_rewrite_entry(const char *pattern, const bool is_delete) { - struct tomoyo_no_rewrite_entry *ptr; struct tomoyo_no_rewrite_entry e = { }; - int error = is_delete ? -ENOENT : -ENOMEM; + int error; if (!tomoyo_is_correct_word(pattern)) return -EINVAL; e.pattern = tomoyo_get_name(pattern); if (!e.pattern) - return error; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, head.list) { - if (ptr->pattern != e.pattern) - continue; - ptr->head.is_deleted = is_delete; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_no_rewrite_entry *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &tomoyo_no_rewrite_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); - out: + return -ENOMEM; + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + &tomoyo_no_rewrite_list, + tomoyo_same_no_rewrite); tomoyo_put_name(e.pattern); return error; } diff --git a/security/tomoyo/number_group.c b/security/tomoyo/number_group.c index afc5b697212..7266a7462c4 100644 --- a/security/tomoyo/number_group.c +++ b/security/tomoyo/number_group.c @@ -56,6 +56,18 @@ struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name) return !error ? group : NULL; } +static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + return !memcmp(&container_of(a, struct tomoyo_number_group_member, + head)->number, + &container_of(b, struct tomoyo_number_group_member, + head)->number, + sizeof(container_of(a, + struct tomoyo_number_group_member, + head)->number)); +} + /** * tomoyo_write_number_group_policy - Write "struct tomoyo_number_group" list. * @@ -68,40 +80,19 @@ int tomoyo_write_number_group_policy(char *data, const bool is_delete) { struct tomoyo_number_group *group; struct tomoyo_number_group_member e = { }; - struct tomoyo_number_group_member *member; - int error = is_delete ? -ENOENT : -ENOMEM; + int error; char *w[2]; if (!tomoyo_tokenize(data, w, sizeof(w))) return -EINVAL; - if (!tomoyo_parse_number_union(w[1], &e.number)) - return -EINVAL; - if (e.number.is_group || e.number.values[0] > e.number.values[1]) { - tomoyo_put_number_union(&e.number); + if (w[1][0] == '@' || !tomoyo_parse_number_union(w[1], &e.number) || + e.number.values[0] > e.number.values[1]) return -EINVAL; - } group = tomoyo_get_number_group(w[0]); if (!group) return -ENOMEM; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(member, &group->member_list, head.list) { - if (memcmp(&member->number, &e.number, sizeof(e.number))) - continue; - member->head.is_deleted = is_delete; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_number_group_member *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &group->member_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); - out: + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + &group->member_list, + tomoyo_same_number_group); tomoyo_put_number_group(group); return error; } diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c index 7838f768129..5b71d886845 100644 --- a/security/tomoyo/path_group.c +++ b/security/tomoyo/path_group.c @@ -54,6 +54,15 @@ struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name) return !error ? group : NULL; } +static bool tomoyo_same_path_group(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + return container_of(a, struct tomoyo_path_group_member, head) + ->member_name == + container_of(b, struct tomoyo_path_group_member, head) + ->member_name; +} + /** * tomoyo_write_path_group_policy - Write "struct tomoyo_path_group" list. * @@ -65,7 +74,6 @@ struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name) int tomoyo_write_path_group_policy(char *data, const bool is_delete) { struct tomoyo_path_group *group; - struct tomoyo_path_group_member *member; struct tomoyo_path_group_member e = { }; int error = is_delete ? -ENOENT : -ENOMEM; char *w[2]; @@ -77,25 +85,9 @@ int tomoyo_write_path_group_policy(char *data, const bool is_delete) e.member_name = tomoyo_get_name(w[1]); if (!e.member_name) goto out; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(member, &group->member_list, head.list) { - if (member->member_name != e.member_name) - continue; - member->head.is_deleted = is_delete; - error = 0; - break; - } - if (!is_delete && error) { - struct tomoyo_path_group_member *entry = - tomoyo_commit_ok(&e, sizeof(e)); - if (entry) { - list_add_tail_rcu(&entry->head.list, - &group->member_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + &group->member_list, + tomoyo_same_path_group); out: tomoyo_put_name(e.member_name); tomoyo_put_path_group(group); -- cgit v1.2.3-70-g09d2 From d2f8b2348f3406652ee00ee7221441bd36fe0195 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 15 Jun 2010 10:10:37 +0900 Subject: TOMOYO: Use common code for garbage collection. Use common code for elements using "struct list_head" + "bool" structure. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/gc.c | 212 ++++++++++++++++----------------------------------- 1 file changed, 66 insertions(+), 146 deletions(-) diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 2dd9665af26..4290e519eaa 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -11,7 +11,7 @@ #include #include -enum tomoyo_gc_id { +enum tomoyo_policy_id { TOMOYO_ID_PATH_GROUP, TOMOYO_ID_PATH_GROUP_MEMBER, TOMOYO_ID_NUMBER_GROUP, @@ -26,7 +26,8 @@ enum tomoyo_gc_id { TOMOYO_ID_MANAGER, TOMOYO_ID_NAME, TOMOYO_ID_ACL, - TOMOYO_ID_DOMAIN + TOMOYO_ID_DOMAIN, + TOMOYO_MAX_POLICY }; struct tomoyo_gc_entry { @@ -209,117 +210,58 @@ static void tomoyo_del_number_group(struct tomoyo_number_group *group) tomoyo_put_name(group->group_name); } +static struct list_head *tomoyo_policy_list[TOMOYO_MAX_POLICY] = { + [TOMOYO_ID_GLOBALLY_READABLE] = &tomoyo_globally_readable_list, + [TOMOYO_ID_PATTERN] = &tomoyo_pattern_list, + [TOMOYO_ID_NO_REWRITE] = &tomoyo_no_rewrite_list, + [TOMOYO_ID_DOMAIN_INITIALIZER] = &tomoyo_domain_initializer_list, + [TOMOYO_ID_DOMAIN_KEEPER] = &tomoyo_domain_keeper_list, + [TOMOYO_ID_AGGREGATOR] = &tomoyo_aggregator_list, + [TOMOYO_ID_ALIAS] = &tomoyo_alias_list, + [TOMOYO_ID_MANAGER] = &tomoyo_policy_manager_list, +}; + +static bool tomoyo_collect_member(struct list_head *member_list, int id) +{ + struct tomoyo_acl_head *member; + list_for_each_entry(member, member_list, list) { + if (!member->is_deleted) + continue; + if (!tomoyo_add_to_gc(id, &member->list)) + return false; + list_del_rcu(&member->list); + } + return true; +} + +static bool tomoyo_collect_acl(struct tomoyo_domain_info *domain) +{ + struct tomoyo_acl_info *acl; + list_for_each_entry(acl, &domain->acl_info_list, list) { + if (!acl->is_deleted) + continue; + if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list)) + return false; + list_del_rcu(&acl->list); + } + return true; +} + static void tomoyo_collect_entry(void) { + int i; if (mutex_lock_interruptible(&tomoyo_policy_lock)) return; - { - struct tomoyo_globally_readable_file_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, - head.list) { - if (!ptr->head.is_deleted) - continue; - if (tomoyo_add_to_gc(TOMOYO_ID_GLOBALLY_READABLE, ptr)) - list_del_rcu(&ptr->head.list); - else - break; - } - } - { - struct tomoyo_pattern_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, head.list) { - if (!ptr->head.is_deleted) - continue; - if (tomoyo_add_to_gc(TOMOYO_ID_PATTERN, ptr)) - list_del_rcu(&ptr->head.list); - else - break; - } - } - { - struct tomoyo_no_rewrite_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, - head.list) { - if (!ptr->head.is_deleted) - continue; - if (tomoyo_add_to_gc(TOMOYO_ID_NO_REWRITE, ptr)) - list_del_rcu(&ptr->head.list); - else - break; - } - } - { - struct tomoyo_domain_initializer_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, - head.list) { - if (!ptr->head.is_deleted) - continue; - if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_INITIALIZER, ptr)) - list_del_rcu(&ptr->head.list); - else - break; - } - } - { - struct tomoyo_domain_keeper_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, - head.list) { - if (!ptr->head.is_deleted) - continue; - if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN_KEEPER, ptr)) - list_del_rcu(&ptr->head.list); - else - break; - } - } - { - struct tomoyo_aggregator_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, - head.list) { - if (!ptr->head.is_deleted) - continue; - if (tomoyo_add_to_gc(TOMOYO_ID_AGGREGATOR, ptr)) - list_del_rcu(&ptr->head.list); - else - break; - } - } - { - struct tomoyo_alias_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_alias_list, head.list) { - if (!ptr->head.is_deleted) - continue; - if (tomoyo_add_to_gc(TOMOYO_ID_ALIAS, ptr)) - list_del_rcu(&ptr->head.list); - else - break; - } - } - { - struct tomoyo_policy_manager_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, - head.list) { - if (!ptr->head.is_deleted) - continue; - if (tomoyo_add_to_gc(TOMOYO_ID_MANAGER, ptr)) - list_del_rcu(&ptr->head.list); - else - break; - } + for (i = 0; i < TOMOYO_MAX_POLICY; i++) { + if (tomoyo_policy_list[i]) + if (!tomoyo_collect_member(tomoyo_policy_list[i], i)) + goto unlock; } { struct tomoyo_domain_info *domain; list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) { - struct tomoyo_acl_info *acl; - list_for_each_entry_rcu(acl, &domain->acl_info_list, - list) { - if (!acl->is_deleted) - continue; - if (tomoyo_add_to_gc(TOMOYO_ID_ACL, acl)) - list_del_rcu(&acl->list); - else - break; - } + if (!tomoyo_collect_acl(domain)) + goto unlock; if (!domain->is_deleted || atomic_read(&domain->users)) continue; /* @@ -330,72 +272,50 @@ static void tomoyo_collect_entry(void) if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, domain)) list_del_rcu(&domain->list); else - break; + goto unlock; } } - { - int i; - for (i = 0; i < TOMOYO_MAX_HASH; i++) { - struct tomoyo_name_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], - list) { - if (atomic_read(&ptr->users)) - continue; - if (tomoyo_add_to_gc(TOMOYO_ID_NAME, ptr)) - list_del_rcu(&ptr->list); - else { - i = TOMOYO_MAX_HASH; - break; - } - } + for (i = 0; i < TOMOYO_MAX_HASH; i++) { + struct tomoyo_name_entry *ptr; + list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], list) { + if (atomic_read(&ptr->users)) + continue; + if (tomoyo_add_to_gc(TOMOYO_ID_NAME, ptr)) + list_del_rcu(&ptr->list); + else + goto unlock; } } { struct tomoyo_path_group *group; list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) { - struct tomoyo_path_group_member *member; - list_for_each_entry_rcu(member, &group->member_list, - head.list) { - if (!member->head.is_deleted) - continue; - if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP_MEMBER, - member)) - list_del_rcu(&member->head.list); - else - break; - } + tomoyo_collect_member(&group->member_list, + TOMOYO_ID_PATH_GROUP_MEMBER); if (!list_empty(&group->member_list) || atomic_read(&group->users)) continue; if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP, group)) list_del_rcu(&group->list); else - break; + goto unlock; } } { struct tomoyo_number_group *group; - list_for_each_entry_rcu(group, &tomoyo_number_group_list, list) { - struct tomoyo_number_group_member *member; - list_for_each_entry_rcu(member, &group->member_list, - head.list) { - if (!member->head.is_deleted) - continue; - if (tomoyo_add_to_gc(TOMOYO_ID_NUMBER_GROUP_MEMBER, - member)) - list_del_rcu(&member->head.list); - else - break; - } + list_for_each_entry_rcu(group, &tomoyo_number_group_list, + list) { + tomoyo_collect_member(&group->member_list, + TOMOYO_ID_NUMBER_GROUP_MEMBER); if (!list_empty(&group->member_list) || atomic_read(&group->users)) continue; if (tomoyo_add_to_gc(TOMOYO_ID_NUMBER_GROUP, group)) list_del_rcu(&group->list); else - break; + goto unlock; } } + unlock: mutex_unlock(&tomoyo_policy_lock); } -- cgit v1.2.3-70-g09d2 From 9ee0c823c18119914283358b35a1c3ebb14c2f90 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 11 Jun 2010 12:37:05 -0400 Subject: SELinux: seperate range transition rules to a seperate function Move the range transition rule to a separate function, range_read(), rather than doing it all in policydb_read() Signed-off-by: Eric Paris Acked-by: Stephen Smalley Signed-off-by: James Morris --- security/selinux/ss/policydb.c | 139 ++++++++++++++++++++++------------------- 1 file changed, 75 insertions(+), 64 deletions(-) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index c57802a164d..a39d38af220 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1701,6 +1701,78 @@ u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name) return 1U << (perdatum->value-1); } +static int range_read(struct policydb *p, void *fp) +{ + struct range_trans *rt = NULL; + struct mls_range *r = NULL; + int i, rc; + __le32 buf[2]; + u32 nel; + + if (p->policyvers < POLICYDB_VERSION_MLS) + return 0; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + nel = le32_to_cpu(buf[0]); + for (i = 0; i < nel; i++) { + rc = -ENOMEM; + rt = kzalloc(sizeof(*rt), GFP_KERNEL); + if (!rt) + goto out; + + rc = next_entry(buf, fp, (sizeof(u32) * 2)); + if (rc) + goto out; + + rt->source_type = le32_to_cpu(buf[0]); + rt->target_type = le32_to_cpu(buf[1]); + if (p->policyvers >= POLICYDB_VERSION_RANGETRANS) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + rt->target_class = le32_to_cpu(buf[0]); + } else + rt->target_class = p->process_class; + + rc = -EINVAL; + if (!policydb_type_isvalid(p, rt->source_type) || + !policydb_type_isvalid(p, rt->target_type) || + !policydb_class_isvalid(p, rt->target_class)) + goto out; + + rc = -ENOMEM; + r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) + goto out; + + rc = mls_read_range_helper(r, fp); + if (rc) + goto out; + + rc = -EINVAL; + if (!mls_range_isvalid(p, r)) { + printk(KERN_WARNING "SELinux: rangetrans: invalid range\n"); + goto out; + } + + rc = hashtab_insert(p->range_tr, rt, r); + if (rc) + goto out; + + rt = NULL; + r = NULL; + } + rangetr_hash_eval(p->range_tr); + rc = 0; +out: + kfree(rt); + kfree(r); + return rc; +} + /* * Read the configuration data from a policy database binary * representation file into a policy database structure. @@ -1717,8 +1789,6 @@ int policydb_read(struct policydb *p, void *fp) u32 len, len2, nprim, nel, nel2; char *policydb_str; struct policydb_compat_info *info; - struct range_trans *rt; - struct mls_range *r; rc = policydb_init(p); if (rc) @@ -2131,68 +2201,9 @@ int policydb_read(struct policydb *p, void *fp) } } - if (p->policyvers >= POLICYDB_VERSION_MLS) { - int new_rangetr = p->policyvers >= POLICYDB_VERSION_RANGETRANS; - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel = le32_to_cpu(buf[0]); - for (i = 0; i < nel; i++) { - rt = kzalloc(sizeof(*rt), GFP_KERNEL); - if (!rt) { - rc = -ENOMEM; - goto bad; - } - rc = next_entry(buf, fp, (sizeof(u32) * 2)); - if (rc < 0) { - kfree(rt); - goto bad; - } - rt->source_type = le32_to_cpu(buf[0]); - rt->target_type = le32_to_cpu(buf[1]); - if (new_rangetr) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) { - kfree(rt); - goto bad; - } - rt->target_class = le32_to_cpu(buf[0]); - } else - rt->target_class = p->process_class; - if (!policydb_type_isvalid(p, rt->source_type) || - !policydb_type_isvalid(p, rt->target_type) || - !policydb_class_isvalid(p, rt->target_class)) { - kfree(rt); - rc = -EINVAL; - goto bad; - } - r = kzalloc(sizeof(*r), GFP_KERNEL); - if (!r) { - kfree(rt); - rc = -ENOMEM; - goto bad; - } - rc = mls_read_range_helper(r, fp); - if (rc) { - kfree(rt); - kfree(r); - goto bad; - } - if (!mls_range_isvalid(p, r)) { - printk(KERN_WARNING "SELinux: rangetrans: invalid range\n"); - kfree(rt); - kfree(r); - goto bad; - } - rc = hashtab_insert(p->range_tr, rt, r); - if (rc) { - kfree(rt); - kfree(r); - goto bad; - } - } - rangetr_hash_eval(p->range_tr); - } + rc = range_read(p, fp); + if (rc) + goto bad; p->type_attr_map = kmalloc(p->p_types.nprim * sizeof(struct ebitmap), GFP_KERNEL); if (!p->type_attr_map) -- cgit v1.2.3-70-g09d2 From 05336dee9f5a23c042e5938b42f996dd35e31ee6 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 16 Jun 2010 16:20:24 +0900 Subject: TOMOYO: Use common code for open and mkdir etc. tomoyo_file_perm() and tomoyo_path_permission() are similar. We can embed tomoyo_file_perm() into tomoyo_path_permission(). Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.h | 2 +- security/tomoyo/domain.c | 2 +- security/tomoyo/file.c | 102 ++++++++++------------------------------------- 3 files changed, 22 insertions(+), 84 deletions(-) diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index c8ab7553c48..20345402541 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -880,7 +880,7 @@ int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head); /* Initialize mm related code. */ void __init tomoyo_mm_init(void); -int tomoyo_check_exec_perm(struct tomoyo_request_info *r, +int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, const struct tomoyo_path_info *filename); int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct path *path, const int flag); diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index fe621af46c2..35317e783f3 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -960,7 +960,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) } /* Check execute permission. */ - retval = tomoyo_check_exec_perm(&r, &rn); + retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn); if (retval == TOMOYO_RETRY_REQUEST) goto retry; if (retval < 0) diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 8015719926d..50875d7e860 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -670,62 +670,6 @@ static int tomoyo_path_acl(const struct tomoyo_request_info *r, return error; } -/** - * tomoyo_file_perm - Check permission for opening files. - * - * @r: Pointer to "struct tomoyo_request_info". - * @filename: Filename to check. - * @mode: Mode ("read" or "write" or "read/write" or "execute"). - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_file_perm(struct tomoyo_request_info *r, - const struct tomoyo_path_info *filename, - const u8 mode) -{ - const char *msg = ""; - int error = 0; - u32 perm = 0; - - if (!filename) - return 0; - - if (mode == 6) { - msg = tomoyo_path2keyword(TOMOYO_TYPE_READ_WRITE); - perm = 1 << TOMOYO_TYPE_READ_WRITE; - } else if (mode == 4) { - msg = tomoyo_path2keyword(TOMOYO_TYPE_READ); - perm = 1 << TOMOYO_TYPE_READ; - } else if (mode == 2) { - msg = tomoyo_path2keyword(TOMOYO_TYPE_WRITE); - perm = 1 << TOMOYO_TYPE_WRITE; - } else if (mode == 1) { - msg = tomoyo_path2keyword(TOMOYO_TYPE_EXECUTE); - perm = 1 << TOMOYO_TYPE_EXECUTE; - } else - BUG(); - do { - error = tomoyo_path_acl(r, filename, perm); - if (error && mode == 4 && !r->domain->ignore_global_allow_read - && tomoyo_is_globally_readable_file(filename)) - error = 0; - if (!error) - break; - tomoyo_warn_log(r, "%s %s", msg, filename->name); - error = tomoyo_supervisor(r, "allow_%s %s\n", msg, - tomoyo_file_pattern(filename)); - /* - * Do not retry for execute request, for alias may have - * changed. - */ - } while (error == TOMOYO_RETRY_REQUEST && mode != 1); - if (r->mode != TOMOYO_CONFIG_ENFORCING) - error = 0; - return error; -} - static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a, const struct tomoyo_acl_info *b) { @@ -1018,8 +962,8 @@ static int tomoyo_path2_acl(const struct tomoyo_request_info *r, const u8 type, * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, - const struct tomoyo_path_info *filename) +int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, + const struct tomoyo_path_info *filename) { const char *msg; int error; @@ -1031,15 +975,22 @@ static int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, return 0; do { error = tomoyo_path_acl(r, filename, 1 << operation); + if (error && operation == TOMOYO_TYPE_READ && + !r->domain->ignore_global_allow_read && + tomoyo_is_globally_readable_file(filename)) + error = 0; if (!error) break; msg = tomoyo_path2keyword(operation); tomoyo_warn_log(r, "%s %s", msg, filename->name); error = tomoyo_supervisor(r, "allow_%s %s\n", msg, tomoyo_file_pattern(filename)); - } while (error == TOMOYO_RETRY_REQUEST); - if (r->mode != TOMOYO_CONFIG_ENFORCING) - error = 0; + /* + * Do not retry for execute request, for alias may have + * changed. + */ + } while (error == TOMOYO_RETRY_REQUEST && + operation != TOMOYO_TYPE_EXECUTE); /* * Since "allow_truncate" doesn't imply "allow_rewrite" permission, * we need to check "allow_rewrite" permission if the filename is @@ -1202,8 +1153,6 @@ static int tomoyo_path_number_perm2(struct tomoyo_request_info *r, tomoyo_file_pattern(filename), buffer); } while (error == TOMOYO_RETRY_REQUEST); - if (r->mode != TOMOYO_CONFIG_ENFORCING) - error = 0; return error; } @@ -1241,24 +1190,6 @@ int tomoyo_path_number_perm(const u8 type, struct path *path, return error; } -/** - * tomoyo_check_exec_perm - Check permission for "execute". - * - * @r: Pointer to "struct tomoyo_request_info". - * @filename: Check permission for "execute". - * - * Returns 0 on success, negativevalue otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -int tomoyo_check_exec_perm(struct tomoyo_request_info *r, - const struct tomoyo_path_info *filename) -{ - if (r->mode == TOMOYO_CONFIG_DISABLED) - return 0; - return tomoyo_file_perm(r, filename, 1); -} - /** * tomoyo_check_open_permission - Check permission for "read" and "write". * @@ -1305,11 +1236,18 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, if (!error && acc_mode && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN) != TOMOYO_CONFIG_DISABLED) { + u8 operation; if (!buf.name && !tomoyo_get_realpath(&buf, path)) { error = -ENOMEM; goto out; } - error = tomoyo_file_perm(&r, &buf, acc_mode); + if (acc_mode == (MAY_READ | MAY_WRITE)) + operation = TOMOYO_TYPE_READ_WRITE; + else if (acc_mode == MAY_READ) + operation = TOMOYO_TYPE_READ; + else + operation = TOMOYO_TYPE_WRITE; + error = tomoyo_path_permission(&r, operation, &buf); } out: kfree(buf.name); -- cgit v1.2.3-70-g09d2 From cf6e9a6468ec82a94cbc707b607452ec4454182c Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 16 Jun 2010 16:21:36 +0900 Subject: TOMOYO: Pass parameters via structure. To make it possible to use callback function, pass parameters via "struct tomoyo_request_info". Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.h | 33 +++++++++++++++++++++++++++++++++ security/tomoyo/file.c | 20 +++++++++++++++++++- security/tomoyo/mount.c | 6 ++++++ 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 20345402541..f055e273ec0 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -212,6 +212,39 @@ struct tomoyo_acl_head { */ struct tomoyo_request_info { struct tomoyo_domain_info *domain; + /* For holding parameters. */ + union { + struct { + const struct tomoyo_path_info *filename; + u8 operation; + } path; + struct { + const struct tomoyo_path_info *filename1; + const struct tomoyo_path_info *filename2; + u8 operation; + } path2; + struct { + const struct tomoyo_path_info *filename; + unsigned int mode; + unsigned int major; + unsigned int minor; + u8 operation; + } mkdev; + struct { + const struct tomoyo_path_info *filename; + unsigned long number; + u8 operation; + } path_number; + struct { + const struct tomoyo_path_info *type; + const struct tomoyo_path_info *dir; + const struct tomoyo_path_info *dev; + unsigned long flags; + int need_dev; + } mount; + } param; + u8 param_type; + bool granted; u8 retry; u8 profile; u8 mode; /* One of tomoyo_mode_index . */ diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 50875d7e860..32661df10e8 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -973,6 +973,9 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, r->mode = tomoyo_get_mode(r->profile, r->type); if (r->mode == TOMOYO_CONFIG_DISABLED) return 0; + r->param_type = TOMOYO_TYPE_PATH_ACL; + r->param.path.filename = filename; + r->param.path.operation = operation; do { error = tomoyo_path_acl(r, filename, 1 << operation); if (error && operation == TOMOYO_TYPE_READ && @@ -1143,6 +1146,10 @@ static int tomoyo_path_number_perm2(struct tomoyo_request_info *r, break; } tomoyo_print_ulong(buffer, sizeof(buffer), number, radix); + r->param_type = TOMOYO_TYPE_PATH_NUMBER_ACL; + r->param.path_number.operation = type; + r->param.path_number.filename = filename; + r->param.path_number.number = number; do { error = tomoyo_path_number_acl(r, type, filename, number); if (!error) @@ -1369,8 +1376,15 @@ int tomoyo_path_number3_perm(const u8 operation, struct path *path, idx = tomoyo_read_lock(); error = -ENOMEM; if (tomoyo_get_realpath(&buf, path)) { + dev = new_decode_dev(dev); + r.param_type = TOMOYO_TYPE_PATH_NUMBER3_ACL; + r.param.mkdev.filename = &buf; + r.param.mkdev.operation = operation; + r.param.mkdev.mode = mode; + r.param.mkdev.major = MAJOR(dev); + r.param.mkdev.minor = MINOR(dev); error = tomoyo_path_number3_perm2(&r, operation, &buf, mode, - new_decode_dev(dev)); + dev); kfree(buf.name); } tomoyo_read_unlock(idx); @@ -1421,6 +1435,10 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, tomoyo_add_slash(&buf2); break; } + r.param_type = TOMOYO_TYPE_PATH2_ACL; + r.param.path2.operation = operation; + r.param.path2.filename1 = &buf1; + r.param.path2.filename2 = &buf2; do { error = tomoyo_path2_acl(&r, operation, &buf1, &buf2); if (!error) diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index c170b41c383..554de173152 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -112,6 +112,12 @@ static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, } rdev.name = requested_dev_name; tomoyo_fill_path_info(&rdev); + r->param_type = TOMOYO_TYPE_MOUNT_ACL; + r->param.mount.need_dev = need_dev; + r->param.mount.dev = &rdev; + r->param.mount.dir = &rdir; + r->param.mount.type = &rtype; + r->param.mount.flags = flags; list_for_each_entry_rcu(ptr, &r->domain->acl_info_list, list) { struct tomoyo_mount_acl *acl; if (ptr->is_deleted || ptr->type != TOMOYO_TYPE_MOUNT_ACL) -- cgit v1.2.3-70-g09d2 From 99a852596beb26cc449ca1a79834c107ef4080e1 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 16 Jun 2010 16:22:51 +0900 Subject: TOMOYO: Use callback for permission check. We can use callback function since parameters are passed via "const struct tomoyo_request_info". Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.h | 3 + security/tomoyo/domain.c | 18 +++ security/tomoyo/file.c | 394 +++++++++++++++++++---------------------------- security/tomoyo/mount.c | 73 ++++++--- 4 files changed, 230 insertions(+), 258 deletions(-) diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index f055e273ec0..36b027460ea 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -952,6 +952,9 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, *, const struct tomoyo_acl_head *)); +void tomoyo_check_acl(struct tomoyo_request_info *r, + bool (*check_entry) (const struct tomoyo_request_info *, + const struct tomoyo_acl_info *)); /********** External variable definitions. **********/ diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 35317e783f3..13f4f39baf8 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -109,6 +109,24 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, return error; } +void tomoyo_check_acl(struct tomoyo_request_info *r, + bool (*check_entry) (const struct tomoyo_request_info *, + const struct tomoyo_acl_info *)) +{ + const struct tomoyo_domain_info *domain = r->domain; + struct tomoyo_acl_info *ptr; + + list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { + if (ptr->is_deleted || ptr->type != r->param_type) + continue; + if (check_entry(r, ptr)) { + r->granted = true; + return; + } + } + r->granted = false; +} + /* * tomoyo_domain_list is used for holding list of domains. * The ->acl_info_list of "struct tomoyo_domain_info" is used for holding diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 32661df10e8..18969e77f5e 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -218,6 +218,108 @@ static bool tomoyo_get_realpath(struct tomoyo_path_info *buf, struct path *path) return false; } +/** + * tomoyo_audit_path_log - Audit path request log. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_path_log(struct tomoyo_request_info *r) +{ + const char *operation = tomoyo_path_keyword[r->param.path.operation]; + const struct tomoyo_path_info *filename = r->param.path.filename; + if (r->granted) + return 0; + tomoyo_warn_log(r, "%s %s", operation, filename->name); + return tomoyo_supervisor(r, "allow_%s %s\n", operation, + tomoyo_file_pattern(filename)); +} + +/** + * tomoyo_audit_path2_log - Audit path/path request log. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_path2_log(struct tomoyo_request_info *r) +{ + const char *operation = tomoyo_path2_keyword[r->param.path2.operation]; + const struct tomoyo_path_info *filename1 = r->param.path2.filename1; + const struct tomoyo_path_info *filename2 = r->param.path2.filename2; + if (r->granted) + return 0; + tomoyo_warn_log(r, "%s %s %s", operation, filename1->name, + filename2->name); + return tomoyo_supervisor(r, "allow_%s %s %s\n", operation, + tomoyo_file_pattern(filename1), + tomoyo_file_pattern(filename2)); +} + +/** + * tomoyo_audit_mkdev_log - Audit path/number/number/number request log. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r) +{ + const char *operation = tomoyo_path_number32keyword(r->param.mkdev. + operation); + const struct tomoyo_path_info *filename = r->param.mkdev.filename; + const unsigned int major = r->param.mkdev.major; + const unsigned int minor = r->param.mkdev.minor; + const unsigned int mode = r->param.mkdev.mode; + if (r->granted) + return 0; + tomoyo_warn_log(r, "%s %s 0%o %u %u", operation, filename->name, mode, + major, minor); + return tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", operation, + tomoyo_file_pattern(filename), mode, major, + minor); +} + +/** + * tomoyo_audit_path_number_log - Audit path/number request log. + * + * @r: Pointer to "struct tomoyo_request_info". + * @error: Error code. + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r) +{ + const u8 type = r->param.path_number.operation; + u8 radix; + const struct tomoyo_path_info *filename = r->param.path_number.filename; + const char *operation = tomoyo_path_number_keyword[type]; + char buffer[64]; + if (r->granted) + return 0; + switch (type) { + case TOMOYO_TYPE_CREATE: + case TOMOYO_TYPE_MKDIR: + case TOMOYO_TYPE_MKFIFO: + case TOMOYO_TYPE_MKSOCK: + case TOMOYO_TYPE_CHMOD: + radix = TOMOYO_VALUE_TYPE_OCTAL; + break; + case TOMOYO_TYPE_IOCTL: + radix = TOMOYO_VALUE_TYPE_HEXADECIMAL; + break; + default: + radix = TOMOYO_VALUE_TYPE_DECIMAL; + break; + } + tomoyo_print_ulong(buffer, sizeof(buffer), r->param.path_number.number, + radix); + tomoyo_warn_log(r, "%s %s %s", operation, filename->name, buffer); + return tomoyo_supervisor(r, "allow_%s %s %s\n", operation, + tomoyo_file_pattern(filename), buffer); +} + static int tomoyo_update_path2_acl(const u8 type, const char *filename1, const char *filename2, struct tomoyo_domain_info *const domain, @@ -637,37 +739,52 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) return done; } -/** - * tomoyo_path_acl - Check permission for single path operation. - * - * @r: Pointer to "struct tomoyo_request_info". - * @filename: Filename to check. - * @perm: Permission. - * - * Returns 0 on success, -EPERM otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_path_acl(const struct tomoyo_request_info *r, - const struct tomoyo_path_info *filename, - const u32 perm) +static bool tomoyo_check_path_acl(const struct tomoyo_request_info *r, + const struct tomoyo_acl_info *ptr) { - struct tomoyo_domain_info *domain = r->domain; - struct tomoyo_acl_info *ptr; - int error = -EPERM; + const struct tomoyo_path_acl *acl = container_of(ptr, typeof(*acl), + head); + return (acl->perm & (1 << r->param.path.operation)) && + tomoyo_compare_name_union(r->param.path.filename, &acl->name); +} - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { - struct tomoyo_path_acl *acl; - if (ptr->type != TOMOYO_TYPE_PATH_ACL) - continue; - acl = container_of(ptr, struct tomoyo_path_acl, head); - if (!(acl->perm & perm) || - !tomoyo_compare_name_union(filename, &acl->name)) - continue; - error = 0; - break; - } - return error; +static bool tomoyo_check_path_number_acl(const struct tomoyo_request_info *r, + const struct tomoyo_acl_info *ptr) +{ + const struct tomoyo_path_number_acl *acl = + container_of(ptr, typeof(*acl), head); + return (acl->perm & (1 << r->param.path_number.operation)) && + tomoyo_compare_number_union(r->param.path_number.number, + &acl->number) && + tomoyo_compare_name_union(r->param.path_number.filename, + &acl->name); +} + +static bool tomoyo_check_path2_acl(const struct tomoyo_request_info *r, + const struct tomoyo_acl_info *ptr) +{ + const struct tomoyo_path2_acl *acl = + container_of(ptr, typeof(*acl), head); + return (acl->perm & (1 << r->param.path2.operation)) && + tomoyo_compare_name_union(r->param.path2.filename1, &acl->name1) + && tomoyo_compare_name_union(r->param.path2.filename2, + &acl->name2); +} + +static bool tomoyo_check_mkdev_acl(const struct tomoyo_request_info *r, + const struct tomoyo_acl_info *ptr) +{ + const struct tomoyo_path_number3_acl *acl = + container_of(ptr, typeof(*acl), head); + return (acl->perm & (1 << r->param.mkdev.operation)) && + tomoyo_compare_number_union(r->param.mkdev.mode, + &acl->mode) && + tomoyo_compare_number_union(r->param.mkdev.major, + &acl->major) && + tomoyo_compare_number_union(r->param.mkdev.minor, + &acl->minor) && + tomoyo_compare_name_union(r->param.mkdev.filename, + &acl->name); } static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a, @@ -869,88 +986,6 @@ static int tomoyo_update_path2_acl(const u8 type, const char *filename1, return error; } -/** - * tomoyo_path_number3_acl - Check permission for path/number/number/number operation. - * - * @r: Pointer to "struct tomoyo_request_info". - * @filename: Filename to check. - * @perm: Permission. - * @mode: Create mode. - * @major: Device major number. - * @minor: Device minor number. - * - * Returns 0 on success, -EPERM otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_path_number3_acl(struct tomoyo_request_info *r, - const struct tomoyo_path_info *filename, - const u16 perm, const unsigned int mode, - const unsigned int major, - const unsigned int minor) -{ - struct tomoyo_domain_info *domain = r->domain; - struct tomoyo_acl_info *ptr; - int error = -EPERM; - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { - struct tomoyo_path_number3_acl *acl; - if (ptr->type != TOMOYO_TYPE_PATH_NUMBER3_ACL) - continue; - acl = container_of(ptr, struct tomoyo_path_number3_acl, head); - if (!tomoyo_compare_number_union(mode, &acl->mode)) - continue; - if (!tomoyo_compare_number_union(major, &acl->major)) - continue; - if (!tomoyo_compare_number_union(minor, &acl->minor)) - continue; - if (!(acl->perm & perm)) - continue; - if (!tomoyo_compare_name_union(filename, &acl->name)) - continue; - error = 0; - break; - } - return error; -} - -/** - * tomoyo_path2_acl - Check permission for double path operation. - * - * @r: Pointer to "struct tomoyo_request_info". - * @type: Type of operation. - * @filename1: First filename to check. - * @filename2: Second filename to check. - * - * Returns 0 on success, -EPERM otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_path2_acl(const struct tomoyo_request_info *r, const u8 type, - const struct tomoyo_path_info *filename1, - const struct tomoyo_path_info *filename2) -{ - const struct tomoyo_domain_info *domain = r->domain; - struct tomoyo_acl_info *ptr; - const u8 perm = 1 << type; - int error = -EPERM; - - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { - struct tomoyo_path2_acl *acl; - if (ptr->type != TOMOYO_TYPE_PATH2_ACL) - continue; - acl = container_of(ptr, struct tomoyo_path2_acl, head); - if (!(acl->perm & perm)) - continue; - if (!tomoyo_compare_name_union(filename1, &acl->name1)) - continue; - if (!tomoyo_compare_name_union(filename2, &acl->name2)) - continue; - error = 0; - break; - } - return error; -} - /** * tomoyo_path_permission - Check permission for single path operation. * @@ -965,7 +1000,6 @@ static int tomoyo_path2_acl(const struct tomoyo_request_info *r, const u8 type, int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, const struct tomoyo_path_info *filename) { - const char *msg; int error; next: @@ -977,17 +1011,12 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, r->param.path.filename = filename; r->param.path.operation = operation; do { - error = tomoyo_path_acl(r, filename, 1 << operation); - if (error && operation == TOMOYO_TYPE_READ && + tomoyo_check_acl(r, tomoyo_check_path_acl); + if (!r->granted && operation == TOMOYO_TYPE_READ && !r->domain->ignore_global_allow_read && tomoyo_is_globally_readable_file(filename)) - error = 0; - if (!error) - break; - msg = tomoyo_path2keyword(operation); - tomoyo_warn_log(r, "%s %s", msg, filename->name); - error = tomoyo_supervisor(r, "allow_%s %s\n", msg, - tomoyo_file_pattern(filename)); + r->granted = true; + error = tomoyo_audit_path_log(r); /* * Do not retry for execute request, for alias may have * changed. @@ -1007,42 +1036,6 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, return error; } -/** - * tomoyo_path_number_acl - Check permission for ioctl/chmod/chown/chgrp operation. - * - * @r: Pointer to "struct tomoyo_request_info". - * @type: Operation. - * @filename: Filename to check. - * @number: Number. - * - * Returns 0 on success, -EPERM otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_path_number_acl(struct tomoyo_request_info *r, const u8 type, - const struct tomoyo_path_info *filename, - const unsigned long number) -{ - struct tomoyo_domain_info *domain = r->domain; - struct tomoyo_acl_info *ptr; - const u8 perm = 1 << type; - int error = -EPERM; - list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { - struct tomoyo_path_number_acl *acl; - if (ptr->type != TOMOYO_TYPE_PATH_NUMBER_ACL) - continue; - acl = container_of(ptr, struct tomoyo_path_number_acl, - head); - if (!(acl->perm & perm) || - !tomoyo_compare_number_union(number, &acl->number) || - !tomoyo_compare_name_union(filename, &acl->name)) - continue; - error = 0; - break; - } - return error; -} - static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a, const struct tomoyo_acl_info *b) { @@ -1123,42 +1116,17 @@ static int tomoyo_path_number_perm2(struct tomoyo_request_info *r, const struct tomoyo_path_info *filename, const unsigned long number) { - char buffer[64]; int error; - u8 radix; - const char *msg; if (!filename) return 0; - switch (type) { - case TOMOYO_TYPE_CREATE: - case TOMOYO_TYPE_MKDIR: - case TOMOYO_TYPE_MKFIFO: - case TOMOYO_TYPE_MKSOCK: - case TOMOYO_TYPE_CHMOD: - radix = TOMOYO_VALUE_TYPE_OCTAL; - break; - case TOMOYO_TYPE_IOCTL: - radix = TOMOYO_VALUE_TYPE_HEXADECIMAL; - break; - default: - radix = TOMOYO_VALUE_TYPE_DECIMAL; - break; - } - tomoyo_print_ulong(buffer, sizeof(buffer), number, radix); r->param_type = TOMOYO_TYPE_PATH_NUMBER_ACL; r->param.path_number.operation = type; r->param.path_number.filename = filename; r->param.path_number.number = number; do { - error = tomoyo_path_number_acl(r, type, filename, number); - if (!error) - break; - msg = tomoyo_path_number2keyword(type); - tomoyo_warn_log(r, "%s %s %s", msg, filename->name, buffer); - error = tomoyo_supervisor(r, "allow_%s %s %s\n", msg, - tomoyo_file_pattern(filename), - buffer); + tomoyo_check_acl(r, tomoyo_check_path_number_acl); + error = tomoyo_audit_path_number_log(r); } while (error == TOMOYO_RETRY_REQUEST); return error; } @@ -1310,47 +1278,6 @@ int tomoyo_path_perm(const u8 operation, struct path *path) return error; } -/** - * tomoyo_path_number3_perm2 - Check permission for path/number/number/number operation. - * - * @r: Pointer to "struct tomoyo_request_info". - * @operation: Type of operation. - * @filename: Filename to check. - * @mode: Create mode. - * @dev: Device number. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_path_number3_perm2(struct tomoyo_request_info *r, - const u8 operation, - const struct tomoyo_path_info *filename, - const unsigned int mode, - const unsigned int dev) -{ - int error; - const char *msg; - const unsigned int major = MAJOR(dev); - const unsigned int minor = MINOR(dev); - - do { - error = tomoyo_path_number3_acl(r, filename, 1 << operation, - mode, major, minor); - if (!error) - break; - msg = tomoyo_path_number32keyword(operation); - tomoyo_warn_log(r, "%s %s 0%o %u %u", msg, filename->name, - mode, major, minor); - error = tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", msg, - tomoyo_file_pattern(filename), mode, - major, minor); - } while (error == TOMOYO_RETRY_REQUEST); - if (r->mode != TOMOYO_CONFIG_ENFORCING) - error = 0; - return error; -} - /** * tomoyo_path_number3_perm - Check permission for "mkblock" and "mkchar". * @@ -1383,8 +1310,8 @@ int tomoyo_path_number3_perm(const u8 operation, struct path *path, r.param.mkdev.mode = mode; r.param.mkdev.major = MAJOR(dev); r.param.mkdev.minor = MINOR(dev); - error = tomoyo_path_number3_perm2(&r, operation, &buf, mode, - dev); + tomoyo_check_acl(&r, tomoyo_check_mkdev_acl); + error = tomoyo_audit_mkdev_log(&r); kfree(buf.name); } tomoyo_read_unlock(idx); @@ -1406,7 +1333,6 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, struct path *path2) { int error = -ENOMEM; - const char *msg; struct tomoyo_path_info buf1; struct tomoyo_path_info buf2; struct tomoyo_request_info r; @@ -1440,15 +1366,9 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, r.param.path2.filename1 = &buf1; r.param.path2.filename2 = &buf2; do { - error = tomoyo_path2_acl(&r, operation, &buf1, &buf2); - if (!error) - break; - msg = tomoyo_path22keyword(operation); - tomoyo_warn_log(&r, "%s %s %s", msg, buf1.name, buf2.name); - error = tomoyo_supervisor(&r, "allow_%s %s %s\n", msg, - tomoyo_file_pattern(&buf1), - tomoyo_file_pattern(&buf2)); - } while (error == TOMOYO_RETRY_REQUEST); + tomoyo_check_acl(&r, tomoyo_check_path2_acl); + error = tomoyo_audit_path2_log(&r); + } while (error == TOMOYO_RETRY_REQUEST); out: kfree(buf1.name); kfree(buf2.name); diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 554de173152..8f3ac251c57 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -24,6 +24,54 @@ /* Allow to call 'mount --make-shared /dir' */ #define TOMOYO_MOUNT_MAKE_SHARED_KEYWORD "--make-shared" +/** + * tomoyo_audit_mount_log - Audit mount log. + * + * @r: Pointer to "struct tomoyo_request_info". + * + * Returns 0 on success, negative value otherwise. + */ +static int tomoyo_audit_mount_log(struct tomoyo_request_info *r) +{ + const char *dev = r->param.mount.dev->name; + const char *dir = r->param.mount.dir->name; + const char *type = r->param.mount.type->name; + const unsigned long flags = r->param.mount.flags; + if (r->granted) + return 0; + if (!strcmp(type, TOMOYO_MOUNT_REMOUNT_KEYWORD)) + tomoyo_warn_log(r, "mount -o remount %s 0x%lX", dir, flags); + else if (!strcmp(type, TOMOYO_MOUNT_BIND_KEYWORD) + || !strcmp(type, TOMOYO_MOUNT_MOVE_KEYWORD)) + tomoyo_warn_log(r, "mount %s %s %s 0x%lX", type, dev, dir, + flags); + else if (!strcmp(type, TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD) || + !strcmp(type, TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD) || + !strcmp(type, TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD) || + !strcmp(type, TOMOYO_MOUNT_MAKE_SHARED_KEYWORD)) + tomoyo_warn_log(r, "mount %s %s 0x%lX", type, dir, flags); + else + tomoyo_warn_log(r, "mount -t %s %s %s 0x%lX", type, dev, dir, + flags); + return tomoyo_supervisor(r, + TOMOYO_KEYWORD_ALLOW_MOUNT "%s %s %s 0x%lX\n", + tomoyo_file_pattern(r->param.mount.dev), + tomoyo_file_pattern(r->param.mount.dir), type, + flags); +} + +static bool tomoyo_check_mount_acl(const struct tomoyo_request_info *r, + const struct tomoyo_acl_info *ptr) +{ + const struct tomoyo_mount_acl *acl = + container_of(ptr, typeof(*acl), head); + return tomoyo_compare_number_union(r->param.mount.flags, &acl->flags) && + tomoyo_compare_name_union(r->param.mount.type, &acl->fs_type) && + tomoyo_compare_name_union(r->param.mount.dir, &acl->dir_name) && + (!r->param.mount.need_dev || + tomoyo_compare_name_union(r->param.mount.dev, &acl->dev_name)); +} + /** * tomoyo_mount_acl2 - Check permission for mount() operation. * @@ -41,7 +89,6 @@ static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, struct path *dir, char *type, unsigned long flags) { struct path path; - struct tomoyo_acl_info *ptr; struct file_system_type *fstype = NULL; const char *requested_type = NULL; const char *requested_dir_name = NULL; @@ -118,26 +165,10 @@ static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, r->param.mount.dir = &rdir; r->param.mount.type = &rtype; r->param.mount.flags = flags; - list_for_each_entry_rcu(ptr, &r->domain->acl_info_list, list) { - struct tomoyo_mount_acl *acl; - if (ptr->is_deleted || ptr->type != TOMOYO_TYPE_MOUNT_ACL) - continue; - acl = container_of(ptr, struct tomoyo_mount_acl, head); - if (!tomoyo_compare_number_union(flags, &acl->flags) || - !tomoyo_compare_name_union(&rtype, &acl->fs_type) || - !tomoyo_compare_name_union(&rdir, &acl->dir_name) || - (need_dev && - !tomoyo_compare_name_union(&rdev, &acl->dev_name))) - continue; - error = 0; - break; - } - if (error) - error = tomoyo_supervisor(r, TOMOYO_KEYWORD_ALLOW_MOUNT - "%s %s %s 0x%lX\n", - tomoyo_file_pattern(&rdev), - tomoyo_file_pattern(&rdir), - requested_type, flags); + do { + tomoyo_check_acl(r, tomoyo_check_mount_acl); + error = tomoyo_audit_mount_log(r); + } while (error == TOMOYO_RETRY_REQUEST); out: kfree(requested_dev_name); kfree(requested_dir_name); -- cgit v1.2.3-70-g09d2 From 75093152a97ee0ec281895b4f6229ff3c481fd64 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 16 Jun 2010 16:23:55 +0900 Subject: TOMOYO: Rename symbols. Use shorter name in order to make it easier to fix 80 columns limit. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 44 ++++++++--------- security/tomoyo/common.h | 42 ++++++++--------- security/tomoyo/domain.c | 38 +++++++-------- security/tomoyo/file.c | 104 ++++++++++++++++++++--------------------- security/tomoyo/gc.c | 4 +- security/tomoyo/mount.c | 10 ++-- security/tomoyo/number_group.c | 2 +- security/tomoyo/path_group.c | 2 +- security/tomoyo/tomoyo.c | 2 +- security/tomoyo/util.c | 66 +++++++++++++------------- 10 files changed, 157 insertions(+), 157 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 4ee47af0917..1a22fff89e7 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -494,12 +494,12 @@ static int tomoyo_update_manager_entry(const char *manager, struct tomoyo_policy_manager_entry e = { }; int error; - if (tomoyo_is_domain_def(manager)) { - if (!tomoyo_is_correct_domain(manager)) + if (tomoyo_domain_def(manager)) { + if (!tomoyo_correct_domain(manager)) return -EINVAL; e.is_domain = true; } else { - if (!tomoyo_is_correct_path(manager)) + if (!tomoyo_correct_path(manager)) return -EINVAL; } e.manager = tomoyo_get_name(manager); @@ -565,14 +565,14 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) } /** - * tomoyo_is_policy_manager - Check whether the current process is a policy manager. + * tomoyo_policy_manager - Check whether the current process is a policy manager. * * Returns true if the current process is permitted to modify policy * via /sys/kernel/security/tomoyo/ interface. * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_is_policy_manager(void) +static bool tomoyo_policy_manager(void) { struct tomoyo_policy_manager_entry *ptr; const char *exe; @@ -617,7 +617,7 @@ static bool tomoyo_is_policy_manager(void) } /** - * tomoyo_is_select_one - Parse select command. + * tomoyo_select_one - Parse select command. * * @head: Pointer to "struct tomoyo_io_buffer". * @data: String to parse. @@ -626,7 +626,7 @@ static bool tomoyo_is_policy_manager(void) * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, +static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) { unsigned int pid; @@ -647,7 +647,7 @@ static bool tomoyo_is_select_one(struct tomoyo_io_buffer *head, read_unlock(&tasklist_lock); rcu_read_unlock(); } else if (!strncmp(data, "domain=", 7)) { - if (tomoyo_is_domain_def(data + 7)) + if (tomoyo_domain_def(data + 7)) domain = tomoyo_find_domain(data + 7); } else return false; @@ -748,12 +748,12 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) is_delete = true; else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT)) is_select = true; - if (is_select && tomoyo_is_select_one(head, data)) + if (is_select && tomoyo_select_one(head, data)) return 0; /* Don't allow updating policies by non manager programs. */ - if (!tomoyo_is_policy_manager()) + if (!tomoyo_policy_manager()) return -EPERM; - if (tomoyo_is_domain_def(data)) { + if (tomoyo_domain_def(data)) { domain = NULL; if (is_delete) tomoyo_delete_domain(data); @@ -894,26 +894,26 @@ static bool tomoyo_print_path_number_acl(struct tomoyo_io_buffer *head, } /** - * tomoyo_print_path_number3_acl - Print a path_number3 ACL entry. + * tomoyo_print_mkdev_acl - Print a mkdev ACL entry. * * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_path_number3_acl". + * @ptr: Pointer to "struct tomoyo_mkdev_acl". * * Returns true on success, false otherwise. */ -static bool tomoyo_print_path_number3_acl(struct tomoyo_io_buffer *head, - struct tomoyo_path_number3_acl *ptr) +static bool tomoyo_print_mkdev_acl(struct tomoyo_io_buffer *head, + struct tomoyo_mkdev_acl *ptr) { int pos; u8 bit; const u16 perm = ptr->perm; - for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_NUMBER3_OPERATION; + for (bit = head->read_bit; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; pos = head->read_avail; if (!tomoyo_io_printf(head, "allow_%s", - tomoyo_path_number32keyword(bit)) || + tomoyo_mkdev2keyword(bit)) || !tomoyo_print_name_union(head, &ptr->name) || !tomoyo_print_number_union(head, &ptr->mode) || !tomoyo_print_number_union(head, &ptr->major) || @@ -984,11 +984,11 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, head); return tomoyo_print_path_number_acl(head, acl); } - if (acl_type == TOMOYO_TYPE_PATH_NUMBER3_ACL) { - struct tomoyo_path_number3_acl *acl - = container_of(ptr, struct tomoyo_path_number3_acl, + if (acl_type == TOMOYO_TYPE_MKDEV_ACL) { + struct tomoyo_mkdev_acl *acl + = container_of(ptr, struct tomoyo_mkdev_acl, head); - return tomoyo_print_path_number3_acl(head, acl); + return tomoyo_print_mkdev_acl(head, acl); } if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { struct tomoyo_mount_acl *acl @@ -1910,7 +1910,7 @@ int tomoyo_write_control(struct file *file, const char __user *buffer, /* Don't allow updating policies by non manager programs. */ if (head->write != tomoyo_write_pid && head->write != tomoyo_write_domain_policy && - !tomoyo_is_policy_manager()) + !tomoyo_policy_manager()) return -EPERM; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 36b027460ea..9b106e9adbe 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -80,7 +80,7 @@ enum tomoyo_acl_entry_type_index { TOMOYO_TYPE_PATH_ACL, TOMOYO_TYPE_PATH2_ACL, TOMOYO_TYPE_PATH_NUMBER_ACL, - TOMOYO_TYPE_PATH_NUMBER3_ACL, + TOMOYO_TYPE_MKDEV_ACL, TOMOYO_TYPE_MOUNT_ACL, }; @@ -114,10 +114,10 @@ enum tomoyo_path_acl_index { #define TOMOYO_RW_MASK ((1 << TOMOYO_TYPE_READ) | (1 << TOMOYO_TYPE_WRITE)) -enum tomoyo_path_number3_acl_index { +enum tomoyo_mkdev_acl_index { TOMOYO_TYPE_MKBLOCK, TOMOYO_TYPE_MKCHAR, - TOMOYO_MAX_PATH_NUMBER3_OPERATION + TOMOYO_MAX_MKDEV_OPERATION }; enum tomoyo_path2_acl_index { @@ -342,7 +342,7 @@ struct tomoyo_number_group_member { * * Packing "struct tomoyo_acl_info" allows * "struct tomoyo_path_acl" to embed "u16" and "struct tomoyo_path2_acl" - * "struct tomoyo_path_number_acl" "struct tomoyo_path_number3_acl" to embed + * "struct tomoyo_path_number_acl" "struct tomoyo_mkdev_acl" to embed * "u8" without enlarging their structure size. */ struct tomoyo_acl_info { @@ -439,7 +439,7 @@ struct tomoyo_path_number_acl { }; /* - * tomoyo_path_number3_acl is a structure which is used for holding an + * tomoyo_mkdev_acl is a structure which is used for holding an * entry with one pathname and three numbers operation. * It has following fields. * @@ -452,8 +452,8 @@ struct tomoyo_path_number_acl { * Directives held by this structure are "allow_mkchar", "allow_mkblock". * */ -struct tomoyo_path_number3_acl { - struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_PATH_NUMBER3_ACL */ +struct tomoyo_mkdev_acl { + struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MKDEV_ACL */ u8 perm; struct tomoyo_name_union name; struct tomoyo_number_union mode; @@ -756,12 +756,12 @@ int tomoyo_get_mode(const u8 profile, const u8 index); bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); /* Check whether the domainname is correct. */ -bool tomoyo_is_correct_domain(const unsigned char *domainname); +bool tomoyo_correct_domain(const unsigned char *domainname); /* Check whether the token is correct. */ -bool tomoyo_is_correct_path(const char *filename); -bool tomoyo_is_correct_word(const char *string); +bool tomoyo_correct_path(const char *filename); +bool tomoyo_correct_word(const char *string); /* Check whether the token can be a domainname. */ -bool tomoyo_is_domain_def(const unsigned char *buffer); +bool tomoyo_domain_def(const unsigned char *buffer); bool tomoyo_parse_name_union(const char *filename, struct tomoyo_name_union *ptr); /* Check whether the given filename matches the given path_group. */ @@ -807,7 +807,7 @@ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain); /* Convert double path operation to operation name. */ const char *tomoyo_path22keyword(const u8 operation); const char *tomoyo_path_number2keyword(const u8 operation); -const char *tomoyo_path_number32keyword(const u8 operation); +const char *tomoyo_mkdev2keyword(const u8 operation); /* Get the last component of the given domainname. */ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain); /* Convert single path operation to operation name. */ @@ -919,8 +919,8 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct path *path, const int flag); int tomoyo_path_number_perm(const u8 operation, struct path *path, unsigned long number); -int tomoyo_path_number3_perm(const u8 operation, struct path *path, - const unsigned int mode, unsigned int dev); +int tomoyo_mkdev_perm(const u8 operation, struct path *path, + const unsigned int mode, unsigned int dev); int tomoyo_path_perm(const u8 operation, struct path *path); int tomoyo_path2_perm(const u8 operation, struct path *path1, struct path *path2); @@ -1008,25 +1008,25 @@ static inline bool tomoyo_pathcmp(const struct tomoyo_path_info *a, } /** - * tomoyo_is_valid - Check whether the character is a valid char. + * tomoyo_valid - Check whether the character is a valid char. * * @c: The character to check. * * Returns true if @c is a valid character, false otherwise. */ -static inline bool tomoyo_is_valid(const unsigned char c) +static inline bool tomoyo_valid(const unsigned char c) { return c > ' ' && c < 127; } /** - * tomoyo_is_invalid - Check whether the character is an invalid char. + * tomoyo_invalid - Check whether the character is an invalid char. * * @c: The character to check. * * Returns true if @c is an invalid character, false otherwise. */ -static inline bool tomoyo_is_invalid(const unsigned char c) +static inline bool tomoyo_invalid(const unsigned char c) { return c && (c <= ' ' || c >= 127); } @@ -1063,20 +1063,20 @@ static inline struct tomoyo_domain_info *tomoyo_real_domain(struct task_struct return task_cred_xxx(task, security); } -static inline bool tomoyo_is_same_acl_head(const struct tomoyo_acl_info *p1, +static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *p1, const struct tomoyo_acl_info *p2) { return p1->type == p2->type; } -static inline bool tomoyo_is_same_name_union +static inline bool tomoyo_same_name_union (const struct tomoyo_name_union *p1, const struct tomoyo_name_union *p2) { return p1->filename == p2->filename && p1->group == p2->group && p1->is_group == p2->is_group; } -static inline bool tomoyo_is_same_number_union +static inline bool tomoyo_same_number_union (const struct tomoyo_number_union *p1, const struct tomoyo_number_union *p2) { return p1->values[0] == p2->values[0] && p1->values[1] == p2->values[1] diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 13f4f39baf8..1a122974240 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -256,13 +256,13 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname, struct tomoyo_domain_initializer_entry e = { .is_not = is_not }; int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(program)) + if (!tomoyo_correct_path(program)) return -EINVAL; if (domainname) { - if (!tomoyo_is_domain_def(domainname) && - tomoyo_is_correct_path(domainname)) + if (!tomoyo_domain_def(domainname) && + tomoyo_correct_path(domainname)) e.is_last_name = true; - else if (!tomoyo_is_correct_domain(domainname)) + else if (!tomoyo_correct_domain(domainname)) return -EINVAL; e.domainname = tomoyo_get_name(domainname); if (!e.domainname) @@ -346,7 +346,7 @@ int tomoyo_write_domain_initializer_policy(char *data, const bool is_not, } /** - * tomoyo_is_domain_initializer - Check whether the given program causes domainname reinitialization. + * tomoyo_domain_initializer - Check whether the given program causes domainname reinitialization. * * @domainname: The name of domain. * @program: The name of program. @@ -357,7 +357,7 @@ int tomoyo_write_domain_initializer_policy(char *data, const bool is_not, * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info * +static bool tomoyo_domain_initializer(const struct tomoyo_path_info * domainname, const struct tomoyo_path_info *program, const struct tomoyo_path_info * @@ -462,13 +462,13 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname, struct tomoyo_domain_keeper_entry e = { .is_not = is_not }; int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_domain_def(domainname) && - tomoyo_is_correct_path(domainname)) + if (!tomoyo_domain_def(domainname) && + tomoyo_correct_path(domainname)) e.is_last_name = true; - else if (!tomoyo_is_correct_domain(domainname)) + else if (!tomoyo_correct_domain(domainname)) return -EINVAL; if (program) { - if (!tomoyo_is_correct_path(program)) + if (!tomoyo_correct_path(program)) return -EINVAL; e.program = tomoyo_get_name(program); if (!e.program) @@ -549,7 +549,7 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head) } /** - * tomoyo_is_domain_keeper - Check whether the given program causes domain transition suppression. + * tomoyo_domain_keeper - Check whether the given program causes domain transition suppression. * * @domainname: The name of domain. * @program: The name of program. @@ -560,7 +560,7 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head) * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname, +static bool tomoyo_domain_keeper(const struct tomoyo_path_info *domainname, const struct tomoyo_path_info *program, const struct tomoyo_path_info *last_name) { @@ -646,8 +646,8 @@ static int tomoyo_update_aggregator_entry(const char *original_name, struct tomoyo_aggregator_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(original_name) || - !tomoyo_is_correct_path(aggregated_name)) + if (!tomoyo_correct_path(original_name) || + !tomoyo_correct_path(aggregated_name)) return -EINVAL; e.original_name = tomoyo_get_name(original_name); e.aggregated_name = tomoyo_get_name(aggregated_name); @@ -774,8 +774,8 @@ static int tomoyo_update_alias_entry(const char *original_name, struct tomoyo_alias_entry e = { }; int error = is_delete ? -ENOENT : -ENOMEM; - if (!tomoyo_is_correct_path(original_name) || - !tomoyo_is_correct_path(aliased_name)) + if (!tomoyo_correct_path(original_name) || + !tomoyo_correct_path(aliased_name)) return -EINVAL; e.original_name = tomoyo_get_name(original_name); e.aliased_name = tomoyo_get_name(aliased_name); @@ -859,7 +859,7 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * const struct tomoyo_path_info *saved_domainname; bool found = false; - if (!tomoyo_is_correct_domain(domainname)) + if (!tomoyo_correct_domain(domainname)) return NULL; saved_domainname = tomoyo_get_name(domainname); if (!saved_domainname) @@ -984,7 +984,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) if (retval < 0) goto out; - if (tomoyo_is_domain_initializer(old_domain->domainname, &rn, &ln)) { + if (tomoyo_domain_initializer(old_domain->domainname, &rn, &ln)) { /* Transit to the child of tomoyo_kernel_domain domain. */ snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " " "%s", rn.name); @@ -996,7 +996,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) * initializers because they might start before /sbin/init. */ domain = old_domain; - } else if (tomoyo_is_domain_keeper(old_domain->domainname, &rn, &ln)) { + } else if (tomoyo_domain_keeper(old_domain->domainname, &rn, &ln)) { /* Keep current domain. */ domain = old_domain; } else { diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 18969e77f5e..94e1493ab6b 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -25,8 +25,8 @@ static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { }; /* Keyword array for operations with one pathname and three numbers. */ -static const char *tomoyo_path_number3_keyword -[TOMOYO_MAX_PATH_NUMBER3_OPERATION] = { +static const char *tomoyo_mkdev_keyword +[TOMOYO_MAX_MKDEV_OPERATION] = { [TOMOYO_TYPE_MKBLOCK] = "mkblock", [TOMOYO_TYPE_MKCHAR] = "mkchar", }; @@ -65,7 +65,7 @@ static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = { [TOMOYO_TYPE_UMOUNT] = TOMOYO_MAC_FILE_UMOUNT, }; -static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_PATH_NUMBER3_OPERATION] = { +static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_MKDEV_OPERATION] = { [TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK, [TOMOYO_TYPE_MKCHAR] = TOMOYO_MAC_FILE_MKCHAR, }; @@ -133,16 +133,16 @@ const char *tomoyo_path2keyword(const u8 operation) } /** - * tomoyo_path_number32keyword - Get the name of path/number/number/number operations. + * tomoyo_mkdev2keyword - Get the name of path/number/number/number operations. * * @operation: Type of operation. * * Returns the name of path/number/number/number operation. */ -const char *tomoyo_path_number32keyword(const u8 operation) +const char *tomoyo_mkdev2keyword(const u8 operation) { - return (operation < TOMOYO_MAX_PATH_NUMBER3_OPERATION) - ? tomoyo_path_number3_keyword[operation] : NULL; + return (operation < TOMOYO_MAX_MKDEV_OPERATION) + ? tomoyo_mkdev_keyword[operation] : NULL; } /** @@ -266,7 +266,7 @@ static int tomoyo_audit_path2_log(struct tomoyo_request_info *r) */ static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r) { - const char *operation = tomoyo_path_number32keyword(r->param.mkdev. + const char *operation = tomoyo_mkdev2keyword(r->param.mkdev. operation); const struct tomoyo_path_info *filename = r->param.mkdev.filename; const unsigned int major = r->param.mkdev.major; @@ -380,7 +380,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename, struct tomoyo_globally_readable_file_entry e = { }; int error; - if (!tomoyo_is_correct_word(filename)) + if (!tomoyo_correct_word(filename)) return -EINVAL; e.filename = tomoyo_get_name(filename); if (!e.filename) @@ -393,7 +393,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename, } /** - * tomoyo_is_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading. + * tomoyo_globally_readable_file - Check if the file is unconditionnaly permitted to be open()ed for reading. * * @filename: The filename to check. * @@ -401,7 +401,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename, * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_is_globally_readable_file(const struct tomoyo_path_info * +static bool tomoyo_globally_readable_file(const struct tomoyo_path_info * filename) { struct tomoyo_globally_readable_file_entry *ptr; @@ -517,7 +517,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, struct tomoyo_pattern_entry e = { }; int error; - if (!tomoyo_is_correct_word(pattern)) + if (!tomoyo_correct_word(pattern)) return -EINVAL; e.pattern = tomoyo_get_name(pattern); if (!e.pattern) @@ -658,7 +658,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern, struct tomoyo_no_rewrite_entry e = { }; int error; - if (!tomoyo_is_correct_word(pattern)) + if (!tomoyo_correct_word(pattern)) return -EINVAL; e.pattern = tomoyo_get_name(pattern); if (!e.pattern) @@ -671,7 +671,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern, } /** - * tomoyo_is_no_rewrite_file - Check if the given pathname is not permitted to be rewrited. + * tomoyo_no_rewrite_file - Check if the given pathname is not permitted to be rewrited. * * @filename: Filename to check. * @@ -680,7 +680,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern, * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_is_no_rewrite_file(const struct tomoyo_path_info *filename) +static bool tomoyo_no_rewrite_file(const struct tomoyo_path_info *filename) { struct tomoyo_no_rewrite_entry *ptr; bool found = false; @@ -774,7 +774,7 @@ static bool tomoyo_check_path2_acl(const struct tomoyo_request_info *r, static bool tomoyo_check_mkdev_acl(const struct tomoyo_request_info *r, const struct tomoyo_acl_info *ptr) { - const struct tomoyo_path_number3_acl *acl = + const struct tomoyo_mkdev_acl *acl = container_of(ptr, typeof(*acl), head); return (acl->perm & (1 << r->param.mkdev.operation)) && tomoyo_compare_number_union(r->param.mkdev.mode, @@ -792,8 +792,8 @@ static bool tomoyo_same_path_acl(const struct tomoyo_acl_info *a, { const struct tomoyo_path_acl *p1 = container_of(a, typeof(*p1), head); const struct tomoyo_path_acl *p2 = container_of(b, typeof(*p2), head); - return tomoyo_is_same_acl_head(&p1->head, &p2->head) && - tomoyo_is_same_name_union(&p1->name, &p2->name); + return tomoyo_same_acl_head(&p1->head, &p2->head) && + tomoyo_same_name_union(&p1->name, &p2->name); } static bool tomoyo_merge_path_acl(struct tomoyo_acl_info *a, @@ -853,28 +853,28 @@ static int tomoyo_update_path_acl(const u8 type, const char *filename, return error; } -static bool tomoyo_same_path_number3_acl(const struct tomoyo_acl_info *a, +static bool tomoyo_same_mkdev_acl(const struct tomoyo_acl_info *a, const struct tomoyo_acl_info *b) { - const struct tomoyo_path_number3_acl *p1 = container_of(a, typeof(*p1), + const struct tomoyo_mkdev_acl *p1 = container_of(a, typeof(*p1), head); - const struct tomoyo_path_number3_acl *p2 = container_of(b, typeof(*p2), + const struct tomoyo_mkdev_acl *p2 = container_of(b, typeof(*p2), head); - return tomoyo_is_same_acl_head(&p1->head, &p2->head) - && tomoyo_is_same_name_union(&p1->name, &p2->name) - && tomoyo_is_same_number_union(&p1->mode, &p2->mode) - && tomoyo_is_same_number_union(&p1->major, &p2->major) - && tomoyo_is_same_number_union(&p1->minor, &p2->minor); + return tomoyo_same_acl_head(&p1->head, &p2->head) + && tomoyo_same_name_union(&p1->name, &p2->name) + && tomoyo_same_number_union(&p1->mode, &p2->mode) + && tomoyo_same_number_union(&p1->major, &p2->major) + && tomoyo_same_number_union(&p1->minor, &p2->minor); } -static bool tomoyo_merge_path_number3_acl(struct tomoyo_acl_info *a, +static bool tomoyo_merge_mkdev_acl(struct tomoyo_acl_info *a, struct tomoyo_acl_info *b, const bool is_delete) { - u8 *const a_perm = &container_of(a, struct tomoyo_path_number3_acl, + u8 *const a_perm = &container_of(a, struct tomoyo_mkdev_acl, head)->perm; u8 perm = *a_perm; - const u8 b_perm = container_of(b, struct tomoyo_path_number3_acl, head) + const u8 b_perm = container_of(b, struct tomoyo_mkdev_acl, head) ->perm; if (is_delete) perm &= ~b_perm; @@ -885,7 +885,7 @@ static bool tomoyo_merge_path_number3_acl(struct tomoyo_acl_info *a, } /** - * tomoyo_update_path_number3_acl - Update "struct tomoyo_path_number3_acl" list. + * tomoyo_update_mkdev_acl - Update "struct tomoyo_mkdev_acl" list. * * @type: Type of operation. * @filename: Filename. @@ -899,13 +899,13 @@ static bool tomoyo_merge_path_number3_acl(struct tomoyo_acl_info *a, * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_update_path_number3_acl(const u8 type, const char *filename, +static int tomoyo_update_mkdev_acl(const u8 type, const char *filename, char *mode, char *major, char *minor, struct tomoyo_domain_info * const domain, const bool is_delete) { - struct tomoyo_path_number3_acl e = { - .head.type = TOMOYO_TYPE_PATH_NUMBER3_ACL, + struct tomoyo_mkdev_acl e = { + .head.type = TOMOYO_TYPE_MKDEV_ACL, .perm = 1 << type }; int error = is_delete ? -ENOENT : -ENOMEM; @@ -915,8 +915,8 @@ static int tomoyo_update_path_number3_acl(const u8 type, const char *filename, !tomoyo_parse_number_union(minor, &e.minor)) goto out; error = tomoyo_update_domain(&e.head, sizeof(e), is_delete, domain, - tomoyo_same_path_number3_acl, - tomoyo_merge_path_number3_acl); + tomoyo_same_mkdev_acl, + tomoyo_merge_mkdev_acl); out: tomoyo_put_name_union(&e.name); tomoyo_put_number_union(&e.mode); @@ -930,9 +930,9 @@ static bool tomoyo_same_path2_acl(const struct tomoyo_acl_info *a, { const struct tomoyo_path2_acl *p1 = container_of(a, typeof(*p1), head); const struct tomoyo_path2_acl *p2 = container_of(b, typeof(*p2), head); - return tomoyo_is_same_acl_head(&p1->head, &p2->head) - && tomoyo_is_same_name_union(&p1->name1, &p2->name1) - && tomoyo_is_same_name_union(&p1->name2, &p2->name2); + return tomoyo_same_acl_head(&p1->head, &p2->head) + && tomoyo_same_name_union(&p1->name1, &p2->name1) + && tomoyo_same_name_union(&p1->name2, &p2->name2); } static bool tomoyo_merge_path2_acl(struct tomoyo_acl_info *a, @@ -1014,7 +1014,7 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, tomoyo_check_acl(r, tomoyo_check_path_acl); if (!r->granted && operation == TOMOYO_TYPE_READ && !r->domain->ignore_global_allow_read && - tomoyo_is_globally_readable_file(filename)) + tomoyo_globally_readable_file(filename)) r->granted = true; error = tomoyo_audit_path_log(r); /* @@ -1029,7 +1029,7 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, * specified by "deny_rewrite" keyword. */ if (!error && operation == TOMOYO_TYPE_TRUNCATE && - tomoyo_is_no_rewrite_file(filename)) { + tomoyo_no_rewrite_file(filename)) { operation = TOMOYO_TYPE_REWRITE; goto next; } @@ -1043,9 +1043,9 @@ static bool tomoyo_same_path_number_acl(const struct tomoyo_acl_info *a, head); const struct tomoyo_path_number_acl *p2 = container_of(b, typeof(*p2), head); - return tomoyo_is_same_acl_head(&p1->head, &p2->head) - && tomoyo_is_same_name_union(&p1->name, &p2->name) - && tomoyo_is_same_number_union(&p1->number, &p2->number); + return tomoyo_same_acl_head(&p1->head, &p2->head) + && tomoyo_same_name_union(&p1->name, &p2->name) + && tomoyo_same_number_union(&p1->number, &p2->number); } static bool tomoyo_merge_path_number_acl(struct tomoyo_acl_info *a, @@ -1204,7 +1204,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, error = -ENOMEM; goto out; } - if (tomoyo_is_no_rewrite_file(&buf)) + if (tomoyo_no_rewrite_file(&buf)) error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, &buf); } @@ -1258,7 +1258,7 @@ int tomoyo_path_perm(const u8 operation, struct path *path) goto out; switch (operation) { case TOMOYO_TYPE_REWRITE: - if (!tomoyo_is_no_rewrite_file(&buf)) { + if (!tomoyo_no_rewrite_file(&buf)) { error = 0; goto out; } @@ -1279,7 +1279,7 @@ int tomoyo_path_perm(const u8 operation, struct path *path) } /** - * tomoyo_path_number3_perm - Check permission for "mkblock" and "mkchar". + * tomoyo_mkdev_perm - Check permission for "mkblock" and "mkchar". * * @operation: Type of operation. (TOMOYO_TYPE_MKCHAR or TOMOYO_TYPE_MKBLOCK) * @path: Pointer to "struct path". @@ -1288,7 +1288,7 @@ int tomoyo_path_perm(const u8 operation, struct path *path) * * Returns 0 on success, negative value otherwise. */ -int tomoyo_path_number3_perm(const u8 operation, struct path *path, +int tomoyo_mkdev_perm(const u8 operation, struct path *path, const unsigned int mode, unsigned int dev) { struct tomoyo_request_info r; @@ -1304,7 +1304,7 @@ int tomoyo_path_number3_perm(const u8 operation, struct path *path, error = -ENOMEM; if (tomoyo_get_realpath(&buf, path)) { dev = new_decode_dev(dev); - r.param_type = TOMOYO_TYPE_PATH_NUMBER3_ACL; + r.param_type = TOMOYO_TYPE_MKDEV_ACL; r.param.mkdev.filename = &buf; r.param.mkdev.operation = operation; r.param.mkdev.mode = mode; @@ -1420,11 +1420,11 @@ int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, } if (!w[3][0] || !w[4][0]) goto out; - for (type = 0; type < TOMOYO_MAX_PATH_NUMBER3_OPERATION; type++) { - if (strcmp(w[0], tomoyo_path_number3_keyword[type])) + for (type = 0; type < TOMOYO_MAX_MKDEV_OPERATION; type++) { + if (strcmp(w[0], tomoyo_mkdev_keyword[type])) continue; - return tomoyo_update_path_number3_acl(type, w[1], w[2], w[3], - w[4], domain, is_delete); + return tomoyo_update_mkdev_acl(type, w[1], w[2], w[3], + w[4], domain, is_delete); } out: return -EINVAL; diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 4290e519eaa..d129317ca48 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -122,9 +122,9 @@ static void tomoyo_del_acl(struct tomoyo_acl_info *acl) tomoyo_put_number_union(&entry->number); } break; - case TOMOYO_TYPE_PATH_NUMBER3_ACL: + case TOMOYO_TYPE_MKDEV_ACL: { - struct tomoyo_path_number3_acl *entry + struct tomoyo_mkdev_acl *entry = container_of(acl, typeof(*entry), head); tomoyo_put_name_union(&entry->name); tomoyo_put_number_union(&entry->mode); diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 8f3ac251c57..54015b9964d 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -300,11 +300,11 @@ static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a, { const struct tomoyo_mount_acl *p1 = container_of(a, typeof(*p1), head); const struct tomoyo_mount_acl *p2 = container_of(b, typeof(*p2), head); - return tomoyo_is_same_acl_head(&p1->head, &p2->head) && - tomoyo_is_same_name_union(&p1->dev_name, &p2->dev_name) && - tomoyo_is_same_name_union(&p1->dir_name, &p2->dir_name) && - tomoyo_is_same_name_union(&p1->fs_type, &p2->fs_type) && - tomoyo_is_same_number_union(&p1->flags, &p2->flags); + return tomoyo_same_acl_head(&p1->head, &p2->head) && + tomoyo_same_name_union(&p1->dev_name, &p2->dev_name) && + tomoyo_same_name_union(&p1->dir_name, &p2->dir_name) && + tomoyo_same_name_union(&p1->fs_type, &p2->fs_type) && + tomoyo_same_number_union(&p1->flags, &p2->flags); } /** diff --git a/security/tomoyo/number_group.c b/security/tomoyo/number_group.c index 7266a7462c4..8779309c5b4 100644 --- a/security/tomoyo/number_group.c +++ b/security/tomoyo/number_group.c @@ -24,7 +24,7 @@ struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name) struct tomoyo_number_group *group = NULL; const struct tomoyo_path_info *saved_group_name; int error = -ENOMEM; - if (!tomoyo_is_correct_word(group_name)) + if (!tomoyo_correct_word(group_name)) return NULL; saved_group_name = tomoyo_get_name(group_name); if (!saved_group_name) diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c index 5b71d886845..c78c57cd94e 100644 --- a/security/tomoyo/path_group.c +++ b/security/tomoyo/path_group.c @@ -22,7 +22,7 @@ struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name) struct tomoyo_path_group *group = NULL; const struct tomoyo_path_info *saved_group_name; int error = -ENOMEM; - if (!tomoyo_is_correct_word(group_name)) + if (!tomoyo_correct_word(group_name)) return NULL; saved_group_name = tomoyo_get_name(group_name); if (!saved_group_name) diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 7be732cadd4..95d3f957223 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -142,7 +142,7 @@ static int tomoyo_path_mknod(struct path *parent, struct dentry *dentry, default: goto no_dev; } - return tomoyo_path_number3_perm(type, &path, perm, dev); + return tomoyo_mkdev_perm(type, &path, perm, dev); no_dev: switch (mode & S_IFMT) { case S_IFIFO: diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index e5931686ca3..945eeefbbdf 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -89,7 +89,7 @@ void tomoyo_print_ulong(char *buffer, const int buffer_len, bool tomoyo_parse_name_union(const char *filename, struct tomoyo_name_union *ptr) { - if (!tomoyo_is_correct_word(filename)) + if (!tomoyo_correct_word(filename)) return false; if (filename[0] == '@') { ptr->group = tomoyo_get_path_group(filename + 1); @@ -115,7 +115,7 @@ bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) unsigned long v; memset(num, 0, sizeof(*num)); if (data[0] == '@') { - if (!tomoyo_is_correct_word(data)) + if (!tomoyo_correct_word(data)) return false; num->group = tomoyo_get_number_group(data + 1); num->is_group = true; @@ -142,7 +142,7 @@ bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) } /** - * tomoyo_is_byte_range - Check whether the string is a \ooo style octal value. + * tomoyo_byte_range - Check whether the string is a \ooo style octal value. * * @str: Pointer to the string. * @@ -151,7 +151,7 @@ bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) * TOMOYO uses \ooo style representation for 0x01 - 0x20 and 0x7F - 0xFF. * This function verifies that \ooo is in valid range. */ -static inline bool tomoyo_is_byte_range(const char *str) +static inline bool tomoyo_byte_range(const char *str) { return *str >= '0' && *str++ <= '3' && *str >= '0' && *str++ <= '7' && @@ -159,13 +159,13 @@ static inline bool tomoyo_is_byte_range(const char *str) } /** - * tomoyo_is_alphabet_char - Check whether the character is an alphabet. + * tomoyo_alphabet_char - Check whether the character is an alphabet. * * @c: The character to check. * * Returns true if @c is an alphabet character, false otherwise. */ -static inline bool tomoyo_is_alphabet_char(const char c) +static inline bool tomoyo_alphabet_char(const char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } @@ -223,15 +223,15 @@ void tomoyo_normalize_line(unsigned char *buffer) unsigned char *dp = buffer; bool first = true; - while (tomoyo_is_invalid(*sp)) + while (tomoyo_invalid(*sp)) sp++; while (*sp) { if (!first) *dp++ = ' '; first = false; - while (tomoyo_is_valid(*sp)) + while (tomoyo_valid(*sp)) *dp++ = *sp++; - while (tomoyo_is_invalid(*sp)) + while (tomoyo_invalid(*sp)) sp++; } *dp = '\0'; @@ -265,7 +265,7 @@ bool tomoyo_tokenize(char *buffer, char *w[], size_t size) } /** - * tomoyo_is_correct_word2 - Validate a string. + * tomoyo_correct_word2 - Validate a string. * * @string: The string to check. May be non-'\0'-terminated. * @len: Length of @string. @@ -273,7 +273,7 @@ bool tomoyo_tokenize(char *buffer, char *w[], size_t size) * Check whether the given string follows the naming rules. * Returns true if @string follows the naming rules, false otherwise. */ -static bool tomoyo_is_correct_word2(const char *string, size_t len) +static bool tomoyo_correct_word2(const char *string, size_t len) { const char *const start = string; bool in_repetition = false; @@ -325,13 +325,13 @@ static bool tomoyo_is_correct_word2(const char *string, size_t len) if (d < '0' || d > '7' || e < '0' || e > '7') break; c = tomoyo_make_byte(c, d, e); - if (tomoyo_is_invalid(c)) + if (tomoyo_invalid(c)) continue; /* pattern is not \000 */ } goto out; } else if (in_repetition && c == '/') { goto out; - } else if (tomoyo_is_invalid(c)) { + } else if (tomoyo_invalid(c)) { goto out; } } @@ -343,39 +343,39 @@ static bool tomoyo_is_correct_word2(const char *string, size_t len) } /** - * tomoyo_is_correct_word - Validate a string. + * tomoyo_correct_word - Validate a string. * * @string: The string to check. * * Check whether the given string follows the naming rules. * Returns true if @string follows the naming rules, false otherwise. */ -bool tomoyo_is_correct_word(const char *string) +bool tomoyo_correct_word(const char *string) { - return tomoyo_is_correct_word2(string, strlen(string)); + return tomoyo_correct_word2(string, strlen(string)); } /** - * tomoyo_is_correct_path - Validate a pathname. + * tomoyo_correct_path - Validate a pathname. * * @filename: The pathname to check. * * Check whether the given pathname follows the naming rules. * Returns true if @filename follows the naming rules, false otherwise. */ -bool tomoyo_is_correct_path(const char *filename) +bool tomoyo_correct_path(const char *filename) { - return *filename == '/' && tomoyo_is_correct_word(filename); + return *filename == '/' && tomoyo_correct_word(filename); } /** - * tomoyo_is_correct_domain - Check whether the given domainname follows the naming rules. + * tomoyo_correct_domain - Check whether the given domainname follows the naming rules. * * @domainname: The domainname to check. * * Returns true if @domainname follows the naming rules, false otherwise. */ -bool tomoyo_is_correct_domain(const unsigned char *domainname) +bool tomoyo_correct_domain(const unsigned char *domainname) { if (!domainname || strncmp(domainname, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN)) @@ -390,23 +390,23 @@ bool tomoyo_is_correct_domain(const unsigned char *domainname) if (!cp) break; if (*domainname != '/' || - !tomoyo_is_correct_word2(domainname, cp - domainname - 1)) + !tomoyo_correct_word2(domainname, cp - domainname - 1)) goto out; domainname = cp + 1; } - return tomoyo_is_correct_path(domainname); + return tomoyo_correct_path(domainname); out: return false; } /** - * tomoyo_is_domain_def - Check whether the given token can be a domainname. + * tomoyo_domain_def - Check whether the given token can be a domainname. * * @buffer: The token to check. * * Returns true if @buffer possibly be a domainname, false otherwise. */ -bool tomoyo_is_domain_def(const unsigned char *buffer) +bool tomoyo_domain_def(const unsigned char *buffer) { return !strncmp(buffer, TOMOYO_ROOT_NAME, TOMOYO_ROOT_NAME_LEN); } @@ -528,7 +528,7 @@ static bool tomoyo_file_matches_pattern2(const char *filename, } else if (c == '\\') { if (filename[1] == '\\') filename++; - else if (tomoyo_is_byte_range(filename + 1)) + else if (tomoyo_byte_range(filename + 1)) filename += 3; else return false; @@ -549,14 +549,14 @@ static bool tomoyo_file_matches_pattern2(const char *filename, return false; break; case 'a': - if (!tomoyo_is_alphabet_char(c)) + if (!tomoyo_alphabet_char(c)) return false; break; case '0': case '1': case '2': case '3': - if (c == '\\' && tomoyo_is_byte_range(filename + 1) + if (c == '\\' && tomoyo_byte_range(filename + 1) && strncmp(filename + 1, pattern, 3) == 0) { filename += 3; pattern += 2; @@ -577,7 +577,7 @@ static bool tomoyo_file_matches_pattern2(const char *filename, continue; if (filename[i + 1] == '\\') i++; - else if (tomoyo_is_byte_range(filename + i + 1)) + else if (tomoyo_byte_range(filename + i + 1)) i += 3; else break; /* Bad pattern. */ @@ -593,7 +593,7 @@ static bool tomoyo_file_matches_pattern2(const char *filename, while (isxdigit(filename[j])) j++; } else if (c == 'A') { - while (tomoyo_is_alphabet_char(filename[j])) + while (tomoyo_alphabet_char(filename[j])) j++; } for (i = 1; i <= j; i++) { @@ -939,10 +939,10 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) if (perm & (1 << i)) count++; break; - case TOMOYO_TYPE_PATH_NUMBER3_ACL: - perm = container_of(ptr, struct tomoyo_path_number3_acl, + case TOMOYO_TYPE_MKDEV_ACL: + perm = container_of(ptr, struct tomoyo_mkdev_acl, head)->perm; - for (i = 0; i < TOMOYO_MAX_PATH_NUMBER3_OPERATION; i++) + for (i = 0; i < TOMOYO_MAX_MKDEV_OPERATION; i++) if (perm & (1 << i)) count++; break; -- cgit v1.2.3-70-g09d2 From d795ef9e751b72c94600c91e31bdaef55987a9f6 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 16 Jun 2010 16:24:58 +0900 Subject: TOMOYO: Loosen parameter check for mount operation. If invalid combination of mount flags are given, it will be rejected later. Thus, no need for TOMOYO to reject invalid combination of mount flags. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/mount.c | 124 +++++++++++++----------------------------------- 1 file changed, 33 insertions(+), 91 deletions(-) diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 54015b9964d..7872226f72e 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -73,7 +73,7 @@ static bool tomoyo_check_mount_acl(const struct tomoyo_request_info *r, } /** - * tomoyo_mount_acl2 - Check permission for mount() operation. + * tomoyo_mount_acl - Check permission for mount() operation. * * @r: Pointer to "struct tomoyo_request_info". * @dev_name: Name of device file. @@ -85,8 +85,8 @@ static bool tomoyo_check_mount_acl(const struct tomoyo_request_info *r, * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, - struct path *dir, char *type, unsigned long flags) +static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, + struct path *dir, char *type, unsigned long flags) { struct path path; struct file_system_type *fstype = NULL; @@ -178,94 +178,6 @@ static int tomoyo_mount_acl2(struct tomoyo_request_info *r, char *dev_name, return error; } -/** - * tomoyo_mount_acl - Check permission for mount() operation. - * - * @r: Pointer to "struct tomoyo_request_info". - * @dev_name: Name of device file. - * @dir: Pointer to "struct path". - * @type: Name of filesystem type. - * @flags: Mount options. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_mount_acl(struct tomoyo_request_info *r, char *dev_name, - struct path *dir, char *type, unsigned long flags) -{ - int error; - error = -EPERM; - if ((flags & MS_MGC_MSK) == MS_MGC_VAL) - flags &= ~MS_MGC_MSK; - switch (flags & (MS_REMOUNT | MS_MOVE | MS_BIND)) { - case MS_REMOUNT: - case MS_MOVE: - case MS_BIND: - case 0: - break; - default: - printk(KERN_WARNING "ERROR: " - "%s%s%sare given for single mount operation.\n", - flags & MS_REMOUNT ? "'remount' " : "", - flags & MS_MOVE ? "'move' " : "", - flags & MS_BIND ? "'bind' " : ""); - return -EINVAL; - } - switch (flags & (MS_UNBINDABLE | MS_PRIVATE | MS_SLAVE | MS_SHARED)) { - case MS_UNBINDABLE: - case MS_PRIVATE: - case MS_SLAVE: - case MS_SHARED: - case 0: - break; - default: - printk(KERN_WARNING "ERROR: " - "%s%s%s%sare given for single mount operation.\n", - flags & MS_UNBINDABLE ? "'unbindable' " : "", - flags & MS_PRIVATE ? "'private' " : "", - flags & MS_SLAVE ? "'slave' " : "", - flags & MS_SHARED ? "'shared' " : ""); - return -EINVAL; - } - if (flags & MS_REMOUNT) - error = tomoyo_mount_acl(r, dev_name, dir, - TOMOYO_MOUNT_REMOUNT_KEYWORD, - flags & ~MS_REMOUNT); - else if (flags & MS_MOVE) - error = tomoyo_mount_acl(r, dev_name, dir, - TOMOYO_MOUNT_MOVE_KEYWORD, - flags & ~MS_MOVE); - else if (flags & MS_BIND) - error = tomoyo_mount_acl(r, dev_name, dir, - TOMOYO_MOUNT_BIND_KEYWORD, - flags & ~MS_BIND); - else if (flags & MS_UNBINDABLE) - error = tomoyo_mount_acl(r, dev_name, dir, - TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD, - flags & ~MS_UNBINDABLE); - else if (flags & MS_PRIVATE) - error = tomoyo_mount_acl(r, dev_name, dir, - TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD, - flags & ~MS_PRIVATE); - else if (flags & MS_SLAVE) - error = tomoyo_mount_acl(r, dev_name, dir, - TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD, - flags & ~MS_SLAVE); - else if (flags & MS_SHARED) - error = tomoyo_mount_acl(r, dev_name, dir, - TOMOYO_MOUNT_MAKE_SHARED_KEYWORD, - flags & ~MS_SHARED); - else - do { - error = tomoyo_mount_acl2(r, dev_name, dir, type, - flags); - } while (error == TOMOYO_RETRY_REQUEST); - if (r->mode != TOMOYO_CONFIG_ENFORCING) - error = 0; - return error; -} - /** * tomoyo_mount_permission - Check permission for mount() operation. * @@ -287,6 +199,36 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_MOUNT) == TOMOYO_CONFIG_DISABLED) return 0; + if ((flags & MS_MGC_MSK) == MS_MGC_VAL) + flags &= ~MS_MGC_MSK; + if (flags & MS_REMOUNT) { + type = TOMOYO_MOUNT_REMOUNT_KEYWORD; + flags &= ~MS_REMOUNT; + } + if (flags & MS_MOVE) { + type = TOMOYO_MOUNT_MOVE_KEYWORD; + flags &= ~MS_MOVE; + } + if (flags & MS_BIND) { + type = TOMOYO_MOUNT_BIND_KEYWORD; + flags &= ~MS_BIND; + } + if (flags & MS_UNBINDABLE) { + type = TOMOYO_MOUNT_MAKE_UNBINDABLE_KEYWORD; + flags &= ~MS_UNBINDABLE; + } + if (flags & MS_PRIVATE) { + type = TOMOYO_MOUNT_MAKE_PRIVATE_KEYWORD; + flags &= ~MS_PRIVATE; + } + if (flags & MS_SLAVE) { + type = TOMOYO_MOUNT_MAKE_SLAVE_KEYWORD; + flags &= ~MS_SLAVE; + } + if (flags & MS_SHARED) { + type = TOMOYO_MOUNT_MAKE_SHARED_KEYWORD; + flags &= ~MS_SHARED; + } if (!type) type = ""; idx = tomoyo_read_lock(); -- cgit v1.2.3-70-g09d2 From 71c282362d0672235c5205a7db1f3ac3fcf32981 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 16 Jun 2010 16:26:38 +0900 Subject: TOMOYO: Remove wrapper function for reading keyword. Keyword strings are read-only. We can directly access them to reduce code size. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 8 +++--- security/tomoyo/common.h | 11 ++++---- security/tomoyo/file.c | 65 ++++-------------------------------------------- 3 files changed, 14 insertions(+), 70 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 1a22fff89e7..ba836038289 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -812,7 +812,7 @@ static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head, continue; pos = head->read_avail; if (!tomoyo_io_printf(head, "allow_%s ", - tomoyo_path2keyword(bit)) || + tomoyo_path_keyword[bit]) || !tomoyo_print_name_union(head, &ptr->name) || !tomoyo_io_printf(head, "\n")) goto out; @@ -845,7 +845,7 @@ static bool tomoyo_print_path2_acl(struct tomoyo_io_buffer *head, continue; pos = head->read_avail; if (!tomoyo_io_printf(head, "allow_%s ", - tomoyo_path22keyword(bit)) || + tomoyo_path2_keyword[bit]) || !tomoyo_print_name_union(head, &ptr->name1) || !tomoyo_print_name_union(head, &ptr->name2) || !tomoyo_io_printf(head, "\n")) @@ -879,7 +879,7 @@ static bool tomoyo_print_path_number_acl(struct tomoyo_io_buffer *head, continue; pos = head->read_avail; if (!tomoyo_io_printf(head, "allow_%s", - tomoyo_path_number2keyword(bit)) || + tomoyo_path_number_keyword[bit]) || !tomoyo_print_name_union(head, &ptr->name) || !tomoyo_print_number_union(head, &ptr->number) || !tomoyo_io_printf(head, "\n")) @@ -913,7 +913,7 @@ static bool tomoyo_print_mkdev_acl(struct tomoyo_io_buffer *head, continue; pos = head->read_avail; if (!tomoyo_io_printf(head, "allow_%s", - tomoyo_mkdev2keyword(bit)) || + tomoyo_mkdev_keyword[bit]) || !tomoyo_print_name_union(head, &ptr->name) || !tomoyo_print_number_union(head, &ptr->mode) || !tomoyo_print_number_union(head, &ptr->major) || diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 9b106e9adbe..f4da7a8034a 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -804,14 +804,8 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head); bool tomoyo_tokenize(char *buffer, char *w[], size_t size); /* Write domain policy violation warning message to console? */ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain); -/* Convert double path operation to operation name. */ -const char *tomoyo_path22keyword(const u8 operation); -const char *tomoyo_path_number2keyword(const u8 operation); -const char *tomoyo_mkdev2keyword(const u8 operation); /* Get the last component of the given domainname. */ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain); -/* Convert single path operation to operation name. */ -const char *tomoyo_path2keyword(const u8 operation); /* Fill "struct tomoyo_request_info". */ int tomoyo_init_request_info(struct tomoyo_request_info *r, struct tomoyo_domain_info *domain, @@ -985,6 +979,11 @@ extern bool tomoyo_policy_loaded; /* The kernel's domain. */ extern struct tomoyo_domain_info tomoyo_kernel_domain; +extern const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION]; +extern const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION]; +extern const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION]; +extern const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION]; + extern unsigned int tomoyo_quota_for_query; extern unsigned int tomoyo_query_memory_size; diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 94e1493ab6b..67e65c7dde7 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -10,7 +10,7 @@ #include /* Keyword array for operations with one pathname. */ -static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { +const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { [TOMOYO_TYPE_READ_WRITE] = "read/write", [TOMOYO_TYPE_EXECUTE] = "execute", [TOMOYO_TYPE_READ] = "read", @@ -25,22 +25,20 @@ static const char *tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { }; /* Keyword array for operations with one pathname and three numbers. */ -static const char *tomoyo_mkdev_keyword -[TOMOYO_MAX_MKDEV_OPERATION] = { +const char *tomoyo_mkdev_keyword[TOMOYO_MAX_MKDEV_OPERATION] = { [TOMOYO_TYPE_MKBLOCK] = "mkblock", [TOMOYO_TYPE_MKCHAR] = "mkchar", }; /* Keyword array for operations with two pathnames. */ -static const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = { +const char *tomoyo_path2_keyword[TOMOYO_MAX_PATH2_OPERATION] = { [TOMOYO_TYPE_LINK] = "link", [TOMOYO_TYPE_RENAME] = "rename", [TOMOYO_TYPE_PIVOT_ROOT] = "pivot_root", }; /* Keyword array for operations with one pathname and one number. */ -static const char *tomoyo_path_number_keyword -[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { +const char *tomoyo_path_number_keyword[TOMOYO_MAX_PATH_NUMBER_OPERATION] = { [TOMOYO_TYPE_CREATE] = "create", [TOMOYO_TYPE_MKDIR] = "mkdir", [TOMOYO_TYPE_MKFIFO] = "mkfifo", @@ -119,58 +117,6 @@ bool tomoyo_compare_number_union(const unsigned long value, return value >= ptr->values[0] && value <= ptr->values[1]; } -/** - * tomoyo_path2keyword - Get the name of single path operation. - * - * @operation: Type of operation. - * - * Returns the name of single path operation. - */ -const char *tomoyo_path2keyword(const u8 operation) -{ - return (operation < TOMOYO_MAX_PATH_OPERATION) - ? tomoyo_path_keyword[operation] : NULL; -} - -/** - * tomoyo_mkdev2keyword - Get the name of path/number/number/number operations. - * - * @operation: Type of operation. - * - * Returns the name of path/number/number/number operation. - */ -const char *tomoyo_mkdev2keyword(const u8 operation) -{ - return (operation < TOMOYO_MAX_MKDEV_OPERATION) - ? tomoyo_mkdev_keyword[operation] : NULL; -} - -/** - * tomoyo_path22keyword - Get the name of double path operation. - * - * @operation: Type of operation. - * - * Returns the name of double path operation. - */ -const char *tomoyo_path22keyword(const u8 operation) -{ - return (operation < TOMOYO_MAX_PATH2_OPERATION) - ? tomoyo_path2_keyword[operation] : NULL; -} - -/** - * tomoyo_path_number2keyword - Get the name of path/number operations. - * - * @operation: Type of operation. - * - * Returns the name of path/number operation. - */ -const char *tomoyo_path_number2keyword(const u8 operation) -{ - return (operation < TOMOYO_MAX_PATH_NUMBER_OPERATION) - ? tomoyo_path_number_keyword[operation] : NULL; -} - static void tomoyo_add_slash(struct tomoyo_path_info *buf) { if (buf->is_dir) @@ -266,8 +212,7 @@ static int tomoyo_audit_path2_log(struct tomoyo_request_info *r) */ static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r) { - const char *operation = tomoyo_mkdev2keyword(r->param.mkdev. - operation); + const char *operation = tomoyo_mkdev_keyword[r->param.mkdev.operation]; const struct tomoyo_path_info *filename = r->param.mkdev.filename; const unsigned int major = r->param.mkdev.major; const unsigned int minor = r->param.mkdev.minor; -- cgit v1.2.3-70-g09d2 From cb917cf517075a357ce43b74e8a5a57f2c69a734 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 16 Jun 2010 16:28:21 +0900 Subject: TOMOYO: Merge functions. Embed tomoyo_path_number_perm2() into tomoyo_path_number_perm(). Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/file.c | 50 +++++++++----------------------------------------- 1 file changed, 9 insertions(+), 41 deletions(-) diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 67e65c7dde7..de87c45ba9d 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -265,14 +265,6 @@ static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r) tomoyo_file_pattern(filename), buffer); } -static int tomoyo_update_path2_acl(const u8 type, const char *filename1, - const char *filename2, - struct tomoyo_domain_info *const domain, - const bool is_delete); -static int tomoyo_update_path_acl(const u8 type, const char *filename, - struct tomoyo_domain_info *const domain, - const bool is_delete); - /* * tomoyo_globally_readable_list is used for holding list of pathnames which * are by default allowed to be open()ed for reading by any process. @@ -1045,37 +1037,6 @@ static int tomoyo_update_path_number_acl(const u8 type, const char *filename, return error; } -/** - * tomoyo_path_number_perm2 - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp". - * - * @r: Pointer to "strct tomoyo_request_info". - * @filename: Filename to check. - * @number: Number. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_path_number_perm2(struct tomoyo_request_info *r, - const u8 type, - const struct tomoyo_path_info *filename, - const unsigned long number) -{ - int error; - - if (!filename) - return 0; - r->param_type = TOMOYO_TYPE_PATH_NUMBER_ACL; - r->param.path_number.operation = type; - r->param.path_number.filename = filename; - r->param.path_number.number = number; - do { - tomoyo_check_acl(r, tomoyo_check_path_number_acl); - error = tomoyo_audit_path_number_log(r); - } while (error == TOMOYO_RETRY_REQUEST); - return error; -} - /** * tomoyo_path_number_perm - Check permission for "create", "mkdir", "mkfifo", "mksock", "ioctl", "chmod", "chown", "chgrp". * @@ -1101,9 +1062,16 @@ int tomoyo_path_number_perm(const u8 type, struct path *path, goto out; if (type == TOMOYO_TYPE_MKDIR) tomoyo_add_slash(&buf); - error = tomoyo_path_number_perm2(&r, type, &buf, number); - out: + r.param_type = TOMOYO_TYPE_PATH_NUMBER_ACL; + r.param.path_number.operation = type; + r.param.path_number.filename = &buf; + r.param.path_number.number = number; + do { + tomoyo_check_acl(&r, tomoyo_check_path_number_acl); + error = tomoyo_audit_path_number_log(&r); + } while (error == TOMOYO_RETRY_REQUEST); kfree(buf.name); + out: tomoyo_read_unlock(idx); if (r.mode != TOMOYO_CONFIG_ENFORCING) error = 0; -- cgit v1.2.3-70-g09d2 From 8fbe71f0e0ac28a39e4a93694c34d670c2f31e88 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 16 Jun 2010 16:29:59 +0900 Subject: TOMOYO: Make read function to void. Read functions do not fail. Make them from int to void. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 55 +++++++++++++++--------------------------------- security/tomoyo/common.h | 4 ++-- security/tomoyo/memory.c | 3 +-- 3 files changed, 20 insertions(+), 42 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index ba836038289..9eeb19ec6af 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -352,14 +352,12 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) * tomoyo_read_profile - Read profile table. * * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns 0. */ -static int tomoyo_read_profile(struct tomoyo_io_buffer *head) +static void tomoyo_read_profile(struct tomoyo_io_buffer *head) { int index; if (head->read_eof) - return 0; + return; if (head->read_bit) goto body; tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903"); @@ -434,7 +432,6 @@ static int tomoyo_read_profile(struct tomoyo_io_buffer *head) } if (index == TOMOYO_MAX_PROFILES) head->read_eof = true; - return 0; } /* @@ -538,17 +535,15 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head) * * @head: Pointer to "struct tomoyo_io_buffer". * - * Returns 0. - * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) +static void tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) { struct list_head *pos; bool done = true; if (head->read_eof) - return 0; + return; list_for_each_cookie(pos, head->read_var2, &tomoyo_policy_manager_list) { struct tomoyo_policy_manager_entry *ptr; @@ -561,7 +556,6 @@ static int tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) break; } head->read_eof = done; - return 0; } /** @@ -1004,18 +998,16 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, * * @head: Pointer to "struct tomoyo_io_buffer". * - * Returns 0. - * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_read_domain_policy(struct tomoyo_io_buffer *head) +static void tomoyo_read_domain_policy(struct tomoyo_io_buffer *head) { struct list_head *dpos; struct list_head *apos; bool done = true; if (head->read_eof) - return 0; + return; if (head->read_step == 0) head->read_step = 1; list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) { @@ -1070,7 +1062,6 @@ tail_mark: break; } head->read_eof = done; - return 0; } /** @@ -1122,13 +1113,13 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) +static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) { struct list_head *pos; bool done = true; if (head->read_eof) - return 0; + return; list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) { struct tomoyo_domain_info *domain; domain = list_entry(pos, struct tomoyo_domain_info, list); @@ -1140,7 +1131,6 @@ static int tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) break; } head->read_eof = done; - return 0; } /** @@ -1170,7 +1160,7 @@ static int tomoyo_write_pid(struct tomoyo_io_buffer *head) * The PID is specified by tomoyo_write_pid() so that the user can obtain * using read()/write() interface rather than sysctl() interface. */ -static int tomoyo_read_pid(struct tomoyo_io_buffer *head) +static void tomoyo_read_pid(struct tomoyo_io_buffer *head) { if (head->read_avail == 0 && !head->read_eof) { const int pid = head->read_step; @@ -1188,7 +1178,6 @@ static int tomoyo_read_pid(struct tomoyo_io_buffer *head) domain->domainname->name); head->read_eof = true; } - return 0; } /** @@ -1238,11 +1227,9 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) * * @head: Pointer to "struct tomoyo_io_buffer". * - * Returns 0 on success, -EINVAL otherwise. - * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) +static void tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) { if (!head->read_eof) { switch (head->read_step) { @@ -1302,11 +1289,8 @@ static int tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) case 11: head->read_eof = true; break; - default: - return -EINVAL; } } - return 0; } /** @@ -1560,17 +1544,15 @@ static int tomoyo_poll_query(struct file *file, poll_table *wait) * tomoyo_read_query - Read access requests which violated policy in enforcing mode. * * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns 0. */ -static int tomoyo_read_query(struct tomoyo_io_buffer *head) +static void tomoyo_read_query(struct tomoyo_io_buffer *head) { struct list_head *tmp; int pos = 0; int len = 0; char *buf; if (head->read_avail) - return 0; + return; if (head->read_buf) { kfree(head->read_buf); head->read_buf = NULL; @@ -1590,11 +1572,11 @@ static int tomoyo_read_query(struct tomoyo_io_buffer *head) spin_unlock(&tomoyo_query_list_lock); if (!len) { head->read_step = 0; - return 0; + return; } buf = kzalloc(len, GFP_NOFS); if (!buf) - return 0; + return; pos = 0; spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { @@ -1621,7 +1603,6 @@ static int tomoyo_read_query(struct tomoyo_io_buffer *head) } else { kfree(buf); } - return 0; } /** @@ -1667,13 +1648,12 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) * * Returns version information. */ -static int tomoyo_read_version(struct tomoyo_io_buffer *head) +static void tomoyo_read_version(struct tomoyo_io_buffer *head) { if (!head->read_eof) { tomoyo_io_printf(head, "2.3.0-pre"); head->read_eof = true; } - return 0; } /** @@ -1683,7 +1663,7 @@ static int tomoyo_read_version(struct tomoyo_io_buffer *head) * * Returns the current process's domainname. */ -static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) +static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head) { if (!head->read_eof) { /* @@ -1694,7 +1674,6 @@ static int tomoyo_read_self_domain(struct tomoyo_io_buffer *head) tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name); head->read_eof = true; } - return 0; } /** @@ -1862,7 +1841,7 @@ int tomoyo_read_control(struct file *file, char __user *buffer, if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; /* Call the policy handler. */ - len = head->read(head); + head->read(head); if (len < 0) goto out; /* Write to buffer. */ diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index f4da7a8034a..e0c4ae11bf6 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -526,7 +526,7 @@ struct tomoyo_mount_acl { * is appended. */ struct tomoyo_io_buffer { - int (*read) (struct tomoyo_io_buffer *); + void (*read) (struct tomoyo_io_buffer *); int (*write) (struct tomoyo_io_buffer *); int (*poll) (struct file *file, poll_table *wait); /* Exclusive lock for this structure. */ @@ -900,7 +900,7 @@ void *tomoyo_commit_ok(void *data, const unsigned int size); const struct tomoyo_path_info *tomoyo_get_name(const char *name); /* Check for memory usage. */ -int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head); +void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head); /* Set memory quota. */ int tomoyo_write_memory_quota(struct tomoyo_io_buffer *head); diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 4809febc1ac..8de5333109a 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -184,7 +184,7 @@ unsigned int tomoyo_quota_for_query; * * Returns memory usage. */ -int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) +void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) { if (!head->read_eof) { const unsigned int policy @@ -212,7 +212,6 @@ int tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) tomoyo_io_printf(head, "Total: %10u\n", policy + query); head->read_eof = true; } - return 0; } /** -- cgit v1.2.3-70-g09d2 From e79acf0ef45e0b54aed47ebea7f25c540d3f527e Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 16 Jun 2010 16:31:50 +0900 Subject: TOMOYO: Pass "struct list_head" rather than "void *". Pass "struct list_head" to tomoyo_add_to_gc() and bring list_del_rcu() to tomoyo_add_to_gc(). Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/gc.c | 130 ++++++++++++++++++++++++++++----------------------- 1 file changed, 72 insertions(+), 58 deletions(-) diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index d129317ca48..a54bd823fcd 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -33,13 +33,13 @@ enum tomoyo_policy_id { struct tomoyo_gc_entry { struct list_head list; int type; - void *element; + struct list_head *element; }; static LIST_HEAD(tomoyo_gc_queue); static DEFINE_MUTEX(tomoyo_gc_mutex); /* Caller holds tomoyo_policy_lock mutex. */ -static bool tomoyo_add_to_gc(const int type, void *element) +static bool tomoyo_add_to_gc(const int type, struct list_head *element) { struct tomoyo_gc_entry *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) @@ -47,57 +47,74 @@ static bool tomoyo_add_to_gc(const int type, void *element) entry->type = type; entry->element = element; list_add(&entry->list, &tomoyo_gc_queue); + list_del_rcu(element); return true; } -static void tomoyo_del_allow_read -(struct tomoyo_globally_readable_file_entry *ptr) +static void tomoyo_del_allow_read(struct list_head *element) { + struct tomoyo_globally_readable_file_entry *ptr = + container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->filename); } -static void tomoyo_del_file_pattern(struct tomoyo_pattern_entry *ptr) +static void tomoyo_del_file_pattern(struct list_head *element) { + struct tomoyo_pattern_entry *ptr = + container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->pattern); } -static void tomoyo_del_no_rewrite(struct tomoyo_no_rewrite_entry *ptr) +static void tomoyo_del_no_rewrite(struct list_head *element) { + struct tomoyo_no_rewrite_entry *ptr = + container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->pattern); } -static void tomoyo_del_domain_initializer -(struct tomoyo_domain_initializer_entry *ptr) +static void tomoyo_del_domain_initializer(struct list_head *element) { + struct tomoyo_domain_initializer_entry *ptr = + container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->domainname); tomoyo_put_name(ptr->program); } -static void tomoyo_del_domain_keeper(struct tomoyo_domain_keeper_entry *ptr) +static void tomoyo_del_domain_keeper(struct list_head *element) { + struct tomoyo_domain_keeper_entry *ptr = + container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->domainname); tomoyo_put_name(ptr->program); } -static void tomoyo_del_aggregator(struct tomoyo_aggregator_entry *ptr) +static void tomoyo_del_aggregator(struct list_head *element) { + struct tomoyo_aggregator_entry *ptr = + container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->original_name); tomoyo_put_name(ptr->aggregated_name); } -static void tomoyo_del_alias(struct tomoyo_alias_entry *ptr) +static void tomoyo_del_alias(struct list_head *element) { + struct tomoyo_alias_entry *ptr = + container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->original_name); tomoyo_put_name(ptr->aliased_name); } -static void tomoyo_del_manager(struct tomoyo_policy_manager_entry *ptr) +static void tomoyo_del_manager(struct list_head *element) { + struct tomoyo_policy_manager_entry *ptr = + container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->manager); } -static void tomoyo_del_acl(struct tomoyo_acl_info *acl) +static void tomoyo_del_acl(struct list_head *element) { + struct tomoyo_acl_info *acl = + container_of(element, typeof(*acl), list); switch (acl->type) { case TOMOYO_TYPE_PATH_ACL: { @@ -142,14 +159,13 @@ static void tomoyo_del_acl(struct tomoyo_acl_info *acl) tomoyo_put_number_union(&entry->flags); } break; - default: - printk(KERN_WARNING "Unknown type\n"); - break; } } -static bool tomoyo_del_domain(struct tomoyo_domain_info *domain) +static bool tomoyo_del_domain(struct list_head *element) { + struct tomoyo_domain_info *domain = + container_of(element, typeof(*domain), list); struct tomoyo_acl_info *acl; struct tomoyo_acl_info *tmp; /* @@ -177,7 +193,7 @@ static bool tomoyo_del_domain(struct tomoyo_domain_info *domain) if (atomic_read(&domain->users)) return false; list_for_each_entry_safe(acl, tmp, &domain->acl_info_list, list) { - tomoyo_del_acl(acl); + tomoyo_del_acl(&acl->list); tomoyo_memory_free(acl); } tomoyo_put_name(domain->domainname); @@ -185,28 +201,36 @@ static bool tomoyo_del_domain(struct tomoyo_domain_info *domain) } -static void tomoyo_del_name(const struct tomoyo_name_entry *ptr) +static void tomoyo_del_name(struct list_head *element) { + const struct tomoyo_name_entry *ptr = + container_of(element, typeof(*ptr), list); } -static void tomoyo_del_path_group_member(struct tomoyo_path_group_member - *member) +static void tomoyo_del_path_group_member(struct list_head *element) { + struct tomoyo_path_group_member *member = + container_of(element, typeof(*member), head.list); tomoyo_put_name(member->member_name); } -static void tomoyo_del_path_group(struct tomoyo_path_group *group) +static void tomoyo_del_path_group(struct list_head *element) { + struct tomoyo_path_group *group = + container_of(element, typeof(*group), list); tomoyo_put_name(group->group_name); } -static void tomoyo_del_number_group_member(struct tomoyo_number_group_member - *member) +static void tomoyo_del_number_group_member(struct list_head *element) { + struct tomoyo_number_group_member *member = + container_of(element, typeof(*member), head.list); } -static void tomoyo_del_number_group(struct tomoyo_number_group *group) +static void tomoyo_del_number_group(struct list_head *element) { + struct tomoyo_number_group *group = + container_of(element, typeof(*group), list); tomoyo_put_name(group->group_name); } @@ -229,7 +253,6 @@ static bool tomoyo_collect_member(struct list_head *member_list, int id) continue; if (!tomoyo_add_to_gc(id, &member->list)) return false; - list_del_rcu(&member->list); } return true; } @@ -242,7 +265,6 @@ static bool tomoyo_collect_acl(struct tomoyo_domain_info *domain) continue; if (!tomoyo_add_to_gc(TOMOYO_ID_ACL, &acl->list)) return false; - list_del_rcu(&acl->list); } return true; } @@ -269,9 +291,7 @@ static void tomoyo_collect_entry(void) * refer this domain after successful execve(). * We recheck domain->users after SRCU synchronization. */ - if (tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, domain)) - list_del_rcu(&domain->list); - else + if (!tomoyo_add_to_gc(TOMOYO_ID_DOMAIN, &domain->list)) goto unlock; } } @@ -280,9 +300,7 @@ static void tomoyo_collect_entry(void) list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], list) { if (atomic_read(&ptr->users)) continue; - if (tomoyo_add_to_gc(TOMOYO_ID_NAME, ptr)) - list_del_rcu(&ptr->list); - else + if (!tomoyo_add_to_gc(TOMOYO_ID_NAME, &ptr->list)) goto unlock; } } @@ -294,9 +312,8 @@ static void tomoyo_collect_entry(void) if (!list_empty(&group->member_list) || atomic_read(&group->users)) continue; - if (tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP, group)) - list_del_rcu(&group->list); - else + if (!tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP, + &group->list)) goto unlock; } } @@ -309,9 +326,8 @@ static void tomoyo_collect_entry(void) if (!list_empty(&group->member_list) || atomic_read(&group->users)) continue; - if (tomoyo_add_to_gc(TOMOYO_ID_NUMBER_GROUP, group)) - list_del_rcu(&group->list); - else + if (!tomoyo_add_to_gc(TOMOYO_ID_NUMBER_GROUP, + &group->list)) goto unlock; } } @@ -325,58 +341,56 @@ static void tomoyo_kfree_entry(void) struct tomoyo_gc_entry *tmp; list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { + struct list_head *element = p->element; switch (p->type) { case TOMOYO_ID_DOMAIN_INITIALIZER: - tomoyo_del_domain_initializer(p->element); + tomoyo_del_domain_initializer(element); break; case TOMOYO_ID_DOMAIN_KEEPER: - tomoyo_del_domain_keeper(p->element); + tomoyo_del_domain_keeper(element); break; case TOMOYO_ID_AGGREGATOR: - tomoyo_del_aggregator(p->element); + tomoyo_del_aggregator(element); break; case TOMOYO_ID_ALIAS: - tomoyo_del_alias(p->element); + tomoyo_del_alias(element); break; case TOMOYO_ID_GLOBALLY_READABLE: - tomoyo_del_allow_read(p->element); + tomoyo_del_allow_read(element); break; case TOMOYO_ID_PATTERN: - tomoyo_del_file_pattern(p->element); + tomoyo_del_file_pattern(element); break; case TOMOYO_ID_NO_REWRITE: - tomoyo_del_no_rewrite(p->element); + tomoyo_del_no_rewrite(element); break; case TOMOYO_ID_MANAGER: - tomoyo_del_manager(p->element); + tomoyo_del_manager(element); break; case TOMOYO_ID_NAME: - tomoyo_del_name(p->element); + tomoyo_del_name(element); break; case TOMOYO_ID_ACL: - tomoyo_del_acl(p->element); + tomoyo_del_acl(element); break; case TOMOYO_ID_DOMAIN: - if (!tomoyo_del_domain(p->element)) + if (!tomoyo_del_domain(element)) continue; break; case TOMOYO_ID_PATH_GROUP_MEMBER: - tomoyo_del_path_group_member(p->element); + tomoyo_del_path_group_member(element); break; case TOMOYO_ID_PATH_GROUP: - tomoyo_del_path_group(p->element); + tomoyo_del_path_group(element); break; case TOMOYO_ID_NUMBER_GROUP_MEMBER: - tomoyo_del_number_group_member(p->element); + tomoyo_del_number_group_member(element); break; case TOMOYO_ID_NUMBER_GROUP: - tomoyo_del_number_group(p->element); - break; - default: - printk(KERN_WARNING "Unknown type\n"); + tomoyo_del_number_group(element); break; } - tomoyo_memory_free(p->element); + tomoyo_memory_free(element); list_del(&p->list); kfree(p); } -- cgit v1.2.3-70-g09d2 From 4d1e24514d80cb266231d0c1b6c02161970ad019 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 22 Apr 2010 14:46:18 -0400 Subject: selinux: Set the peer label correctly on connected UNIX domain sockets Correct a problem where we weren't setting the peer label correctly on the client end of a pair of connected UNIX sockets. Signed-off-by: Paul Moore Acked-by: Eric Paris Signed-off-by: James Morris --- security/selinux/hooks.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5c9f25ba1c9..190fd0ffb13 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3980,34 +3980,32 @@ static int selinux_socket_unix_stream_connect(struct socket *sock, struct socket *other, struct sock *newsk) { - struct sk_security_struct *sksec; - struct inode_security_struct *isec; - struct inode_security_struct *other_isec; + struct sk_security_struct *sksec_sock = sock->sk->sk_security; + struct sk_security_struct *sksec_other = other->sk->sk_security; + struct sk_security_struct *sksec_new = newsk->sk_security; struct common_audit_data ad; int err; - isec = SOCK_INODE(sock)->i_security; - other_isec = SOCK_INODE(other)->i_security; - COMMON_AUDIT_DATA_INIT(&ad, NET); ad.u.net.sk = other->sk; - err = avc_has_perm(isec->sid, other_isec->sid, - isec->sclass, + err = avc_has_perm(sksec_sock->sid, sksec_other->sid, + sksec_other->sclass, UNIX_STREAM_SOCKET__CONNECTTO, &ad); if (err) return err; - /* connecting socket */ - sksec = sock->sk->sk_security; - sksec->peer_sid = other_isec->sid; - /* server child socket */ - sksec = newsk->sk_security; - sksec->peer_sid = isec->sid; - err = security_sid_mls_copy(other_isec->sid, sksec->peer_sid, &sksec->sid); + sksec_new->peer_sid = sksec_sock->sid; + err = security_sid_mls_copy(sksec_other->sid, sksec_sock->sid, + &sksec_new->sid); + if (err) + return err; - return err; + /* connecting socket */ + sksec_sock->peer_sid = sksec_new->sid; + + return 0; } static int selinux_socket_unix_may_send(struct socket *sock, -- cgit v1.2.3-70-g09d2 From d4f2d97841827cb876da8b607df05a3dab812416 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 22 Apr 2010 14:46:18 -0400 Subject: selinux: Consolidate sockcreate_sid logic Consolidate the basic sockcreate_sid logic into a single helper function which allows us to do some cleanups in the related code. Signed-off-by: Paul Moore Acked-by: Eric Paris Signed-off-by: James Morris --- security/selinux/hooks.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 190fd0ffb13..2d94a406574 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3671,6 +3671,12 @@ static int selinux_skb_peerlbl_sid(struct sk_buff *skb, u16 family, u32 *sid) } /* socket security operations */ + +static u32 socket_sockcreate_sid(const struct task_security_struct *tsec) +{ + return tsec->sockcreate_sid ? : tsec->sid; +} + static int socket_has_perm(struct task_struct *task, struct socket *sock, u32 perms) { @@ -3698,21 +3704,15 @@ static int selinux_socket_create(int family, int type, { const struct cred *cred = current_cred(); const struct task_security_struct *tsec = cred->security; - u32 sid, newsid; + u32 newsid; u16 secclass; - int err = 0; if (kern) - goto out; - - sid = tsec->sid; - newsid = tsec->sockcreate_sid ?: sid; + return 0; + newsid = socket_sockcreate_sid(tsec); secclass = socket_type_to_security_class(family, type, protocol); - err = avc_has_perm(sid, newsid, secclass, SOCKET__CREATE, NULL); - -out: - return err; + return avc_has_perm(tsec->sid, newsid, secclass, SOCKET__CREATE, NULL); } static int selinux_socket_post_create(struct socket *sock, int family, @@ -3720,22 +3720,14 @@ static int selinux_socket_post_create(struct socket *sock, int family, { const struct cred *cred = current_cred(); const struct task_security_struct *tsec = cred->security; - struct inode_security_struct *isec; + struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; struct sk_security_struct *sksec; - u32 sid, newsid; int err = 0; - sid = tsec->sid; - newsid = tsec->sockcreate_sid; - - isec = SOCK_INODE(sock)->i_security; - if (kern) isec->sid = SECINITSID_KERNEL; - else if (newsid) - isec->sid = newsid; else - isec->sid = sid; + isec->sid = socket_sockcreate_sid(tsec); isec->sclass = socket_type_to_security_class(family, type, protocol); isec->initialized = 1; -- cgit v1.2.3-70-g09d2 From 84914b7ed1c5e0f3199a5a6997022758a70fcaff Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 22 Apr 2010 14:46:18 -0400 Subject: selinux: Shuffle the sk_security_struct alloc and free routines The sk_alloc_security() and sk_free_security() functions were only being called by the selinux_sk_alloc_security() and selinux_sk_free_security() functions so we just move the guts of the alloc/free routines to the callers and eliminate a layer of indirection. Signed-off-by: Paul Moore Acked-by: Eric Paris Signed-off-by: James Morris --- security/selinux/hooks.c | 45 +++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 2d94a406574..01f52424cfe 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -279,32 +279,6 @@ static void superblock_free_security(struct super_block *sb) kfree(sbsec); } -static int sk_alloc_security(struct sock *sk, int family, gfp_t priority) -{ - struct sk_security_struct *sksec; - - sksec = kzalloc(sizeof(*sksec), priority); - if (!sksec) - return -ENOMEM; - - sksec->peer_sid = SECINITSID_UNLABELED; - sksec->sid = SECINITSID_UNLABELED; - sk->sk_security = sksec; - - selinux_netlbl_sk_security_reset(sksec); - - return 0; -} - -static void sk_free_security(struct sock *sk) -{ - struct sk_security_struct *sksec = sk->sk_security; - - sk->sk_security = NULL; - selinux_netlbl_sk_security_free(sksec); - kfree(sksec); -} - /* The security server must be initialized before any labeling or access decisions can be provided. */ extern int ss_initialized; @@ -4224,12 +4198,27 @@ out: static int selinux_sk_alloc_security(struct sock *sk, int family, gfp_t priority) { - return sk_alloc_security(sk, family, priority); + struct sk_security_struct *sksec; + + sksec = kzalloc(sizeof(*sksec), priority); + if (!sksec) + return -ENOMEM; + + sksec->peer_sid = SECINITSID_UNLABELED; + sksec->sid = SECINITSID_UNLABELED; + selinux_netlbl_sk_security_reset(sksec); + sk->sk_security = sksec; + + return 0; } static void selinux_sk_free_security(struct sock *sk) { - sk_free_security(sk); + struct sk_security_struct *sksec = sk->sk_security; + + sk->sk_security = NULL; + selinux_netlbl_sk_security_free(sksec); + kfree(sksec); } static void selinux_sk_clone_security(const struct sock *sk, struct sock *newsk) -- cgit v1.2.3-70-g09d2 From 253bfae6e0ad97554799affa0266052968a45808 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 22 Apr 2010 14:46:19 -0400 Subject: selinux: Convert socket related access controls to use socket labels At present, the socket related access controls use a mix of inode and socket labels; while there should be no practical difference (they _should_ always be the same), it makes the code more confusing. This patch attempts to convert all of the socket related access control points (with the exception of some of the inode/fd based controls) to use the socket's own label. In the process, I also converted the socket_has_perm() function to take a 'sock' argument instead of a 'socket' since that was adding a bit more overhead in some cases. Signed-off-by: Paul Moore Acked-by: Eric Paris Signed-off-by: James Morris --- security/selinux/hooks.c | 119 ++++++++++++++++++----------------------------- 1 file changed, 45 insertions(+), 74 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 01f52424cfe..e95004010c8 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3651,26 +3651,19 @@ static u32 socket_sockcreate_sid(const struct task_security_struct *tsec) return tsec->sockcreate_sid ? : tsec->sid; } -static int socket_has_perm(struct task_struct *task, struct socket *sock, - u32 perms) +static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) { - struct inode_security_struct *isec; + struct sk_security_struct *sksec = sk->sk_security; struct common_audit_data ad; - u32 sid; - int err = 0; + u32 tsid = task_sid(task); - isec = SOCK_INODE(sock)->i_security; - - if (isec->sid == SECINITSID_KERNEL) - goto out; - sid = task_sid(task); + if (sksec->sid == SECINITSID_KERNEL) + return 0; COMMON_AUDIT_DATA_INIT(&ad, NET); - ad.u.net.sk = sock->sk; - err = avc_has_perm(sid, isec->sid, isec->sclass, perms, &ad); + ad.u.net.sk = sk; -out: - return err; + return avc_has_perm(tsid, sksec->sid, sksec->sclass, perms, &ad); } static int selinux_socket_create(int family, int type, @@ -3722,10 +3715,11 @@ static int selinux_socket_post_create(struct socket *sock, int family, static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, int addrlen) { + struct sock *sk = sock->sk; u16 family; int err; - err = socket_has_perm(current, sock, SOCKET__BIND); + err = sock_has_perm(current, sk, SOCKET__BIND); if (err) goto out; @@ -3734,19 +3728,16 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in * Multiple address binding for SCTP is not supported yet: we just * check the first address now. */ - family = sock->sk->sk_family; + family = sk->sk_family; if (family == PF_INET || family == PF_INET6) { char *addrp; - struct inode_security_struct *isec; + struct sk_security_struct *sksec = sk->sk_security; struct common_audit_data ad; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; unsigned short snum; - struct sock *sk = sock->sk; u32 sid, node_perm; - isec = SOCK_INODE(sock)->i_security; - if (family == PF_INET) { addr4 = (struct sockaddr_in *)address; snum = ntohs(addr4->sin_port); @@ -3770,15 +3761,15 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in COMMON_AUDIT_DATA_INIT(&ad, NET); ad.u.net.sport = htons(snum); ad.u.net.family = family; - err = avc_has_perm(isec->sid, sid, - isec->sclass, + err = avc_has_perm(sksec->sid, sid, + sksec->sclass, SOCKET__NAME_BIND, &ad); if (err) goto out; } } - switch (isec->sclass) { + switch (sksec->sclass) { case SECCLASS_TCP_SOCKET: node_perm = TCP_SOCKET__NODE_BIND; break; @@ -3809,8 +3800,8 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in else ipv6_addr_copy(&ad.u.net.v6info.saddr, &addr6->sin6_addr); - err = avc_has_perm(isec->sid, sid, - isec->sclass, node_perm, &ad); + err = avc_has_perm(sksec->sid, sid, + sksec->sclass, node_perm, &ad); if (err) goto out; } @@ -3821,19 +3812,18 @@ out: static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, int addrlen) { struct sock *sk = sock->sk; - struct inode_security_struct *isec; + struct sk_security_struct *sksec = sk->sk_security; int err; - err = socket_has_perm(current, sock, SOCKET__CONNECT); + err = sock_has_perm(current, sk, SOCKET__CONNECT); if (err) return err; /* * If a TCP or DCCP socket, check name_connect permission for the port. */ - isec = SOCK_INODE(sock)->i_security; - if (isec->sclass == SECCLASS_TCP_SOCKET || - isec->sclass == SECCLASS_DCCP_SOCKET) { + if (sksec->sclass == SECCLASS_TCP_SOCKET || + sksec->sclass == SECCLASS_DCCP_SOCKET) { struct common_audit_data ad; struct sockaddr_in *addr4 = NULL; struct sockaddr_in6 *addr6 = NULL; @@ -3856,13 +3846,13 @@ static int selinux_socket_connect(struct socket *sock, struct sockaddr *address, if (err) goto out; - perm = (isec->sclass == SECCLASS_TCP_SOCKET) ? + perm = (sksec->sclass == SECCLASS_TCP_SOCKET) ? TCP_SOCKET__NAME_CONNECT : DCCP_SOCKET__NAME_CONNECT; COMMON_AUDIT_DATA_INIT(&ad, NET); ad.u.net.dport = htons(snum); ad.u.net.family = sk->sk_family; - err = avc_has_perm(isec->sid, sid, isec->sclass, perm, &ad); + err = avc_has_perm(sksec->sid, sid, sksec->sclass, perm, &ad); if (err) goto out; } @@ -3875,7 +3865,7 @@ out: static int selinux_socket_listen(struct socket *sock, int backlog) { - return socket_has_perm(current, sock, SOCKET__LISTEN); + return sock_has_perm(current, sock->sk, SOCKET__LISTEN); } static int selinux_socket_accept(struct socket *sock, struct socket *newsock) @@ -3884,7 +3874,7 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) struct inode_security_struct *isec; struct inode_security_struct *newisec; - err = socket_has_perm(current, sock, SOCKET__ACCEPT); + err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT); if (err) return err; @@ -3901,30 +3891,30 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) static int selinux_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size) { - return socket_has_perm(current, sock, SOCKET__WRITE); + return sock_has_perm(current, sock->sk, SOCKET__WRITE); } static int selinux_socket_recvmsg(struct socket *sock, struct msghdr *msg, int size, int flags) { - return socket_has_perm(current, sock, SOCKET__READ); + return sock_has_perm(current, sock->sk, SOCKET__READ); } static int selinux_socket_getsockname(struct socket *sock) { - return socket_has_perm(current, sock, SOCKET__GETATTR); + return sock_has_perm(current, sock->sk, SOCKET__GETATTR); } static int selinux_socket_getpeername(struct socket *sock) { - return socket_has_perm(current, sock, SOCKET__GETATTR); + return sock_has_perm(current, sock->sk, SOCKET__GETATTR); } static int selinux_socket_setsockopt(struct socket *sock, int level, int optname) { int err; - err = socket_has_perm(current, sock, SOCKET__SETOPT); + err = sock_has_perm(current, sock->sk, SOCKET__SETOPT); if (err) return err; @@ -3934,12 +3924,12 @@ static int selinux_socket_setsockopt(struct socket *sock, int level, int optname static int selinux_socket_getsockopt(struct socket *sock, int level, int optname) { - return socket_has_perm(current, sock, SOCKET__GETOPT); + return sock_has_perm(current, sock->sk, SOCKET__GETOPT); } static int selinux_socket_shutdown(struct socket *sock, int how) { - return socket_has_perm(current, sock, SOCKET__SHUTDOWN); + return sock_has_perm(current, sock->sk, SOCKET__SHUTDOWN); } static int selinux_socket_unix_stream_connect(struct socket *sock, @@ -3977,23 +3967,15 @@ static int selinux_socket_unix_stream_connect(struct socket *sock, static int selinux_socket_unix_may_send(struct socket *sock, struct socket *other) { - struct inode_security_struct *isec; - struct inode_security_struct *other_isec; + struct sk_security_struct *ssec = sock->sk->sk_security; + struct sk_security_struct *osec = other->sk->sk_security; struct common_audit_data ad; - int err; - - isec = SOCK_INODE(sock)->i_security; - other_isec = SOCK_INODE(other)->i_security; COMMON_AUDIT_DATA_INIT(&ad, NET); ad.u.net.sk = other->sk; - err = avc_has_perm(isec->sid, other_isec->sid, - isec->sclass, SOCKET__SENDTO, &ad); - if (err) - return err; - - return 0; + return avc_has_perm(ssec->sid, osec->sid, osec->sclass, SOCKET__SENDTO, + &ad); } static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, @@ -4132,26 +4114,18 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op int err = 0; char *scontext; u32 scontext_len; - struct sk_security_struct *sksec; - struct inode_security_struct *isec; + struct sk_security_struct *sksec = sock->sk->sk_security; u32 peer_sid = SECSID_NULL; - isec = SOCK_INODE(sock)->i_security; - - if (isec->sclass == SECCLASS_UNIX_STREAM_SOCKET || - isec->sclass == SECCLASS_TCP_SOCKET) { - sksec = sock->sk->sk_security; + if (sksec->sclass == SECCLASS_UNIX_STREAM_SOCKET || + sksec->sclass == SECCLASS_TCP_SOCKET) peer_sid = sksec->peer_sid; - } - if (peer_sid == SECSID_NULL) { - err = -ENOPROTOOPT; - goto out; - } + if (peer_sid == SECSID_NULL) + return -ENOPROTOOPT; err = security_sid_to_context(peer_sid, &scontext, &scontext_len); - if (err) - goto out; + return err; if (scontext_len > len) { err = -ERANGE; @@ -4164,9 +4138,7 @@ static int selinux_socket_getpeersec_stream(struct socket *sock, char __user *op out_len: if (put_user(scontext_len, optlen)) err = -EFAULT; - kfree(scontext); -out: return err; } @@ -4378,8 +4350,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) int err = 0; u32 perm; struct nlmsghdr *nlh; - struct socket *sock = sk->sk_socket; - struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; + struct sk_security_struct *sksec = sk->sk_security; if (skb->len < NLMSG_SPACE(0)) { err = -EINVAL; @@ -4387,13 +4358,13 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) } nlh = nlmsg_hdr(skb); - err = selinux_nlmsg_lookup(isec->sclass, nlh->nlmsg_type, &perm); + err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); if (err) { if (err == -EINVAL) { audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, "SELinux: unrecognized netlink message" " type=%hu for sclass=%hu\n", - nlh->nlmsg_type, isec->sclass); + nlh->nlmsg_type, sksec->sclass); if (!selinux_enforcing || security_get_allow_unknown()) err = 0; } @@ -4404,7 +4375,7 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) goto out; } - err = socket_has_perm(current, sock, perm); + err = sock_has_perm(current, sk, perm); out: return err; } -- cgit v1.2.3-70-g09d2 From 5fb49870e6d48d81d8ca0e1ef979073dc9a820f7 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 22 Apr 2010 14:46:19 -0400 Subject: selinux: Use current_security() when possible There were a number of places using the following code pattern: struct cred *cred = current_cred(); struct task_security_struct *tsec = cred->security; ... which were simplified to the following: struct task_security_struct *tsec = current_security(); Signed-off-by: Paul Moore Acked-by: Eric Paris Signed-off-by: James Morris --- security/selinux/hooks.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e95004010c8..0f524b7d102 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -188,7 +188,7 @@ static inline u32 task_sid(const struct task_struct *task) */ static inline u32 current_sid(void) { - const struct task_security_struct *tsec = current_cred()->security; + const struct task_security_struct *tsec = current_security(); return tsec->sid; } @@ -1558,8 +1558,7 @@ static int may_create(struct inode *dir, struct dentry *dentry, u16 tclass) { - const struct cred *cred = current_cred(); - const struct task_security_struct *tsec = cred->security; + const struct task_security_struct *tsec = current_security(); struct inode_security_struct *dsec; struct superblock_security_struct *sbsec; u32 sid, newsid; @@ -2157,8 +2156,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) static int selinux_bprm_secureexec(struct linux_binprm *bprm) { - const struct cred *cred = current_cred(); - const struct task_security_struct *tsec = cred->security; + const struct task_security_struct *tsec = current_security(); u32 sid, osid; int atsecure = 0; @@ -2533,8 +2531,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, char **name, void **value, size_t *len) { - const struct cred *cred = current_cred(); - const struct task_security_struct *tsec = cred->security; + const struct task_security_struct *tsec = current_security(); struct inode_security_struct *dsec; struct superblock_security_struct *sbsec; u32 sid, newsid, clen; @@ -3669,8 +3666,7 @@ static int sock_has_perm(struct task_struct *task, struct sock *sk, u32 perms) static int selinux_socket_create(int family, int type, int protocol, int kern) { - const struct cred *cred = current_cred(); - const struct task_security_struct *tsec = cred->security; + const struct task_security_struct *tsec = current_security(); u32 newsid; u16 secclass; @@ -3685,8 +3681,7 @@ static int selinux_socket_create(int family, int type, static int selinux_socket_post_create(struct socket *sock, int family, int type, int protocol, int kern) { - const struct cred *cred = current_cred(); - const struct task_security_struct *tsec = cred->security; + const struct task_security_struct *tsec = current_security(); struct inode_security_struct *isec = SOCK_INODE(sock)->i_security; struct sk_security_struct *sksec; int err = 0; -- cgit v1.2.3-70-g09d2 From a98aa4debe2728abb3353e35fc5d110dcc0d7f0d Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 17 Jun 2010 16:52:29 +0900 Subject: TOMOYO: Merge tomoyo_path_group and tomoyo_number_group "struct tomoyo_path_group" and "struct tomoyo_number_group" are identical. Rename tomoyo_path_group/tomoyo_number_group to tomoyo_group and tomoyo_path_group_member to tomoyo_path_group and tomoyo_number_group_member to tomoyo_unmber_group. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.h | 36 +++++++++++------------------------- security/tomoyo/file.c | 4 ++-- security/tomoyo/gc.c | 41 +++++++++++++++-------------------------- security/tomoyo/number_group.c | 32 ++++++++++++++++---------------- security/tomoyo/path_group.c | 30 +++++++++++++++--------------- 5 files changed, 59 insertions(+), 84 deletions(-) diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index e0c4ae11bf6..9f289e412a6 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -291,28 +291,20 @@ struct tomoyo_name_entry { struct tomoyo_name_union { const struct tomoyo_path_info *filename; - struct tomoyo_path_group *group; + struct tomoyo_group *group; u8 is_group; }; struct tomoyo_number_union { unsigned long values[2]; - struct tomoyo_number_group *group; + struct tomoyo_group *group; u8 min_type; u8 max_type; u8 is_group; }; -/* Structure for "path_group" directive. */ -struct tomoyo_path_group { - struct list_head list; - const struct tomoyo_path_info *group_name; - struct list_head member_list; - atomic_t users; -}; - -/* Structure for "number_group" directive. */ -struct tomoyo_number_group { +/* Structure for "path_group"/"number_group" directive. */ +struct tomoyo_group { struct list_head list; const struct tomoyo_path_info *group_name; struct list_head member_list; @@ -320,13 +312,13 @@ struct tomoyo_number_group { }; /* Structure for "path_group" directive. */ -struct tomoyo_path_group_member { +struct tomoyo_path_group { struct tomoyo_acl_head head; const struct tomoyo_path_info *member_name; }; /* Structure for "number_group" directive. */ -struct tomoyo_number_group_member { +struct tomoyo_number_group { struct tomoyo_acl_head head; struct tomoyo_number_union number; }; @@ -766,11 +758,11 @@ bool tomoyo_parse_name_union(const char *filename, struct tomoyo_name_union *ptr); /* Check whether the given filename matches the given path_group. */ bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, - const struct tomoyo_path_group *group); + const struct tomoyo_group *group); /* Check whether the given value matches the given number_group. */ bool tomoyo_number_matches_group(const unsigned long min, const unsigned long max, - const struct tomoyo_number_group *group); + const struct tomoyo_group *group); /* Check whether the given filename matches the given pattern. */ bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, const struct tomoyo_path_info *pattern); @@ -858,8 +850,8 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * const u8 profile); struct tomoyo_profile *tomoyo_profile(const u8 profile); /* Allocate memory for "struct tomoyo_path_group". */ -struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name); -struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name); +struct tomoyo_group *tomoyo_get_path_group(const char *group_name); +struct tomoyo_group *tomoyo_get_number_group(const char *group_name); /* Check mode for specified functionality. */ unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, @@ -1039,13 +1031,7 @@ static inline void tomoyo_put_name(const struct tomoyo_path_info *name) } } -static inline void tomoyo_put_path_group(struct tomoyo_path_group *group) -{ - if (group) - atomic_dec(&group->users); -} - -static inline void tomoyo_put_number_group(struct tomoyo_number_group *group) +static inline void tomoyo_put_group(struct tomoyo_group *group) { if (group) atomic_dec(&group->users); diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index de87c45ba9d..6c2ba69fc89 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -90,7 +90,7 @@ void tomoyo_put_name_union(struct tomoyo_name_union *ptr) if (!ptr) return; if (ptr->is_group) - tomoyo_put_path_group(ptr->group); + tomoyo_put_group(ptr->group); else tomoyo_put_name(ptr->filename); } @@ -106,7 +106,7 @@ bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, void tomoyo_put_number_union(struct tomoyo_number_union *ptr) { if (ptr && ptr->is_group) - tomoyo_put_number_group(ptr->group); + tomoyo_put_group(ptr->group); } bool tomoyo_compare_number_union(const unsigned long value, diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index a54bd823fcd..446d59f8ae2 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -12,10 +12,9 @@ #include enum tomoyo_policy_id { + TOMOYO_ID_GROUP, TOMOYO_ID_PATH_GROUP, - TOMOYO_ID_PATH_GROUP_MEMBER, TOMOYO_ID_NUMBER_GROUP, - TOMOYO_ID_NUMBER_GROUP_MEMBER, TOMOYO_ID_DOMAIN_INITIALIZER, TOMOYO_ID_DOMAIN_KEEPER, TOMOYO_ID_AGGREGATOR, @@ -207,31 +206,24 @@ static void tomoyo_del_name(struct list_head *element) container_of(element, typeof(*ptr), list); } -static void tomoyo_del_path_group_member(struct list_head *element) +static void tomoyo_del_path_group(struct list_head *element) { - struct tomoyo_path_group_member *member = + struct tomoyo_path_group *member = container_of(element, typeof(*member), head.list); tomoyo_put_name(member->member_name); } -static void tomoyo_del_path_group(struct list_head *element) +static void tomoyo_del_group(struct list_head *element) { - struct tomoyo_path_group *group = + struct tomoyo_group *group = container_of(element, typeof(*group), list); tomoyo_put_name(group->group_name); } -static void tomoyo_del_number_group_member(struct list_head *element) -{ - struct tomoyo_number_group_member *member = - container_of(element, typeof(*member), head.list); -} - static void tomoyo_del_number_group(struct list_head *element) { - struct tomoyo_number_group *group = - container_of(element, typeof(*group), list); - tomoyo_put_name(group->group_name); + struct tomoyo_number_group *member = + container_of(element, typeof(*member), head.list); } static struct list_head *tomoyo_policy_list[TOMOYO_MAX_POLICY] = { @@ -305,28 +297,28 @@ static void tomoyo_collect_entry(void) } } { - struct tomoyo_path_group *group; + struct tomoyo_group *group; list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) { tomoyo_collect_member(&group->member_list, - TOMOYO_ID_PATH_GROUP_MEMBER); + TOMOYO_ID_PATH_GROUP); if (!list_empty(&group->member_list) || atomic_read(&group->users)) continue; - if (!tomoyo_add_to_gc(TOMOYO_ID_PATH_GROUP, + if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, &group->list)) goto unlock; } } { - struct tomoyo_number_group *group; + struct tomoyo_group *group; list_for_each_entry_rcu(group, &tomoyo_number_group_list, list) { tomoyo_collect_member(&group->member_list, - TOMOYO_ID_NUMBER_GROUP_MEMBER); + TOMOYO_ID_NUMBER_GROUP); if (!list_empty(&group->member_list) || atomic_read(&group->users)) continue; - if (!tomoyo_add_to_gc(TOMOYO_ID_NUMBER_GROUP, + if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, &group->list)) goto unlock; } @@ -377,14 +369,11 @@ static void tomoyo_kfree_entry(void) if (!tomoyo_del_domain(element)) continue; break; - case TOMOYO_ID_PATH_GROUP_MEMBER: - tomoyo_del_path_group_member(element); - break; case TOMOYO_ID_PATH_GROUP: tomoyo_del_path_group(element); break; - case TOMOYO_ID_NUMBER_GROUP_MEMBER: - tomoyo_del_number_group_member(element); + case TOMOYO_ID_GROUP: + tomoyo_del_group(element); break; case TOMOYO_ID_NUMBER_GROUP: tomoyo_del_number_group(element); diff --git a/security/tomoyo/number_group.c b/security/tomoyo/number_group.c index 8779309c5b4..eca20d6f4e8 100644 --- a/security/tomoyo/number_group.c +++ b/security/tomoyo/number_group.c @@ -11,17 +11,17 @@ LIST_HEAD(tomoyo_number_group_list); /** - * tomoyo_get_number_group - Allocate memory for "struct tomoyo_number_group". + * tomoyo_get_group - Allocate memory for "struct tomoyo_number_group". * * @group_name: The name of number group. * * Returns pointer to "struct tomoyo_number_group" on success, * NULL otherwise. */ -struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name) +struct tomoyo_group *tomoyo_get_number_group(const char *group_name) { - struct tomoyo_number_group *entry = NULL; - struct tomoyo_number_group *group = NULL; + struct tomoyo_group *entry = NULL; + struct tomoyo_group *group = NULL; const struct tomoyo_path_info *saved_group_name; int error = -ENOMEM; if (!tomoyo_correct_word(group_name)) @@ -59,12 +59,12 @@ struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name) static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { - return !memcmp(&container_of(a, struct tomoyo_number_group_member, + return !memcmp(&container_of(a, struct tomoyo_number_group, head)->number, - &container_of(b, struct tomoyo_number_group_member, + &container_of(b, struct tomoyo_number_group, head)->number, sizeof(container_of(a, - struct tomoyo_number_group_member, + struct tomoyo_number_group, head)->number)); } @@ -78,8 +78,8 @@ static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a, */ int tomoyo_write_number_group_policy(char *data, const bool is_delete) { - struct tomoyo_number_group *group; - struct tomoyo_number_group_member e = { }; + struct tomoyo_group *group; + struct tomoyo_number_group e = { }; int error; char *w[2]; if (!tomoyo_tokenize(data, w, sizeof(w))) @@ -93,7 +93,7 @@ int tomoyo_write_number_group_policy(char *data, const bool is_delete) error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, &group->member_list, tomoyo_same_number_group); - tomoyo_put_number_group(group); + tomoyo_put_group(group); return error; } @@ -111,16 +111,16 @@ bool tomoyo_read_number_group_policy(struct tomoyo_io_buffer *head) struct list_head *gpos; struct list_head *mpos; list_for_each_cookie(gpos, head->read_var1, &tomoyo_number_group_list) { - struct tomoyo_number_group *group; + struct tomoyo_group *group; const char *name; - group = list_entry(gpos, struct tomoyo_number_group, list); + group = list_entry(gpos, struct tomoyo_group, list); name = group->group_name->name; list_for_each_cookie(mpos, head->read_var2, &group->member_list) { int pos; - const struct tomoyo_number_group_member *member + const struct tomoyo_number_group *member = list_entry(mpos, - struct tomoyo_number_group_member, + struct tomoyo_number_group, head.list); if (member->head.is_deleted) continue; @@ -150,9 +150,9 @@ bool tomoyo_read_number_group_policy(struct tomoyo_io_buffer *head) */ bool tomoyo_number_matches_group(const unsigned long min, const unsigned long max, - const struct tomoyo_number_group *group) + const struct tomoyo_group *group) { - struct tomoyo_number_group_member *member; + struct tomoyo_number_group *member; bool matched = false; list_for_each_entry_rcu(member, &group->member_list, head.list) { if (member->head.is_deleted) diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c index c78c57cd94e..bce2524402f 100644 --- a/security/tomoyo/path_group.c +++ b/security/tomoyo/path_group.c @@ -10,16 +10,16 @@ LIST_HEAD(tomoyo_path_group_list); /** - * tomoyo_get_path_group - Allocate memory for "struct tomoyo_path_group". + * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group". * * @group_name: The name of pathname group. * * Returns pointer to "struct tomoyo_path_group" on success, NULL otherwise. */ -struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name) +struct tomoyo_group *tomoyo_get_path_group(const char *group_name) { - struct tomoyo_path_group *entry = NULL; - struct tomoyo_path_group *group = NULL; + struct tomoyo_group *entry = NULL; + struct tomoyo_group *group = NULL; const struct tomoyo_path_info *saved_group_name; int error = -ENOMEM; if (!tomoyo_correct_word(group_name)) @@ -57,9 +57,9 @@ struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name) static bool tomoyo_same_path_group(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { - return container_of(a, struct tomoyo_path_group_member, head) + return container_of(a, struct tomoyo_path_group, head) ->member_name == - container_of(b, struct tomoyo_path_group_member, head) + container_of(b, struct tomoyo_path_group, head) ->member_name; } @@ -73,8 +73,8 @@ static bool tomoyo_same_path_group(const struct tomoyo_acl_head *a, */ int tomoyo_write_path_group_policy(char *data, const bool is_delete) { - struct tomoyo_path_group *group; - struct tomoyo_path_group_member e = { }; + struct tomoyo_group *group; + struct tomoyo_path_group e = { }; int error = is_delete ? -ENOENT : -ENOMEM; char *w[2]; if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) @@ -90,7 +90,7 @@ int tomoyo_write_path_group_policy(char *data, const bool is_delete) tomoyo_same_path_group); out: tomoyo_put_name(e.member_name); - tomoyo_put_path_group(group); + tomoyo_put_group(group); return error; } @@ -108,13 +108,13 @@ bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head) struct list_head *gpos; struct list_head *mpos; list_for_each_cookie(gpos, head->read_var1, &tomoyo_path_group_list) { - struct tomoyo_path_group *group; - group = list_entry(gpos, struct tomoyo_path_group, list); + struct tomoyo_group *group; + group = list_entry(gpos, struct tomoyo_group, list); list_for_each_cookie(mpos, head->read_var2, &group->member_list) { - struct tomoyo_path_group_member *member; + struct tomoyo_path_group *member; member = list_entry(mpos, - struct tomoyo_path_group_member, + struct tomoyo_path_group, head.list); if (member->head.is_deleted) continue; @@ -139,9 +139,9 @@ bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head) * Caller holds tomoyo_read_lock(). */ bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, - const struct tomoyo_path_group *group) + const struct tomoyo_group *group) { - struct tomoyo_path_group_member *member; + struct tomoyo_path_group *member; bool matched = false; list_for_each_entry_rcu(member, &group->member_list, head.list) { if (member->head.is_deleted) -- cgit v1.2.3-70-g09d2 From a230f9e7121cbcbfe23bd5a630abf6b53cece555 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 17 Jun 2010 16:53:24 +0900 Subject: TOMOYO: Use array of "struct list_head". Assign list id and make the lists as array of "struct list_head". Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 42 ++------ security/tomoyo/common.h | 38 ++++--- security/tomoyo/domain.c | 219 +++++------------------------------------ security/tomoyo/file.c | 114 +++------------------ security/tomoyo/gc.c | 41 ++------ security/tomoyo/memory.c | 4 + security/tomoyo/number_group.c | 12 +-- security/tomoyo/path_group.c | 11 ++- 8 files changed, 98 insertions(+), 383 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 9eeb19ec6af..cb1aaf148ad 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -434,38 +434,6 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) head->read_eof = true; } -/* - * tomoyo_policy_manager_list is used for holding list of domainnames or - * programs which are permitted to modify configuration via - * /sys/kernel/security/tomoyo/ interface. - * - * An entry is added by - * - * # echo ' /sbin/mingetty /bin/login /bin/bash' > \ - * /sys/kernel/security/tomoyo/manager - * (if you want to specify by a domainname) - * - * or - * - * # echo '/usr/sbin/tomoyo-editpolicy' > /sys/kernel/security/tomoyo/manager - * (if you want to specify by a program's location) - * - * and is deleted by - * - * # echo 'delete /sbin/mingetty /bin/login /bin/bash' > \ - * /sys/kernel/security/tomoyo/manager - * - * or - * - * # echo 'delete /usr/sbin/tomoyo-editpolicy' > \ - * /sys/kernel/security/tomoyo/manager - * - * and all entries are retrieved by - * - * # cat /sys/kernel/security/tomoyo/manager - */ -LIST_HEAD(tomoyo_policy_manager_list); - static bool tomoyo_same_manager_entry(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { @@ -503,7 +471,7 @@ static int tomoyo_update_manager_entry(const char *manager, if (!e.manager) return -ENOMEM; error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_policy_manager_list, + &tomoyo_policy_list[TOMOYO_ID_MANAGER], tomoyo_same_manager_entry); tomoyo_put_name(e.manager); return error; @@ -545,7 +513,7 @@ static void tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) if (head->read_eof) return; list_for_each_cookie(pos, head->read_var2, - &tomoyo_policy_manager_list) { + &tomoyo_policy_list[TOMOYO_ID_MANAGER]) { struct tomoyo_policy_manager_entry *ptr; ptr = list_entry(pos, struct tomoyo_policy_manager_entry, head.list); @@ -578,7 +546,8 @@ static bool tomoyo_policy_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_manager_list, head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], + head.list) { if (!ptr->head.is_deleted && ptr->is_domain && !tomoyo_pathcmp(domainname, ptr->manager)) { found = true; @@ -590,7 +559,8 @@ static bool tomoyo_policy_manager(void) exe = tomoyo_get_exe(); if (!exe) return false; - list_for_each_entry_rcu(ptr, &tomoyo_policy_manager_list, head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], + head.list) { if (!ptr->head.is_deleted && !ptr->is_domain && !strcmp(exe, ptr->manager->name)) { found = true; diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 9f289e412a6..451dc3c1036 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -46,6 +46,30 @@ enum tomoyo_mode_index { TOMOYO_CONFIG_USE_DEFAULT = 255 }; +enum tomoyo_policy_id { + TOMOYO_ID_GROUP, + TOMOYO_ID_PATH_GROUP, + TOMOYO_ID_NUMBER_GROUP, + TOMOYO_ID_DOMAIN_INITIALIZER, + TOMOYO_ID_DOMAIN_KEEPER, + TOMOYO_ID_AGGREGATOR, + TOMOYO_ID_ALIAS, + TOMOYO_ID_GLOBALLY_READABLE, + TOMOYO_ID_PATTERN, + TOMOYO_ID_NO_REWRITE, + TOMOYO_ID_MANAGER, + TOMOYO_ID_NAME, + TOMOYO_ID_ACL, + TOMOYO_ID_DOMAIN, + TOMOYO_MAX_POLICY +}; + +enum tomoyo_group_id { + TOMOYO_PATH_GROUP, + TOMOYO_NUMBER_GROUP, + TOMOYO_MAX_GROUP +}; + /* Keywords for ACLs. */ #define TOMOYO_KEYWORD_AGGREGATOR "aggregator " #define TOMOYO_KEYWORD_ALIAS "alias " @@ -570,7 +594,7 @@ struct tomoyo_globally_readable_file_entry { /* * tomoyo_pattern_entry is a structure which is used for holding - * "tomoyo_pattern_list" entries. + * "file_pattern" entries. * It has following fields. * * (1) "head" is "struct tomoyo_acl_head". @@ -950,16 +974,8 @@ 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_path_group_list; -extern struct list_head tomoyo_number_group_list; -extern struct list_head tomoyo_domain_initializer_list; -extern struct list_head tomoyo_domain_keeper_list; -extern struct list_head tomoyo_aggregator_list; -extern struct list_head tomoyo_alias_list; -extern struct list_head tomoyo_globally_readable_list; -extern struct list_head tomoyo_pattern_list; -extern struct list_head tomoyo_no_rewrite_list; -extern struct list_head tomoyo_policy_manager_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. */ diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 1a122974240..3575b0e7c7f 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -127,46 +127,12 @@ void tomoyo_check_acl(struct tomoyo_request_info *r, r->granted = false; } -/* - * tomoyo_domain_list is used for holding list of domains. - * The ->acl_info_list of "struct tomoyo_domain_info" is used for holding - * permissions (e.g. "allow_read /lib/libc-2.5.so") given to each domain. - * - * An entry is added by - * - * # ( echo ""; echo "allow_execute /sbin/init" ) > \ - * /sys/kernel/security/tomoyo/domain_policy - * - * and is deleted by - * - * # ( echo ""; echo "delete allow_execute /sbin/init" ) > \ - * /sys/kernel/security/tomoyo/domain_policy - * - * and all entries are retrieved by - * - * # cat /sys/kernel/security/tomoyo/domain_policy - * - * A domain is added by - * - * # echo "" > /sys/kernel/security/tomoyo/domain_policy - * - * and is deleted by - * - * # echo "delete " > /sys/kernel/security/tomoyo/domain_policy - * - * and all domains are retrieved by - * - * # grep '^' /sys/kernel/security/tomoyo/domain_policy - * - * Normally, a domainname is monotonically getting longer because a domainname - * which the process will belong to if an execve() operation succeeds is - * defined as a concatenation of "current domainname" + "pathname passed to - * execve()". - * See tomoyo_domain_initializer_list and tomoyo_domain_keeper_list for - * exceptions. - */ +/* 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_get_last_name - Get last component of a domainname. * @@ -184,44 +150,6 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain) return cp0; } -/* - * tomoyo_domain_initializer_list is used for holding list of programs which - * triggers reinitialization of domainname. Normally, a domainname is - * monotonically getting longer. But sometimes, we restart daemon programs. - * It would be convenient for us that "a daemon started upon system boot" and - * "the daemon restarted from console" belong to the same domain. Thus, TOMOYO - * provides a way to shorten domainnames. - * - * An entry is added by - * - * # echo 'initialize_domain /usr/sbin/httpd' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and is deleted by - * - * # echo 'delete initialize_domain /usr/sbin/httpd' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and all entries are retrieved by - * - * # grep ^initialize_domain /sys/kernel/security/tomoyo/exception_policy - * - * In the example above, /usr/sbin/httpd will belong to - * " /usr/sbin/httpd" domain. - * - * You may specify a domainname using "from" keyword. - * "initialize_domain /usr/sbin/httpd from /etc/rc.d/init.d/httpd" - * will cause "/usr/sbin/httpd" executed from " /etc/rc.d/init.d/httpd" - * domain to belong to " /usr/sbin/httpd" domain. - * - * You may add "no_" prefix to "initialize_domain". - * "initialize_domain /usr/sbin/httpd" and - * "no_initialize_domain /usr/sbin/httpd from /etc/rc.d/init.d/httpd" - * will cause "/usr/sbin/httpd" to belong to " /usr/sbin/httpd" domain - * unless executed from " /etc/rc.d/init.d/httpd" domain. - */ -LIST_HEAD(tomoyo_domain_initializer_list); - static bool tomoyo_same_domain_initializer_entry(const struct tomoyo_acl_head * a, const struct tomoyo_acl_head * @@ -272,7 +200,8 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname, if (!e.program) goto out; error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_domain_initializer_list, + &tomoyo_policy_list + [TOMOYO_ID_DOMAIN_INITIALIZER], tomoyo_same_domain_initializer_entry); out: tomoyo_put_name(e.domainname); @@ -294,8 +223,8 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head) struct list_head *pos; bool done = true; - list_for_each_cookie(pos, head->read_var2, - &tomoyo_domain_initializer_list) { + list_for_each_cookie(pos, head->read_var2, &tomoyo_policy_list + [TOMOYO_ID_DOMAIN_INITIALIZER]) { const char *no; const char *from = ""; const char *domain = ""; @@ -366,8 +295,8 @@ static bool tomoyo_domain_initializer(const struct tomoyo_path_info * struct tomoyo_domain_initializer_entry *ptr; bool flag = false; - list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, - head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_policy_list + [TOMOYO_ID_DOMAIN_INITIALIZER], head.list) { if (ptr->head.is_deleted) continue; if (ptr->domainname) { @@ -390,46 +319,6 @@ static bool tomoyo_domain_initializer(const struct tomoyo_path_info * return flag; } -/* - * tomoyo_domain_keeper_list is used for holding list of domainnames which - * suppresses domain transition. Normally, a domainname is monotonically - * getting longer. But sometimes, we want to suppress domain transition. - * It would be convenient for us that programs executed from a login session - * belong to the same domain. Thus, TOMOYO provides a way to suppress domain - * transition. - * - * An entry is added by - * - * # echo 'keep_domain /usr/sbin/sshd /bin/bash' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and is deleted by - * - * # echo 'delete keep_domain /usr/sbin/sshd /bin/bash' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and all entries are retrieved by - * - * # grep ^keep_domain /sys/kernel/security/tomoyo/exception_policy - * - * In the example above, any process which belongs to - * " /usr/sbin/sshd /bin/bash" domain will remain in that domain, - * unless explicitly specified by "initialize_domain" or "no_keep_domain". - * - * You may specify a program using "from" keyword. - * "keep_domain /bin/pwd from /usr/sbin/sshd /bin/bash" - * will cause "/bin/pwd" executed from " /usr/sbin/sshd /bin/bash" - * domain to remain in " /usr/sbin/sshd /bin/bash" domain. - * - * You may add "no_" prefix to "keep_domain". - * "keep_domain /usr/sbin/sshd /bin/bash" and - * "no_keep_domain /usr/bin/passwd from /usr/sbin/sshd /bin/bash" will - * cause "/usr/bin/passwd" to belong to - * " /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless - * explicitly specified by "initialize_domain". - */ -LIST_HEAD(tomoyo_domain_keeper_list); - static bool tomoyo_same_domain_keeper_entry(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { @@ -478,7 +367,8 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname, if (!e.domainname) goto out; error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_domain_keeper_list, + &tomoyo_policy_list + [TOMOYO_ID_DOMAIN_KEEPER], tomoyo_same_domain_keeper_entry); out: tomoyo_put_name(e.domainname); @@ -523,7 +413,7 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head) bool done = true; list_for_each_cookie(pos, head->read_var2, - &tomoyo_domain_keeper_list) { + &tomoyo_policy_list[TOMOYO_ID_DOMAIN_KEEPER]) { struct tomoyo_domain_keeper_entry *ptr; const char *no; const char *from = ""; @@ -567,7 +457,9 @@ static bool tomoyo_domain_keeper(const struct tomoyo_path_info *domainname, struct tomoyo_domain_keeper_entry *ptr; bool flag = false; - list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, head.list) { + list_for_each_entry_rcu(ptr, + &tomoyo_policy_list[TOMOYO_ID_DOMAIN_KEEPER], + head.list) { if (ptr->head.is_deleted) continue; if (!ptr->is_last_name) { @@ -588,35 +480,6 @@ static bool tomoyo_domain_keeper(const struct tomoyo_path_info *domainname, return flag; } -/* - * tomoyo_aggregator_list is used for holding list of rewrite table for - * execve() request. Some programs provides similar functionality. This keyword - * allows users to aggregate such programs. - * - * Entries are added by - * - * # echo 'aggregator /usr/bin/vi /./editor' > \ - * /sys/kernel/security/tomoyo/exception_policy - * # echo 'aggregator /usr/bin/emacs /./editor' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and are deleted by - * - * # echo 'delete aggregator /usr/bin/vi /./editor' > \ - * /sys/kernel/security/tomoyo/exception_policy - * # echo 'delete aggregator /usr/bin/emacs /./editor' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and all entries are retrieved by - * - * # grep ^aggregator /sys/kernel/security/tomoyo/exception_policy - * - * In the example above, if /usr/bin/vi or /usr/bin/emacs are executed, - * permission is checked for /./editor and domainname which the current process - * will belong to after execve() succeeds is calculated using /./editor . - */ -LIST_HEAD(tomoyo_aggregator_list); - static bool tomoyo_same_aggregator_entry(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { @@ -655,7 +518,7 @@ static int tomoyo_update_aggregator_entry(const char *original_name, e.aggregated_name->is_patterned) /* No patterns allowed. */ goto out; error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_aggregator_list, + &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR], tomoyo_same_aggregator_entry); out: tomoyo_put_name(e.original_name); @@ -677,7 +540,8 @@ bool tomoyo_read_aggregator_policy(struct tomoyo_io_buffer *head) struct list_head *pos; bool done = true; - list_for_each_cookie(pos, head->read_var2, &tomoyo_aggregator_list) { + list_for_each_cookie(pos, head->read_var2, + &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR]) { struct tomoyo_aggregator_entry *ptr; ptr = list_entry(pos, struct tomoyo_aggregator_entry, @@ -713,38 +577,6 @@ int tomoyo_write_aggregator_policy(char *data, const bool is_delete) return tomoyo_update_aggregator_entry(data, cp, is_delete); } -/* - * tomoyo_alias_list is used for holding list of symlink's pathnames which are - * allowed to be passed to an execve() request. Normally, the domainname which - * the current process will belong to after execve() succeeds is calculated - * using dereferenced pathnames. But some programs behave differently depending - * on the name passed to argv[0]. For busybox, calculating domainname using - * dereferenced pathnames will cause all programs in the busybox to belong to - * the same domain. Thus, TOMOYO provides a way to allow use of symlink's - * pathname for checking execve()'s permission and calculating domainname which - * the current process will belong to after execve() succeeds. - * - * An entry is added by - * - * # echo 'alias /bin/busybox /bin/cat' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and is deleted by - * - * # echo 'delete alias /bin/busybox /bin/cat' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and all entries are retrieved by - * - * # grep ^alias /sys/kernel/security/tomoyo/exception_policy - * - * In the example above, if /bin/cat is a symlink to /bin/busybox and execution - * of /bin/cat is requested, permission is checked for /bin/cat rather than - * /bin/busybox and domainname which the current process will belong to after - * execve() succeeds is calculated using /bin/cat rather than /bin/busybox . - */ -LIST_HEAD(tomoyo_alias_list); - static bool tomoyo_same_alias_entry(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { @@ -783,7 +615,7 @@ static int tomoyo_update_alias_entry(const char *original_name, e.original_name->is_patterned || e.aliased_name->is_patterned) goto out; /* No patterns allowed. */ error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_alias_list, + &tomoyo_policy_list[TOMOYO_ID_ALIAS], tomoyo_same_alias_entry); out: tomoyo_put_name(e.original_name); @@ -805,7 +637,8 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head) struct list_head *pos; bool done = true; - list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) { + list_for_each_cookie(pos, head->read_var2, + &tomoyo_policy_list[TOMOYO_ID_ALIAS]) { struct tomoyo_alias_entry *ptr; ptr = list_entry(pos, struct tomoyo_alias_entry, head.list); @@ -946,7 +779,9 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) if (tomoyo_pathcmp(&rn, &sn)) { struct tomoyo_alias_entry *ptr; /* Is this program allowed to be called via symbolic links? */ - list_for_each_entry_rcu(ptr, &tomoyo_alias_list, head.list) { + list_for_each_entry_rcu(ptr, + &tomoyo_policy_list[TOMOYO_ID_ALIAS], + head.list) { if (ptr->head.is_deleted || tomoyo_pathcmp(&rn, ptr->original_name) || tomoyo_pathcmp(&sn, ptr->aliased_name)) @@ -962,8 +797,8 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) /* Check 'aggregator' directive. */ { struct tomoyo_aggregator_entry *ptr; - list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, - head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_policy_list + [TOMOYO_ID_AGGREGATOR], head.list) { if (ptr->head.is_deleted || !tomoyo_path_matches_pattern(&rn, ptr->original_name)) diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 6c2ba69fc89..df3b203d7d4 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -265,33 +265,6 @@ static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r) tomoyo_file_pattern(filename), buffer); } -/* - * tomoyo_globally_readable_list is used for holding list of pathnames which - * are by default allowed to be open()ed for reading by any process. - * - * An entry is added by - * - * # echo 'allow_read /lib/libc-2.5.so' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and is deleted by - * - * # echo 'delete allow_read /lib/libc-2.5.so' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and all entries are retrieved by - * - * # grep ^allow_read /sys/kernel/security/tomoyo/exception_policy - * - * In the example above, any process is allowed to - * open("/lib/libc-2.5.so", O_RDONLY). - * One exception is, if the domain which current process belongs to is marked - * as "ignore_global_allow_read", current process can't do so unless explicitly - * given "allow_read /lib/libc-2.5.so" to the domain which current process - * belongs to. - */ -LIST_HEAD(tomoyo_globally_readable_list); - static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { @@ -323,7 +296,8 @@ static int tomoyo_update_globally_readable_entry(const char *filename, if (!e.filename) return -ENOMEM; error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_globally_readable_list, + &tomoyo_policy_list + [TOMOYO_ID_GLOBALLY_READABLE], tomoyo_same_globally_readable); tomoyo_put_name(e.filename); return error; @@ -344,8 +318,8 @@ static bool tomoyo_globally_readable_file(const struct tomoyo_path_info * struct tomoyo_globally_readable_file_entry *ptr; bool found = false; - list_for_each_entry_rcu(ptr, &tomoyo_globally_readable_list, - head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_policy_list + [TOMOYO_ID_GLOBALLY_READABLE], head.list) { if (!ptr->head.is_deleted && tomoyo_path_matches_pattern(filename, ptr->filename)) { found = true; @@ -385,7 +359,7 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head) bool done = true; list_for_each_cookie(pos, head->read_var2, - &tomoyo_globally_readable_list) { + &tomoyo_policy_list[TOMOYO_ID_GLOBALLY_READABLE]) { struct tomoyo_globally_readable_file_entry *ptr; ptr = list_entry(pos, struct tomoyo_globally_readable_file_entry, @@ -400,37 +374,6 @@ bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head) return done; } -/* tomoyo_pattern_list is used for holding list of pathnames which are used for - * converting pathnames to pathname patterns during learning mode. - * - * An entry is added by - * - * # echo 'file_pattern /proc/\$/mounts' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and is deleted by - * - * # echo 'delete file_pattern /proc/\$/mounts' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and all entries are retrieved by - * - * # grep ^file_pattern /sys/kernel/security/tomoyo/exception_policy - * - * In the example above, if a process which belongs to a domain which is in - * learning mode requested open("/proc/1/mounts", O_RDONLY), - * "allow_read /proc/\$/mounts" is automatically added to the domain which that - * process belongs to. - * - * It is not a desirable behavior that we have to use /proc/\$/ instead of - * /proc/self/ when current process needs to access only current process's - * information. As of now, LSM version of TOMOYO is using __d_path() for - * calculating pathname. Non LSM version of TOMOYO is using its own function - * which pretends as if /proc/self/ is not a symlink; so that we can forbid - * current process from accessing other process's information. - */ -LIST_HEAD(tomoyo_pattern_list); - static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { @@ -460,7 +403,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, if (!e.pattern) return -ENOMEM; error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_pattern_list, + &tomoyo_policy_list[TOMOYO_ID_PATTERN], tomoyo_same_pattern); tomoyo_put_name(e.pattern); return error; @@ -480,7 +423,8 @@ const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename) struct tomoyo_pattern_entry *ptr; const struct tomoyo_path_info *pattern = NULL; - list_for_each_entry_rcu(ptr, &tomoyo_pattern_list, head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_PATTERN], + head.list) { if (ptr->head.is_deleted) continue; if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) @@ -527,7 +471,8 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head) struct list_head *pos; bool done = true; - list_for_each_cookie(pos, head->read_var2, &tomoyo_pattern_list) { + list_for_each_cookie(pos, head->read_var2, + &tomoyo_policy_list[TOMOYO_ID_PATTERN]) { struct tomoyo_pattern_entry *ptr; ptr = list_entry(pos, struct tomoyo_pattern_entry, head.list); if (ptr->head.is_deleted) @@ -540,37 +485,6 @@ bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head) return done; } -/* - * tomoyo_no_rewrite_list is used for holding list of pathnames which are by - * default forbidden to modify already written content of a file. - * - * An entry is added by - * - * # echo 'deny_rewrite /var/log/messages' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and is deleted by - * - * # echo 'delete deny_rewrite /var/log/messages' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and all entries are retrieved by - * - * # grep ^deny_rewrite /sys/kernel/security/tomoyo/exception_policy - * - * In the example above, if a process requested to rewrite /var/log/messages , - * the process can't rewrite unless the domain which that process belongs to - * has "allow_rewrite /var/log/messages" entry. - * - * It is not a desirable behavior that we have to add "\040(deleted)" suffix - * when we want to allow rewriting already unlink()ed file. As of now, - * LSM version of TOMOYO is using __d_path() for calculating pathname. - * Non LSM version of TOMOYO is using its own function which doesn't append - * " (deleted)" suffix if the file is already unlink()ed; so that we don't - * need to worry whether the file is already unlink()ed or not. - */ -LIST_HEAD(tomoyo_no_rewrite_list); - static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { @@ -601,7 +515,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern, if (!e.pattern) return -ENOMEM; error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_no_rewrite_list, + &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE], tomoyo_same_no_rewrite); tomoyo_put_name(e.pattern); return error; @@ -622,7 +536,8 @@ static bool tomoyo_no_rewrite_file(const struct tomoyo_path_info *filename) struct tomoyo_no_rewrite_entry *ptr; bool found = false; - list_for_each_entry_rcu(ptr, &tomoyo_no_rewrite_list, head.list) { + list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE], + head.list) { if (ptr->head.is_deleted) continue; if (!tomoyo_path_matches_pattern(filename, ptr->pattern)) @@ -662,7 +577,8 @@ bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) struct list_head *pos; bool done = true; - list_for_each_cookie(pos, head->read_var2, &tomoyo_no_rewrite_list) { + list_for_each_cookie(pos, head->read_var2, + &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE]) { struct tomoyo_no_rewrite_entry *ptr; ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, head.list); diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 446d59f8ae2..414e18bd93c 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -11,24 +11,6 @@ #include #include -enum tomoyo_policy_id { - TOMOYO_ID_GROUP, - TOMOYO_ID_PATH_GROUP, - TOMOYO_ID_NUMBER_GROUP, - TOMOYO_ID_DOMAIN_INITIALIZER, - TOMOYO_ID_DOMAIN_KEEPER, - TOMOYO_ID_AGGREGATOR, - TOMOYO_ID_ALIAS, - TOMOYO_ID_GLOBALLY_READABLE, - TOMOYO_ID_PATTERN, - TOMOYO_ID_NO_REWRITE, - TOMOYO_ID_MANAGER, - TOMOYO_ID_NAME, - TOMOYO_ID_ACL, - TOMOYO_ID_DOMAIN, - TOMOYO_MAX_POLICY -}; - struct tomoyo_gc_entry { struct list_head list; int type; @@ -226,17 +208,6 @@ static void tomoyo_del_number_group(struct list_head *element) container_of(element, typeof(*member), head.list); } -static struct list_head *tomoyo_policy_list[TOMOYO_MAX_POLICY] = { - [TOMOYO_ID_GLOBALLY_READABLE] = &tomoyo_globally_readable_list, - [TOMOYO_ID_PATTERN] = &tomoyo_pattern_list, - [TOMOYO_ID_NO_REWRITE] = &tomoyo_no_rewrite_list, - [TOMOYO_ID_DOMAIN_INITIALIZER] = &tomoyo_domain_initializer_list, - [TOMOYO_ID_DOMAIN_KEEPER] = &tomoyo_domain_keeper_list, - [TOMOYO_ID_AGGREGATOR] = &tomoyo_aggregator_list, - [TOMOYO_ID_ALIAS] = &tomoyo_alias_list, - [TOMOYO_ID_MANAGER] = &tomoyo_policy_manager_list, -}; - static bool tomoyo_collect_member(struct list_head *member_list, int id) { struct tomoyo_acl_head *member; @@ -267,9 +238,8 @@ static void tomoyo_collect_entry(void) if (mutex_lock_interruptible(&tomoyo_policy_lock)) return; for (i = 0; i < TOMOYO_MAX_POLICY; i++) { - if (tomoyo_policy_list[i]) - if (!tomoyo_collect_member(tomoyo_policy_list[i], i)) - goto unlock; + if (!tomoyo_collect_member(&tomoyo_policy_list[i], i)) + goto unlock; } { struct tomoyo_domain_info *domain; @@ -298,7 +268,9 @@ static void tomoyo_collect_entry(void) } { struct tomoyo_group *group; - list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) { + list_for_each_entry_rcu(group, + &tomoyo_group_list[TOMOYO_PATH_GROUP], + list) { tomoyo_collect_member(&group->member_list, TOMOYO_ID_PATH_GROUP); if (!list_empty(&group->member_list) || @@ -311,7 +283,8 @@ static void tomoyo_collect_entry(void) } { struct tomoyo_group *group; - list_for_each_entry_rcu(group, &tomoyo_number_group_list, + list_for_each_entry_rcu(group, + &tomoyo_group_list[TOMOYO_NUMBER_GROUP], list) { tomoyo_collect_member(&group->member_list, TOMOYO_ID_NUMBER_GROUP); diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 8de5333109a..249835abdf4 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -153,6 +153,10 @@ 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]); INIT_LIST_HEAD(&tomoyo_kernel_domain.acl_info_list); diff --git a/security/tomoyo/number_group.c b/security/tomoyo/number_group.c index eca20d6f4e8..99694153b94 100644 --- a/security/tomoyo/number_group.c +++ b/security/tomoyo/number_group.c @@ -7,9 +7,6 @@ #include #include "common.h" -/* The list for "struct tomoyo_number_group". */ -LIST_HEAD(tomoyo_number_group_list); - /** * tomoyo_get_group - Allocate memory for "struct tomoyo_number_group". * @@ -32,7 +29,8 @@ struct tomoyo_group *tomoyo_get_number_group(const char *group_name) entry = kzalloc(sizeof(*entry), GFP_NOFS); if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(group, &tomoyo_number_group_list, list) { + list_for_each_entry_rcu(group, &tomoyo_group_list[TOMOYO_NUMBER_GROUP], + list) { if (saved_group_name != group->group_name) continue; atomic_inc(&group->users); @@ -44,7 +42,8 @@ struct tomoyo_group *tomoyo_get_number_group(const char *group_name) entry->group_name = saved_group_name; saved_group_name = NULL; atomic_set(&entry->users, 1); - list_add_tail_rcu(&entry->list, &tomoyo_number_group_list); + list_add_tail_rcu(&entry->list, + &tomoyo_group_list[TOMOYO_NUMBER_GROUP]); group = entry; entry = NULL; error = 0; @@ -110,7 +109,8 @@ bool tomoyo_read_number_group_policy(struct tomoyo_io_buffer *head) { struct list_head *gpos; struct list_head *mpos; - list_for_each_cookie(gpos, head->read_var1, &tomoyo_number_group_list) { + list_for_each_cookie(gpos, head->read_var1, + &tomoyo_group_list[TOMOYO_NUMBER_GROUP]) { struct tomoyo_group *group; const char *name; group = list_entry(gpos, struct tomoyo_group, list); diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c index bce2524402f..44e8a5b1ca6 100644 --- a/security/tomoyo/path_group.c +++ b/security/tomoyo/path_group.c @@ -6,8 +6,6 @@ #include #include "common.h" -/* The list for "struct tomoyo_path_group". */ -LIST_HEAD(tomoyo_path_group_list); /** * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group". @@ -30,7 +28,8 @@ struct tomoyo_group *tomoyo_get_path_group(const char *group_name) entry = kzalloc(sizeof(*entry), GFP_NOFS); if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; - list_for_each_entry_rcu(group, &tomoyo_path_group_list, list) { + list_for_each_entry_rcu(group, &tomoyo_group_list[TOMOYO_PATH_GROUP], + list) { if (saved_group_name != group->group_name) continue; atomic_inc(&group->users); @@ -42,7 +41,8 @@ struct tomoyo_group *tomoyo_get_path_group(const char *group_name) entry->group_name = saved_group_name; saved_group_name = NULL; atomic_set(&entry->users, 1); - list_add_tail_rcu(&entry->list, &tomoyo_path_group_list); + list_add_tail_rcu(&entry->list, + &tomoyo_group_list[TOMOYO_PATH_GROUP]); group = entry; entry = NULL; error = 0; @@ -107,7 +107,8 @@ bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head) { struct list_head *gpos; struct list_head *mpos; - list_for_each_cookie(gpos, head->read_var1, &tomoyo_path_group_list) { + list_for_each_cookie(gpos, head->read_var1, + &tomoyo_group_list[TOMOYO_PATH_GROUP]) { struct tomoyo_group *group; group = list_entry(gpos, struct tomoyo_group, list); list_for_each_cookie(mpos, head->read_var2, -- cgit v1.2.3-70-g09d2 From 31845e8c6d3f4f26702e567c667277f9fd1f73a3 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 17 Jun 2010 16:54:33 +0900 Subject: TOMOYO: Aggregate reader functions. Now lists are accessible via array index. Aggregate reader functions using index. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 259 +++++++++++++++++++++++++++++++---------- security/tomoyo/common.h | 22 ---- security/tomoyo/domain.c | 140 ---------------------- security/tomoyo/file.c | 87 -------------- security/tomoyo/number_group.c | 41 ------- security/tomoyo/path_group.c | 35 ------ 6 files changed, 200 insertions(+), 384 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index cb1aaf148ad..044115d4903 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1192,75 +1192,216 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) return -EINVAL; } +static void tomoyo_print_number(char *buffer, int buffer_len, + const struct tomoyo_number_union *ptr) +{ + int i; + unsigned long min = ptr->values[0]; + const unsigned long max = ptr->values[1]; + u8 min_type = ptr->min_type; + const u8 max_type = ptr->max_type; + memset(buffer, 0, buffer_len); + buffer_len -= 2; + for (i = 0; i < 2; i++) { + int len; + switch (min_type) { + case TOMOYO_VALUE_TYPE_HEXADECIMAL: + snprintf(buffer, buffer_len, "0x%lX", min); + break; + case TOMOYO_VALUE_TYPE_OCTAL: + snprintf(buffer, buffer_len, "0%lo", min); + break; + default: + snprintf(buffer, buffer_len, "%lu", min); + break; + } + if (min == max && min_type == max_type) + break; + len = strlen(buffer); + buffer[len++] = '-'; + buffer += len; + buffer_len -= len; + min_type = max_type; + min = max; + } +} + +static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { + [TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP, + [TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP +}; + /** - * tomoyo_read_exception_policy - Read exception policy. + * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group" list. * * @head: Pointer to "struct tomoyo_io_buffer". + * @idx: Index number. + * + * Returns true on success, false otherwise. * * Caller holds tomoyo_read_lock(). */ -static void tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) +static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) { - if (!head->read_eof) { - switch (head->read_step) { - case 0: - head->read_var2 = NULL; - head->read_step = 1; - case 1: - if (!tomoyo_read_domain_keeper_policy(head)) - break; - head->read_var2 = NULL; - head->read_step = 2; - case 2: - if (!tomoyo_read_globally_readable_policy(head)) - break; - head->read_var2 = NULL; - head->read_step = 3; - case 3: - head->read_var2 = NULL; - head->read_step = 4; - case 4: - if (!tomoyo_read_domain_initializer_policy(head)) - break; - head->read_var2 = NULL; - head->read_step = 5; - case 5: - if (!tomoyo_read_alias_policy(head)) - break; - head->read_var2 = NULL; - head->read_step = 6; - case 6: - if (!tomoyo_read_aggregator_policy(head)) - break; - head->read_var2 = NULL; - head->read_step = 7; - case 7: - if (!tomoyo_read_file_pattern(head)) - break; - head->read_var2 = NULL; - head->read_step = 8; - case 8: - if (!tomoyo_read_no_rewrite_policy(head)) - break; - head->read_var2 = NULL; - head->read_step = 9; - case 9: - if (!tomoyo_read_path_group_policy(head)) - break; - head->read_var1 = NULL; - head->read_var2 = NULL; - head->read_step = 10; - case 10: - if (!tomoyo_read_number_group_policy(head)) - break; - head->read_var1 = NULL; - head->read_var2 = NULL; - head->read_step = 11; - case 11: - head->read_eof = true; + struct list_head *gpos; + struct list_head *mpos; + const char *w[3] = { "", "", "" }; + w[0] = tomoyo_group_name[idx]; + list_for_each_cookie(gpos, head->read_var1, &tomoyo_group_list[idx]) { + struct tomoyo_group *group = + list_entry(gpos, struct tomoyo_group, list); + w[1] = group->group_name->name; + list_for_each_cookie(mpos, head->read_var2, + &group->member_list) { + char buffer[128]; + struct tomoyo_acl_head *ptr = + list_entry(mpos, struct tomoyo_acl_head, list); + if (ptr->is_deleted) + continue; + if (idx == TOMOYO_PATH_GROUP) { + w[2] = container_of(ptr, + struct tomoyo_path_group, + head)->member_name->name; + } else if (idx == TOMOYO_NUMBER_GROUP) { + tomoyo_print_number(buffer, sizeof(buffer), + &container_of + (ptr, struct + tomoyo_number_group, + head)->number); + w[2] = buffer; + } + if (!tomoyo_io_printf(head, "%s%s %s\n", w[0], w[1], + w[2])) + return false; + } + } + return true; +} + +/** + * tomoyo_read_policy - Read "struct tomoyo_..._entry" list. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @idx: Index number. + * + * Returns true on success, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) +{ + struct list_head *pos; + list_for_each_cookie(pos, head->read_var2, &tomoyo_policy_list[idx]) { + const char *w[4] = { "", "", "", "" }; + struct tomoyo_acl_head *acl = container_of(pos, typeof(*acl), + list); + if (acl->is_deleted) + continue; + switch (idx) { + case TOMOYO_ID_DOMAIN_KEEPER: + { + struct tomoyo_domain_keeper_entry *ptr = + container_of(acl, typeof(*ptr), head); + w[0] = ptr->is_not ? + TOMOYO_KEYWORD_NO_KEEP_DOMAIN : + TOMOYO_KEYWORD_KEEP_DOMAIN; + if (ptr->program) { + w[1] = ptr->program->name; + w[2] = " from "; + } + w[3] = ptr->domainname->name; + } + break; + case TOMOYO_ID_DOMAIN_INITIALIZER: + { + struct tomoyo_domain_initializer_entry *ptr = + container_of(acl, typeof(*ptr), head); + w[0] = ptr->is_not ? + TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN : + TOMOYO_KEYWORD_INITIALIZE_DOMAIN; + w[1] = ptr->program->name; + if (ptr->domainname) { + w[2] = " from "; + w[3] = ptr->domainname->name; + } + } break; + case TOMOYO_ID_GLOBALLY_READABLE: + { + struct tomoyo_globally_readable_file_entry *ptr + = container_of(acl, typeof(*ptr), head); + w[0] = TOMOYO_KEYWORD_ALLOW_READ; + w[1] = ptr->filename->name; + } + break; + case TOMOYO_ID_ALIAS: + { + struct tomoyo_alias_entry *ptr = + container_of(acl, typeof(*ptr), head); + w[0] = TOMOYO_KEYWORD_ALIAS; + w[1] = ptr->original_name->name; + w[2] = " "; + w[3] = ptr->aliased_name->name; + } + break; + case TOMOYO_ID_AGGREGATOR: + { + struct tomoyo_aggregator_entry *ptr = + container_of(acl, typeof(*ptr), head); + w[0] = TOMOYO_KEYWORD_AGGREGATOR; + w[1] = ptr->original_name->name; + w[2] = " "; + w[3] = ptr->aggregated_name->name; + } + break; + case TOMOYO_ID_PATTERN: + { + struct tomoyo_pattern_entry *ptr = + container_of(acl, typeof(*ptr), head); + w[0] = TOMOYO_KEYWORD_FILE_PATTERN; + w[1] = ptr->pattern->name; + } + break; + case TOMOYO_ID_NO_REWRITE: + { + struct tomoyo_no_rewrite_entry *ptr = + container_of(acl, typeof(*ptr), head); + w[0] = TOMOYO_KEYWORD_DENY_REWRITE; + w[1] = ptr->pattern->name; + } + break; + default: + continue; } + if (!tomoyo_io_printf(head, "%s%s%s%s\n", w[0], w[1], w[2], + w[3])) + return false; } + return true; +} + +/** + * tomoyo_read_exception_policy - Read exception policy. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Caller holds tomoyo_read_lock(). + */ +static void tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) +{ + if (head->read_eof) + return; + while (head->read_step < TOMOYO_MAX_POLICY && + tomoyo_read_policy(head, head->read_step)) + head->read_step++; + if (head->read_step < TOMOYO_MAX_POLICY) + return; + while (head->read_step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP && + tomoyo_read_group(head, head->read_step - TOMOYO_MAX_POLICY)) + head->read_step++; + if (head->read_step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP) + return; + head->read_eof = true; } /** diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 451dc3c1036..21eb1e7885b 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -794,28 +794,6 @@ bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, bool tomoyo_print_number_union(struct tomoyo_io_buffer *head, const struct tomoyo_number_union *ptr); bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num); - -/* Read "aggregator" entry in exception policy. */ -bool tomoyo_read_aggregator_policy(struct tomoyo_io_buffer *head); -/* Read "alias" entry in exception policy. */ -bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head); -/* - * Read "initialize_domain" and "no_initialize_domain" entry - * in exception policy. - */ -bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head); -/* Read "keep_domain" and "no_keep_domain" entry in exception policy. */ -bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head); -/* Read "file_pattern" entry in exception policy. */ -bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head); -/* Read "path_group" entry in exception policy. */ -bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head); -/* Read "number_group" entry in exception policy. */ -bool tomoyo_read_number_group_policy(struct tomoyo_io_buffer *head); -/* Read "allow_read" entry in exception policy. */ -bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head); -/* Read "deny_rewrite" entry in exception policy. */ -bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head); /* Tokenize a line. */ bool tomoyo_tokenize(char *buffer, char *w[], size_t size); /* Write domain policy violation warning message to console? */ diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 3575b0e7c7f..038071a8a3d 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -209,45 +209,6 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname, return error; } -/** - * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head) -{ - struct list_head *pos; - bool done = true; - - list_for_each_cookie(pos, head->read_var2, &tomoyo_policy_list - [TOMOYO_ID_DOMAIN_INITIALIZER]) { - const char *no; - const char *from = ""; - const char *domain = ""; - struct tomoyo_domain_initializer_entry *ptr; - ptr = list_entry(pos, struct tomoyo_domain_initializer_entry, - head.list); - if (ptr->head.is_deleted) - continue; - no = ptr->is_not ? "no_" : ""; - if (ptr->domainname) { - from = " from "; - domain = ptr->domainname->name; - } - done = tomoyo_io_printf(head, - "%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN - "%s%s%s\n", no, ptr->program->name, - from, domain); - if (!done) - break; - } - return done; -} - /** * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list. * @@ -398,46 +359,6 @@ int tomoyo_write_domain_keeper_policy(char *data, const bool is_not, return tomoyo_update_domain_keeper_entry(data, NULL, is_not, is_delete); } -/** - * tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head) -{ - struct list_head *pos; - bool done = true; - - list_for_each_cookie(pos, head->read_var2, - &tomoyo_policy_list[TOMOYO_ID_DOMAIN_KEEPER]) { - struct tomoyo_domain_keeper_entry *ptr; - const char *no; - const char *from = ""; - const char *program = ""; - - ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, - head.list); - if (ptr->head.is_deleted) - continue; - no = ptr->is_not ? "no_" : ""; - if (ptr->program) { - from = " from "; - program = ptr->program->name; - } - done = tomoyo_io_printf(head, - "%s" TOMOYO_KEYWORD_KEEP_DOMAIN - "%s%s%s\n", no, program, from, - ptr->domainname->name); - if (!done) - break; - } - return done; -} - /** * tomoyo_domain_keeper - Check whether the given program causes domain transition suppression. * @@ -526,37 +447,6 @@ static int tomoyo_update_aggregator_entry(const char *original_name, return error; } -/** - * tomoyo_read_aggregator_policy - Read "struct tomoyo_aggregator_entry" list. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -bool tomoyo_read_aggregator_policy(struct tomoyo_io_buffer *head) -{ - struct list_head *pos; - bool done = true; - - list_for_each_cookie(pos, head->read_var2, - &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR]) { - struct tomoyo_aggregator_entry *ptr; - - ptr = list_entry(pos, struct tomoyo_aggregator_entry, - head.list); - if (ptr->head.is_deleted) - continue; - done = tomoyo_io_printf(head, TOMOYO_KEYWORD_AGGREGATOR - "%s %s\n", ptr->original_name->name, - ptr->aggregated_name->name); - if (!done) - break; - } - return done; -} - /** * tomoyo_write_aggregator_policy - Write "struct tomoyo_aggregator_entry" list. * @@ -623,36 +513,6 @@ static int tomoyo_update_alias_entry(const char *original_name, return error; } -/** - * tomoyo_read_alias_policy - Read "struct tomoyo_alias_entry" list. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head) -{ - struct list_head *pos; - bool done = true; - - list_for_each_cookie(pos, head->read_var2, - &tomoyo_policy_list[TOMOYO_ID_ALIAS]) { - struct tomoyo_alias_entry *ptr; - - ptr = list_entry(pos, struct tomoyo_alias_entry, head.list); - if (ptr->head.is_deleted) - continue; - done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n", - ptr->original_name->name, - ptr->aliased_name->name); - if (!done) - break; - } - return done; -} - /** * tomoyo_write_alias_policy - Write "struct tomoyo_alias_entry" list. * diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index df3b203d7d4..e7687ebdc5f 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -344,36 +344,6 @@ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete) return tomoyo_update_globally_readable_entry(data, is_delete); } -/** - * tomoyo_read_globally_readable_policy - Read "struct tomoyo_globally_readable_file_entry" list. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -bool tomoyo_read_globally_readable_policy(struct tomoyo_io_buffer *head) -{ - struct list_head *pos; - bool done = true; - - list_for_each_cookie(pos, head->read_var2, - &tomoyo_policy_list[TOMOYO_ID_GLOBALLY_READABLE]) { - struct tomoyo_globally_readable_file_entry *ptr; - ptr = list_entry(pos, - struct tomoyo_globally_readable_file_entry, - head.list); - if (ptr->head.is_deleted) - continue; - done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_READ "%s\n", - ptr->filename->name); - if (!done) - break; - } - return done; -} - static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { @@ -457,34 +427,6 @@ int tomoyo_write_pattern_policy(char *data, const bool is_delete) return tomoyo_update_file_pattern_entry(data, is_delete); } -/** - * tomoyo_read_file_pattern - Read "struct tomoyo_pattern_entry" list. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -bool tomoyo_read_file_pattern(struct tomoyo_io_buffer *head) -{ - struct list_head *pos; - bool done = true; - - list_for_each_cookie(pos, head->read_var2, - &tomoyo_policy_list[TOMOYO_ID_PATTERN]) { - struct tomoyo_pattern_entry *ptr; - ptr = list_entry(pos, struct tomoyo_pattern_entry, head.list); - if (ptr->head.is_deleted) - continue; - done = tomoyo_io_printf(head, TOMOYO_KEYWORD_FILE_PATTERN - "%s\n", ptr->pattern->name); - if (!done) - break; - } - return done; -} - static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { @@ -563,35 +505,6 @@ int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete) return tomoyo_update_no_rewrite_entry(data, is_delete); } -/** - * tomoyo_read_no_rewrite_policy - Read "struct tomoyo_no_rewrite_entry" list. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -bool tomoyo_read_no_rewrite_policy(struct tomoyo_io_buffer *head) -{ - struct list_head *pos; - bool done = true; - - list_for_each_cookie(pos, head->read_var2, - &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE]) { - struct tomoyo_no_rewrite_entry *ptr; - ptr = list_entry(pos, struct tomoyo_no_rewrite_entry, - head.list); - if (ptr->head.is_deleted) - continue; - done = tomoyo_io_printf(head, TOMOYO_KEYWORD_DENY_REWRITE - "%s\n", ptr->pattern->name); - if (!done) - break; - } - return done; -} - static bool tomoyo_check_path_acl(const struct tomoyo_request_info *r, const struct tomoyo_acl_info *ptr) { diff --git a/security/tomoyo/number_group.c b/security/tomoyo/number_group.c index 99694153b94..5e75f5314bd 100644 --- a/security/tomoyo/number_group.c +++ b/security/tomoyo/number_group.c @@ -96,47 +96,6 @@ int tomoyo_write_number_group_policy(char *data, const bool is_delete) return error; } -/** - * tomoyo_read_number_group_policy - Read "struct tomoyo_number_group" list. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -bool tomoyo_read_number_group_policy(struct tomoyo_io_buffer *head) -{ - struct list_head *gpos; - struct list_head *mpos; - list_for_each_cookie(gpos, head->read_var1, - &tomoyo_group_list[TOMOYO_NUMBER_GROUP]) { - struct tomoyo_group *group; - const char *name; - group = list_entry(gpos, struct tomoyo_group, list); - name = group->group_name->name; - list_for_each_cookie(mpos, head->read_var2, - &group->member_list) { - int pos; - const struct tomoyo_number_group *member - = list_entry(mpos, - struct tomoyo_number_group, - head.list); - if (member->head.is_deleted) - continue; - pos = head->read_avail; - if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_NUMBER_GROUP - "%s", name) || - !tomoyo_print_number_union(head, &member->number) || - !tomoyo_io_printf(head, "\n")) { - head->read_avail = pos; - return false; - } - } - } - return true; -} - /** * tomoyo_number_matches_group - Check whether the given number matches members of the given number group. * diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c index 44e8a5b1ca6..2f9f9240bf5 100644 --- a/security/tomoyo/path_group.c +++ b/security/tomoyo/path_group.c @@ -94,41 +94,6 @@ int tomoyo_write_path_group_policy(char *data, const bool is_delete) return error; } -/** - * tomoyo_read_path_group_policy - Read "struct tomoyo_path_group" list. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns true on success, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -bool tomoyo_read_path_group_policy(struct tomoyo_io_buffer *head) -{ - struct list_head *gpos; - struct list_head *mpos; - list_for_each_cookie(gpos, head->read_var1, - &tomoyo_group_list[TOMOYO_PATH_GROUP]) { - struct tomoyo_group *group; - group = list_entry(gpos, struct tomoyo_group, list); - list_for_each_cookie(mpos, head->read_var2, - &group->member_list) { - struct tomoyo_path_group *member; - member = list_entry(mpos, - struct tomoyo_path_group, - head.list); - if (member->head.is_deleted) - continue; - if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_PATH_GROUP - "%s %s\n", - group->group_name->name, - member->member_name->name)) - return false; - } - } - return true; -} - /** * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group. * -- cgit v1.2.3-70-g09d2 From 7c2ea22e3c5463627ca98924cd65cb9e480dc29c Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 17 Jun 2010 16:55:58 +0900 Subject: TOMOYO: Merge path_group and number_group. Use common code for "path_group" and "number_group". Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/Makefile | 2 +- security/tomoyo/common.c | 4 +- security/tomoyo/common.h | 13 ++--- security/tomoyo/gc.c | 36 +++++------- security/tomoyo/group.c | 130 +++++++++++++++++++++++++++++++++++++++++ security/tomoyo/memory.c | 44 ++++++++++++++ security/tomoyo/number_group.c | 126 --------------------------------------- security/tomoyo/path_group.c | 122 -------------------------------------- security/tomoyo/util.c | 4 +- 9 files changed, 199 insertions(+), 282 deletions(-) create mode 100644 security/tomoyo/group.c delete mode 100644 security/tomoyo/number_group.c delete mode 100644 security/tomoyo/path_group.c diff --git a/security/tomoyo/Makefile b/security/tomoyo/Makefile index 3aa6f076948..91640e96bd0 100644 --- a/security/tomoyo/Makefile +++ b/security/tomoyo/Makefile @@ -1 +1 @@ -obj-y = common.o domain.o file.o gc.o load_policy.o memory.o mount.o number_group.o path_group.o realpath.o securityfs_if.o tomoyo.o util.o +obj-y = common.o domain.o file.o gc.o group.o load_policy.o memory.o mount.o realpath.o securityfs_if.o tomoyo.o util.o diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 044115d4903..183fe651340 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1186,9 +1186,9 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE)) return tomoyo_write_no_rewrite_policy(data, is_delete); if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_PATH_GROUP)) - return tomoyo_write_path_group_policy(data, is_delete); + return tomoyo_write_group(data, is_delete, TOMOYO_PATH_GROUP); if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NUMBER_GROUP)) - return tomoyo_write_number_group_policy(data, is_delete); + return tomoyo_write_group(data, is_delete, TOMOYO_NUMBER_GROUP); return -EINVAL; } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 21eb1e7885b..ec3ed488ee3 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -838,12 +838,10 @@ int tomoyo_write_mount_policy(char *data, struct tomoyo_domain_info *domain, int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete); /* Create "file_pattern" entry in exception policy. */ int tomoyo_write_pattern_policy(char *data, const bool is_delete); -/* Create "path_group" entry in exception policy. */ -int tomoyo_write_path_group_policy(char *data, const bool is_delete); +/* Create "path_group"/"number_group" entry in exception policy. */ +int tomoyo_write_group(char *data, const bool is_delete, const u8 type); int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); -/* Create "number_group" entry in exception policy. */ -int tomoyo_write_number_group_policy(char *data, const bool is_delete); /* Find a domain by the given name. */ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); /* Find or create a domain by the given name. */ @@ -851,9 +849,10 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * domainname, const u8 profile); struct tomoyo_profile *tomoyo_profile(const u8 profile); -/* Allocate memory for "struct tomoyo_path_group". */ -struct tomoyo_group *tomoyo_get_path_group(const char *group_name); -struct tomoyo_group *tomoyo_get_number_group(const char *group_name); +/* + * Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group". + */ +struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 type); /* Check mode for specified functionality. */ unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain, diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 414e18bd93c..cf62a4ee79c 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -266,33 +266,25 @@ static void tomoyo_collect_entry(void) goto unlock; } } - { + for (i = 0; i < TOMOYO_MAX_GROUP; i++) { + struct list_head *list = &tomoyo_group_list[i]; + int id; struct tomoyo_group *group; - list_for_each_entry_rcu(group, - &tomoyo_group_list[TOMOYO_PATH_GROUP], - list) { - tomoyo_collect_member(&group->member_list, - TOMOYO_ID_PATH_GROUP); - if (!list_empty(&group->member_list) || - atomic_read(&group->users)) - continue; - if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, - &group->list)) - goto unlock; + switch (i) { + case 0: + id = TOMOYO_ID_PATH_GROUP; + break; + default: + id = TOMOYO_ID_NUMBER_GROUP; + break; } - } - { - struct tomoyo_group *group; - list_for_each_entry_rcu(group, - &tomoyo_group_list[TOMOYO_NUMBER_GROUP], - list) { - tomoyo_collect_member(&group->member_list, - TOMOYO_ID_NUMBER_GROUP); + list_for_each_entry(group, list, list) { + if (!tomoyo_collect_member(&group->member_list, id)) + goto unlock; if (!list_empty(&group->member_list) || atomic_read(&group->users)) continue; - if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, - &group->list)) + if (!tomoyo_add_to_gc(TOMOYO_ID_GROUP, &group->list)) goto unlock; } } diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c new file mode 100644 index 00000000000..3f0a2abf65c --- /dev/null +++ b/security/tomoyo/group.c @@ -0,0 +1,130 @@ +/* + * security/tomoyo/group.c + * + * Copyright (C) 2005-2010 NTT DATA CORPORATION + */ + +#include +#include "common.h" + +static bool tomoyo_same_path_group(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + return container_of(a, struct tomoyo_path_group, head)->member_name == + container_of(b, struct tomoyo_path_group, head)->member_name; +} + +static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + return !memcmp(&container_of(a, struct tomoyo_number_group, head) + ->number, + &container_of(b, struct tomoyo_number_group, head) + ->number, + sizeof(container_of(a, struct tomoyo_number_group, head) + ->number)); +} + +/** + * tomoyo_write_group - Write "struct tomoyo_path_group"/"struct tomoyo_number_group" list. + * + * @data: String to parse. + * @is_delete: True if it is a delete request. + * @type: Type of this group. + * + * Returns 0 on success, negative value otherwise. + */ +int tomoyo_write_group(char *data, const bool is_delete, const u8 type) +{ + struct tomoyo_group *group; + struct list_head *member; + char *w[2]; + int error = -EINVAL; + if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) + return -EINVAL; + group = tomoyo_get_group(w[0], type); + if (!group) + return -ENOMEM; + member = &group->member_list; + if (type == TOMOYO_PATH_GROUP) { + struct tomoyo_path_group e = { }; + e.member_name = tomoyo_get_name(w[1]); + if (!e.member_name) { + error = -ENOMEM; + goto out; + } + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + member, tomoyo_same_path_group); + tomoyo_put_name(e.member_name); + } else if (type == TOMOYO_NUMBER_GROUP) { + struct tomoyo_number_group e = { }; + if (w[1][0] == '@' + || !tomoyo_parse_number_union(w[1], &e.number) + || e.number.values[0] > e.number.values[1]) + goto out; + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + member, tomoyo_same_number_group); + /* + * tomoyo_put_number_union() is not needed because + * w[1][0] != '@'. + */ + } + out: + tomoyo_put_group(group); + return error; +} + +/** + * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group. + * + * @pathname: The name of pathname. + * @group: Pointer to "struct tomoyo_path_group". + * + * Returns true if @pathname matches pathnames in @group, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, + const struct tomoyo_group *group) +{ + struct tomoyo_path_group *member; + bool matched = false; + list_for_each_entry_rcu(member, &group->member_list, head.list) { + if (member->head.is_deleted) + continue; + if (!tomoyo_path_matches_pattern(pathname, member->member_name)) + continue; + matched = true; + break; + } + return matched; +} + +/** + * tomoyo_number_matches_group - Check whether the given number matches members of the given number group. + * + * @min: Min number. + * @max: Max number. + * @group: Pointer to "struct tomoyo_number_group". + * + * Returns true if @min and @max partially overlaps @group, false otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +bool tomoyo_number_matches_group(const unsigned long min, + const unsigned long max, + const struct tomoyo_group *group) +{ + struct tomoyo_number_group *member; + bool matched = false; + list_for_each_entry_rcu(member, &group->member_list, head.list) { + if (member->head.is_deleted) + continue; + if (min > member->number.values[1] || + max < member->number.values[0]) + continue; + matched = true; + break; + } + return matched; +} diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 249835abdf4..a1d75df93e1 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -89,6 +89,50 @@ void tomoyo_memory_free(void *ptr) kfree(ptr); } +/** + * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group". + * + * @group_name: The name of address group. + * @idx: Index number. + * + * Returns pointer to "struct tomoyo_group" on success, NULL otherwise. + */ +struct tomoyo_group *tomoyo_get_group(const char *group_name, const u8 idx) +{ + struct tomoyo_group e = { }; + struct tomoyo_group *group = NULL; + bool found = false; + if (!tomoyo_correct_word(group_name) || idx >= TOMOYO_MAX_GROUP) + return NULL; + e.group_name = tomoyo_get_name(group_name); + if (!e.group_name) + return NULL; + if (mutex_lock_interruptible(&tomoyo_policy_lock)) + goto out; + list_for_each_entry(group, &tomoyo_group_list[idx], list) { + if (e.group_name != group->group_name) + continue; + atomic_inc(&group->users); + found = true; + break; + } + if (!found) { + struct tomoyo_group *entry = tomoyo_commit_ok(&e, sizeof(e)); + if (entry) { + INIT_LIST_HEAD(&entry->member_list); + atomic_set(&entry->users, 1); + list_add_tail_rcu(&entry->list, + &tomoyo_group_list[idx]); + group = entry; + found = true; + } + } + mutex_unlock(&tomoyo_policy_lock); + out: + tomoyo_put_name(e.group_name); + return found ? group : NULL; +} + /* * tomoyo_name_list is used for holding string data used by TOMOYO. * Since same string data is likely used for multiple times (e.g. diff --git a/security/tomoyo/number_group.c b/security/tomoyo/number_group.c deleted file mode 100644 index 5e75f5314bd..00000000000 --- a/security/tomoyo/number_group.c +++ /dev/null @@ -1,126 +0,0 @@ -/* - * security/tomoyo/number_group.c - * - * Copyright (C) 2005-2009 NTT DATA CORPORATION - */ - -#include -#include "common.h" - -/** - * tomoyo_get_group - Allocate memory for "struct tomoyo_number_group". - * - * @group_name: The name of number group. - * - * Returns pointer to "struct tomoyo_number_group" on success, - * NULL otherwise. - */ -struct tomoyo_group *tomoyo_get_number_group(const char *group_name) -{ - struct tomoyo_group *entry = NULL; - struct tomoyo_group *group = NULL; - const struct tomoyo_path_info *saved_group_name; - int error = -ENOMEM; - if (!tomoyo_correct_word(group_name)) - return NULL; - saved_group_name = tomoyo_get_name(group_name); - if (!saved_group_name) - return NULL; - entry = kzalloc(sizeof(*entry), GFP_NOFS); - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(group, &tomoyo_group_list[TOMOYO_NUMBER_GROUP], - list) { - if (saved_group_name != group->group_name) - continue; - atomic_inc(&group->users); - error = 0; - break; - } - if (error && tomoyo_memory_ok(entry)) { - INIT_LIST_HEAD(&entry->member_list); - entry->group_name = saved_group_name; - saved_group_name = NULL; - atomic_set(&entry->users, 1); - list_add_tail_rcu(&entry->list, - &tomoyo_group_list[TOMOYO_NUMBER_GROUP]); - group = entry; - entry = NULL; - error = 0; - } - mutex_unlock(&tomoyo_policy_lock); - out: - tomoyo_put_name(saved_group_name); - kfree(entry); - return !error ? group : NULL; -} - -static bool tomoyo_same_number_group(const struct tomoyo_acl_head *a, - const struct tomoyo_acl_head *b) -{ - return !memcmp(&container_of(a, struct tomoyo_number_group, - head)->number, - &container_of(b, struct tomoyo_number_group, - head)->number, - sizeof(container_of(a, - struct tomoyo_number_group, - head)->number)); -} - -/** - * tomoyo_write_number_group_policy - Write "struct tomoyo_number_group" list. - * - * @data: String to parse. - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, nagative value otherwise. - */ -int tomoyo_write_number_group_policy(char *data, const bool is_delete) -{ - struct tomoyo_group *group; - struct tomoyo_number_group e = { }; - int error; - char *w[2]; - if (!tomoyo_tokenize(data, w, sizeof(w))) - return -EINVAL; - if (w[1][0] == '@' || !tomoyo_parse_number_union(w[1], &e.number) || - e.number.values[0] > e.number.values[1]) - return -EINVAL; - group = tomoyo_get_number_group(w[0]); - if (!group) - return -ENOMEM; - error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &group->member_list, - tomoyo_same_number_group); - tomoyo_put_group(group); - return error; -} - -/** - * tomoyo_number_matches_group - Check whether the given number matches members of the given number group. - * - * @min: Min number. - * @max: Max number. - * @group: Pointer to "struct tomoyo_number_group". - * - * Returns true if @min and @max partially overlaps @group, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -bool tomoyo_number_matches_group(const unsigned long min, - const unsigned long max, - const struct tomoyo_group *group) -{ - struct tomoyo_number_group *member; - bool matched = false; - list_for_each_entry_rcu(member, &group->member_list, head.list) { - if (member->head.is_deleted) - continue; - if (min > member->number.values[1] || - max < member->number.values[0]) - continue; - matched = true; - break; - } - return matched; -} diff --git a/security/tomoyo/path_group.c b/security/tomoyo/path_group.c deleted file mode 100644 index 2f9f9240bf5..00000000000 --- a/security/tomoyo/path_group.c +++ /dev/null @@ -1,122 +0,0 @@ -/* - * security/tomoyo/path_group.c - * - * Copyright (C) 2005-2009 NTT DATA CORPORATION - */ - -#include -#include "common.h" - -/** - * tomoyo_get_group - Allocate memory for "struct tomoyo_path_group". - * - * @group_name: The name of pathname group. - * - * Returns pointer to "struct tomoyo_path_group" on success, NULL otherwise. - */ -struct tomoyo_group *tomoyo_get_path_group(const char *group_name) -{ - struct tomoyo_group *entry = NULL; - struct tomoyo_group *group = NULL; - const struct tomoyo_path_info *saved_group_name; - int error = -ENOMEM; - if (!tomoyo_correct_word(group_name)) - return NULL; - saved_group_name = tomoyo_get_name(group_name); - if (!saved_group_name) - return NULL; - entry = kzalloc(sizeof(*entry), GFP_NOFS); - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(group, &tomoyo_group_list[TOMOYO_PATH_GROUP], - list) { - if (saved_group_name != group->group_name) - continue; - atomic_inc(&group->users); - error = 0; - break; - } - if (error && tomoyo_memory_ok(entry)) { - INIT_LIST_HEAD(&entry->member_list); - entry->group_name = saved_group_name; - saved_group_name = NULL; - atomic_set(&entry->users, 1); - list_add_tail_rcu(&entry->list, - &tomoyo_group_list[TOMOYO_PATH_GROUP]); - group = entry; - entry = NULL; - error = 0; - } - mutex_unlock(&tomoyo_policy_lock); - out: - tomoyo_put_name(saved_group_name); - kfree(entry); - return !error ? group : NULL; -} - -static bool tomoyo_same_path_group(const struct tomoyo_acl_head *a, - const struct tomoyo_acl_head *b) -{ - return container_of(a, struct tomoyo_path_group, head) - ->member_name == - container_of(b, struct tomoyo_path_group, head) - ->member_name; -} - -/** - * tomoyo_write_path_group_policy - Write "struct tomoyo_path_group" list. - * - * @data: String to parse. - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, nagative value otherwise. - */ -int tomoyo_write_path_group_policy(char *data, const bool is_delete) -{ - struct tomoyo_group *group; - struct tomoyo_path_group e = { }; - int error = is_delete ? -ENOENT : -ENOMEM; - char *w[2]; - if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0]) - return -EINVAL; - group = tomoyo_get_path_group(w[0]); - if (!group) - return -ENOMEM; - e.member_name = tomoyo_get_name(w[1]); - if (!e.member_name) - goto out; - error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &group->member_list, - tomoyo_same_path_group); - out: - tomoyo_put_name(e.member_name); - tomoyo_put_group(group); - return error; -} - -/** - * tomoyo_path_matches_group - Check whether the given pathname matches members of the given pathname group. - * - * @pathname: The name of pathname. - * @group: Pointer to "struct tomoyo_path_group". - * - * Returns true if @pathname matches pathnames in @group, false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, - const struct tomoyo_group *group) -{ - struct tomoyo_path_group *member; - bool matched = false; - list_for_each_entry_rcu(member, &group->member_list, head.list) { - if (member->head.is_deleted) - continue; - if (!tomoyo_path_matches_pattern(pathname, - member->member_name)) - continue; - matched = true; - break; - } - return matched; -} diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 945eeefbbdf..20abba22af4 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -92,7 +92,7 @@ bool tomoyo_parse_name_union(const char *filename, if (!tomoyo_correct_word(filename)) return false; if (filename[0] == '@') { - ptr->group = tomoyo_get_path_group(filename + 1); + ptr->group = tomoyo_get_group(filename + 1, TOMOYO_PATH_GROUP); ptr->is_group = true; return ptr->group != NULL; } @@ -117,7 +117,7 @@ bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num) if (data[0] == '@') { if (!tomoyo_correct_word(data)) return false; - num->group = tomoyo_get_number_group(data + 1); + num->group = tomoyo_get_group(data + 1, TOMOYO_NUMBER_GROUP); num->is_group = true; return num->group != NULL; } -- cgit v1.2.3-70-g09d2 From 0617c7ff34dc9b1d641640c3953274bb2dbe21a6 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 21 Jun 2010 09:58:53 +0900 Subject: TOMOYO: Remove alias keyword. Some programs behave differently depending on argv[0] passed to execve(). TOMOYO has "alias" keyword in order to allow administrators to define different domains if requested pathname passed to execve() is a symlink. But "alias" keyword is incomplete because this keyword assumes that requested pathname and argv[0] are identical. Thus, remove "alias" keyword (by this patch) and add syntax for checking argv[0] (by future patches). Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 12 ------ security/tomoyo/common.h | 30 +++----------- security/tomoyo/domain.c | 100 ++------------------------------------------- security/tomoyo/gc.c | 11 ----- security/tomoyo/realpath.c | 19 --------- 5 files changed, 8 insertions(+), 164 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 183fe651340..0e6b1b598b8 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1177,8 +1177,6 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) is_delete); if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_AGGREGATOR)) return tomoyo_write_aggregator_policy(data, is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALIAS)) - return tomoyo_write_alias_policy(data, is_delete); if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_READ)) return tomoyo_write_globally_readable_policy(data, is_delete); if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_FILE_PATTERN)) @@ -1334,16 +1332,6 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) w[1] = ptr->filename->name; } break; - case TOMOYO_ID_ALIAS: - { - struct tomoyo_alias_entry *ptr = - container_of(acl, typeof(*ptr), head); - w[0] = TOMOYO_KEYWORD_ALIAS; - w[1] = ptr->original_name->name; - w[2] = " "; - w[3] = ptr->aliased_name->name; - } - break; case TOMOYO_ID_AGGREGATOR: { struct tomoyo_aggregator_entry *ptr = diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index ec3ed488ee3..12b0c5c46c8 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -53,7 +53,6 @@ enum tomoyo_policy_id { TOMOYO_ID_DOMAIN_INITIALIZER, TOMOYO_ID_DOMAIN_KEEPER, TOMOYO_ID_AGGREGATOR, - TOMOYO_ID_ALIAS, TOMOYO_ID_GLOBALLY_READABLE, TOMOYO_ID_PATTERN, TOMOYO_ID_NO_REWRITE, @@ -72,7 +71,6 @@ enum tomoyo_group_id { /* Keywords for ACLs. */ #define TOMOYO_KEYWORD_AGGREGATOR "aggregator " -#define TOMOYO_KEYWORD_ALIAS "alias " #define TOMOYO_KEYWORD_ALLOW_MOUNT "allow_mount " #define TOMOYO_KEYWORD_ALLOW_READ "allow_read " #define TOMOYO_KEYWORD_DELETE "delete " @@ -682,20 +680,6 @@ struct tomoyo_aggregator_entry { const struct tomoyo_path_info *aggregated_name; }; -/* - * tomoyo_alias_entry is a structure which is used for holding "alias" entries. - * It has following fields. - * - * (1) "head" is "struct tomoyo_acl_head". - * (2) "original_name" which is a dereferenced pathname. - * (3) "aliased_name" which is a symlink's pathname. - */ -struct tomoyo_alias_entry { - struct tomoyo_acl_head head; - const struct tomoyo_path_info *original_name; - const struct tomoyo_path_info *aliased_name; -}; - /* * tomoyo_policy_manager_entry is a structure which is used for holding list of * domainnames or programs which are permitted to modify configuration via @@ -809,8 +793,6 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, unsigned long flags, void *data_page); /* Create "aggregator" entry in exception policy. */ int tomoyo_write_aggregator_policy(char *data, const bool is_delete); -/* Create "alias" entry in exception policy. */ -int tomoyo_write_alias_policy(char *data, const bool is_delete); /* * Create "initialize_domain" and "no_initialize_domain" entry * in exception policy. @@ -868,16 +850,14 @@ void tomoyo_put_number_union(struct tomoyo_number_union *ptr); char *tomoyo_encode(const char *str); /* - * Returns realpath(3) of the given pathname but ignores chroot'ed root. - * These functions use kzalloc(), so the caller must call kfree() - * if these functions didn't return NULL. + * Returns realpath(3) of the given pathname except that + * ignores chroot'ed root and does not follow the final symlink. */ -char *tomoyo_realpath(const char *pathname); +char *tomoyo_realpath_nofollow(const char *pathname); /* - * Same with tomoyo_realpath() except that it doesn't follow the final symlink. + * Returns realpath(3) of the given pathname except that + * ignores chroot'ed root and the pathname is already solved. */ -char *tomoyo_realpath_nofollow(const char *pathname); -/* Same with tomoyo_realpath() except that the pathname is already solved. */ char *tomoyo_realpath_from_path(struct path *path); /* Get patterned pathname. */ const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename); diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 038071a8a3d..273e670acf0 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -467,72 +467,6 @@ int tomoyo_write_aggregator_policy(char *data, const bool is_delete) return tomoyo_update_aggregator_entry(data, cp, is_delete); } -static bool tomoyo_same_alias_entry(const struct tomoyo_acl_head *a, - const struct tomoyo_acl_head *b) -{ - const struct tomoyo_alias_entry *p1 = container_of(a, typeof(*p1), - head); - const struct tomoyo_alias_entry *p2 = container_of(b, typeof(*p2), - head); - return p1->original_name == p2->original_name && - p1->aliased_name == p2->aliased_name; -} - -/** - * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list. - * - * @original_name: The original program's real name. - * @aliased_name: The symbolic program's symbolic link's name. - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_update_alias_entry(const char *original_name, - const char *aliased_name, - const bool is_delete) -{ - struct tomoyo_alias_entry e = { }; - int error = is_delete ? -ENOENT : -ENOMEM; - - if (!tomoyo_correct_path(original_name) || - !tomoyo_correct_path(aliased_name)) - return -EINVAL; - e.original_name = tomoyo_get_name(original_name); - e.aliased_name = tomoyo_get_name(aliased_name); - if (!e.original_name || !e.aliased_name || - e.original_name->is_patterned || e.aliased_name->is_patterned) - goto out; /* No patterns allowed. */ - error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_policy_list[TOMOYO_ID_ALIAS], - tomoyo_same_alias_entry); - out: - tomoyo_put_name(e.original_name); - tomoyo_put_name(e.aliased_name); - return error; -} - -/** - * tomoyo_write_alias_policy - Write "struct tomoyo_alias_entry" list. - * - * @data: String to parse. - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -int tomoyo_write_alias_policy(char *data, const bool is_delete) -{ - char *cp = strchr(data, ' '); - - if (!cp) - return -EINVAL; - *cp++ = '\0'; - return tomoyo_update_alias_entry(data, cp, is_delete); -} - /** * tomoyo_find_or_assign_new_domain - Create a domain. * @@ -606,7 +540,6 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) int retval = -ENOMEM; bool need_kfree = false; struct tomoyo_path_info rn = { }; /* real name */ - struct tomoyo_path_info sn = { }; /* symlink name */ struct tomoyo_path_info ln; /* last name */ ln.name = tomoyo_get_last_name(old_domain); @@ -621,39 +554,14 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) kfree(rn.name); need_kfree = false; } - /* Get tomoyo_realpath of program. */ + /* Get symlink's pathname of program. */ retval = -ENOENT; - rn.name = tomoyo_realpath(original_name); + rn.name = tomoyo_realpath_nofollow(original_name); if (!rn.name) goto out; tomoyo_fill_path_info(&rn); need_kfree = true; - /* Get tomoyo_realpath of symbolic link. */ - sn.name = tomoyo_realpath_nofollow(original_name); - if (!sn.name) - goto out; - tomoyo_fill_path_info(&sn); - - /* Check 'alias' directive. */ - if (tomoyo_pathcmp(&rn, &sn)) { - struct tomoyo_alias_entry *ptr; - /* Is this program allowed to be called via symbolic links? */ - list_for_each_entry_rcu(ptr, - &tomoyo_policy_list[TOMOYO_ID_ALIAS], - head.list) { - if (ptr->head.is_deleted || - tomoyo_pathcmp(&rn, ptr->original_name) || - tomoyo_pathcmp(&sn, ptr->aliased_name)) - continue; - kfree(rn.name); - need_kfree = false; - /* This is OK because it is read only. */ - rn = *ptr->aliased_name; - break; - } - } - /* Check 'aggregator' directive. */ { struct tomoyo_aggregator_entry *ptr; @@ -663,8 +571,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) !tomoyo_path_matches_pattern(&rn, ptr->original_name)) continue; - if (need_kfree) - kfree(rn.name); + kfree(rn.name); need_kfree = false; /* This is OK because it is read only. */ rn = *ptr->aggregated_name; @@ -729,7 +636,6 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) bprm->cred->security = domain; if (need_kfree) kfree(rn.name); - kfree(sn.name); kfree(tmp); return retval; } diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index cf62a4ee79c..4d4ba84f874 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -77,14 +77,6 @@ static void tomoyo_del_aggregator(struct list_head *element) tomoyo_put_name(ptr->aggregated_name); } -static void tomoyo_del_alias(struct list_head *element) -{ - struct tomoyo_alias_entry *ptr = - container_of(element, typeof(*ptr), head.list); - tomoyo_put_name(ptr->original_name); - tomoyo_put_name(ptr->aliased_name); -} - static void tomoyo_del_manager(struct list_head *element) { struct tomoyo_policy_manager_entry *ptr = @@ -309,9 +301,6 @@ static void tomoyo_kfree_entry(void) case TOMOYO_ID_AGGREGATOR: tomoyo_del_aggregator(element); break; - case TOMOYO_ID_ALIAS: - tomoyo_del_alias(element); - break; case TOMOYO_ID_GLOBALLY_READABLE: tomoyo_del_allow_read(element); break; diff --git a/security/tomoyo/realpath.c b/security/tomoyo/realpath.c index 153fa23a05c..ed8ccd68010 100644 --- a/security/tomoyo/realpath.c +++ b/security/tomoyo/realpath.c @@ -161,25 +161,6 @@ char *tomoyo_realpath_from_path(struct path *path) return name; } -/** - * tomoyo_realpath - Get realpath of a pathname. - * - * @pathname: The pathname to solve. - * - * Returns the realpath of @pathname on success, NULL otherwise. - */ -char *tomoyo_realpath(const char *pathname) -{ - struct path path; - - if (pathname && kern_path(pathname, LOOKUP_FOLLOW, &path) == 0) { - char *buf = tomoyo_realpath_from_path(&path); - path_put(&path); - return buf; - } - return NULL; -} - /** * tomoyo_realpath_nofollow - Get realpath of a pathname. * -- cgit v1.2.3-70-g09d2 From 5448ec4f5062ef75ce74f8d7784d4cea9c46ad00 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 21 Jun 2010 11:14:39 +0900 Subject: TOMOYO: Use common code for domain transition control. Use common code for "initialize_domain"/"no_initialize_domain"/"keep_domain"/ "no_keep_domain" keywords. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 55 ++++----- security/tomoyo/common.h | 64 ++++------ security/tomoyo/domain.c | 316 ++++++++++++++++------------------------------- security/tomoyo/gc.c | 19 +-- security/tomoyo/memory.c | 8 +- security/tomoyo/util.c | 2 +- 6 files changed, 159 insertions(+), 305 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 0e6b1b598b8..6568ef18112 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1150,6 +1150,15 @@ 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] + = TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN, + [TOMOYO_TRANSITION_CONTROL_INITIALIZE] + = TOMOYO_KEYWORD_INITIALIZE_DOMAIN, + [TOMOYO_TRANSITION_CONTROL_NO_KEEP] = TOMOYO_KEYWORD_NO_KEEP_DOMAIN, + [TOMOYO_TRANSITION_CONTROL_KEEP] = TOMOYO_KEYWORD_KEEP_DOMAIN +}; + /** * tomoyo_write_exception_policy - Write exception policy. * @@ -1163,18 +1172,13 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) { char *data = head->write_buf; bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); + u8 i; - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_KEEP_DOMAIN)) - return tomoyo_write_domain_keeper_policy(data, false, - is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_KEEP_DOMAIN)) - return tomoyo_write_domain_keeper_policy(data, true, is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_INITIALIZE_DOMAIN)) - return tomoyo_write_domain_initializer_policy(data, false, - is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN)) - return tomoyo_write_domain_initializer_policy(data, true, - is_delete); + for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++) { + if (tomoyo_str_starts(&data, tomoyo_transition_type[i])) + return tomoyo_write_transition_control(data, is_delete, + i); + } if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_AGGREGATOR)) return tomoyo_write_aggregator_policy(data, is_delete); if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_READ)) @@ -1296,32 +1300,17 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) if (acl->is_deleted) continue; switch (idx) { - case TOMOYO_ID_DOMAIN_KEEPER: + case TOMOYO_ID_TRANSITION_CONTROL: { - struct tomoyo_domain_keeper_entry *ptr = + struct tomoyo_transition_control *ptr = container_of(acl, typeof(*ptr), head); - w[0] = ptr->is_not ? - TOMOYO_KEYWORD_NO_KEEP_DOMAIN : - TOMOYO_KEYWORD_KEEP_DOMAIN; - if (ptr->program) { + w[0] = tomoyo_transition_type[ptr->type]; + if (ptr->program) w[1] = ptr->program->name; - w[2] = " from "; - } - w[3] = ptr->domainname->name; - } - break; - case TOMOYO_ID_DOMAIN_INITIALIZER: - { - struct tomoyo_domain_initializer_entry *ptr = - container_of(acl, typeof(*ptr), head); - w[0] = ptr->is_not ? - TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN : - TOMOYO_KEYWORD_INITIALIZE_DOMAIN; - w[1] = ptr->program->name; - if (ptr->domainname) { - w[2] = " from "; + if (ptr->domainname) w[3] = ptr->domainname->name; - } + if (w[1][0] && w[3][0]) + w[2] = " from "; } break; case TOMOYO_ID_GLOBALLY_READABLE: diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 12b0c5c46c8..1277724edae 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -50,8 +50,7 @@ enum tomoyo_policy_id { TOMOYO_ID_GROUP, TOMOYO_ID_PATH_GROUP, TOMOYO_ID_NUMBER_GROUP, - TOMOYO_ID_DOMAIN_INITIALIZER, - TOMOYO_ID_DOMAIN_KEEPER, + TOMOYO_ID_TRANSITION_CONTROL, TOMOYO_ID_AGGREGATOR, TOMOYO_ID_GLOBALLY_READABLE, TOMOYO_ID_PATTERN, @@ -97,6 +96,15 @@ enum tomoyo_group_id { #define TOMOYO_VALUE_TYPE_OCTAL 2 #define TOMOYO_VALUE_TYPE_HEXADECIMAL 3 +enum tomoyo_transition_type { + /* Do not change this order, */ + TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE, + TOMOYO_TRANSITION_CONTROL_INITIALIZE, + TOMOYO_TRANSITION_CONTROL_NO_KEEP, + TOMOYO_TRANSITION_CONTROL_KEEP, + TOMOYO_MAX_TRANSITION_TYPE +}; + /* Index numbers for Access Controls. */ enum tomoyo_acl_entry_type_index { TOMOYO_TYPE_PATH_ACL, @@ -619,50 +627,26 @@ struct tomoyo_no_rewrite_entry { }; /* - * tomoyo_domain_initializer_entry is a structure which is used for holding - * "initialize_domain" and "no_initialize_domain" entries. + * tomoyo_transition_control is a structure which is used for holding + * "initialize_domain"/"no_initialize_domain"/"keep_domain"/"no_keep_domain" + * entries. * It has following fields. * * (1) "head" is "struct tomoyo_acl_head". - * (2) "is_not" is a bool which is true if "no_initialize_domain", false - * otherwise. - * (3) "is_last_name" is a bool which is true if "domainname" is "the last - * component of a domainname", false otherwise. - * (4) "domainname" which is "a domainname" or "the last component of a - * domainname". This field is NULL if "from" clause is not specified. - * (5) "program" which is a program's pathname. - */ -struct tomoyo_domain_initializer_entry { - struct tomoyo_acl_head head; - bool is_not; /* True if this entry is "no_initialize_domain". */ - /* True if the domainname is tomoyo_get_last_name(). */ - bool is_last_name; - const struct tomoyo_path_info *domainname; /* This may be NULL */ - const struct tomoyo_path_info *program; -}; - -/* - * tomoyo_domain_keeper_entry is a structure which is used for holding - * "keep_domain" and "no_keep_domain" entries. - * It has following fields. - * - * (1) "head" is "struct tomoyo_acl_head". - * (2) "is_not" is a bool which is true if "no_initialize_domain", false - * otherwise. + * (2) "type" is type of this entry. * (3) "is_last_name" is a bool which is true if "domainname" is "the last * component of a domainname", false otherwise. * (4) "domainname" which is "a domainname" or "the last component of a * domainname". * (5) "program" which is a program's pathname. - * This field is NULL if "from" clause is not specified. */ -struct tomoyo_domain_keeper_entry { +struct tomoyo_transition_control { struct tomoyo_acl_head head; - bool is_not; /* True if this entry is "no_keep_domain". */ + u8 type; /* One of values in "enum tomoyo_transition_type". */ /* True if the domainname is tomoyo_get_last_name(). */ bool is_last_name; - const struct tomoyo_path_info *domainname; - const struct tomoyo_path_info *program; /* This may be NULL */ + const struct tomoyo_path_info *domainname; /* Maybe NULL */ + const struct tomoyo_path_info *program; /* Maybe NULL */ }; /* @@ -793,15 +777,8 @@ int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, unsigned long flags, void *data_page); /* Create "aggregator" entry in exception policy. */ int tomoyo_write_aggregator_policy(char *data, const bool is_delete); -/* - * Create "initialize_domain" and "no_initialize_domain" entry - * in exception policy. - */ -int tomoyo_write_domain_initializer_policy(char *data, const bool is_not, - const bool is_delete); -/* Create "keep_domain" and "no_keep_domain" entry in exception policy. */ -int tomoyo_write_domain_keeper_policy(char *data, const bool is_not, - const bool is_delete); +int tomoyo_write_transition_control(char *data, const bool is_delete, + const u8 type); /* * Create "allow_read/write", "allow_execute", "allow_read", "allow_write", * "allow_create", "allow_unlink", "allow_mkdir", "allow_rmdir", @@ -922,6 +899,7 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, void tomoyo_check_acl(struct tomoyo_request_info *r, bool (*check_entry) (const struct tomoyo_request_info *, const struct tomoyo_acl_info *)); +const char *tomoyo_last_word(const char *name); /********** External variable definitions. **********/ diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 273e670acf0..05450b17c57 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -150,59 +150,60 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain) return cp0; } -static bool tomoyo_same_domain_initializer_entry(const struct tomoyo_acl_head * +static bool tomoyo_same_transition_control_entry(const struct tomoyo_acl_head * a, const struct tomoyo_acl_head * b) { - const struct tomoyo_domain_initializer_entry *p1 = - container_of(a, typeof(*p1), head); - const struct tomoyo_domain_initializer_entry *p2 = - container_of(b, typeof(*p2), head); - return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name + const struct tomoyo_transition_control *p1 = container_of(a, + typeof(*p1), + head); + const struct tomoyo_transition_control *p2 = container_of(b, + typeof(*p2), + head); + return p1->type == p2->type && p1->is_last_name == p2->is_last_name && p1->domainname == p2->domainname && p1->program == p2->program; } /** - * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list. + * tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list. * - * @domainname: The name of domain. May be NULL. - * @program: The name of program. - * @is_not: True if it is "no_initialize_domain" entry. + * @domainname: The name of domain. Maybe NULL. + * @program: The name of program. Maybe NULL. + * @type: Type of transition. * @is_delete: True if it is a delete request. * * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). */ -static int tomoyo_update_domain_initializer_entry(const char *domainname, +static int tomoyo_update_transition_control_entry(const char *domainname, const char *program, - const bool is_not, + const u8 type, const bool is_delete) { - struct tomoyo_domain_initializer_entry e = { .is_not = is_not }; + struct tomoyo_transition_control e = { .type = type }; int error = is_delete ? -ENOENT : -ENOMEM; - - if (!tomoyo_correct_path(program)) - return -EINVAL; + if (program) { + if (!tomoyo_correct_path(program)) + return -EINVAL; + e.program = tomoyo_get_name(program); + if (!e.program) + goto out; + } if (domainname) { - if (!tomoyo_domain_def(domainname) && - tomoyo_correct_path(domainname)) + if (!tomoyo_correct_domain(domainname)) { + if (!tomoyo_correct_path(domainname)) + goto out; e.is_last_name = true; - else if (!tomoyo_correct_domain(domainname)) - return -EINVAL; + } e.domainname = tomoyo_get_name(domainname); if (!e.domainname) goto out; } - e.program = tomoyo_get_name(program); - if (!e.program) - goto out; error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, &tomoyo_policy_list - [TOMOYO_ID_DOMAIN_INITIALIZER], - tomoyo_same_domain_initializer_entry); + [TOMOYO_ID_TRANSITION_CONTROL], + tomoyo_same_transition_control_entry); out: tomoyo_put_name(e.domainname); tomoyo_put_name(e.program); @@ -210,195 +211,85 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname, } /** - * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list. + * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list. * * @data: String to parse. - * @is_not: True if it is "no_initialize_domain" entry. * @is_delete: True if it is a delete request. + * @type: Type of this entry. * * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). */ -int tomoyo_write_domain_initializer_policy(char *data, const bool is_not, - const bool is_delete) +int tomoyo_write_transition_control(char *data, const bool is_delete, + const u8 type) { - char *cp = strstr(data, " from "); - - if (cp) { - *cp = '\0'; - return tomoyo_update_domain_initializer_entry(cp + 6, data, - is_not, - is_delete); + char *domainname = strstr(data, " from "); + if (domainname) { + *domainname = '\0'; + domainname += 6; + } else if (type == TOMOYO_TRANSITION_CONTROL_NO_KEEP || + type == TOMOYO_TRANSITION_CONTROL_KEEP) { + domainname = data; + data = NULL; } - return tomoyo_update_domain_initializer_entry(NULL, data, is_not, + return tomoyo_update_transition_control_entry(domainname, data, type, is_delete); } /** - * tomoyo_domain_initializer - Check whether the given program causes domainname reinitialization. + * tomoyo_transition_type - Get domain transition type. * * @domainname: The name of domain. * @program: The name of program. - * @last_name: The last component of @domainname. * - * Returns true if executing @program reinitializes domain transition, - * false otherwise. + * Returns TOMOYO_TRANSITION_CONTROL_INITIALIZE if executing @program + * reinitializes domain transition, TOMOYO_TRANSITION_CONTROL_KEEP if executing + * @program suppresses domain transition, others otherwise. * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_domain_initializer(const struct tomoyo_path_info * - domainname, - const struct tomoyo_path_info *program, - const struct tomoyo_path_info * - last_name) +static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname, + const struct tomoyo_path_info *program) { - struct tomoyo_domain_initializer_entry *ptr; - bool flag = false; - - list_for_each_entry_rcu(ptr, &tomoyo_policy_list - [TOMOYO_ID_DOMAIN_INITIALIZER], head.list) { - if (ptr->head.is_deleted) - continue; - if (ptr->domainname) { - if (!ptr->is_last_name) { - if (ptr->domainname != domainname) - continue; - } else { - if (tomoyo_pathcmp(ptr->domainname, last_name)) - continue; - } - } - if (tomoyo_pathcmp(ptr->program, program)) - continue; - if (ptr->is_not) { - flag = false; - break; - } - flag = true; - } - return flag; -} - -static bool tomoyo_same_domain_keeper_entry(const struct tomoyo_acl_head *a, - const struct tomoyo_acl_head *b) -{ - const struct tomoyo_domain_keeper_entry *p1 = - container_of(a, typeof(*p1), head); - const struct tomoyo_domain_keeper_entry *p2 = - container_of(b, typeof(*p2), head); - return p1->is_not == p2->is_not && p1->is_last_name == p2->is_last_name - && p1->domainname == p2->domainname - && p1->program == p2->program; -} - -/** - * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list. - * - * @domainname: The name of domain. - * @program: The name of program. May be NULL. - * @is_not: True if it is "no_keep_domain" entry. - * @is_delete: True if it is a delete request. - * - * Returns 0 on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_update_domain_keeper_entry(const char *domainname, - const char *program, - const bool is_not, - const bool is_delete) -{ - struct tomoyo_domain_keeper_entry e = { .is_not = is_not }; - int error = is_delete ? -ENOENT : -ENOMEM; - - if (!tomoyo_domain_def(domainname) && - tomoyo_correct_path(domainname)) - e.is_last_name = true; - else if (!tomoyo_correct_domain(domainname)) - return -EINVAL; - if (program) { - if (!tomoyo_correct_path(program)) - return -EINVAL; - e.program = tomoyo_get_name(program); - if (!e.program) - goto out; - } - e.domainname = tomoyo_get_name(domainname); - if (!e.domainname) - goto out; - error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, - &tomoyo_policy_list - [TOMOYO_ID_DOMAIN_KEEPER], - tomoyo_same_domain_keeper_entry); - out: - tomoyo_put_name(e.domainname); - tomoyo_put_name(e.program); - return error; -} - -/** - * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list. - * - * @data: String to parse. - * @is_not: True if it is "no_keep_domain" entry. - * @is_delete: True if it is a delete request. - * - * Caller holds tomoyo_read_lock(). - */ -int tomoyo_write_domain_keeper_policy(char *data, const bool is_not, - const bool is_delete) -{ - char *cp = strstr(data, " from "); - - if (cp) { - *cp = '\0'; - return tomoyo_update_domain_keeper_entry(cp + 6, data, is_not, - is_delete); - } - return tomoyo_update_domain_keeper_entry(data, NULL, is_not, is_delete); -} - -/** - * tomoyo_domain_keeper - Check whether the given program causes domain transition suppression. - * - * @domainname: The name of domain. - * @program: The name of program. - * @last_name: The last component of @domainname. - * - * Returns true if executing @program supresses domain transition, - * false otherwise. - * - * Caller holds tomoyo_read_lock(). - */ -static bool tomoyo_domain_keeper(const struct tomoyo_path_info *domainname, - const struct tomoyo_path_info *program, - const struct tomoyo_path_info *last_name) -{ - struct tomoyo_domain_keeper_entry *ptr; - bool flag = false; - - list_for_each_entry_rcu(ptr, - &tomoyo_policy_list[TOMOYO_ID_DOMAIN_KEEPER], - head.list) { - if (ptr->head.is_deleted) - continue; - if (!ptr->is_last_name) { - if (ptr->domainname != domainname) + 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; - } else { - if (tomoyo_pathcmp(ptr->domainname, last_name)) + 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) { + /* + * Do not check for initialize_domain if + * no_initialize_domain matched. + */ + type = TOMOYO_TRANSITION_CONTROL_NO_KEEP; + goto next; + } + goto done; } - if (ptr->program && tomoyo_pathcmp(ptr->program, program)) - continue; - if (ptr->is_not) { - flag = false; - break; - } - flag = true; } - return flag; + done: + return type; } static bool tomoyo_same_aggregator_entry(const struct tomoyo_acl_head *a, @@ -533,7 +424,6 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS); struct tomoyo_domain_info *old_domain = tomoyo_domain(); struct tomoyo_domain_info *domain = NULL; - const char *old_domain_name = old_domain->domainname->name; const char *original_name = bprm->filename; u8 mode; bool is_enforce; @@ -586,25 +476,33 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) if (retval < 0) goto out; - if (tomoyo_domain_initializer(old_domain->domainname, &rn, &ln)) { + /* Calculate domain to transit to. */ + switch (tomoyo_transition_type(old_domain->domainname, &rn)) { + 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); - } else if (old_domain == &tomoyo_kernel_domain && - !tomoyo_policy_loaded) { - /* - * Needn't to transit from kernel domain before starting - * /sbin/init. But transit from kernel domain if executing - * initializers because they might start before /sbin/init. - */ - domain = old_domain; - } else if (tomoyo_domain_keeper(old_domain->domainname, &rn, &ln)) { + snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, TOMOYO_ROOT_NAME " " + "%s", rn.name); + break; + case TOMOYO_TRANSITION_CONTROL_KEEP: /* Keep current domain. */ domain = old_domain; - } else { - /* Normal domain transition. */ - snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, - "%s %s", old_domain_name, rn.name); + break; + default: + if (old_domain == &tomoyo_kernel_domain && + !tomoyo_policy_loaded) { + /* + * Needn't to transit from kernel domain before + * starting /sbin/init. But transit from kernel domain + * if executing initializers because they might start + * before /sbin/init. + */ + domain = old_domain; + } else { + /* Normal domain transition. */ + snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", + old_domain->domainname->name, rn.name); + } + break; } if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10) goto done; diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 4d4ba84f874..254ac114555 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -53,17 +53,9 @@ static void tomoyo_del_no_rewrite(struct list_head *element) tomoyo_put_name(ptr->pattern); } -static void tomoyo_del_domain_initializer(struct list_head *element) +static void tomoyo_del_transition_control(struct list_head *element) { - struct tomoyo_domain_initializer_entry *ptr = - container_of(element, typeof(*ptr), head.list); - tomoyo_put_name(ptr->domainname); - tomoyo_put_name(ptr->program); -} - -static void tomoyo_del_domain_keeper(struct list_head *element) -{ - struct tomoyo_domain_keeper_entry *ptr = + struct tomoyo_transition_control *ptr = container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->domainname); tomoyo_put_name(ptr->program); @@ -292,11 +284,8 @@ static void tomoyo_kfree_entry(void) list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { struct list_head *element = p->element; switch (p->type) { - case TOMOYO_ID_DOMAIN_INITIALIZER: - tomoyo_del_domain_initializer(element); - break; - case TOMOYO_ID_DOMAIN_KEEPER: - tomoyo_del_domain_keeper(element); + case TOMOYO_ID_TRANSITION_CONTROL: + tomoyo_del_transition_control(element); break; case TOMOYO_ID_AGGREGATOR: tomoyo_del_aggregator(element); diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index a1d75df93e1..95a77599ff9 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -211,10 +211,10 @@ void __init tomoyo_mm_init(void) panic("Can't register tomoyo_kernel_domain"); { /* Load built-in policy. */ - tomoyo_write_domain_initializer_policy("/sbin/hotplug", - false, false); - tomoyo_write_domain_initializer_policy("/sbin/modprobe", - false, false); + tomoyo_write_transition_control("/sbin/hotplug", false, + TOMOYO_TRANSITION_CONTROL_INITIALIZE); + tomoyo_write_transition_control("/sbin/modprobe", false, + TOMOYO_TRANSITION_CONTROL_INITIALIZE); } tomoyo_read_unlock(idx); } diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 20abba22af4..12a768e6ee3 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -844,7 +844,7 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r, * * Returns the last word of a line. */ -static const char *tomoyo_last_word(const char *name) +const char *tomoyo_last_word(const char *name) { const char *cp = strrchr(name, ' '); if (cp) -- cgit v1.2.3-70-g09d2 From 475e6fa3d340e75a454ea09191a29e52e2ee6e71 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 24 Jun 2010 11:28:14 +0900 Subject: TOMOYO: Change list iterator. Change list_for_each_cookie to (1) start from current position rather than next position (2) remove temporary cursor (3) check that srcu_read_lock() is held Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 75 +++++++++++++++++++----------------------------- security/tomoyo/common.h | 16 +++-------- 2 files changed, 34 insertions(+), 57 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 6568ef18112..2a5330ec06c 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -507,16 +507,14 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head) */ static void tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) { - struct list_head *pos; bool done = true; if (head->read_eof) return; - list_for_each_cookie(pos, head->read_var2, + list_for_each_cookie(head->read_var2, &tomoyo_policy_list[TOMOYO_ID_MANAGER]) { - struct tomoyo_policy_manager_entry *ptr; - ptr = list_entry(pos, struct tomoyo_policy_manager_entry, - head.list); + struct tomoyo_policy_manager_entry *ptr = + list_entry(head->read_var2, typeof(*ptr), head.list); if (ptr->head.is_deleted) continue; done = tomoyo_io_printf(head, "%s\n", ptr->manager->name); @@ -590,8 +588,7 @@ static bool tomoyo_policy_manager(void) * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_select_one(struct tomoyo_io_buffer *head, - const char *data) +static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) { unsigned int pid; struct tomoyo_domain_info *domain = NULL; @@ -623,20 +620,12 @@ static bool tomoyo_select_one(struct tomoyo_io_buffer *head, tomoyo_io_printf(head, "# select %s\n", data); head->read_single_domain = true; head->read_eof = !domain; - if (domain) { - struct tomoyo_domain_info *d; - head->read_var1 = NULL; - list_for_each_entry_rcu(d, &tomoyo_domain_list, list) { - if (d == domain) - break; - head->read_var1 = &d->list; - } - head->read_var2 = NULL; - head->read_bit = 0; - head->read_step = 0; - if (domain->is_deleted) - tomoyo_io_printf(head, "# This is a deleted domain.\n"); - } + head->read_var1 = &domain->list; + head->read_var2 = NULL; + head->read_bit = 0; + head->read_step = 0; + if (domain && domain->is_deleted) + tomoyo_io_printf(head, "# This is a deleted domain.\n"); return true; } @@ -972,20 +961,18 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, */ static void tomoyo_read_domain_policy(struct tomoyo_io_buffer *head) { - struct list_head *dpos; - struct list_head *apos; bool done = true; if (head->read_eof) return; if (head->read_step == 0) head->read_step = 1; - list_for_each_cookie(dpos, head->read_var1, &tomoyo_domain_list) { - struct tomoyo_domain_info *domain; + list_for_each_cookie(head->read_var1, &tomoyo_domain_list) { + struct tomoyo_domain_info *domain = + list_entry(head->read_var1, typeof(*domain), list); const char *quota_exceeded = ""; const char *transition_failed = ""; const char *ignore_global_allow_read = ""; - domain = list_entry(dpos, struct tomoyo_domain_info, list); if (head->read_step != 1) goto acl_loop; if (domain->is_deleted && !head->read_single_domain) @@ -1011,17 +998,17 @@ acl_loop: if (head->read_step == 3) goto tail_mark; /* Print ACL entries in the domain. */ - list_for_each_cookie(apos, head->read_var2, + list_for_each_cookie(head->read_var2, &domain->acl_info_list) { - struct tomoyo_acl_info *ptr - = list_entry(apos, struct tomoyo_acl_info, - list); + struct tomoyo_acl_info *ptr = + list_entry(head->read_var2, typeof(*ptr), list); done = tomoyo_print_entry(head, ptr); if (!done) break; } if (!done) break; + head->read_var2 = NULL; head->read_step = 3; tail_mark: done = tomoyo_io_printf(head, "\n"); @@ -1085,14 +1072,13 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) */ static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) { - struct list_head *pos; bool done = true; if (head->read_eof) return; - list_for_each_cookie(pos, head->read_var1, &tomoyo_domain_list) { - struct tomoyo_domain_info *domain; - domain = list_entry(pos, struct tomoyo_domain_info, list); + list_for_each_cookie(head->read_var1, &tomoyo_domain_list) { + struct tomoyo_domain_info *domain = + list_entry(head->read_var1, typeof(*domain), list); if (domain->is_deleted) continue; done = tomoyo_io_printf(head, "%u %s\n", domain->profile, @@ -1245,19 +1231,16 @@ static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { */ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) { - struct list_head *gpos; - struct list_head *mpos; const char *w[3] = { "", "", "" }; w[0] = tomoyo_group_name[idx]; - list_for_each_cookie(gpos, head->read_var1, &tomoyo_group_list[idx]) { + list_for_each_cookie(head->read_var1, &tomoyo_group_list[idx]) { struct tomoyo_group *group = - list_entry(gpos, struct tomoyo_group, list); + list_entry(head->read_var1, typeof(*group), list); w[1] = group->group_name->name; - list_for_each_cookie(mpos, head->read_var2, - &group->member_list) { + list_for_each_cookie(head->read_var2, &group->member_list) { char buffer[128]; struct tomoyo_acl_head *ptr = - list_entry(mpos, struct tomoyo_acl_head, list); + list_entry(head->read_var2, typeof(*ptr), list); if (ptr->is_deleted) continue; if (idx == TOMOYO_PATH_GROUP) { @@ -1276,7 +1259,9 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) w[2])) return false; } + head->read_var2 = NULL; } + head->read_var1 = NULL; return true; } @@ -1292,11 +1277,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) { - struct list_head *pos; - list_for_each_cookie(pos, head->read_var2, &tomoyo_policy_list[idx]) { + list_for_each_cookie(head->read_var2, &tomoyo_policy_list[idx]) { const char *w[4] = { "", "", "", "" }; - struct tomoyo_acl_head *acl = container_of(pos, typeof(*acl), - list); + struct tomoyo_acl_head *acl = + container_of(head->read_var2, typeof(*acl), list); if (acl->is_deleted) continue; switch (idx) { @@ -1354,6 +1338,7 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) w[3])) return false; } + head->read_var2 = NULL; return true; } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 1277724edae..cdc9ef56fd8 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -1023,19 +1023,11 @@ static inline bool tomoyo_same_number_union /** * list_for_each_cookie - iterate over a list with cookie. * @pos: the &struct list_head to use as a loop cursor. - * @cookie: the &struct list_head to use as a cookie. * @head: the head for your list. - * - * Same with list_for_each_rcu() except that this primitive uses @cookie - * so that we can continue iteration. - * @cookie must be NULL when iteration starts, and @cookie will become - * NULL when iteration finishes. */ -#define list_for_each_cookie(pos, cookie, head) \ - for (({ if (!cookie) \ - cookie = head; }), \ - pos = rcu_dereference((cookie)->next); \ - prefetch(pos->next), pos != (head) || ((cookie) = NULL); \ - (cookie) = pos, pos = rcu_dereference(pos->next)) +#define list_for_each_cookie(pos, head) \ + if (!pos) \ + pos = srcu_dereference((head)->next, &tomoyo_ss); \ + for ( ; pos != (head); pos = srcu_dereference(pos->next, &tomoyo_ss)) #endif /* !defined(_SECURITY_TOMOYO_COMMON_H) */ -- cgit v1.2.3-70-g09d2 From 063821c8160568b3390044390c8328e36c5696ad Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 24 Jun 2010 12:00:25 +0900 Subject: TOMOYO: Allow reading only execute permission. Policy editor needs to know allow_execute entries in order to build domain transition tree. Reading all entries is slow. Thus, allow reading only allow_execute entries. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 8 ++++++++ security/tomoyo/common.h | 2 ++ 2 files changed, 10 insertions(+) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 2a5330ec06c..6c68981c0f5 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -594,6 +594,10 @@ static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) struct tomoyo_domain_info *domain = NULL; bool global_pid = false; + if (!strcmp(data, "allow_execute")) { + head->print_execute_only = true; + return true; + } if (sscanf(data, "pid=%u", &pid) == 1 || (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) { struct task_struct *p; @@ -759,6 +763,8 @@ static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head, for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; + if (head->print_execute_only && bit != TOMOYO_TYPE_EXECUTE) + continue; /* Print "read/write" instead of "read" and "write". */ if ((bit == TOMOYO_TYPE_READ || bit == TOMOYO_TYPE_WRITE) && (perm & (1 << TOMOYO_TYPE_READ_WRITE))) @@ -926,6 +932,8 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, = container_of(ptr, struct tomoyo_path_acl, head); return tomoyo_print_path_acl(head, acl); } + if (head->print_execute_only) + return true; if (acl_type == TOMOYO_TYPE_PATH2_ACL) { struct tomoyo_path2_acl *acl = container_of(ptr, struct tomoyo_path2_acl, head); diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index cdc9ef56fd8..67b9aeae80a 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -571,6 +571,8 @@ struct tomoyo_io_buffer { bool read_single_domain; /* Extra variable for reading. */ u8 read_bit; + /* Read only TOMOYO_TYPE_EXECUTE */ + bool print_execute_only; /* Bytes available for reading. */ int read_avail; /* Size of read buffer. */ -- cgit v1.2.3-70-g09d2 From 5db5a39b6462c8360c9178b28f4b07c320dfca1c Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 24 Jun 2010 12:24:19 +0900 Subject: TOMOYO: Use common code for policy reading. tomoyo_print_..._acl() are similar. Merge them. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 260 +++++++++++++++-------------------------------- 1 file changed, 82 insertions(+), 178 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 6c68981c0f5..a3c3b5de3b7 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -746,220 +746,124 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) } /** - * tomoyo_print_path_acl - Print a single path ACL entry. + * tomoyo_fns - Find next set bit. * - * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_path_acl". + * @perm: 8 bits value. + * @bit: First bit to find. * - * Returns true on success, false otherwise. + * Returns next on-bit on success, 8 otherwise. */ -static bool tomoyo_print_path_acl(struct tomoyo_io_buffer *head, - struct tomoyo_path_acl *ptr) +static u8 tomoyo_fns(const u8 perm, u8 bit) { - int pos; - u8 bit; - const u16 perm = ptr->perm; - - for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { - if (!(perm & (1 << bit))) - continue; - if (head->print_execute_only && bit != TOMOYO_TYPE_EXECUTE) - continue; - /* Print "read/write" instead of "read" and "write". */ - if ((bit == TOMOYO_TYPE_READ || bit == TOMOYO_TYPE_WRITE) - && (perm & (1 << TOMOYO_TYPE_READ_WRITE))) - continue; - pos = head->read_avail; - if (!tomoyo_io_printf(head, "allow_%s ", - tomoyo_path_keyword[bit]) || - !tomoyo_print_name_union(head, &ptr->name) || - !tomoyo_io_printf(head, "\n")) - goto out; - } - head->read_bit = 0; - return true; - out: - head->read_bit = bit; - head->read_avail = pos; - return false; + for ( ; bit < 8; bit++) + if (perm & (1 << bit)) + break; + return bit; } /** - * tomoyo_print_path2_acl - Print a double path ACL entry. + * tomoyo_print_entry - Print an ACL entry. * * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_path2_acl". + * @acl: Pointer to an ACL entry. * * Returns true on success, false otherwise. */ -static bool tomoyo_print_path2_acl(struct tomoyo_io_buffer *head, - struct tomoyo_path2_acl *ptr) +static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, + struct tomoyo_acl_info *acl) { - int pos; - const u8 perm = ptr->perm; - u8 bit; + const u8 acl_type = acl->type; + u8 bit = head->read_bit; + int pos = head->read_avail; - for (bit = head->read_bit; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) { - if (!(perm & (1 << bit))) - continue; - pos = head->read_avail; + if (acl->is_deleted) + return true; + next: + if (acl_type == TOMOYO_TYPE_PATH_ACL) { + struct tomoyo_path_acl *ptr = + container_of(acl, typeof(*ptr), head); + const u16 perm = ptr->perm; + for ( ; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { + if (!(perm & (1 << bit))) + continue; + if (head->print_execute_only && + bit != TOMOYO_TYPE_EXECUTE) + continue; + /* Print "read/write" instead of "read" and "write". */ + if ((bit == TOMOYO_TYPE_READ || + bit == TOMOYO_TYPE_WRITE) + && (perm & (1 << TOMOYO_TYPE_READ_WRITE))) + continue; + break; + } + if (bit >= TOMOYO_MAX_PATH_OPERATION) + goto done; + if (!tomoyo_io_printf(head, "allow_%s ", + tomoyo_path_keyword[bit]) || + !tomoyo_print_name_union(head, &ptr->name)) + goto out; + } else if (head->print_execute_only) { + return true; + } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) { + struct tomoyo_path2_acl *ptr = + container_of(acl, typeof(*ptr), head); + bit = tomoyo_fns(ptr->perm, bit); + if (bit >= TOMOYO_MAX_PATH2_OPERATION) + goto done; if (!tomoyo_io_printf(head, "allow_%s ", tomoyo_path2_keyword[bit]) || !tomoyo_print_name_union(head, &ptr->name1) || - !tomoyo_print_name_union(head, &ptr->name2) || - !tomoyo_io_printf(head, "\n")) + !tomoyo_print_name_union(head, &ptr->name2)) goto out; - } - head->read_bit = 0; - return true; - out: - head->read_bit = bit; - head->read_avail = pos; - return false; -} - -/** - * tomoyo_print_path_number_acl - Print a path_number ACL entry. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_path_number_acl". - * - * Returns true on success, false otherwise. - */ -static bool tomoyo_print_path_number_acl(struct tomoyo_io_buffer *head, - struct tomoyo_path_number_acl *ptr) -{ - int pos; - u8 bit; - const u8 perm = ptr->perm; - for (bit = head->read_bit; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; - bit++) { - if (!(perm & (1 << bit))) - continue; - pos = head->read_avail; + } else if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) { + struct tomoyo_path_number_acl *ptr = + container_of(acl, typeof(*ptr), head); + bit = tomoyo_fns(ptr->perm, bit); + if (bit >= TOMOYO_MAX_PATH_NUMBER_OPERATION) + goto done; if (!tomoyo_io_printf(head, "allow_%s", tomoyo_path_number_keyword[bit]) || !tomoyo_print_name_union(head, &ptr->name) || - !tomoyo_print_number_union(head, &ptr->number) || - !tomoyo_io_printf(head, "\n")) + !tomoyo_print_number_union(head, &ptr->number)) goto out; - } - head->read_bit = 0; - return true; - out: - head->read_bit = bit; - head->read_avail = pos; - return false; -} - -/** - * tomoyo_print_mkdev_acl - Print a mkdev ACL entry. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_mkdev_acl". - * - * Returns true on success, false otherwise. - */ -static bool tomoyo_print_mkdev_acl(struct tomoyo_io_buffer *head, - struct tomoyo_mkdev_acl *ptr) -{ - int pos; - u8 bit; - const u16 perm = ptr->perm; - for (bit = head->read_bit; bit < TOMOYO_MAX_MKDEV_OPERATION; - bit++) { - if (!(perm & (1 << bit))) - continue; - pos = head->read_avail; + } else if (acl_type == TOMOYO_TYPE_MKDEV_ACL) { + struct tomoyo_mkdev_acl *ptr = + container_of(acl, typeof(*ptr), head); + bit = tomoyo_fns(ptr->perm, bit); + if (bit >= TOMOYO_MAX_MKDEV_OPERATION) + goto done; if (!tomoyo_io_printf(head, "allow_%s", tomoyo_mkdev_keyword[bit]) || !tomoyo_print_name_union(head, &ptr->name) || !tomoyo_print_number_union(head, &ptr->mode) || !tomoyo_print_number_union(head, &ptr->major) || - !tomoyo_print_number_union(head, &ptr->minor) || - !tomoyo_io_printf(head, "\n")) + !tomoyo_print_number_union(head, &ptr->minor)) + goto out; + } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { + struct tomoyo_mount_acl *ptr = + container_of(acl, typeof(*ptr), head); + if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_MOUNT) || + !tomoyo_print_name_union(head, &ptr->dev_name) || + !tomoyo_print_name_union(head, &ptr->dir_name) || + !tomoyo_print_name_union(head, &ptr->fs_type) || + !tomoyo_print_number_union(head, &ptr->flags)) goto out; } + if (!tomoyo_io_printf(head, "\n")) + goto out; + head->read_bit = bit; + if (acl_type != TOMOYO_TYPE_MOUNT_ACL) { + bit++; + goto next; + } + done: head->read_bit = 0; return true; out: - head->read_bit = bit; head->read_avail = pos; return false; } -/** - * tomoyo_print_mount_acl - Print a mount ACL entry. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_mount_acl". - * - * Returns true on success, false otherwise. - */ -static bool tomoyo_print_mount_acl(struct tomoyo_io_buffer *head, - struct tomoyo_mount_acl *ptr) -{ - const int pos = head->read_avail; - if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_MOUNT) || - !tomoyo_print_name_union(head, &ptr->dev_name) || - !tomoyo_print_name_union(head, &ptr->dir_name) || - !tomoyo_print_name_union(head, &ptr->fs_type) || - !tomoyo_print_number_union(head, &ptr->flags) || - !tomoyo_io_printf(head, "\n")) { - head->read_avail = pos; - return false; - } - return true; -} - -/** - * tomoyo_print_entry - Print an ACL entry. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to an ACL entry. - * - * Returns true on success, false otherwise. - */ -static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, - struct tomoyo_acl_info *ptr) -{ - const u8 acl_type = ptr->type; - - if (ptr->is_deleted) - return true; - if (acl_type == TOMOYO_TYPE_PATH_ACL) { - struct tomoyo_path_acl *acl - = container_of(ptr, struct tomoyo_path_acl, head); - return tomoyo_print_path_acl(head, acl); - } - if (head->print_execute_only) - return true; - if (acl_type == TOMOYO_TYPE_PATH2_ACL) { - struct tomoyo_path2_acl *acl - = container_of(ptr, struct tomoyo_path2_acl, head); - return tomoyo_print_path2_acl(head, acl); - } - if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) { - struct tomoyo_path_number_acl *acl - = container_of(ptr, struct tomoyo_path_number_acl, - head); - return tomoyo_print_path_number_acl(head, acl); - } - if (acl_type == TOMOYO_TYPE_MKDEV_ACL) { - struct tomoyo_mkdev_acl *acl - = container_of(ptr, struct tomoyo_mkdev_acl, - head); - return tomoyo_print_mkdev_acl(head, acl); - } - if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { - struct tomoyo_mount_acl *acl - = container_of(ptr, struct tomoyo_mount_acl, head); - return tomoyo_print_mount_acl(head, acl); - } - BUG(); /* This must not happen. */ - return false; -} - /** * tomoyo_read_domain_policy - Read domain policy. * -- cgit v1.2.3-70-g09d2 From f23571e866309a2048030ef6a5f0725cf139d4c9 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 24 Jun 2010 14:57:16 +0900 Subject: TOMOYO: Copy directly to userspace buffer. When userspace program reads policy from /sys/kernel/security/tomoyo/ interface, TOMOYO uses line buffered mode. A line has at least one word. Commit 006dacc "TOMOYO: Support longer pathname." changed a word's max length from 4000 bytes to max kmalloc()able bytes. By that commit, a line's max length changed from 8192 bytes to more than max kmalloc()able bytes. Max number of words in a line remains finite. This patch changes the way of buffering so that all words in a line are firstly directly copied to userspace buffer as much as possible and are secondly queued for next read request. Words queued are guaranteed to be valid until /sys/kernel/security/tomoyo/ interface is close()d. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 891 +++++++++++++++++++++++++---------------------- security/tomoyo/common.h | 63 ++-- security/tomoyo/memory.c | 4 +- 3 files changed, 499 insertions(+), 459 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index a3c3b5de3b7..7cb6c0459d9 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -28,7 +28,7 @@ static unsigned int tomoyo_profile_version; static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; /* String table for functionality that takes 4 modes. */ -static const char *tomoyo_mode_4[4] = { +static const char *tomoyo_mode[4] = { "disabled", "learning", "permissive", "enforcing" }; @@ -76,102 +76,175 @@ static const char *tomoyo_yesno(const unsigned int value) return value ? "yes" : "no"; } +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); +} + /** - * tomoyo_print_name_union - Print a tomoyo_name_union. + * tomoyo_flush - Flush queued string to userspace's buffer. * - * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_name_union". + * @head: Pointer to "struct tomoyo_io_buffer". * - * Returns true on success, false otherwise. + * Returns true if all data was flushed, false otherwise. */ -static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head, - const struct tomoyo_name_union *ptr) +static bool tomoyo_flush(struct tomoyo_io_buffer *head) { - int pos = head->read_avail; - if (pos && head->read_buf[pos - 1] == ' ') - head->read_avail--; - if (ptr->is_group) - return tomoyo_io_printf(head, " @%s", - ptr->group->group_name->name); - return tomoyo_io_printf(head, " %s", ptr->filename->name); + while (head->r.w_pos) { + const char *w = head->r.w[0]; + int len = strlen(w); + if (len) { + if (len > head->read_user_buf_avail) + len = head->read_user_buf_avail; + if (!len) + return false; + if (copy_to_user(head->read_user_buf, w, len)) + return false; + head->read_user_buf_avail -= len; + head->read_user_buf += len; + w += len; + } + if (*w) { + head->r.w[0] = w; + return false; + } + /* Add '\0' for query. */ + if (head->poll) { + if (!head->read_user_buf_avail || + copy_to_user(head->read_user_buf, "", 1)) + return false; + head->read_user_buf_avail--; + head->read_user_buf++; + } + head->r.w_pos--; + for (len = 0; len < head->r.w_pos; len++) + head->r.w[len] = head->r.w[len + 1]; + } + head->r.avail = 0; + return true; } /** - * tomoyo_print_number_union - Print a tomoyo_number_union. + * tomoyo_set_string - Queue string to "struct tomoyo_io_buffer" structure. * - * @head: Pointer to "struct tomoyo_io_buffer". - * @ptr: Pointer to "struct tomoyo_number_union". + * @head: Pointer to "struct tomoyo_io_buffer". + * @string: String to print. * - * Returns true on success, false otherwise. + * Note that @string has to be kept valid until @head is kfree()d. + * This means that char[] allocated on stack memory cannot be passed to + * this function. Use tomoyo_io_printf() for char[] allocated on stack memory. */ -bool tomoyo_print_number_union(struct tomoyo_io_buffer *head, - const struct tomoyo_number_union *ptr) +static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string) { - unsigned long min; - unsigned long max; - u8 min_type; - u8 max_type; - if (!tomoyo_io_printf(head, " ")) - return false; - if (ptr->is_group) - return tomoyo_io_printf(head, "@%s", - ptr->group->group_name->name); - min_type = ptr->min_type; - max_type = ptr->max_type; - min = ptr->values[0]; - max = ptr->values[1]; - switch (min_type) { - case TOMOYO_VALUE_TYPE_HEXADECIMAL: - if (!tomoyo_io_printf(head, "0x%lX", min)) - return false; - break; - case TOMOYO_VALUE_TYPE_OCTAL: - if (!tomoyo_io_printf(head, "0%lo", min)) - return false; - break; - default: - if (!tomoyo_io_printf(head, "%lu", min)) - return false; - break; - } - if (min == max && min_type == max_type) - return true; - switch (max_type) { - case TOMOYO_VALUE_TYPE_HEXADECIMAL: - return tomoyo_io_printf(head, "-0x%lX", max); - case TOMOYO_VALUE_TYPE_OCTAL: - return tomoyo_io_printf(head, "-0%lo", max); - default: - return tomoyo_io_printf(head, "-%lu", max); - } + if (head->r.w_pos < TOMOYO_MAX_IO_READ_QUEUE) { + head->r.w[head->r.w_pos++] = string; + tomoyo_flush(head); + } else + WARN_ON(1); } /** - * tomoyo_io_printf - Transactional printf() to "struct tomoyo_io_buffer" structure. + * tomoyo_io_printf - printf() to "struct tomoyo_io_buffer" structure. * * @head: Pointer to "struct tomoyo_io_buffer". * @fmt: The printf()'s format string, followed by parameters. - * - * Returns true if output was written, false otherwise. - * - * The snprintf() will truncate, but tomoyo_io_printf() won't. */ -bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) +void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) { va_list args; int len; - int pos = head->read_avail; + int pos = head->r.avail; int size = head->readbuf_size - pos; - if (size <= 0) - return false; + return; va_start(args, fmt); - len = vsnprintf(head->read_buf + pos, size, fmt, args); + len = vsnprintf(head->read_buf + pos, size, fmt, args) + 1; va_end(args); - if (pos + len >= head->readbuf_size) - return false; - head->read_avail += len; - return true; + if (pos + len >= head->readbuf_size) { + WARN_ON(1); + return; + } + head->r.avail += len; + tomoyo_set_string(head, head->read_buf + pos); +} + +static void tomoyo_set_space(struct tomoyo_io_buffer *head) +{ + tomoyo_set_string(head, " "); +} + +static bool tomoyo_set_lf(struct tomoyo_io_buffer *head) +{ + tomoyo_set_string(head, "\n"); + return !head->r.w_pos; +} + +/** + * tomoyo_print_name_union - Print a tomoyo_name_union. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_name_union". + */ +static void tomoyo_print_name_union(struct tomoyo_io_buffer *head, + const struct tomoyo_name_union *ptr) +{ + tomoyo_set_space(head); + if (ptr->is_group) { + tomoyo_set_string(head, "@"); + tomoyo_set_string(head, ptr->group->group_name->name); + } else { + tomoyo_set_string(head, ptr->filename->name); + } +} + +/** + * tomoyo_print_number_union - Print a tomoyo_number_union. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr: Pointer to "struct tomoyo_number_union". + */ +static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, + const struct tomoyo_number_union *ptr) +{ + tomoyo_set_space(head); + if (ptr->is_group) { + tomoyo_set_string(head, "@"); + tomoyo_set_string(head, ptr->group->group_name->name); + } else { + int i; + unsigned long min = ptr->values[0]; + const unsigned long max = ptr->values[1]; + u8 min_type = ptr->min_type; + const u8 max_type = ptr->max_type; + char buffer[128]; + buffer[0] = '\0'; + for (i = 0; i < 2; i++) { + switch (min_type) { + case TOMOYO_VALUE_TYPE_HEXADECIMAL: + tomoyo_addprintf(buffer, sizeof(buffer), + "0x%lX", min); + break; + case TOMOYO_VALUE_TYPE_OCTAL: + tomoyo_addprintf(buffer, sizeof(buffer), + "0%lo", min); + break; + default: + tomoyo_addprintf(buffer, sizeof(buffer), + "%lu", min); + break; + } + if (min == max && min_type == max_type) + break; + tomoyo_addprintf(buffer, sizeof(buffer), "-"); + min_type = max_type; + min = max; + } + tomoyo_io_printf(head, "%s", buffer); + } } /** @@ -334,7 +407,7 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) config = TOMOYO_CONFIG_USE_DEFAULT; } else { for (mode = 3; mode >= 0; mode--) - if (strstr(cp, tomoyo_mode_4[mode])) + if (strstr(cp, tomoyo_mode[mode])) /* * Update lower 3 bits in order to distinguish * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'. @@ -348,6 +421,52 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) return 0; } +static void tomoyo_print_preference(struct tomoyo_io_buffer *head, + const int idx) +{ + struct tomoyo_preference *pref = &tomoyo_default_profile.preference; + const struct tomoyo_profile *profile = idx >= 0 ? + tomoyo_profile_ptr[idx] : NULL; + char buffer[16] = ""; + if (profile) { + buffer[sizeof(buffer) - 1] = '\0'; + snprintf(buffer, sizeof(buffer) - 1, "%u-", idx); + } + if (profile) { + pref = profile->learning; + if (pref == &tomoyo_default_profile.preference) + goto skip1; + } + tomoyo_io_printf(head, "%sPREFERENCE::%s={ " + "verbose=%s max_entry=%u }\n", + buffer, "learning", + tomoyo_yesno(pref->learning_verbose), + pref->learning_max_entry); + skip1: + if (profile) { + pref = profile->permissive; + if (pref == &tomoyo_default_profile.preference) + goto skip2; + } + tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n", + buffer, "permissive", + tomoyo_yesno(pref->permissive_verbose)); + skip2: + if (profile) { + pref = profile->enforcing; + if (pref == &tomoyo_default_profile.preference) + return; + } + tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n", + buffer, "enforcing", + tomoyo_yesno(pref->enforcing_verbose)); +} + +static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config) +{ + tomoyo_io_printf(head, "={ mode=%s }\n", tomoyo_mode[config & 3]); +} + /** * tomoyo_read_profile - Read profile table. * @@ -355,83 +474,67 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) */ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) { - int index; - if (head->read_eof) - return; - if (head->read_bit) - goto body; - tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903"); - tomoyo_io_printf(head, "PREFERENCE::learning={ verbose=%s " - "max_entry=%u }\n", - tomoyo_yesno(tomoyo_default_profile.preference. - learning_verbose), - tomoyo_default_profile.preference.learning_max_entry); - tomoyo_io_printf(head, "PREFERENCE::permissive={ verbose=%s }\n", - tomoyo_yesno(tomoyo_default_profile.preference. - permissive_verbose)); - tomoyo_io_printf(head, "PREFERENCE::enforcing={ verbose=%s }\n", - tomoyo_yesno(tomoyo_default_profile.preference. - enforcing_verbose)); - head->read_bit = 1; - body: - for (index = head->read_step; index < TOMOYO_MAX_PROFILES; index++) { - bool done; - u8 config; - int i; - int pos; - const struct tomoyo_profile *profile - = tomoyo_profile_ptr[index]; - const struct tomoyo_path_info *comment; - head->read_step = index; - if (!profile) - continue; - pos = head->read_avail; - comment = profile->comment; - done = tomoyo_io_printf(head, "%u-COMMENT=%s\n", index, - comment ? comment->name : ""); - if (!done) - goto out; - config = profile->default_config; - if (!tomoyo_io_printf(head, "%u-CONFIG={ mode=%s }\n", index, - tomoyo_mode_4[config & 3])) - goto out; - for (i = 0; i < TOMOYO_MAX_MAC_INDEX + - TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { - config = profile->config[i]; + u8 index; + const struct tomoyo_profile *profile; + next: + index = head->r.index; + profile = tomoyo_profile_ptr[index]; + switch (head->r.step) { + case 0: + tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903"); + tomoyo_print_preference(head, -1); + head->r.step++; + break; + case 1: + for ( ; head->r.index < TOMOYO_MAX_PROFILES; + head->r.index++) + if (tomoyo_profile_ptr[head->r.index]) + break; + if (head->r.index == TOMOYO_MAX_PROFILES) + return; + head->r.step++; + break; + case 2: + { + const struct tomoyo_path_info *comment = + profile->comment; + tomoyo_io_printf(head, "%u-COMMENT=", index); + tomoyo_set_string(head, comment ? comment->name : ""); + tomoyo_set_lf(head); + head->r.step++; + } + break; + case 3: + { + tomoyo_io_printf(head, "%u-%s", index, "CONFIG"); + tomoyo_print_config(head, profile->default_config); + head->r.bit = 0; + head->r.step++; + } + break; + case 4: + for ( ; head->r.bit < TOMOYO_MAX_MAC_INDEX + + 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; - if (!tomoyo_io_printf(head, - "%u-CONFIG::%s={ mode=%s }\n", - index, tomoyo_mac_keywords[i], - tomoyo_mode_4[config & 3])) - goto out; + tomoyo_io_printf(head, "%u-%s%s", index, "CONFIG::", + tomoyo_mac_keywords[i]); + tomoyo_print_config(head, config); + head->r.bit++; + break; + } + if (head->r.bit == TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX) { + tomoyo_print_preference(head, index); + head->r.index++; + head->r.step = 1; } - if (profile->learning != &tomoyo_default_profile.preference && - !tomoyo_io_printf(head, "%u-PREFERENCE::learning={ " - "verbose=%s max_entry=%u }\n", index, - tomoyo_yesno(profile->preference. - learning_verbose), - profile->preference.learning_max_entry)) - goto out; - if (profile->permissive != &tomoyo_default_profile.preference - && !tomoyo_io_printf(head, "%u-PREFERENCE::permissive={ " - "verbose=%s }\n", index, - tomoyo_yesno(profile->preference. - permissive_verbose))) - goto out; - if (profile->enforcing != &tomoyo_default_profile.preference && - !tomoyo_io_printf(head, "%u-PREFERENCE::enforcing={ " - "verbose=%s }\n", index, - tomoyo_yesno(profile->preference. - enforcing_verbose))) - goto out; - continue; - out: - head->read_avail = pos; break; } - if (index == TOMOYO_MAX_PROFILES) - head->read_eof = true; + if (tomoyo_flush(head)) + goto next; } static bool tomoyo_same_manager_entry(const struct tomoyo_acl_head *a, @@ -507,21 +610,20 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head) */ static void tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) { - bool done = true; - - if (head->read_eof) + if (head->r.eof) return; - list_for_each_cookie(head->read_var2, + list_for_each_cookie(head->r.acl, &tomoyo_policy_list[TOMOYO_ID_MANAGER]) { struct tomoyo_policy_manager_entry *ptr = - list_entry(head->read_var2, typeof(*ptr), head.list); + list_entry(head->r.acl, typeof(*ptr), head.list); if (ptr->head.is_deleted) continue; - done = tomoyo_io_printf(head, "%s\n", ptr->manager->name); - if (!done) - break; + if (!tomoyo_flush(head)) + return; + tomoyo_set_string(head, ptr->manager->name); + tomoyo_set_lf(head); } - head->read_eof = done; + head->r.eof = true; } /** @@ -595,7 +697,7 @@ static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) bool global_pid = false; if (!strcmp(data, "allow_execute")) { - head->print_execute_only = true; + head->r.print_execute_only = true; return true; } if (sscanf(data, "pid=%u", &pid) == 1 || @@ -620,14 +722,11 @@ static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) /* Accessing read_buf is safe because head->io_sem is held. */ if (!head->read_buf) return true; /* Do nothing if open(O_WRONLY). */ - head->read_avail = 0; + memset(&head->r, 0, sizeof(head->r)); + head->r.print_this_domain_only = true; + head->r.eof = !domain; + head->r.domain = &domain->list; tomoyo_io_printf(head, "# select %s\n", data); - head->read_single_domain = true; - head->read_eof = !domain; - head->read_var1 = &domain->list; - head->read_var2 = NULL; - head->read_bit = 0; - head->read_step = 0; if (domain && domain->is_deleted) tomoyo_io_printf(head, "# This is a deleted domain.\n"); return true; @@ -773,20 +872,22 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, struct tomoyo_acl_info *acl) { const u8 acl_type = acl->type; - u8 bit = head->read_bit; - int pos = head->read_avail; + u8 bit; if (acl->is_deleted) return true; next: - if (acl_type == TOMOYO_TYPE_PATH_ACL) { + bit = head->r.bit; + if (!tomoyo_flush(head)) + return false; + else if (acl_type == TOMOYO_TYPE_PATH_ACL) { struct tomoyo_path_acl *ptr = container_of(acl, typeof(*ptr), head); const u16 perm = ptr->perm; for ( ; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { if (!(perm & (1 << bit))) continue; - if (head->print_execute_only && + if (head->r.print_execute_only && bit != TOMOYO_TYPE_EXECUTE) continue; /* Print "read/write" instead of "read" and "write". */ @@ -798,11 +899,9 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, } if (bit >= TOMOYO_MAX_PATH_OPERATION) goto done; - if (!tomoyo_io_printf(head, "allow_%s ", - tomoyo_path_keyword[bit]) || - !tomoyo_print_name_union(head, &ptr->name)) - goto out; - } else if (head->print_execute_only) { + tomoyo_io_printf(head, "allow_%s", tomoyo_path_keyword[bit]); + tomoyo_print_name_union(head, &ptr->name); + } else if (head->r.print_execute_only) { return true; } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) { struct tomoyo_path2_acl *ptr = @@ -810,58 +909,69 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, bit = tomoyo_fns(ptr->perm, bit); if (bit >= TOMOYO_MAX_PATH2_OPERATION) goto done; - if (!tomoyo_io_printf(head, "allow_%s ", - tomoyo_path2_keyword[bit]) || - !tomoyo_print_name_union(head, &ptr->name1) || - !tomoyo_print_name_union(head, &ptr->name2)) - goto out; + tomoyo_io_printf(head, "allow_%s", tomoyo_path2_keyword[bit]); + tomoyo_print_name_union(head, &ptr->name1); + tomoyo_print_name_union(head, &ptr->name2); } else if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) { struct tomoyo_path_number_acl *ptr = container_of(acl, typeof(*ptr), head); bit = tomoyo_fns(ptr->perm, bit); if (bit >= TOMOYO_MAX_PATH_NUMBER_OPERATION) goto done; - if (!tomoyo_io_printf(head, "allow_%s", - tomoyo_path_number_keyword[bit]) || - !tomoyo_print_name_union(head, &ptr->name) || - !tomoyo_print_number_union(head, &ptr->number)) - goto out; + tomoyo_io_printf(head, "allow_%s", + tomoyo_path_number_keyword[bit]); + tomoyo_print_name_union(head, &ptr->name); + tomoyo_print_number_union(head, &ptr->number); } else if (acl_type == TOMOYO_TYPE_MKDEV_ACL) { struct tomoyo_mkdev_acl *ptr = container_of(acl, typeof(*ptr), head); bit = tomoyo_fns(ptr->perm, bit); if (bit >= TOMOYO_MAX_MKDEV_OPERATION) goto done; - if (!tomoyo_io_printf(head, "allow_%s", - tomoyo_mkdev_keyword[bit]) || - !tomoyo_print_name_union(head, &ptr->name) || - !tomoyo_print_number_union(head, &ptr->mode) || - !tomoyo_print_number_union(head, &ptr->major) || - !tomoyo_print_number_union(head, &ptr->minor)) - goto out; + tomoyo_io_printf(head, "allow_%s", tomoyo_mkdev_keyword[bit]); + tomoyo_print_name_union(head, &ptr->name); + tomoyo_print_number_union(head, &ptr->mode); + tomoyo_print_number_union(head, &ptr->major); + tomoyo_print_number_union(head, &ptr->minor); } else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) { struct tomoyo_mount_acl *ptr = container_of(acl, typeof(*ptr), head); - if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALLOW_MOUNT) || - !tomoyo_print_name_union(head, &ptr->dev_name) || - !tomoyo_print_name_union(head, &ptr->dir_name) || - !tomoyo_print_name_union(head, &ptr->fs_type) || - !tomoyo_print_number_union(head, &ptr->flags)) - goto out; + tomoyo_io_printf(head, "allow_mount"); + tomoyo_print_name_union(head, &ptr->dev_name); + tomoyo_print_name_union(head, &ptr->dir_name); + tomoyo_print_name_union(head, &ptr->fs_type); + tomoyo_print_number_union(head, &ptr->flags); } - if (!tomoyo_io_printf(head, "\n")) - goto out; - head->read_bit = bit; - if (acl_type != TOMOYO_TYPE_MOUNT_ACL) { - bit++; + head->r.bit = bit + 1; + tomoyo_io_printf(head, "\n"); + if (acl_type != TOMOYO_TYPE_MOUNT_ACL) goto next; - } done: - head->read_bit = 0; + head->r.bit = 0; + return true; +} + +/** + * tomoyo_read_domain2 - Read domain policy. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @domain: Pointer to "struct tomoyo_domain_info". + * + * Caller holds tomoyo_read_lock(). + * + * Returns true on success, false otherwise. + */ +static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head, + struct tomoyo_domain_info *domain) +{ + list_for_each_cookie(head->r.acl, &domain->acl_info_list) { + struct tomoyo_acl_info *ptr = + list_entry(head->r.acl, typeof(*ptr), list); + if (!tomoyo_print_entry(head, ptr)) + return false; + } + head->r.acl = NULL; return true; - out: - head->read_avail = pos; - return false; } /** @@ -873,64 +983,48 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, */ static void tomoyo_read_domain_policy(struct tomoyo_io_buffer *head) { - bool done = true; - - if (head->read_eof) + if (head->r.eof) return; - if (head->read_step == 0) - head->read_step = 1; - list_for_each_cookie(head->read_var1, &tomoyo_domain_list) { + list_for_each_cookie(head->r.domain, &tomoyo_domain_list) { struct tomoyo_domain_info *domain = - list_entry(head->read_var1, typeof(*domain), list); - const char *quota_exceeded = ""; - const char *transition_failed = ""; - const char *ignore_global_allow_read = ""; - if (head->read_step != 1) - goto acl_loop; - if (domain->is_deleted && !head->read_single_domain) - continue; - /* Print domainname and flags. */ - if (domain->quota_warned) - quota_exceeded = "quota_exceeded\n"; - if (domain->transition_failed) - transition_failed = "transition_failed\n"; - if (domain->ignore_global_allow_read) - ignore_global_allow_read - = TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ "\n"; - done = tomoyo_io_printf(head, "%s\n" TOMOYO_KEYWORD_USE_PROFILE - "%u\n%s%s%s\n", - domain->domainname->name, - domain->profile, quota_exceeded, - transition_failed, - ignore_global_allow_read); - if (!done) - break; - head->read_step = 2; -acl_loop: - if (head->read_step == 3) - goto tail_mark; - /* Print ACL entries in the domain. */ - list_for_each_cookie(head->read_var2, - &domain->acl_info_list) { - struct tomoyo_acl_info *ptr = - list_entry(head->read_var2, typeof(*ptr), list); - done = tomoyo_print_entry(head, ptr); - if (!done) - break; + list_entry(head->r.domain, typeof(*domain), list); + switch (head->r.step) { + case 0: + if (domain->is_deleted && + !head->r.print_this_domain_only) + continue; + /* Print domainname and flags. */ + tomoyo_set_string(head, domain->domainname->name); + tomoyo_set_lf(head); + tomoyo_io_printf(head, + TOMOYO_KEYWORD_USE_PROFILE "%u\n", + domain->profile); + if (domain->quota_warned) + tomoyo_set_string(head, "quota_exceeded\n"); + if (domain->transition_failed) + tomoyo_set_string(head, "transition_failed\n"); + if (domain->ignore_global_allow_read) + tomoyo_set_string(head, + TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ + "\n"); + head->r.step++; + tomoyo_set_lf(head); + /* fall through */ + case 1: + if (!tomoyo_read_domain2(head, domain)) + return; + head->r.step++; + if (!tomoyo_set_lf(head)) + return; + /* fall through */ + case 2: + head->r.step = 0; + if (head->r.print_this_domain_only) + goto done; } - if (!done) - break; - head->read_var2 = NULL; - head->read_step = 3; -tail_mark: - done = tomoyo_io_printf(head, "\n"); - if (!done) - break; - head->read_step = 1; - if (head->read_single_domain) - break; } - head->read_eof = done; + done: + head->r.eof = true; } /** @@ -984,21 +1078,20 @@ static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) */ static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) { - bool done = true; - - if (head->read_eof) + if (head->r.eof) return; - list_for_each_cookie(head->read_var1, &tomoyo_domain_list) { + list_for_each_cookie(head->r.domain, &tomoyo_domain_list) { struct tomoyo_domain_info *domain = - list_entry(head->read_var1, typeof(*domain), list); + list_entry(head->r.domain, typeof(*domain), list); if (domain->is_deleted) continue; - done = tomoyo_io_printf(head, "%u %s\n", domain->profile, - domain->domainname->name); - if (!done) - break; + if (!tomoyo_flush(head)) + return; + tomoyo_io_printf(head, "%u ", domain->profile); + tomoyo_set_string(head, domain->domainname->name); + tomoyo_set_lf(head); } - head->read_eof = done; + head->r.eof = true; } /** @@ -1010,11 +1103,7 @@ static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) */ static int tomoyo_write_pid(struct tomoyo_io_buffer *head) { - unsigned long pid; - /* No error check. */ - strict_strtoul(head->write_buf, 10, &pid); - head->read_step = (int) pid; - head->read_eof = false; + head->r.eof = false; return 0; } @@ -1030,22 +1119,37 @@ static int tomoyo_write_pid(struct tomoyo_io_buffer *head) */ static void tomoyo_read_pid(struct tomoyo_io_buffer *head) { - if (head->read_avail == 0 && !head->read_eof) { - const int pid = head->read_step; - struct task_struct *p; - struct tomoyo_domain_info *domain = NULL; - rcu_read_lock(); - read_lock(&tasklist_lock); - p = find_task_by_vpid(pid); - if (p) - domain = tomoyo_real_domain(p); - read_unlock(&tasklist_lock); - rcu_read_unlock(); - if (domain) - tomoyo_io_printf(head, "%d %u %s", pid, domain->profile, - domain->domainname->name); - head->read_eof = true; + char *buf = head->write_buf; + bool global_pid = false; + unsigned int pid; + struct task_struct *p; + struct tomoyo_domain_info *domain = NULL; + + /* Accessing write_buf is safe because head->io_sem is held. */ + if (!buf) { + head->r.eof = true; + return; /* Do nothing if open(O_RDONLY). */ } + if (head->r.w_pos || head->r.eof) + return; + head->r.eof = true; + if (tomoyo_str_starts(&buf, "global-pid ")) + global_pid = true; + pid = (unsigned int) simple_strtoul(buf, NULL, 10); + rcu_read_lock(); + read_lock(&tasklist_lock); + if (global_pid) + p = find_task_by_pid_ns(pid, &init_pid_ns); + else + p = find_task_by_vpid(pid); + if (p) + domain = tomoyo_real_domain(p); + read_unlock(&tasklist_lock); + rcu_read_unlock(); + if (!domain) + return; + tomoyo_io_printf(head, "%u %u ", pid, domain->profile); + tomoyo_set_string(head, domain->domainname->name); } static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = { @@ -1092,40 +1196,6 @@ static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) return -EINVAL; } -static void tomoyo_print_number(char *buffer, int buffer_len, - const struct tomoyo_number_union *ptr) -{ - int i; - unsigned long min = ptr->values[0]; - const unsigned long max = ptr->values[1]; - u8 min_type = ptr->min_type; - const u8 max_type = ptr->max_type; - memset(buffer, 0, buffer_len); - buffer_len -= 2; - for (i = 0; i < 2; i++) { - int len; - switch (min_type) { - case TOMOYO_VALUE_TYPE_HEXADECIMAL: - snprintf(buffer, buffer_len, "0x%lX", min); - break; - case TOMOYO_VALUE_TYPE_OCTAL: - snprintf(buffer, buffer_len, "0%lo", min); - break; - default: - snprintf(buffer, buffer_len, "%lu", min); - break; - } - if (min == max && min_type == max_type) - break; - len = strlen(buffer); - buffer[len++] = '-'; - buffer += len; - buffer_len -= len; - min_type = max_type; - min = max; - } -} - static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { [TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP, [TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP @@ -1143,37 +1213,34 @@ static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { */ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx) { - const char *w[3] = { "", "", "" }; - w[0] = tomoyo_group_name[idx]; - list_for_each_cookie(head->read_var1, &tomoyo_group_list[idx]) { + list_for_each_cookie(head->r.group, &tomoyo_group_list[idx]) { struct tomoyo_group *group = - list_entry(head->read_var1, typeof(*group), list); - w[1] = group->group_name->name; - list_for_each_cookie(head->read_var2, &group->member_list) { - char buffer[128]; + list_entry(head->r.group, typeof(*group), list); + list_for_each_cookie(head->r.acl, &group->member_list) { struct tomoyo_acl_head *ptr = - list_entry(head->read_var2, typeof(*ptr), list); + list_entry(head->r.acl, typeof(*ptr), list); if (ptr->is_deleted) continue; + if (!tomoyo_flush(head)) + return false; + tomoyo_set_string(head, tomoyo_group_name[idx]); + tomoyo_set_string(head, group->group_name->name); if (idx == TOMOYO_PATH_GROUP) { - w[2] = container_of(ptr, - struct tomoyo_path_group, - head)->member_name->name; + tomoyo_set_space(head); + tomoyo_set_string(head, container_of + (ptr, struct tomoyo_path_group, + head)->member_name->name); } else if (idx == TOMOYO_NUMBER_GROUP) { - tomoyo_print_number(buffer, sizeof(buffer), - &container_of - (ptr, struct - tomoyo_number_group, - head)->number); - w[2] = buffer; + tomoyo_print_number_union(head, &container_of + (ptr, + struct tomoyo_number_group, + head)->number); } - if (!tomoyo_io_printf(head, "%s%s %s\n", w[0], w[1], - w[2])) - return false; + tomoyo_set_lf(head); } - head->read_var2 = NULL; + head->r.acl = NULL; } - head->read_var1 = NULL; + head->r.group = NULL; return true; } @@ -1189,68 +1256,78 @@ 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->read_var2, &tomoyo_policy_list[idx]) { - const char *w[4] = { "", "", "", "" }; + list_for_each_cookie(head->r.acl, &tomoyo_policy_list[idx]) { struct tomoyo_acl_head *acl = - container_of(head->read_var2, typeof(*acl), list); + container_of(head->r.acl, typeof(*acl), list); if (acl->is_deleted) continue; + if (!tomoyo_flush(head)) + return false; switch (idx) { case TOMOYO_ID_TRANSITION_CONTROL: { struct tomoyo_transition_control *ptr = container_of(acl, typeof(*ptr), head); - w[0] = tomoyo_transition_type[ptr->type]; + tomoyo_set_string(head, + tomoyo_transition_type + [ptr->type]); if (ptr->program) - w[1] = ptr->program->name; + tomoyo_set_string(head, + ptr->program->name); + if (ptr->program && ptr->domainname) + tomoyo_set_string(head, " from "); if (ptr->domainname) - w[3] = ptr->domainname->name; - if (w[1][0] && w[3][0]) - w[2] = " from "; + tomoyo_set_string(head, + ptr->domainname-> + name); } break; case TOMOYO_ID_GLOBALLY_READABLE: { struct tomoyo_globally_readable_file_entry *ptr = container_of(acl, typeof(*ptr), head); - w[0] = TOMOYO_KEYWORD_ALLOW_READ; - w[1] = ptr->filename->name; + tomoyo_set_string(head, + TOMOYO_KEYWORD_ALLOW_READ); + tomoyo_set_string(head, ptr->filename->name); } break; case TOMOYO_ID_AGGREGATOR: { struct tomoyo_aggregator_entry *ptr = container_of(acl, typeof(*ptr), head); - w[0] = TOMOYO_KEYWORD_AGGREGATOR; - w[1] = ptr->original_name->name; - w[2] = " "; - w[3] = ptr->aggregated_name->name; + tomoyo_set_string(head, + TOMOYO_KEYWORD_AGGREGATOR); + tomoyo_set_string(head, + ptr->original_name->name); + tomoyo_set_space(head); + tomoyo_set_string(head, + ptr->aggregated_name->name); } break; case TOMOYO_ID_PATTERN: { struct tomoyo_pattern_entry *ptr = container_of(acl, typeof(*ptr), head); - w[0] = TOMOYO_KEYWORD_FILE_PATTERN; - w[1] = ptr->pattern->name; + tomoyo_set_string(head, + TOMOYO_KEYWORD_FILE_PATTERN); + tomoyo_set_string(head, ptr->pattern->name); } break; case TOMOYO_ID_NO_REWRITE: { struct tomoyo_no_rewrite_entry *ptr = container_of(acl, typeof(*ptr), head); - w[0] = TOMOYO_KEYWORD_DENY_REWRITE; - w[1] = ptr->pattern->name; + tomoyo_set_string(head, + TOMOYO_KEYWORD_DENY_REWRITE); + tomoyo_set_string(head, ptr->pattern->name); } break; default: continue; } - if (!tomoyo_io_printf(head, "%s%s%s%s\n", w[0], w[1], w[2], - w[3])) - return false; + tomoyo_set_lf(head); } - head->read_var2 = NULL; + head->r.acl = NULL; return true; } @@ -1263,19 +1340,19 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) */ static void tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) { - if (head->read_eof) + if (head->r.eof) return; - while (head->read_step < TOMOYO_MAX_POLICY && - tomoyo_read_policy(head, head->read_step)) - head->read_step++; - if (head->read_step < TOMOYO_MAX_POLICY) + while (head->r.step < TOMOYO_MAX_POLICY && + tomoyo_read_policy(head, head->r.step)) + head->r.step++; + if (head->r.step < TOMOYO_MAX_POLICY) return; - while (head->read_step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP && - tomoyo_read_group(head, head->read_step - TOMOYO_MAX_POLICY)) - head->read_step++; - if (head->read_step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP) + while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP && + tomoyo_read_group(head, head->r.step - TOMOYO_MAX_POLICY)) + head->r.step++; + if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP) return; - head->read_eof = true; + head->r.eof = true; } /** @@ -1290,9 +1367,6 @@ static void tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) */ static char *tomoyo_print_header(struct tomoyo_request_info *r) { - static const char *tomoyo_mode_4[4] = { - "disabled", "learning", "permissive", "enforcing" - }; struct timeval tv; const pid_t gpid = task_pid_nr(current); static const int tomoyo_buffer_len = 4096; @@ -1304,7 +1378,7 @@ static char *tomoyo_print_header(struct tomoyo_request_info *r) "#timestamp=%lu profile=%u mode=%s (global-pid=%u)" " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u" " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }", - tv.tv_sec, r->profile, tomoyo_mode_4[r->mode], gpid, + tv.tv_sec, r->profile, tomoyo_mode[r->mode], gpid, (pid_t) sys_getpid(), (pid_t) sys_getppid(), current_uid(), current_gid(), current_euid(), current_egid(), current_suid(), current_sgid(), @@ -1536,27 +1610,26 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) int pos = 0; int len = 0; char *buf; - if (head->read_avail) + if (head->r.w_pos) return; if (head->read_buf) { kfree(head->read_buf); head->read_buf = NULL; - head->readbuf_size = 0; } spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { - struct tomoyo_query_entry *ptr - = list_entry(tmp, struct tomoyo_query_entry, list); + struct tomoyo_query_entry *ptr = + list_entry(tmp, typeof(*ptr), list); if (ptr->answer) continue; - if (pos++ != head->read_step) + if (pos++ != head->r.query_index) continue; len = ptr->query_len; break; } spin_unlock(&tomoyo_query_list_lock); if (!len) { - head->read_step = 0; + head->r.query_index = 0; return; } buf = kzalloc(len, GFP_NOFS); @@ -1565,11 +1638,11 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) pos = 0; spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { - struct tomoyo_query_entry *ptr - = list_entry(tmp, struct tomoyo_query_entry, list); + struct tomoyo_query_entry *ptr = + list_entry(tmp, typeof(*ptr), list); if (ptr->answer) continue; - if (pos++ != head->read_step) + if (pos++ != head->r.query_index) continue; /* * Some query can be skipped because tomoyo_query_list @@ -1581,10 +1654,9 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) } spin_unlock(&tomoyo_query_list_lock); if (buf[0]) { - head->read_avail = len; - head->readbuf_size = head->read_avail; head->read_buf = buf; - head->read_step++; + head->r.w[head->r.w_pos++] = buf; + head->r.query_index++; } else { kfree(buf); } @@ -1635,9 +1707,9 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) */ static void tomoyo_read_version(struct tomoyo_io_buffer *head) { - if (!head->read_eof) { + if (!head->r.eof) { tomoyo_io_printf(head, "2.3.0-pre"); - head->read_eof = true; + head->r.eof = true; } } @@ -1650,14 +1722,14 @@ static void tomoyo_read_version(struct tomoyo_io_buffer *head) */ static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head) { - if (!head->read_eof) { + if (!head->r.eof) { /* * tomoyo_domain()->domainname != NULL * because every process belongs to a domain and * the domain's name cannot be NULL. */ tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name); - head->read_eof = true; + head->r.eof = true; } } @@ -1817,33 +1889,20 @@ int tomoyo_poll_control(struct file *file, poll_table *wait) int tomoyo_read_control(struct file *file, char __user *buffer, const int buffer_len) { - int len = 0; + int len; struct tomoyo_io_buffer *head = file->private_data; - char *cp; if (!head->read) return -ENOSYS; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; - /* Call the policy handler. */ - head->read(head); - if (len < 0) - goto out; - /* Write to buffer. */ - len = head->read_avail; - if (len > buffer_len) - len = buffer_len; - if (!len) - goto out; - /* head->read_buf changes by some functions. */ - cp = head->read_buf; - if (copy_to_user(buffer, cp, len)) { - len = -EFAULT; - goto out; - } - head->read_avail -= len; - memmove(cp, cp + len, head->read_avail); - out: + head->read_user_buf = buffer; + head->read_user_buf_avail = buffer_len; + if (tomoyo_flush(head)) + /* Call the policy handler. */ + head->read(head); + tomoyo_flush(head); + len = head->read_user_buf - buffer; mutex_unlock(&head->io_sem); return len; } diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 67b9aeae80a..ef8fecddb65 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -524,28 +524,11 @@ struct tomoyo_mount_acl { struct tomoyo_number_union flags; }; +#define TOMOYO_MAX_IO_READ_QUEUE 32 + /* - * tomoyo_io_buffer is a structure which is used for reading and modifying - * configuration via /sys/kernel/security/tomoyo/ interface. - * It has many fields. ->read_var1 , ->read_var2 , ->write_var1 are used as - * cursors. - * - * Since the content of /sys/kernel/security/tomoyo/domain_policy is a list of - * "struct tomoyo_domain_info" entries and each "struct tomoyo_domain_info" - * entry has a list of "struct tomoyo_acl_info", we need two cursors when - * reading (one is for traversing tomoyo_domain_list and the other is for - * traversing "struct tomoyo_acl_info"->acl_info_list ). - * - * If a line written to /sys/kernel/security/tomoyo/domain_policy starts with - * "select ", TOMOYO seeks the cursor ->read_var1 and ->write_var1 to the - * domain with the domainname specified by the rest of that line (NULL is set - * if seek failed). - * If a line written to /sys/kernel/security/tomoyo/domain_policy starts with - * "delete ", TOMOYO deletes an entry or a domain specified by the rest of that - * line (->write_var1 is set to NULL if a domain was deleted). - * If a line written to /sys/kernel/security/tomoyo/domain_policy starts with - * neither "select " nor "delete ", an entry or a domain specified by that line - * is appended. + * Structure for reading/writing policy via /sys/kernel/security/tomoyo + * interfaces. */ struct tomoyo_io_buffer { void (*read) (struct tomoyo_io_buffer *); @@ -555,26 +538,27 @@ struct tomoyo_io_buffer { struct mutex io_sem; /* Index returned by tomoyo_read_lock(). */ int reader_idx; - /* The position currently reading from. */ - struct list_head *read_var1; - /* Extra variables for reading. */ - struct list_head *read_var2; + char __user *read_user_buf; + int read_user_buf_avail; + struct { + struct list_head *domain; + struct list_head *group; + struct list_head *acl; + int avail; + int step; + int query_index; + u16 index; + u8 bit; + u8 w_pos; + bool eof; + bool print_this_domain_only; + bool print_execute_only; + const char *w[TOMOYO_MAX_IO_READ_QUEUE]; + } r; /* The position currently writing to. */ struct tomoyo_domain_info *write_var1; - /* The step for reading. */ - int read_step; /* Buffer for reading. */ char *read_buf; - /* EOF flag for reading. */ - bool read_eof; - /* Read domain ACL of specified PID? */ - bool read_single_domain; - /* Extra variable for reading. */ - u8 read_bit; - /* Read only TOMOYO_TYPE_EXECUTE */ - bool print_execute_only; - /* Bytes available for reading. */ - int read_avail; /* Size of read buffer. */ int readbuf_size; /* Buffer for writing. */ @@ -738,8 +722,7 @@ bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, 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); -/* Transactional sprintf() for policy dump. */ -bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) +void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...) __attribute__ ((format(printf, 2, 3))); /* Check whether the domainname is correct. */ bool tomoyo_correct_domain(const unsigned char *domainname); @@ -761,8 +744,6 @@ bool tomoyo_number_matches_group(const unsigned long min, bool tomoyo_path_matches_pattern(const struct tomoyo_path_info *filename, const struct tomoyo_path_info *pattern); -bool tomoyo_print_number_union(struct tomoyo_io_buffer *head, - const struct tomoyo_number_union *ptr); bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num); /* Tokenize a line. */ bool tomoyo_tokenize(char *buffer, char *w[], size_t size); diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index 95a77599ff9..a4aba4d9ca2 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -234,7 +234,7 @@ unsigned int tomoyo_quota_for_query; */ void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) { - if (!head->read_eof) { + if (!head->r.eof) { const unsigned int policy = atomic_read(&tomoyo_policy_memory_size); const unsigned int query = tomoyo_query_memory_size; @@ -258,7 +258,7 @@ void tomoyo_read_memory_counter(struct tomoyo_io_buffer *head) tomoyo_io_printf(head, "Query lists: %10u%s\n", query, buffer); tomoyo_io_printf(head, "Total: %10u\n", policy + query); - head->read_eof = true; + head->r.eof = true; } } -- cgit v1.2.3-70-g09d2 From 8e5686874bcb882f69d5c04e6b38dc92b97facea Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Fri, 25 Jun 2010 09:30:09 +0900 Subject: TOMOYO: Small cleanup. Split tomoyo_write_profile() into several functions. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 195 +++++++++++++++++++++++++++++------------------ 1 file changed, 121 insertions(+), 74 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 7cb6c0459d9..30e4b08905e 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -302,6 +302,124 @@ struct tomoyo_profile *tomoyo_profile(const u8 profile) return ptr; } +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)) + return 1; + else if (!strncmp(cp, "=no", 3)) + return 0; + } + return -1; +} + +static void tomoyo_set_bool(bool *b, const char *string, const char *find) +{ + switch (tomoyo_find_yesno(string, find)) { + case 1: + *b = true; + break; + case 0: + *b = false; + break; + } +} + +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); +} + +static void tomoyo_set_pref(const char *name, const char *value, + const bool use_default, + struct tomoyo_profile *profile) +{ + struct tomoyo_preference **pref; + bool *verbose; + if (!strcmp(name, "enforcing")) { + if (use_default) { + pref = &profile->enforcing; + goto set_default; + } + profile->enforcing = &profile->preference; + verbose = &profile->preference.enforcing_verbose; + goto set_verbose; + } + if (!strcmp(name, "permissive")) { + if (use_default) { + pref = &profile->permissive; + goto set_default; + } + profile->permissive = &profile->preference; + verbose = &profile->preference.permissive_verbose; + goto set_verbose; + } + if (!strcmp(name, "learning")) { + if (use_default) { + pref = &profile->learning; + goto set_default; + } + profile->learning = &profile->preference; + tomoyo_set_uint(&profile->preference.learning_max_entry, value, + "max_entry"); + verbose = &profile->preference.learning_verbose; + goto set_verbose; + } + return; + set_default: + *pref = &tomoyo_default_profile.preference; + return; + set_verbose: + tomoyo_set_bool(verbose, value, "verbose"); +} + +static int tomoyo_set_mode(char *name, const char *value, + const bool use_default, + struct tomoyo_profile *profile) +{ + u8 i; + u8 config; + if (!strcmp(name, "CONFIG")) { + i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; + config = profile->default_config; + } else if (tomoyo_str_starts(&name, "CONFIG::")) { + config = 0; + for (i = 0; i < TOMOYO_MAX_MAC_INDEX + + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { + if (strcmp(name, tomoyo_mac_keywords[i])) + continue; + config = profile->config[i]; + break; + } + if (i == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) + return -EINVAL; + } else { + return -EINVAL; + } + if (use_default) { + 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 = (config & ~7) | mode; + } + if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) + profile->config[i] = config; + else if (config != TOMOYO_CONFIG_USE_DEFAULT) + profile->default_config = config; + return 0; +} + /** * tomoyo_write_profile - Write profile table. * @@ -313,9 +431,6 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) { char *data = head->write_buf; unsigned int i; - int value; - int mode; - u8 config; bool use_default = false; char *cp; struct tomoyo_profile *profile; @@ -338,45 +453,8 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) *cp++ = '\0'; if (profile != &tomoyo_default_profile) use_default = strstr(cp, "use_default") != NULL; - if (strstr(cp, "verbose=yes")) - value = 1; - else if (strstr(cp, "verbose=no")) - value = 0; - else - value = -1; - if (!strcmp(data, "PREFERENCE::enforcing")) { - if (use_default) { - profile->enforcing = &tomoyo_default_profile.preference; - return 0; - } - profile->enforcing = &profile->preference; - if (value >= 0) - profile->preference.enforcing_verbose = value; - return 0; - } - if (!strcmp(data, "PREFERENCE::permissive")) { - if (use_default) { - profile->permissive = &tomoyo_default_profile.preference; - return 0; - } - profile->permissive = &profile->preference; - if (value >= 0) - profile->preference.permissive_verbose = value; - return 0; - } - if (!strcmp(data, "PREFERENCE::learning")) { - char *cp2; - if (use_default) { - profile->learning = &tomoyo_default_profile.preference; - return 0; - } - profile->learning = &profile->preference; - if (value >= 0) - profile->preference.learning_verbose = value; - cp2 = strstr(cp, "max_entry="); - if (cp2) - sscanf(cp2 + 10, "%u", - &profile->preference.learning_max_entry); + if (tomoyo_str_starts(&data, "PREFERENCE::")) { + tomoyo_set_pref(data, cp, use_default, profile); return 0; } if (profile == &tomoyo_default_profile) @@ -387,38 +465,7 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) tomoyo_put_name(old_comment); return 0; } - if (!strcmp(data, "CONFIG")) { - i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; - config = profile->default_config; - } else if (tomoyo_str_starts(&data, "CONFIG::")) { - config = 0; - for (i = 0; i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { - if (strcmp(data, tomoyo_mac_keywords[i])) - continue; - config = profile->config[i]; - break; - } - if (i == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) - return -EINVAL; - } else { - return -EINVAL; - } - if (use_default) { - config = TOMOYO_CONFIG_USE_DEFAULT; - } else { - for (mode = 3; mode >= 0; mode--) - if (strstr(cp, tomoyo_mode[mode])) - /* - * Update lower 3 bits in order to distinguish - * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'. - */ - config = (config & ~7) | mode; - } - if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX) - profile->config[i] = config; - else if (config != TOMOYO_CONFIG_USE_DEFAULT) - profile->default_config = config; - return 0; + return tomoyo_set_mode(data, cp, use_default, profile); } static void tomoyo_print_preference(struct tomoyo_io_buffer *head, -- cgit v1.2.3-70-g09d2 From e2bf69077acefee5247bb661faac2552d29ba7ba Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Fri, 25 Jun 2010 11:16:00 +0900 Subject: TOMOYO: Rename symbols. Use shorter name in order to make it easier to fit 80 columns limit. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 226 +++++++++++++++++++++-------------------------- security/tomoyo/common.h | 54 ++++++----- security/tomoyo/domain.c | 60 ++++++------- security/tomoyo/file.c | 63 +++++++------ security/tomoyo/gc.c | 22 ++--- security/tomoyo/memory.c | 2 +- security/tomoyo/mount.c | 10 +-- security/tomoyo/util.c | 2 +- 8 files changed, 199 insertions(+), 240 deletions(-) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 30e4b08905e..65c18af3ffe 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -248,14 +248,13 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, } /** - * tomoyo_find_or_assign_new_profile - Create a new profile. + * tomoyo_assign_profile - Create a new profile. * * @profile: Profile number to create. * * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise. */ -static struct tomoyo_profile *tomoyo_find_or_assign_new_profile -(const unsigned int profile) +static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile) { struct tomoyo_profile *ptr; struct tomoyo_profile *entry; @@ -443,7 +442,7 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head) if (*cp != '-') return -EINVAL; data = cp + 1; - profile = tomoyo_find_or_assign_new_profile(i); + profile = tomoyo_assign_profile(i); if (!profile) return -EINVAL; } @@ -584,13 +583,11 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head) goto next; } -static bool tomoyo_same_manager_entry(const struct tomoyo_acl_head *a, - const struct tomoyo_acl_head *b) +static bool tomoyo_same_manager(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) { - return container_of(a, struct tomoyo_policy_manager_entry, head) - ->manager == - container_of(b, struct tomoyo_policy_manager_entry, head) - ->manager; + return container_of(a, struct tomoyo_manager, head)->manager == + container_of(b, struct tomoyo_manager, head)->manager; } /** @@ -606,7 +603,7 @@ static bool tomoyo_same_manager_entry(const struct tomoyo_acl_head *a, static int tomoyo_update_manager_entry(const char *manager, const bool is_delete) { - struct tomoyo_policy_manager_entry e = { }; + struct tomoyo_manager e = { }; int error; if (tomoyo_domain_def(manager)) { @@ -622,13 +619,13 @@ static int tomoyo_update_manager_entry(const char *manager, return -ENOMEM; error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, &tomoyo_policy_list[TOMOYO_ID_MANAGER], - tomoyo_same_manager_entry); + tomoyo_same_manager); tomoyo_put_name(e.manager); return error; } /** - * tomoyo_write_manager_policy - Write manager policy. + * tomoyo_write_manager - Write manager policy. * * @head: Pointer to "struct tomoyo_io_buffer". * @@ -636,7 +633,7 @@ static int tomoyo_update_manager_entry(const char *manager, * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head) +static int tomoyo_write_manager(struct tomoyo_io_buffer *head) { char *data = head->write_buf; bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); @@ -649,19 +646,19 @@ static int tomoyo_write_manager_policy(struct tomoyo_io_buffer *head) } /** - * tomoyo_read_manager_policy - Read manager policy. + * tomoyo_read_manager - Read manager policy. * * @head: Pointer to "struct tomoyo_io_buffer". * * Caller holds tomoyo_read_lock(). */ -static void tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) +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]) { - struct tomoyo_policy_manager_entry *ptr = + struct tomoyo_manager *ptr = list_entry(head->r.acl, typeof(*ptr), head.list); if (ptr->head.is_deleted) continue; @@ -674,16 +671,16 @@ static void tomoyo_read_manager_policy(struct tomoyo_io_buffer *head) } /** - * tomoyo_policy_manager - Check whether the current process is a policy manager. + * tomoyo_manager - Check whether the current process is a policy manager. * * Returns true if the current process is permitted to modify policy * via /sys/kernel/security/tomoyo/ interface. * * Caller holds tomoyo_read_lock(). */ -static bool tomoyo_policy_manager(void) +static bool tomoyo_manager(void) { - struct tomoyo_policy_manager_entry *ptr; + struct tomoyo_manager *ptr; const char *exe; const struct task_struct *task = current; const struct tomoyo_path_info *domainname = tomoyo_domain()->domainname; @@ -813,7 +810,7 @@ static int tomoyo_delete_domain(char *domainname) } /** - * tomoyo_write_domain_policy2 - Write domain policy. + * tomoyo_write_domain2 - Write domain policy. * * @head: Pointer to "struct tomoyo_io_buffer". * @@ -821,17 +818,16 @@ static int tomoyo_delete_domain(char *domainname) * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_write_domain_policy2(char *data, - struct tomoyo_domain_info *domain, - const bool is_delete) +static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain, + const bool is_delete) { if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT)) - return tomoyo_write_mount_policy(data, domain, is_delete); - return tomoyo_write_file_policy(data, domain, is_delete); + return tomoyo_write_mount(data, domain, is_delete); + return tomoyo_write_file(data, domain, is_delete); } /** - * tomoyo_write_domain_policy - Write domain policy. + * tomoyo_write_domain - Write domain policy. * * @head: Pointer to "struct tomoyo_io_buffer". * @@ -839,7 +835,7 @@ static int tomoyo_write_domain_policy2(char *data, * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) +static int tomoyo_write_domain(struct tomoyo_io_buffer *head) { char *data = head->write_buf; struct tomoyo_domain_info *domain = head->write_var1; @@ -854,7 +850,7 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) if (is_select && tomoyo_select_one(head, data)) return 0; /* Don't allow updating policies by non manager programs. */ - if (!tomoyo_policy_manager()) + if (!tomoyo_manager()) return -EPERM; if (tomoyo_domain_def(data)) { domain = NULL; @@ -863,7 +859,7 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) else if (is_select) domain = tomoyo_find_domain(data); else - domain = tomoyo_find_or_assign_new_domain(data, 0); + domain = tomoyo_assign_domain(data, 0); head->write_var1 = domain; return 0; } @@ -888,7 +884,7 @@ static int tomoyo_write_domain_policy(struct tomoyo_io_buffer *head) domain->transition_failed = !is_delete; return 0; } - return tomoyo_write_domain_policy2(data, domain, is_delete); + return tomoyo_write_domain2(data, domain, is_delete); } /** @@ -1022,13 +1018,13 @@ static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head, } /** - * tomoyo_read_domain_policy - Read domain policy. + * tomoyo_read_domain - Read domain policy. * * @head: Pointer to "struct tomoyo_io_buffer". * * Caller holds tomoyo_read_lock(). */ -static void tomoyo_read_domain_policy(struct tomoyo_io_buffer *head) +static void tomoyo_read_domain(struct tomoyo_io_buffer *head) { if (head->r.eof) return; @@ -1208,8 +1204,13 @@ static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = { [TOMOYO_TRANSITION_CONTROL_KEEP] = TOMOYO_KEYWORD_KEEP_DOMAIN }; +static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { + [TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP, + [TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP +}; + /** - * tomoyo_write_exception_policy - Write exception policy. + * tomoyo_write_exception - Write exception policy. * * @head: Pointer to "struct tomoyo_io_buffer". * @@ -1217,37 +1218,34 @@ static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = { * * Caller holds tomoyo_read_lock(). */ -static int tomoyo_write_exception_policy(struct tomoyo_io_buffer *head) +static int tomoyo_write_exception(struct tomoyo_io_buffer *head) { char *data = head->write_buf; bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); u8 i; - - for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++) { + static const struct { + const char *keyword; + int (*write) (char *, const bool); + } tomoyo_callback[4] = { + { TOMOYO_KEYWORD_AGGREGATOR, tomoyo_write_aggregator }, + { TOMOYO_KEYWORD_FILE_PATTERN, tomoyo_write_pattern }, + { TOMOYO_KEYWORD_DENY_REWRITE, tomoyo_write_no_rewrite }, + { TOMOYO_KEYWORD_ALLOW_READ, tomoyo_write_globally_readable }, + }; + + for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++) if (tomoyo_str_starts(&data, tomoyo_transition_type[i])) return tomoyo_write_transition_control(data, is_delete, i); - } - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_AGGREGATOR)) - return tomoyo_write_aggregator_policy(data, is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_READ)) - return tomoyo_write_globally_readable_policy(data, is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_FILE_PATTERN)) - return tomoyo_write_pattern_policy(data, is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DENY_REWRITE)) - return tomoyo_write_no_rewrite_policy(data, is_delete); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_PATH_GROUP)) - return tomoyo_write_group(data, is_delete, TOMOYO_PATH_GROUP); - if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_NUMBER_GROUP)) - return tomoyo_write_group(data, is_delete, TOMOYO_NUMBER_GROUP); + for (i = 0; i < 4; i++) + if (tomoyo_str_starts(&data, tomoyo_callback[i].keyword)) + return tomoyo_callback[i].write(data, is_delete); + for (i = 0; i < TOMOYO_MAX_GROUP; i++) + if (tomoyo_str_starts(&data, tomoyo_group_name[i])) + return tomoyo_write_group(data, is_delete, i); return -EINVAL; } -static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { - [TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP, - [TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP -}; - /** * tomoyo_read_group - Read "struct tomoyo_path_group"/"struct tomoyo_number_group" list. * @@ -1331,8 +1329,8 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) break; case TOMOYO_ID_GLOBALLY_READABLE: { - struct tomoyo_globally_readable_file_entry *ptr - = container_of(acl, typeof(*ptr), head); + struct tomoyo_readable_file *ptr = + container_of(acl, typeof(*ptr), head); tomoyo_set_string(head, TOMOYO_KEYWORD_ALLOW_READ); tomoyo_set_string(head, ptr->filename->name); @@ -1340,7 +1338,7 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) break; case TOMOYO_ID_AGGREGATOR: { - struct tomoyo_aggregator_entry *ptr = + struct tomoyo_aggregator *ptr = container_of(acl, typeof(*ptr), head); tomoyo_set_string(head, TOMOYO_KEYWORD_AGGREGATOR); @@ -1353,7 +1351,7 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) break; case TOMOYO_ID_PATTERN: { - struct tomoyo_pattern_entry *ptr = + struct tomoyo_no_pattern *ptr = container_of(acl, typeof(*ptr), head); tomoyo_set_string(head, TOMOYO_KEYWORD_FILE_PATTERN); @@ -1362,7 +1360,7 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) break; case TOMOYO_ID_NO_REWRITE: { - struct tomoyo_no_rewrite_entry *ptr = + struct tomoyo_no_rewrite *ptr = container_of(acl, typeof(*ptr), head); tomoyo_set_string(head, TOMOYO_KEYWORD_DENY_REWRITE); @@ -1379,13 +1377,13 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx) } /** - * tomoyo_read_exception_policy - Read exception policy. + * tomoyo_read_exception - Read exception policy. * * @head: Pointer to "struct tomoyo_io_buffer". * * Caller holds tomoyo_read_lock(). */ -static void tomoyo_read_exception_policy(struct tomoyo_io_buffer *head) +static void tomoyo_read_exception(struct tomoyo_io_buffer *head) { if (head->r.eof) return; @@ -1472,7 +1470,7 @@ static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait); static DEFINE_SPINLOCK(tomoyo_query_list_lock); /* Structure for query. */ -struct tomoyo_query_entry { +struct tomoyo_query { struct list_head list; char *query; int query_len; @@ -1481,7 +1479,7 @@ struct tomoyo_query_entry { int answer; }; -/* The list for "struct tomoyo_query_entry". */ +/* The list for "struct tomoyo_query". */ static LIST_HEAD(tomoyo_query_list); /* @@ -1508,7 +1506,7 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) int pos; int len; static unsigned int tomoyo_serial; - struct tomoyo_query_entry *tomoyo_query_entry = NULL; + struct tomoyo_query *entry = NULL; bool quota_exceeded = false; char *header; switch (r->mode) { @@ -1526,7 +1524,7 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) vsnprintf(buffer, len - 1, fmt, args); va_end(args); tomoyo_normalize_line(buffer); - tomoyo_write_domain_policy2(buffer, r->domain, false); + tomoyo_write_domain2(buffer, r->domain, false); kfree(buffer); /* fall through */ case TOMOYO_CONFIG_PERMISSIVE: @@ -1542,51 +1540,50 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) header = tomoyo_init_audit_log(&len, r); if (!header) goto out; - tomoyo_query_entry = kzalloc(sizeof(*tomoyo_query_entry), GFP_NOFS); - if (!tomoyo_query_entry) + entry = kzalloc(sizeof(*entry), GFP_NOFS); + if (!entry) goto out; - tomoyo_query_entry->query = kzalloc(len, GFP_NOFS); - if (!tomoyo_query_entry->query) + entry->query = kzalloc(len, GFP_NOFS); + if (!entry->query) goto out; - len = ksize(tomoyo_query_entry->query); - INIT_LIST_HEAD(&tomoyo_query_entry->list); + len = ksize(entry->query); spin_lock(&tomoyo_query_list_lock); if (tomoyo_quota_for_query && tomoyo_query_memory_size + len + - sizeof(*tomoyo_query_entry) >= tomoyo_quota_for_query) { + sizeof(*entry) >= tomoyo_quota_for_query) { quota_exceeded = true; } else { - tomoyo_query_memory_size += len + sizeof(*tomoyo_query_entry); - tomoyo_query_entry->serial = tomoyo_serial++; + tomoyo_query_memory_size += len + sizeof(*entry); + entry->serial = tomoyo_serial++; } spin_unlock(&tomoyo_query_list_lock); if (quota_exceeded) goto out; - pos = snprintf(tomoyo_query_entry->query, len - 1, "Q%u-%hu\n%s", - tomoyo_query_entry->serial, r->retry, header); + pos = snprintf(entry->query, len - 1, "Q%u-%hu\n%s", + entry->serial, r->retry, header); kfree(header); header = NULL; va_start(args, fmt); - vsnprintf(tomoyo_query_entry->query + pos, len - 1 - pos, fmt, args); - tomoyo_query_entry->query_len = strlen(tomoyo_query_entry->query) + 1; + vsnprintf(entry->query + pos, len - 1 - pos, fmt, args); + entry->query_len = strlen(entry->query) + 1; va_end(args); spin_lock(&tomoyo_query_list_lock); - list_add_tail(&tomoyo_query_entry->list, &tomoyo_query_list); + list_add_tail(&entry->list, &tomoyo_query_list); spin_unlock(&tomoyo_query_list_lock); /* Give 10 seconds for supervisor's opinion. */ - for (tomoyo_query_entry->timer = 0; - atomic_read(&tomoyo_query_observers) && tomoyo_query_entry->timer < 100; - tomoyo_query_entry->timer++) { + for (entry->timer = 0; + atomic_read(&tomoyo_query_observers) && entry->timer < 100; + entry->timer++) { wake_up(&tomoyo_query_wait); set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(HZ / 10); - if (tomoyo_query_entry->answer) + if (entry->answer) break; } spin_lock(&tomoyo_query_list_lock); - list_del(&tomoyo_query_entry->list); - tomoyo_query_memory_size -= len + sizeof(*tomoyo_query_entry); + list_del(&entry->list); + tomoyo_query_memory_size -= len + sizeof(*entry); spin_unlock(&tomoyo_query_list_lock); - switch (tomoyo_query_entry->answer) { + switch (entry->answer) { case 3: /* Asked to retry by administrator. */ error = TOMOYO_RETRY_REQUEST; r->retry++; @@ -1603,9 +1600,9 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) break; } out: - if (tomoyo_query_entry) - kfree(tomoyo_query_entry->query); - kfree(tomoyo_query_entry); + if (entry) + kfree(entry->query); + kfree(entry); kfree(header); return error; } @@ -1628,9 +1625,8 @@ static int tomoyo_poll_query(struct file *file, poll_table *wait) for (i = 0; i < 2; i++) { spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { - struct tomoyo_query_entry *ptr - = list_entry(tmp, struct tomoyo_query_entry, - list); + struct tomoyo_query *ptr = + list_entry(tmp, typeof(*ptr), list); if (ptr->answer) continue; found = true; @@ -1665,8 +1661,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_entry *ptr = - list_entry(tmp, typeof(*ptr), list); + struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); if (ptr->answer) continue; if (pos++ != head->r.query_index) @@ -1685,8 +1680,7 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head) pos = 0; spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { - struct tomoyo_query_entry *ptr = - list_entry(tmp, typeof(*ptr), list); + struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); if (ptr->answer) continue; if (pos++ != head->r.query_index) @@ -1724,8 +1718,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) unsigned int answer; spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { - struct tomoyo_query_entry *ptr - = list_entry(tmp, struct tomoyo_query_entry, list); + struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); ptr->timer = 0; } spin_unlock(&tomoyo_query_list_lock); @@ -1733,8 +1726,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head) return -EINVAL; spin_lock(&tomoyo_query_list_lock); list_for_each(tmp, &tomoyo_query_list) { - struct tomoyo_query_entry *ptr - = list_entry(tmp, struct tomoyo_query_entry, list); + struct tomoyo_query *ptr = list_entry(tmp, typeof(*ptr), list); if (ptr->serial != serial) continue; if (!ptr->answer) @@ -1801,13 +1793,13 @@ int tomoyo_open_control(const u8 type, struct file *file) switch (type) { case TOMOYO_DOMAINPOLICY: /* /sys/kernel/security/tomoyo/domain_policy */ - head->write = tomoyo_write_domain_policy; - head->read = tomoyo_read_domain_policy; + head->write = tomoyo_write_domain; + head->read = tomoyo_read_domain; break; case TOMOYO_EXCEPTIONPOLICY: /* /sys/kernel/security/tomoyo/exception_policy */ - head->write = tomoyo_write_exception_policy; - head->read = tomoyo_read_exception_policy; + head->write = tomoyo_write_exception; + head->read = tomoyo_read_exception; break; case TOMOYO_SELFDOMAIN: /* /sys/kernel/security/tomoyo/self_domain */ @@ -1846,8 +1838,8 @@ int tomoyo_open_control(const u8 type, struct file *file) break; case TOMOYO_MANAGER: /* /sys/kernel/security/tomoyo/manager */ - head->write = tomoyo_write_manager_policy; - head->read = tomoyo_read_manager_policy; + head->write = tomoyo_write_manager; + head->read = tomoyo_read_manager; break; } if (!(file->f_mode & FMODE_READ)) { @@ -1905,23 +1897,6 @@ int tomoyo_open_control(const u8 type, struct file *file) return 0; } -/** - * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface. - * - * @file: Pointer to "struct file". - * @wait: Pointer to "poll_table". - * - * Waits for read readiness. - * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd . - */ -int tomoyo_poll_control(struct file *file, poll_table *wait) -{ - struct tomoyo_io_buffer *head = file->private_data; - if (!head->poll) - return -ENOSYS; - return head->poll(file, wait); -} - /** * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. * @@ -1979,8 +1954,7 @@ int tomoyo_write_control(struct file *file, const char __user *buffer, return -EFAULT; /* Don't allow updating policies by non manager programs. */ if (head->write != tomoyo_write_pid && - head->write != tomoyo_write_domain_policy && - !tomoyo_policy_manager()) + head->write != tomoyo_write_domain && !tomoyo_manager()) return -EPERM; if (mutex_lock_interruptible(&head->io_sem)) return -EINTR; diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index ef8fecddb65..b8b2dac656f 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -310,10 +310,10 @@ struct tomoyo_path_info { }; /* - * tomoyo_name_entry is a structure which is used for linking + * tomoyo_name is a structure which is used for linking * "struct tomoyo_path_info" into tomoyo_name_list . */ -struct tomoyo_name_entry { +struct tomoyo_name { struct list_head list; atomic_t users; struct tomoyo_path_info entry; @@ -572,20 +572,20 @@ struct tomoyo_io_buffer { }; /* - * tomoyo_globally_readable_file_entry is a structure which is used for holding + * tomoyo_readable_file is a structure which is used for holding * "allow_read" entries. * It has following fields. * * (1) "head" is "struct tomoyo_acl_head". * (2) "filename" is a pathname which is allowed to open(O_RDONLY). */ -struct tomoyo_globally_readable_file_entry { +struct tomoyo_readable_file { struct tomoyo_acl_head head; const struct tomoyo_path_info *filename; }; /* - * tomoyo_pattern_entry is a structure which is used for holding + * tomoyo_no_pattern is a structure which is used for holding * "file_pattern" entries. * It has following fields. * @@ -593,13 +593,13 @@ struct tomoyo_globally_readable_file_entry { * (2) "pattern" is a pathname pattern which is used for converting pathnames * to pathname patterns during learning mode. */ -struct tomoyo_pattern_entry { +struct tomoyo_no_pattern { struct tomoyo_acl_head head; const struct tomoyo_path_info *pattern; }; /* - * tomoyo_no_rewrite_entry is a structure which is used for holding + * tomoyo_no_rewrite is a structure which is used for holding * "deny_rewrite" entries. * It has following fields. * @@ -607,7 +607,7 @@ struct tomoyo_pattern_entry { * (2) "pattern" is a pathname which is by default not permitted to modify * already existing content. */ -struct tomoyo_no_rewrite_entry { +struct tomoyo_no_rewrite { struct tomoyo_acl_head head; const struct tomoyo_path_info *pattern; }; @@ -636,7 +636,7 @@ struct tomoyo_transition_control { }; /* - * tomoyo_aggregator_entry is a structure which is used for holding + * tomoyo_aggregator is a structure which is used for holding * "aggregator" entries. * It has following fields. * @@ -644,14 +644,14 @@ struct tomoyo_transition_control { * (2) "original_name" which is originally requested name. * (3) "aggregated_name" which is name to rewrite. */ -struct tomoyo_aggregator_entry { +struct tomoyo_aggregator { struct tomoyo_acl_head head; const struct tomoyo_path_info *original_name; const struct tomoyo_path_info *aggregated_name; }; /* - * tomoyo_policy_manager_entry is a structure which is used for holding list of + * tomoyo_manager is a structure which is used for holding list of * domainnames or programs which are permitted to modify configuration via * /sys/kernel/security/tomoyo/ interface. * It has following fields. @@ -661,7 +661,7 @@ struct tomoyo_aggregator_entry { * otherwise. * (3) "manager" is a domainname or a program's pathname. */ -struct tomoyo_policy_manager_entry { +struct tomoyo_manager { struct tomoyo_acl_head head; bool is_domain; /* True if manager is a domainname. */ /* A path to program or a domainname. */ @@ -749,8 +749,6 @@ bool tomoyo_parse_number_union(char *data, struct tomoyo_number_union *num); bool tomoyo_tokenize(char *buffer, char *w[], size_t size); /* Write domain policy violation warning message to console? */ bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain); -/* Get the last component of the given domainname. */ -const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain); /* Fill "struct tomoyo_request_info". */ int tomoyo_init_request_info(struct tomoyo_request_info *r, struct tomoyo_domain_info *domain, @@ -759,7 +757,7 @@ int tomoyo_init_request_info(struct tomoyo_request_info *r, int tomoyo_mount_permission(char *dev_name, struct path *path, char *type, unsigned long flags, void *data_page); /* Create "aggregator" entry in exception policy. */ -int tomoyo_write_aggregator_policy(char *data, const bool is_delete); +int tomoyo_write_aggregator(char *data, const bool is_delete); int tomoyo_write_transition_control(char *data, const bool is_delete, const u8 type); /* @@ -769,17 +767,17 @@ int tomoyo_write_transition_control(char *data, const bool is_delete, * "allow_truncate", "allow_symlink", "allow_rewrite", "allow_rename" and * "allow_link" entry in domain policy. */ -int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, - const bool is_delete); +int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain, + const bool is_delete); /* Create "allow_read" entry in exception policy. */ -int tomoyo_write_globally_readable_policy(char *data, const bool is_delete); +int tomoyo_write_globally_readable(char *data, const bool is_delete); /* Create "allow_mount" entry in domain policy. */ -int tomoyo_write_mount_policy(char *data, struct tomoyo_domain_info *domain, - const bool is_delete); +int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain, + const bool is_delete); /* Create "deny_rewrite" entry in exception policy. */ -int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete); +int tomoyo_write_no_rewrite(char *data, const bool is_delete); /* Create "file_pattern" entry in exception policy. */ -int tomoyo_write_pattern_policy(char *data, const bool is_delete); +int tomoyo_write_pattern(char *data, const bool is_delete); /* Create "path_group"/"number_group" entry in exception policy. */ int tomoyo_write_group(char *data, const bool is_delete, const u8 type); int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) @@ -787,9 +785,8 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...) /* Find a domain by the given name. */ struct tomoyo_domain_info *tomoyo_find_domain(const char *domainname); /* Find or create a domain by the given name. */ -struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * - domainname, - const u8 profile); +struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, + const u8 profile); struct tomoyo_profile *tomoyo_profile(const u8 profile); /* * Allocate memory for "struct tomoyo_path_group"/"struct tomoyo_number_group". @@ -820,7 +817,7 @@ char *tomoyo_realpath_nofollow(const char *pathname); */ char *tomoyo_realpath_from_path(struct path *path); /* Get patterned pathname. */ -const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename); +const char *tomoyo_pattern(const struct tomoyo_path_info *filename); /* Check memory quota. */ bool tomoyo_memory_ok(void *ptr); @@ -882,7 +879,6 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, void tomoyo_check_acl(struct tomoyo_request_info *r, bool (*check_entry) (const struct tomoyo_request_info *, const struct tomoyo_acl_info *)); -const char *tomoyo_last_word(const char *name); /********** External variable definitions. **********/ @@ -959,8 +955,8 @@ static inline bool tomoyo_invalid(const unsigned char c) static inline void tomoyo_put_name(const struct tomoyo_path_info *name) { if (name) { - struct tomoyo_name_entry *ptr = - container_of(name, struct tomoyo_name_entry, entry); + struct tomoyo_name *ptr = + container_of(name, typeof(*ptr), entry); atomic_dec(&ptr->users); } } diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 05450b17c57..4e0101b0041 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -134,26 +134,22 @@ struct list_head tomoyo_policy_list[TOMOYO_MAX_POLICY]; struct list_head tomoyo_group_list[TOMOYO_MAX_GROUP]; /** - * tomoyo_get_last_name - Get last component of a domainname. + * tomoyo_last_word - Get last component of a domainname. * - * @domain: Pointer to "struct tomoyo_domain_info". + * @domainname: Domainname to check. * - * Returns the last component of the domainname. + * Returns the last word of @domainname. */ -const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain) +static const char *tomoyo_last_word(const char *name) { - const char *cp0 = domain->domainname->name; - const char *cp1 = strrchr(cp0, ' '); - - if (cp1) - return cp1 + 1; - return cp0; + const char *cp = strrchr(name, ' '); + if (cp) + return cp + 1; + return name; } -static bool tomoyo_same_transition_control_entry(const struct tomoyo_acl_head * - a, - const struct tomoyo_acl_head * - b) +static bool tomoyo_same_transition_control(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) { const struct tomoyo_transition_control *p1 = container_of(a, typeof(*p1), @@ -203,7 +199,7 @@ static int tomoyo_update_transition_control_entry(const char *domainname, error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, &tomoyo_policy_list [TOMOYO_ID_TRANSITION_CONTROL], - tomoyo_same_transition_control_entry); + tomoyo_same_transition_control); out: tomoyo_put_name(e.domainname); tomoyo_put_name(e.program); @@ -292,19 +288,17 @@ static u8 tomoyo_transition_type(const struct tomoyo_path_info *domainname, return type; } -static bool tomoyo_same_aggregator_entry(const struct tomoyo_acl_head *a, - const struct tomoyo_acl_head *b) +static bool tomoyo_same_aggregator(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) { - const struct tomoyo_aggregator_entry *p1 = container_of(a, typeof(*p1), - head); - const struct tomoyo_aggregator_entry *p2 = container_of(b, typeof(*p2), - head); + const struct tomoyo_aggregator *p1 = container_of(a, typeof(*p1), head); + const struct tomoyo_aggregator *p2 = container_of(b, typeof(*p2), head); return p1->original_name == p2->original_name && p1->aggregated_name == p2->aggregated_name; } /** - * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator_entry" list. + * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" list. * * @original_name: The original program's name. * @aggregated_name: The program name to use. @@ -318,7 +312,7 @@ static int tomoyo_update_aggregator_entry(const char *original_name, const char *aggregated_name, const bool is_delete) { - struct tomoyo_aggregator_entry e = { }; + struct tomoyo_aggregator e = { }; int error = is_delete ? -ENOENT : -ENOMEM; if (!tomoyo_correct_path(original_name) || @@ -331,7 +325,7 @@ static int tomoyo_update_aggregator_entry(const char *original_name, goto out; error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR], - tomoyo_same_aggregator_entry); + tomoyo_same_aggregator); out: tomoyo_put_name(e.original_name); tomoyo_put_name(e.aggregated_name); @@ -339,7 +333,7 @@ static int tomoyo_update_aggregator_entry(const char *original_name, } /** - * tomoyo_write_aggregator_policy - Write "struct tomoyo_aggregator_entry" list. + * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list. * * @data: String to parse. * @is_delete: True if it is a delete request. @@ -348,7 +342,7 @@ static int tomoyo_update_aggregator_entry(const char *original_name, * * Caller holds tomoyo_read_lock(). */ -int tomoyo_write_aggregator_policy(char *data, const bool is_delete) +int tomoyo_write_aggregator(char *data, const bool is_delete) { char *cp = strchr(data, ' '); @@ -359,7 +353,7 @@ int tomoyo_write_aggregator_policy(char *data, const bool is_delete) } /** - * tomoyo_find_or_assign_new_domain - Create a domain. + * tomoyo_assign_domain - Create a domain. * * @domainname: The name of domain. * @profile: Profile number to assign if the domain was newly created. @@ -368,9 +362,8 @@ int tomoyo_write_aggregator_policy(char *data, const bool is_delete) * * Caller holds tomoyo_read_lock(). */ -struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * - domainname, - const u8 profile) +struct tomoyo_domain_info *tomoyo_assign_domain(const char *domainname, + const u8 profile) { struct tomoyo_domain_info *entry; struct tomoyo_domain_info *domain = NULL; @@ -430,10 +423,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) int retval = -ENOMEM; bool need_kfree = false; struct tomoyo_path_info rn = { }; /* real name */ - struct tomoyo_path_info ln; /* last name */ - ln.name = tomoyo_get_last_name(old_domain); - tomoyo_fill_path_info(&ln); mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); if (!tmp) @@ -454,7 +444,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) /* Check 'aggregator' directive. */ { - struct tomoyo_aggregator_entry *ptr; + struct tomoyo_aggregator *ptr; list_for_each_entry_rcu(ptr, &tomoyo_policy_list [TOMOYO_ID_AGGREGATOR], head.list) { if (ptr->head.is_deleted || @@ -517,7 +507,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) if (error < 0) goto done; } - domain = tomoyo_find_or_assign_new_domain(tmp, old_domain->profile); + domain = tomoyo_assign_domain(tmp, old_domain->profile); done: if (domain) goto out; diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index e7687ebdc5f..f7877fa80f1 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -179,7 +179,7 @@ static int tomoyo_audit_path_log(struct tomoyo_request_info *r) return 0; tomoyo_warn_log(r, "%s %s", operation, filename->name); return tomoyo_supervisor(r, "allow_%s %s\n", operation, - tomoyo_file_pattern(filename)); + tomoyo_pattern(filename)); } /** @@ -199,8 +199,8 @@ static int tomoyo_audit_path2_log(struct tomoyo_request_info *r) tomoyo_warn_log(r, "%s %s %s", operation, filename1->name, filename2->name); return tomoyo_supervisor(r, "allow_%s %s %s\n", operation, - tomoyo_file_pattern(filename1), - tomoyo_file_pattern(filename2)); + tomoyo_pattern(filename1), + tomoyo_pattern(filename2)); } /** @@ -222,8 +222,7 @@ static int tomoyo_audit_mkdev_log(struct tomoyo_request_info *r) tomoyo_warn_log(r, "%s %s 0%o %u %u", operation, filename->name, mode, major, minor); return tomoyo_supervisor(r, "allow_%s %s 0%o %u %u\n", operation, - tomoyo_file_pattern(filename), mode, major, - minor); + tomoyo_pattern(filename), mode, major, minor); } /** @@ -262,20 +261,20 @@ static int tomoyo_audit_path_number_log(struct tomoyo_request_info *r) radix); tomoyo_warn_log(r, "%s %s %s", operation, filename->name, buffer); return tomoyo_supervisor(r, "allow_%s %s %s\n", operation, - tomoyo_file_pattern(filename), buffer); + tomoyo_pattern(filename), buffer); } static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { - return container_of(a, struct tomoyo_globally_readable_file_entry, + return container_of(a, struct tomoyo_readable_file, head)->filename == - container_of(b, struct tomoyo_globally_readable_file_entry, + container_of(b, struct tomoyo_readable_file, head)->filename; } /** - * tomoyo_update_globally_readable_entry - Update "struct tomoyo_globally_readable_file_entry" list. + * tomoyo_update_globally_readable_entry - Update "struct tomoyo_readable_file" list. * * @filename: Filename unconditionally permitted to open() for reading. * @is_delete: True if it is a delete request. @@ -287,7 +286,7 @@ static bool tomoyo_same_globally_readable(const struct tomoyo_acl_head *a, static int tomoyo_update_globally_readable_entry(const char *filename, const bool is_delete) { - struct tomoyo_globally_readable_file_entry e = { }; + struct tomoyo_readable_file e = { }; int error; if (!tomoyo_correct_word(filename)) @@ -315,7 +314,7 @@ static int tomoyo_update_globally_readable_entry(const char *filename, static bool tomoyo_globally_readable_file(const struct tomoyo_path_info * filename) { - struct tomoyo_globally_readable_file_entry *ptr; + struct tomoyo_readable_file *ptr; bool found = false; list_for_each_entry_rcu(ptr, &tomoyo_policy_list @@ -330,7 +329,7 @@ static bool tomoyo_globally_readable_file(const struct tomoyo_path_info * } /** - * tomoyo_write_globally_readable_policy - Write "struct tomoyo_globally_readable_file_entry" list. + * tomoyo_write_globally_readable - Write "struct tomoyo_readable_file" list. * * @data: String to parse. * @is_delete: True if it is a delete request. @@ -339,7 +338,7 @@ static bool tomoyo_globally_readable_file(const struct tomoyo_path_info * * * Caller holds tomoyo_read_lock(). */ -int tomoyo_write_globally_readable_policy(char *data, const bool is_delete) +int tomoyo_write_globally_readable(char *data, const bool is_delete) { return tomoyo_update_globally_readable_entry(data, is_delete); } @@ -347,12 +346,12 @@ int tomoyo_write_globally_readable_policy(char *data, const bool is_delete) static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { - return container_of(a, struct tomoyo_pattern_entry, head)->pattern == - container_of(b, struct tomoyo_pattern_entry, head)->pattern; + return container_of(a, struct tomoyo_no_pattern, head)->pattern == + container_of(b, struct tomoyo_no_pattern, head)->pattern; } /** - * tomoyo_update_file_pattern_entry - Update "struct tomoyo_pattern_entry" list. + * tomoyo_update_file_pattern_entry - Update "struct tomoyo_no_pattern" list. * * @pattern: Pathname pattern. * @is_delete: True if it is a delete request. @@ -364,7 +363,7 @@ static bool tomoyo_same_pattern(const struct tomoyo_acl_head *a, static int tomoyo_update_file_pattern_entry(const char *pattern, const bool is_delete) { - struct tomoyo_pattern_entry e = { }; + struct tomoyo_no_pattern e = { }; int error; if (!tomoyo_correct_word(pattern)) @@ -380,7 +379,7 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, } /** - * tomoyo_file_pattern - Get patterned pathname. + * tomoyo_pattern - Get patterned pathname. * * @filename: The filename to find patterned pathname. * @@ -388,9 +387,9 @@ static int tomoyo_update_file_pattern_entry(const char *pattern, * * Caller holds tomoyo_read_lock(). */ -const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename) +const char *tomoyo_pattern(const struct tomoyo_path_info *filename) { - struct tomoyo_pattern_entry *ptr; + struct tomoyo_no_pattern *ptr; const struct tomoyo_path_info *pattern = NULL; list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_PATTERN], @@ -413,7 +412,7 @@ const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename) } /** - * tomoyo_write_pattern_policy - Write "struct tomoyo_pattern_entry" list. + * tomoyo_write_pattern - Write "struct tomoyo_no_pattern" list. * * @data: String to parse. * @is_delete: True if it is a delete request. @@ -422,7 +421,7 @@ const char *tomoyo_file_pattern(const struct tomoyo_path_info *filename) * * Caller holds tomoyo_read_lock(). */ -int tomoyo_write_pattern_policy(char *data, const bool is_delete) +int tomoyo_write_pattern(char *data, const bool is_delete) { return tomoyo_update_file_pattern_entry(data, is_delete); } @@ -430,13 +429,13 @@ int tomoyo_write_pattern_policy(char *data, const bool is_delete) static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a, const struct tomoyo_acl_head *b) { - return container_of(a, struct tomoyo_no_rewrite_entry, head)->pattern - == container_of(b, struct tomoyo_no_rewrite_entry, head) + return container_of(a, struct tomoyo_no_rewrite, head)->pattern + == container_of(b, struct tomoyo_no_rewrite, head) ->pattern; } /** - * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite_entry" list. + * tomoyo_update_no_rewrite_entry - Update "struct tomoyo_no_rewrite" list. * * @pattern: Pathname pattern that are not rewritable by default. * @is_delete: True if it is a delete request. @@ -448,7 +447,7 @@ static bool tomoyo_same_no_rewrite(const struct tomoyo_acl_head *a, static int tomoyo_update_no_rewrite_entry(const char *pattern, const bool is_delete) { - struct tomoyo_no_rewrite_entry e = { }; + struct tomoyo_no_rewrite e = { }; int error; if (!tomoyo_correct_word(pattern)) @@ -475,7 +474,7 @@ static int tomoyo_update_no_rewrite_entry(const char *pattern, */ static bool tomoyo_no_rewrite_file(const struct tomoyo_path_info *filename) { - struct tomoyo_no_rewrite_entry *ptr; + struct tomoyo_no_rewrite *ptr; bool found = false; list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_NO_REWRITE], @@ -491,7 +490,7 @@ static bool tomoyo_no_rewrite_file(const struct tomoyo_path_info *filename) } /** - * tomoyo_write_no_rewrite_policy - Write "struct tomoyo_no_rewrite_entry" list. + * tomoyo_write_no_rewrite - Write "struct tomoyo_no_rewrite" list. * * @data: String to parse. * @is_delete: True if it is a delete request. @@ -500,7 +499,7 @@ static bool tomoyo_no_rewrite_file(const struct tomoyo_path_info *filename) * * Caller holds tomoyo_read_lock(). */ -int tomoyo_write_no_rewrite_policy(char *data, const bool is_delete) +int tomoyo_write_no_rewrite(char *data, const bool is_delete) { return tomoyo_update_no_rewrite_entry(data, is_delete); } @@ -1121,7 +1120,7 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, } /** - * tomoyo_write_file_policy - Update file related list. + * tomoyo_write_file - Update file related list. * * @data: String to parse. * @domain: Pointer to "struct tomoyo_domain_info". @@ -1131,8 +1130,8 @@ int tomoyo_path2_perm(const u8 operation, struct path *path1, * * Caller holds tomoyo_read_lock(). */ -int tomoyo_write_file_policy(char *data, struct tomoyo_domain_info *domain, - const bool is_delete) +int tomoyo_write_file(char *data, struct tomoyo_domain_info *domain, + const bool is_delete) { char *w[5]; u8 type; diff --git a/security/tomoyo/gc.c b/security/tomoyo/gc.c index 254ac114555..a877e4c3b10 100644 --- a/security/tomoyo/gc.c +++ b/security/tomoyo/gc.c @@ -11,7 +11,7 @@ #include #include -struct tomoyo_gc_entry { +struct tomoyo_gc { struct list_head list; int type; struct list_head *element; @@ -22,7 +22,7 @@ static DEFINE_MUTEX(tomoyo_gc_mutex); /* Caller holds tomoyo_policy_lock mutex. */ static bool tomoyo_add_to_gc(const int type, struct list_head *element) { - struct tomoyo_gc_entry *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); + struct tomoyo_gc *entry = kzalloc(sizeof(*entry), GFP_ATOMIC); if (!entry) return false; entry->type = type; @@ -34,21 +34,21 @@ static bool tomoyo_add_to_gc(const int type, struct list_head *element) static void tomoyo_del_allow_read(struct list_head *element) { - struct tomoyo_globally_readable_file_entry *ptr = + struct tomoyo_readable_file *ptr = container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->filename); } static void tomoyo_del_file_pattern(struct list_head *element) { - struct tomoyo_pattern_entry *ptr = + struct tomoyo_no_pattern *ptr = container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->pattern); } static void tomoyo_del_no_rewrite(struct list_head *element) { - struct tomoyo_no_rewrite_entry *ptr = + struct tomoyo_no_rewrite *ptr = container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->pattern); } @@ -63,7 +63,7 @@ static void tomoyo_del_transition_control(struct list_head *element) static void tomoyo_del_aggregator(struct list_head *element) { - struct tomoyo_aggregator_entry *ptr = + struct tomoyo_aggregator *ptr = container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->original_name); tomoyo_put_name(ptr->aggregated_name); @@ -71,7 +71,7 @@ static void tomoyo_del_aggregator(struct list_head *element) static void tomoyo_del_manager(struct list_head *element) { - struct tomoyo_policy_manager_entry *ptr = + struct tomoyo_manager *ptr = container_of(element, typeof(*ptr), head.list); tomoyo_put_name(ptr->manager); } @@ -168,7 +168,7 @@ static bool tomoyo_del_domain(struct list_head *element) static void tomoyo_del_name(struct list_head *element) { - const struct tomoyo_name_entry *ptr = + const struct tomoyo_name *ptr = container_of(element, typeof(*ptr), list); } @@ -242,7 +242,7 @@ static void tomoyo_collect_entry(void) } } for (i = 0; i < TOMOYO_MAX_HASH; i++) { - struct tomoyo_name_entry *ptr; + struct tomoyo_name *ptr; list_for_each_entry_rcu(ptr, &tomoyo_name_list[i], list) { if (atomic_read(&ptr->users)) continue; @@ -278,8 +278,8 @@ static void tomoyo_collect_entry(void) static void tomoyo_kfree_entry(void) { - struct tomoyo_gc_entry *p; - struct tomoyo_gc_entry *tmp; + struct tomoyo_gc *p; + struct tomoyo_gc *tmp; list_for_each_entry_safe(p, tmp, &tomoyo_gc_queue, list) { struct list_head *element = p->element; diff --git a/security/tomoyo/memory.c b/security/tomoyo/memory.c index a4aba4d9ca2..297612669c7 100644 --- a/security/tomoyo/memory.c +++ b/security/tomoyo/memory.c @@ -150,7 +150,7 @@ struct list_head tomoyo_name_list[TOMOYO_MAX_HASH]; */ const struct tomoyo_path_info *tomoyo_get_name(const char *name) { - struct tomoyo_name_entry *ptr; + struct tomoyo_name *ptr; unsigned int hash; int len; int allocated_len; diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index 7872226f72e..cfeff871908 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -55,8 +55,8 @@ static int tomoyo_audit_mount_log(struct tomoyo_request_info *r) flags); return tomoyo_supervisor(r, TOMOYO_KEYWORD_ALLOW_MOUNT "%s %s %s 0x%lX\n", - tomoyo_file_pattern(r->param.mount.dev), - tomoyo_file_pattern(r->param.mount.dir), type, + tomoyo_pattern(r->param.mount.dev), + tomoyo_pattern(r->param.mount.dir), type, flags); } @@ -250,7 +250,7 @@ static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a, } /** - * tomoyo_write_mount_policy - Write "struct tomoyo_mount_acl" list. + * tomoyo_write_mount - Write "struct tomoyo_mount_acl" list. * * @data: String to parse. * @domain: Pointer to "struct tomoyo_domain_info". @@ -260,8 +260,8 @@ static bool tomoyo_same_mount_acl(const struct tomoyo_acl_info *a, * * Caller holds tomoyo_read_lock(). */ -int tomoyo_write_mount_policy(char *data, struct tomoyo_domain_info *domain, - const bool is_delete) +int tomoyo_write_mount(char *data, struct tomoyo_domain_info *domain, + const bool is_delete) { struct tomoyo_mount_acl e = { .head.type = TOMOYO_TYPE_MOUNT_ACL }; int error = is_delete ? -ENOENT : -ENOMEM; diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 12a768e6ee3..150167d0cc3 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -26,7 +26,7 @@ bool tomoyo_policy_loaded; * The @src is updated to point the first character after the value * on success. */ -u8 tomoyo_parse_ulong(unsigned long *result, char **str) +static u8 tomoyo_parse_ulong(unsigned long *result, char **str) { const char *cp = *str; char *ep; -- cgit v1.2.3-70-g09d2 From 0849e3ba53c3ef603dffa9758a73e07ed186a937 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Fri, 25 Jun 2010 12:22:09 +0900 Subject: TOMOYO: Add missing poll() hook. Commit 1dae08c "TOMOYO: Add interactive enforcing mode." forgot to register poll() hook. As a result, /usr/sbin/tomoyo-queryd was doing busy loop. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.c | 17 +++++++++++++++++ security/tomoyo/common.h | 2 ++ security/tomoyo/securityfs_if.c | 14 ++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 65c18af3ffe..bff63929e53 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1897,6 +1897,23 @@ int tomoyo_open_control(const u8 type, struct file *file) return 0; } +/** + * tomoyo_poll_control - poll() for /sys/kernel/security/tomoyo/ interface. + * + * @file: Pointer to "struct file". + * @wait: Pointer to "poll_table". + * + * Waits for read readiness. + * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd . + */ +int tomoyo_poll_control(struct file *file, poll_table *wait) +{ + struct tomoyo_io_buffer *head = file->private_data; + if (!head->poll) + return -ENOSYS; + return head->poll(file, wait); +} + /** * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface. * diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index b8b2dac656f..2ffad613855 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -705,6 +705,8 @@ void tomoyo_check_profile(void); int tomoyo_open_control(const u8 type, struct file *file); /* Close /sys/kernel/security/tomoyo/ interface. */ int tomoyo_close_control(struct file *file); +/* Poll operation for /sys/kernel/security/tomoyo/ interface. */ +int tomoyo_poll_control(struct file *file, poll_table *wait); /* Read operation for /sys/kernel/security/tomoyo/ interface. */ int tomoyo_read_control(struct file *file, char __user *buffer, const int buffer_len); diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index 5eb53510c4a..9967c1c5c71 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c @@ -37,6 +37,19 @@ static int tomoyo_release(struct inode *inode, struct file *file) return tomoyo_close_control(file); } +/** + * tomoyo_poll - poll() for /proc/ccs/ interface. + * + * @file: Pointer to "struct file". + * @wait: Pointer to "poll_table". + * + * Returns 0 on success, negative value otherwise. + */ +static unsigned int tomoyo_poll(struct file *file, poll_table *wait) +{ + return tomoyo_poll_control(file, wait); +} + /** * tomoyo_read - read() for /sys/kernel/security/tomoyo/ interface. * @@ -79,6 +92,7 @@ static ssize_t tomoyo_write(struct file *file, const char __user *buf, static const struct file_operations tomoyo_operations = { .open = tomoyo_open, .release = tomoyo_release, + .poll = tomoyo_poll, .read = tomoyo_read, .write = tomoyo_write, }; -- cgit v1.2.3-70-g09d2 From 94fd8405ea62bd2d4a40f3013e8e6935b6643235 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 28 Jun 2010 14:05:04 +0100 Subject: KEYS: Use the variable 'key' in keyctl_describe_key() keyctl_describe_key() turns the key reference it gets into a usable key pointer and assigns that to a variable called 'key', which it then ignores in favour of recomputing the key pointer each time it needs it. Make it use the precomputed pointer instead. Without this patch, gcc 4.6 reports that the variable key is set but not used: building with gcc 4.6 I'm getting a warning message: CC security/keys/keyctl.o security/keys/keyctl.c: In function 'keyctl_describe_key': security/keys/keyctl.c:472:14: warning: variable 'key' set but not used Reported-by: Justin P. Mattock Signed-off-by: David Howells Signed-off-by: James Morris --- security/keys/keyctl.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 639226afd0d..b2b0998d6ab 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -505,13 +505,11 @@ okay: ret = snprintf(tmpbuf, PAGE_SIZE - 1, "%s;%d;%d;%08x;%s", - key_ref_to_ptr(key_ref)->type->name, - key_ref_to_ptr(key_ref)->uid, - key_ref_to_ptr(key_ref)->gid, - key_ref_to_ptr(key_ref)->perm, - key_ref_to_ptr(key_ref)->description ? - key_ref_to_ptr(key_ref)->description : "" - ); + key->type->name, + key->uid, + key->gid, + key->perm, + key->description ?: ""); /* include a NUL char at the end of the data */ if (ret > PAGE_SIZE - 1) -- cgit v1.2.3-70-g09d2 From 5ad18a0d59ba9e65b3c8b2b489fd23bc6b3daf94 Mon Sep 17 00:00:00 2001 From: "Justin P. Mattock" Date: Wed, 30 Jun 2010 10:39:11 +0100 Subject: KEYS: Reinstate lost passing of process keyring ID in call_sbin_request_key() In commit bb952bb98a7e479262c7eb25d5592545a3af147d there was the accidental deletion of a statement from call_sbin_request_key() to render the process keyring ID to a text string so that it can be passed to /sbin/request-key. With gcc 4.6.0 this causes the following warning: CC security/keys/request_key.o security/keys/request_key.c: In function 'call_sbin_request_key': security/keys/request_key.c:102:15: warning: variable 'prkey' set but not used This patch reinstates that statement. Without this statement, /sbin/request-key will get some random rubbish from the stack as that parameter. Signed-off-by: Justin P. Mattock Signed-off-by: David Howells Signed-off-by: James Morris --- security/keys/request_key.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/keys/request_key.c b/security/keys/request_key.c index f5ec9ac5d57..0d26f689bd7 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -144,6 +144,7 @@ static int call_sbin_request_key(struct key_construction *cons, prkey = 0; if (cred->tgcred->process_keyring) prkey = cred->tgcred->process_keyring->serial; + sprintf(keyring_str[1], "%d", prkey); rcu_read_lock(); session = rcu_dereference(cred->tgcred->session_keyring); -- cgit v1.2.3-70-g09d2 From af4f136056c984b0aa67feed7d3170b958370b2f Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Thu, 1 Jul 2010 15:07:43 -0400 Subject: security: move LSM xattrnames to xattr.h Make the security extended attributes names global. Updated to move the remaining Smack xattrs. Signed-off-by: Mimi Zohar Acked-by: Serge Hallyn Signed-off-by: James Morris --- include/linux/capability.h | 3 --- include/linux/xattr.h | 14 ++++++++++++++ security/selinux/hooks.c | 3 --- security/smack/smack.h | 10 ---------- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/include/linux/capability.h b/include/linux/capability.h index 39e5ff512fb..90012b9ddbf 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -49,9 +49,6 @@ typedef struct __user_cap_data_struct { } __user *cap_user_data_t; -#define XATTR_CAPS_SUFFIX "capability" -#define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX - #define VFS_CAP_REVISION_MASK 0xFF000000 #define VFS_CAP_REVISION_SHIFT 24 #define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK diff --git a/include/linux/xattr.h b/include/linux/xattr.h index 0cfa1e9c4cc..f1e5bde4b35 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -33,6 +33,20 @@ #define XATTR_USER_PREFIX "user." #define XATTR_USER_PREFIX_LEN (sizeof (XATTR_USER_PREFIX) - 1) +/* Security namespace */ +#define XATTR_SELINUX_SUFFIX "selinux" +#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX + +#define XATTR_SMACK_SUFFIX "SMACK64" +#define XATTR_SMACK_IPIN "SMACK64IPIN" +#define XATTR_SMACK_IPOUT "SMACK64IPOUT" +#define XATTR_NAME_SMACK XATTR_SECURITY_PREFIX XATTR_SMACK_SUFFIX +#define XATTR_NAME_SMACKIPIN XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN +#define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT + +#define XATTR_CAPS_SUFFIX "capability" +#define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX + struct inode; struct dentry; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 0f524b7d102..85338f0c048 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -87,9 +87,6 @@ #include "netlabel.h" #include "audit.h" -#define XATTR_SELINUX_SUFFIX "selinux" -#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX - #define NUM_SEL_MNT_OPTS 5 extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm); diff --git a/security/smack/smack.h b/security/smack/smack.h index c6e9acae72e..43ae747a5aa 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -123,16 +123,6 @@ struct smack_known { #define SMK_FSHAT "smackfshat=" #define SMK_FSROOT "smackfsroot=" -/* - * xattr names - */ -#define XATTR_SMACK_SUFFIX "SMACK64" -#define XATTR_SMACK_IPIN "SMACK64IPIN" -#define XATTR_SMACK_IPOUT "SMACK64IPOUT" -#define XATTR_NAME_SMACK XATTR_SECURITY_PREFIX XATTR_SMACK_SUFFIX -#define XATTR_NAME_SMACKIPIN XATTR_SECURITY_PREFIX XATTR_SMACK_IPIN -#define XATTR_NAME_SMACKIPOUT XATTR_SECURITY_PREFIX XATTR_SMACK_IPOUT - #define SMACK_CIPSO_OPTION "-CIPSO" /* -- cgit v1.2.3-70-g09d2 From 7e2deb7ce8f662bce877dbfd3b0053e9559c25a3 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 8 Jul 2010 21:57:41 +0900 Subject: TOMOYO: Explicitly set file_operations->llseek pointer. TOMOYO does not deal offset pointer. Thus seek operation makes no sense. Changing default seek operation from default_llseek() to no_llseek() might break some applications. Thus, explicitly set noop_llseek(). Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/securityfs_if.c | 1 + 1 file changed, 1 insertion(+) diff --git a/security/tomoyo/securityfs_if.c b/security/tomoyo/securityfs_if.c index 9967c1c5c71..e43d5554b50 100644 --- a/security/tomoyo/securityfs_if.c +++ b/security/tomoyo/securityfs_if.c @@ -95,6 +95,7 @@ static const struct file_operations tomoyo_operations = { .poll = tomoyo_poll, .read = tomoyo_read, .write = tomoyo_write, + .llseek = noop_llseek, }; /** -- cgit v1.2.3-70-g09d2 From cdcd90f9e450d4edb5fab0490119f9540874e882 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 7 Jul 2010 23:40:15 +0200 Subject: ima: use generic_file_llseek for securityfs The default for llseek will change to no_llseek, so securityfs users need to add explicit .llseek assignments. Since we're dealing with regular files from a VFS perspective, use generic_file_llseek. Signed-off-by: Arnd Bergmann Cc: Mimi Zohar Signed-off-by: James Morris --- security/integrity/ima/ima_fs.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 8fe736aabe7..ef21b96a0b4 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -45,7 +45,8 @@ static ssize_t ima_show_htable_violations(struct file *filp, } static const struct file_operations ima_htable_violations_ops = { - .read = ima_show_htable_violations + .read = ima_show_htable_violations, + .llseek = generic_file_llseek, }; static ssize_t ima_show_measurements_count(struct file *filp, @@ -57,7 +58,8 @@ static ssize_t ima_show_measurements_count(struct file *filp, } static const struct file_operations ima_measurements_count_ops = { - .read = ima_show_measurements_count + .read = ima_show_measurements_count, + .llseek = generic_file_llseek, }; /* returns pointer to hlist_node */ @@ -319,7 +321,8 @@ static int ima_release_policy(struct inode *inode, struct file *file) static const struct file_operations ima_measure_policy_ops = { .open = ima_open_policy, .write = ima_write_policy, - .release = ima_release_policy + .release = ima_release_policy, + .llseek = generic_file_llseek, }; int __init ima_fs_init(void) -- cgit v1.2.3-70-g09d2 From 57a62c2317d60b21b7761c319a733a894482a6af Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 7 Jul 2010 23:40:10 +0200 Subject: selinux: use generic_file_llseek The default for llseek will change to no_llseek, so selinuxfs needs to add explicit .llseek assignments. Since we're dealing with regular files from a VFS perspective, use generic_file_llseek. Signed-off-by: Arnd Bergmann Cc: Stephen Smalley Cc: Eric Paris Signed-off-by: James Morris --- security/selinux/selinuxfs.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 0293843f7ed..79a1bb63566 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -184,6 +184,7 @@ out: static const struct file_operations sel_enforce_ops = { .read = sel_read_enforce, .write = sel_write_enforce, + .llseek = generic_file_llseek, }; static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, @@ -201,6 +202,7 @@ static ssize_t sel_read_handle_unknown(struct file *filp, char __user *buf, static const struct file_operations sel_handle_unknown_ops = { .read = sel_read_handle_unknown, + .llseek = generic_file_llseek, }; #ifdef CONFIG_SECURITY_SELINUX_DISABLE @@ -251,6 +253,7 @@ out: static const struct file_operations sel_disable_ops = { .write = sel_write_disable, + .llseek = generic_file_llseek, }; static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, @@ -265,6 +268,7 @@ static ssize_t sel_read_policyvers(struct file *filp, char __user *buf, static const struct file_operations sel_policyvers_ops = { .read = sel_read_policyvers, + .llseek = generic_file_llseek, }; /* declaration for sel_write_load */ @@ -289,6 +293,7 @@ static ssize_t sel_read_mls(struct file *filp, char __user *buf, static const struct file_operations sel_mls_ops = { .read = sel_read_mls, + .llseek = generic_file_llseek, }; static ssize_t sel_write_load(struct file *file, const char __user *buf, @@ -356,6 +361,7 @@ out: static const struct file_operations sel_load_ops = { .write = sel_write_load, + .llseek = generic_file_llseek, }; static ssize_t sel_write_context(struct file *file, char *buf, size_t size) @@ -437,6 +443,7 @@ out: static const struct file_operations sel_checkreqprot_ops = { .read = sel_read_checkreqprot, .write = sel_write_checkreqprot, + .llseek = generic_file_llseek, }; /* @@ -482,6 +489,7 @@ static const struct file_operations transaction_ops = { .write = selinux_transaction_write, .read = simple_transaction_read, .release = simple_transaction_release, + .llseek = generic_file_llseek, }; /* @@ -883,6 +891,7 @@ out: static const struct file_operations sel_bool_ops = { .read = sel_read_bool, .write = sel_write_bool, + .llseek = generic_file_llseek, }; static ssize_t sel_commit_bools_write(struct file *filep, @@ -935,6 +944,7 @@ out: static const struct file_operations sel_commit_bools_ops = { .write = sel_commit_bools_write, + .llseek = generic_file_llseek, }; static void sel_remove_entries(struct dentry *de) @@ -1127,10 +1137,12 @@ out: static const struct file_operations sel_avc_cache_threshold_ops = { .read = sel_read_avc_cache_threshold, .write = sel_write_avc_cache_threshold, + .llseek = generic_file_llseek, }; static const struct file_operations sel_avc_hash_stats_ops = { .read = sel_read_avc_hash_stats, + .llseek = generic_file_llseek, }; #ifdef CONFIG_SECURITY_SELINUX_AVC_STATS @@ -1255,6 +1267,7 @@ static ssize_t sel_read_initcon(struct file *file, char __user *buf, static const struct file_operations sel_initcon_ops = { .read = sel_read_initcon, + .llseek = generic_file_llseek, }; static int sel_make_initcon_files(struct dentry *dir) @@ -1330,6 +1343,7 @@ out: static const struct file_operations sel_class_ops = { .read = sel_read_class, + .llseek = generic_file_llseek, }; static ssize_t sel_read_perm(struct file *file, char __user *buf, @@ -1354,6 +1368,7 @@ out: static const struct file_operations sel_perm_ops = { .read = sel_read_perm, + .llseek = generic_file_llseek, }; static ssize_t sel_read_policycap(struct file *file, char __user *buf, @@ -1372,6 +1387,7 @@ static ssize_t sel_read_policycap(struct file *file, char __user *buf, static const struct file_operations sel_policycap_ops = { .read = sel_read_policycap, + .llseek = generic_file_llseek, }; static int sel_make_perm_files(char *objclass, int classvalue, -- cgit v1.2.3-70-g09d2 From b8bc83ab4dcbc9938b95a90bbb50d89d1904d5ab Mon Sep 17 00:00:00 2001 From: Tvrtko Ursulin Date: Thu, 15 Jul 2010 13:25:06 +0100 Subject: securityfs: Drop dentry reference count when mknod fails lookup_one_len increments dentry reference count which is not decremented when the create operation fails. This can cause a kernel BUG at fs/dcache.c:676 at unmount time. Also error code returned when new_inode() fails was replaced with more appropriate -ENOMEM. Signed-off-by: Tvrtko Ursulin Acked-by: Serge E. Hallyn Acked-by: Greg Kroah-Hartman Signed-off-by: James Morris --- security/inode.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/security/inode.c b/security/inode.c index 1c812e87450..8c777f022ad 100644 --- a/security/inode.c +++ b/security/inode.c @@ -86,7 +86,7 @@ static int mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev) { struct inode *inode; - int error = -EPERM; + int error = -ENOMEM; if (dentry->d_inode) return -EEXIST; @@ -166,6 +166,8 @@ static int create_by_name(const char *name, mode_t mode, error = mkdir(parent->d_inode, *dentry, mode); else error = create(parent->d_inode, *dentry, mode); + if (error) + dput(*dentry); } else error = PTR_ERR(*dentry); mutex_unlock(&parent->d_inode->i_mutex); -- cgit v1.2.3-70-g09d2 From dce3a3d2ee038d230323fe06b061dbaace6b8f94 Mon Sep 17 00:00:00 2001 From: Chihau Chau Date: Fri, 16 Jul 2010 12:11:54 -0400 Subject: Security: capability: code style issue This fix a little code style issue deleting a space between a function name and a open parenthesis. Signed-off-by: Chihau Chau Acked-by: Andrew G. Morgan Signed-off-by: James Morris --- security/capability.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/capability.c b/security/capability.c index 4aeb699da1b..a0bbf30fb6d 100644 --- a/security/capability.c +++ b/security/capability.c @@ -27,7 +27,7 @@ static int cap_quota_on(struct dentry *dentry) return 0; } -static int cap_bprm_check_security (struct linux_binprm *bprm) +static int cap_bprm_check_security(struct linux_binprm *bprm) { return 0; } -- cgit v1.2.3-70-g09d2 From 9e0bd4cba4460bff64fb07cfb07849cdfd4d325a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 12 Jun 2010 20:50:35 +0200 Subject: selinux: cleanup return codes in avtab_read_item() The avtab_read_item() function tends to return -1 as a default error code which is wrong (-1 means -EPERM). I modified it to return appropriate error codes which is -EINVAL or the error code from next_entry() or insertf(). next_entry() returns -EINVAL. insertf() is a function pointer to either avtab_insert() or cond_insertf(). avtab_insert() returns -EINVAL, -ENOMEM, and -EEXIST. cond_insertf() currently returns -1, but I will fix it in a later patch. There is code in avtab_read() which translates the -1 returns from avtab_read_item() to -EINVAL. The translation is no longer needed, so I removed it. Signed-off-by: Dan Carpenter Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- security/selinux/ss/avtab.c | 39 +++++++++++++++++++-------------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/security/selinux/ss/avtab.c b/security/selinux/ss/avtab.c index 1215b8e47db..929480c6c43 100644 --- a/security/selinux/ss/avtab.c +++ b/security/selinux/ss/avtab.c @@ -342,20 +342,20 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, if (vers < POLICYDB_VERSION_AVTAB) { rc = next_entry(buf32, fp, sizeof(u32)); - if (rc < 0) { + if (rc) { printk(KERN_ERR "SELinux: avtab: truncated entry\n"); - return -1; + return rc; } items2 = le32_to_cpu(buf32[0]); if (items2 > ARRAY_SIZE(buf32)) { printk(KERN_ERR "SELinux: avtab: entry overflow\n"); - return -1; + return -EINVAL; } rc = next_entry(buf32, fp, sizeof(u32)*items2); - if (rc < 0) { + if (rc) { printk(KERN_ERR "SELinux: avtab: truncated entry\n"); - return -1; + return rc; } items = 0; @@ -363,19 +363,19 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, key.source_type = (u16)val; if (key.source_type != val) { printk(KERN_ERR "SELinux: avtab: truncated source type\n"); - return -1; + return -EINVAL; } val = le32_to_cpu(buf32[items++]); key.target_type = (u16)val; if (key.target_type != val) { printk(KERN_ERR "SELinux: avtab: truncated target type\n"); - return -1; + return -EINVAL; } val = le32_to_cpu(buf32[items++]); key.target_class = (u16)val; if (key.target_class != val) { printk(KERN_ERR "SELinux: avtab: truncated target class\n"); - return -1; + return -EINVAL; } val = le32_to_cpu(buf32[items++]); @@ -383,12 +383,12 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, if (!(val & (AVTAB_AV | AVTAB_TYPE))) { printk(KERN_ERR "SELinux: avtab: null entry\n"); - return -1; + return -EINVAL; } if ((val & AVTAB_AV) && (val & AVTAB_TYPE)) { printk(KERN_ERR "SELinux: avtab: entry has both access vectors and types\n"); - return -1; + return -EINVAL; } for (i = 0; i < ARRAY_SIZE(spec_order); i++) { @@ -403,15 +403,15 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, if (items != items2) { printk(KERN_ERR "SELinux: avtab: entry only had %d items, expected %d\n", items2, items); - return -1; + return -EINVAL; } return 0; } rc = next_entry(buf16, fp, sizeof(u16)*4); - if (rc < 0) { + if (rc) { printk(KERN_ERR "SELinux: avtab: truncated entry\n"); - return -1; + return rc; } items = 0; @@ -424,7 +424,7 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, !policydb_type_isvalid(pol, key.target_type) || !policydb_class_isvalid(pol, key.target_class)) { printk(KERN_ERR "SELinux: avtab: invalid type or class\n"); - return -1; + return -EINVAL; } set = 0; @@ -434,19 +434,19 @@ int avtab_read_item(struct avtab *a, void *fp, struct policydb *pol, } if (!set || set > 1) { printk(KERN_ERR "SELinux: avtab: more than one specifier\n"); - return -1; + return -EINVAL; } rc = next_entry(buf32, fp, sizeof(u32)); - if (rc < 0) { + if (rc) { printk(KERN_ERR "SELinux: avtab: truncated entry\n"); - return -1; + return rc; } datum.data = le32_to_cpu(*buf32); if ((key.specified & AVTAB_TYPE) && !policydb_type_isvalid(pol, datum.data)) { printk(KERN_ERR "SELinux: avtab: invalid type\n"); - return -1; + return -EINVAL; } return insertf(a, &key, &datum, p); } @@ -487,8 +487,7 @@ int avtab_read(struct avtab *a, void *fp, struct policydb *pol) printk(KERN_ERR "SELinux: avtab: out of memory\n"); else if (rc == -EEXIST) printk(KERN_ERR "SELinux: avtab: duplicate entry\n"); - else - rc = -EINVAL; + goto bad; } } -- cgit v1.2.3-70-g09d2 From 5241c1074f6e2f2276d45d857eb5d19fbdc2e4b2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 12 Jun 2010 20:51:40 +0200 Subject: selinux: propagate error codes in cond_read_list() These are passed back when the security module gets loaded. The original code always returned -1 (-EPERM) on error but after this patch it can return -EINVAL, or -ENOMEM or propagate the error code from cond_read_node(). cond_read_node() still returns -1 all the time, but I fix that in a later patch. Signed-off-by: Dan Carpenter Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- security/selinux/ss/conditional.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 4a4e35cac22..775418aa0a8 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -445,8 +445,8 @@ int cond_read_list(struct policydb *p, void *fp) int rc; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) - return -1; + if (rc) + return rc; len = le32_to_cpu(buf[0]); @@ -455,11 +455,13 @@ int cond_read_list(struct policydb *p, void *fp) goto err; for (i = 0; i < len; i++) { + rc = -ENOMEM; node = kzalloc(sizeof(struct cond_node), GFP_KERNEL); if (!node) goto err; - if (cond_read_node(p, node, fp) != 0) + rc = cond_read_node(p, node, fp); + if (rc) goto err; if (i == 0) @@ -472,7 +474,7 @@ int cond_read_list(struct policydb *p, void *fp) err: cond_list_destroy(p->cond_list); p->cond_list = NULL; - return -1; + return rc; } /* Determine whether additional permissions are granted by the conditional -- cgit v1.2.3-70-g09d2 From 9d623b17a740d5a85c12108cdc71c64fb15484fc Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 12 Jun 2010 20:52:19 +0200 Subject: selinux: fix error codes in cond_read_av_list() After this patch cond_read_av_list() no longer returns -1 for any errors. It just propagates error code back from lower levels. Those can either be -EINVAL or -ENOMEM. I also modified cond_insertf() since cond_read_av_list() passes that as a function pointer to avtab_read_item(). It isn't used anywhere else. Signed-off-by: Dan Carpenter Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- security/selinux/ss/conditional.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 775418aa0a8..aac40c7ff28 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -263,7 +263,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum struct cond_av_list *other = data->other, *list, *cur; struct avtab_node *node_ptr; u8 found; - + int rc = -EINVAL; /* * For type rules we have to make certain there aren't any @@ -313,12 +313,15 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum node_ptr = avtab_insert_nonunique(&p->te_cond_avtab, k, d); if (!node_ptr) { printk(KERN_ERR "SELinux: could not insert rule.\n"); + rc = -ENOMEM; goto err; } list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL); - if (!list) + if (!list) { + rc = -ENOMEM; goto err; + } list->node = node_ptr; if (!data->head) @@ -331,7 +334,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum err: cond_av_list_destroy(data->head); data->head = NULL; - return -1; + return rc; } static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list **ret_list, struct cond_av_list *other) @@ -345,8 +348,8 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list * len = 0; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - return -1; + if (rc) + return rc; len = le32_to_cpu(buf[0]); if (len == 0) @@ -361,7 +364,6 @@ static int cond_read_av_list(struct policydb *p, void *fp, struct cond_av_list * &data); if (rc) return rc; - } *ret_list = data.head; -- cgit v1.2.3-70-g09d2 From fc5c126e4733e6fb3080d3d822ca63226e74fc84 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 12 Jun 2010 20:53:46 +0200 Subject: selinux: fix error codes in cond_read_node() Originally cond_read_node() returned -1 (-EPERM) on errors which was incorrect. Now it either propagates the error codes from lower level functions next_entry() or cond_read_av_list() or it returns -ENOMEM or -EINVAL. next_entry() returns -EINVAL. cond_read_av_list() returns -EINVAL or -ENOMEM. Signed-off-by: Dan Carpenter Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- security/selinux/ss/conditional.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index aac40c7ff28..a2b3b298e60 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -392,24 +392,25 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) struct cond_expr *expr = NULL, *last = NULL; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - return -1; + if (rc) + return rc; node->cur_state = le32_to_cpu(buf[0]); len = 0; rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - return -1; + if (rc) + return rc; /* expr */ len = le32_to_cpu(buf[0]); for (i = 0; i < len; i++) { rc = next_entry(buf, fp, sizeof(u32) * 2); - if (rc < 0) + if (rc) goto err; + rc = -ENOMEM; expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL); if (!expr) goto err; @@ -418,6 +419,7 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) expr->bool = le32_to_cpu(buf[1]); if (!expr_isvalid(p, expr)) { + rc = -EINVAL; kfree(expr); goto err; } @@ -429,14 +431,16 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) last = expr; } - if (cond_read_av_list(p, fp, &node->true_list, NULL) != 0) + rc = cond_read_av_list(p, fp, &node->true_list, NULL); + if (rc) goto err; - if (cond_read_av_list(p, fp, &node->false_list, node->true_list) != 0) + rc = cond_read_av_list(p, fp, &node->false_list, node->true_list); + if (rc) goto err; return 0; err: cond_node_destroy(node); - return -1; + return rc; } int cond_read_list(struct policydb *p, void *fp) -- cgit v1.2.3-70-g09d2 From 38184c522249dc377366d4edc41dc500c2c3bb9e Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 12 Jun 2010 20:55:01 +0200 Subject: selinux: fix error codes in cond_policydb_init() It's better to propagate the error code from avtab_init() instead of returning -1 (-EPERM). It turns out that avtab_init() never fails so this patch doesn't change how the code runs but it's still a clean up. Signed-off-by: Dan Carpenter Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- security/selinux/ss/conditional.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index a2b3b298e60..0389263b99c 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -117,10 +117,14 @@ int evaluate_cond_node(struct policydb *p, struct cond_node *node) int cond_policydb_init(struct policydb *p) { + int rc; + p->bool_val_to_struct = NULL; p->cond_list = NULL; - if (avtab_init(&p->te_cond_avtab)) - return -1; + + rc = avtab_init(&p->te_cond_avtab); + if (rc) + return rc; return 0; } -- cgit v1.2.3-70-g09d2 From 338437f6a09861cdf76e1396ed5fa6dee9c7cabe Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 12 Jun 2010 20:56:01 +0200 Subject: selinux: fix error codes in cond_read_bool() The original code always returned -1 (-EPERM) on error. The new code returns either -ENOMEM, or -EINVAL or it propagates the error codes from lower level functions next_entry() or hashtab_insert(). next_entry() returns -EINVAL. hashtab_insert() returns -EINVAL, -EEXIST, or -ENOMEM. Signed-off-by: Dan Carpenter Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- security/selinux/ss/conditional.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 0389263b99c..c91e150c308 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -223,34 +223,37 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp) booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); if (!booldatum) - return -1; + return -ENOMEM; rc = next_entry(buf, fp, sizeof buf); - if (rc < 0) + if (rc) goto err; booldatum->value = le32_to_cpu(buf[0]); booldatum->state = le32_to_cpu(buf[1]); + rc = -EINVAL; if (!bool_isvalid(booldatum)) goto err; len = le32_to_cpu(buf[2]); + rc = -ENOMEM; key = kmalloc(len + 1, GFP_KERNEL); if (!key) goto err; rc = next_entry(key, fp, len); - if (rc < 0) + if (rc) goto err; key[len] = '\0'; - if (hashtab_insert(h, key, booldatum)) + rc = hashtab_insert(h, key, booldatum); + if (rc) goto err; return 0; err: cond_destroy_bool(key, booldatum, NULL); - return -1; + return rc; } struct cond_insertf_data { -- cgit v1.2.3-70-g09d2 From 9a7982793c3aee6ce86d8e7e15306215257329f2 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 12 Jun 2010 20:57:39 +0200 Subject: selinux: fix error codes in symtab_init() hashtab_create() only returns NULL on allocation failures to -ENOMEM is appropriate here. Signed-off-by: Dan Carpenter Acked-by: Eric Paris Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- security/selinux/ss/symtab.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/selinux/ss/symtab.c b/security/selinux/ss/symtab.c index bcf9f620426..160326ee99e 100644 --- a/security/selinux/ss/symtab.c +++ b/security/selinux/ss/symtab.c @@ -36,7 +36,7 @@ int symtab_init(struct symtab *s, unsigned int size) { s->table = hashtab_create(symhash, symcmp, size); if (!s->table) - return -1; + return -ENOMEM; s->nprim = 0; return 0; } -- cgit v1.2.3-70-g09d2 From d1b43547e56b163bc5c622243c47d8a13626175b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 21 Jul 2010 12:50:57 -0400 Subject: SELinux: move genfs read to a separate function move genfs read functionality out of policydb_read() and into a new function called genfs_read() Signed-off-by: Eric Paris Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- security/selinux/ss/policydb.c | 238 +++++++++++++++++++++++------------------ 1 file changed, 133 insertions(+), 105 deletions(-) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index a39d38af220..4ea073eaf8b 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -655,6 +655,9 @@ static int range_tr_destroy(void *key, void *datum, void *p) static void ocontext_destroy(struct ocontext *c, int i) { + if (!c) + return; + context_destroy(&c->context[0]); context_destroy(&c->context[1]); if (i == OCON_ISID || i == OCON_FS || @@ -1773,6 +1776,131 @@ out: return rc; } +static int genfs_read(struct policydb *p, void *fp) +{ + int i, j, rc; + u32 nel, nel2, len, len2; + __le32 buf[1]; + struct ocontext *l, *c; + struct ocontext *newc = NULL; + struct genfs *genfs_p, *genfs; + struct genfs *newgenfs = NULL; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + nel = le32_to_cpu(buf[0]); + + for (i = 0; i < nel; i++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + newgenfs = kzalloc(sizeof(*newgenfs), GFP_KERNEL); + if (!newgenfs) + goto out; + + rc = -ENOMEM; + newgenfs->fstype = kmalloc(len + 1, GFP_KERNEL); + if (!newgenfs->fstype) + goto out; + + rc = next_entry(newgenfs->fstype, fp, len); + if (rc) + goto out; + + newgenfs->fstype[len] = 0; + + for (genfs_p = NULL, genfs = p->genfs; genfs; + genfs_p = genfs, genfs = genfs->next) { + rc = -EINVAL; + if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { + printk(KERN_ERR "SELinux: dup genfs fstype %s\n", + newgenfs->fstype); + goto out; + } + if (strcmp(newgenfs->fstype, genfs->fstype) < 0) + break; + } + newgenfs->next = genfs; + if (genfs_p) + genfs_p->next = newgenfs; + else + p->genfs = newgenfs; + genfs = newgenfs; + newgenfs = NULL; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + nel2 = le32_to_cpu(buf[0]); + for (j = 0; j < nel2; j++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + newc = kzalloc(sizeof(*newc), GFP_KERNEL); + if (!newc) + goto out; + + rc = -ENOMEM; + newc->u.name = kmalloc(len + 1, GFP_KERNEL); + if (!newc->u.name) + goto out; + + rc = next_entry(newc->u.name, fp, len); + if (rc) + goto out; + newc->u.name[len] = 0; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + newc->v.sclass = le32_to_cpu(buf[0]); + rc = context_read_and_validate(&newc->context[0], p, fp); + if (rc) + goto out; + + for (l = NULL, c = genfs->head; c; + l = c, c = c->next) { + rc = -EINVAL; + if (!strcmp(newc->u.name, c->u.name) && + (!c->v.sclass || !newc->v.sclass || + newc->v.sclass == c->v.sclass)) { + printk(KERN_ERR "SELinux: dup genfs entry (%s,%s)\n", + genfs->fstype, c->u.name); + goto out; + } + len = strlen(newc->u.name); + len2 = strlen(c->u.name); + if (len > len2) + break; + } + + newc->next = c; + if (l) + l->next = newc; + else + genfs->head = newc; + newc = NULL; + } + } + rc = 0; +out: + if (newgenfs) + kfree(newgenfs->fstype); + kfree(newgenfs); + ocontext_destroy(newc, OCON_FSUSE); + + return rc; +} + /* * Read the configuration data from a policy database binary * representation file into a policy database structure. @@ -1781,12 +1909,12 @@ int policydb_read(struct policydb *p, void *fp) { struct role_allow *ra, *lra; struct role_trans *tr, *ltr; - struct ocontext *l, *c, *newc; - struct genfs *genfs_p, *genfs, *newgenfs; + struct ocontext *l, *c; int i, j, rc; __le32 buf[4]; u32 nodebuf[8]; - u32 len, len2, nprim, nel, nel2; + u32 len, nprim, nel; + char *policydb_str; struct policydb_compat_info *info; @@ -2099,107 +2227,9 @@ int policydb_read(struct policydb *p, void *fp) } } - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) + rc = genfs_read(p, fp); + if (rc) goto bad; - nel = le32_to_cpu(buf[0]); - genfs_p = NULL; - rc = -EINVAL; - for (i = 0; i < nel; i++) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - len = le32_to_cpu(buf[0]); - newgenfs = kzalloc(sizeof(*newgenfs), GFP_KERNEL); - if (!newgenfs) { - rc = -ENOMEM; - goto bad; - } - - newgenfs->fstype = kmalloc(len + 1, GFP_KERNEL); - if (!newgenfs->fstype) { - rc = -ENOMEM; - kfree(newgenfs); - goto bad; - } - rc = next_entry(newgenfs->fstype, fp, len); - if (rc < 0) { - kfree(newgenfs->fstype); - kfree(newgenfs); - goto bad; - } - newgenfs->fstype[len] = 0; - for (genfs_p = NULL, genfs = p->genfs; genfs; - genfs_p = genfs, genfs = genfs->next) { - if (strcmp(newgenfs->fstype, genfs->fstype) == 0) { - printk(KERN_ERR "SELinux: dup genfs " - "fstype %s\n", newgenfs->fstype); - kfree(newgenfs->fstype); - kfree(newgenfs); - goto bad; - } - if (strcmp(newgenfs->fstype, genfs->fstype) < 0) - break; - } - newgenfs->next = genfs; - if (genfs_p) - genfs_p->next = newgenfs; - else - p->genfs = newgenfs; - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel2 = le32_to_cpu(buf[0]); - for (j = 0; j < nel2; j++) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - len = le32_to_cpu(buf[0]); - - newc = kzalloc(sizeof(*newc), GFP_KERNEL); - if (!newc) { - rc = -ENOMEM; - goto bad; - } - - newc->u.name = kmalloc(len + 1, GFP_KERNEL); - if (!newc->u.name) { - rc = -ENOMEM; - goto bad_newc; - } - rc = next_entry(newc->u.name, fp, len); - if (rc < 0) - goto bad_newc; - newc->u.name[len] = 0; - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad_newc; - newc->v.sclass = le32_to_cpu(buf[0]); - if (context_read_and_validate(&newc->context[0], p, fp)) - goto bad_newc; - for (l = NULL, c = newgenfs->head; c; - l = c, c = c->next) { - if (!strcmp(newc->u.name, c->u.name) && - (!c->v.sclass || !newc->v.sclass || - newc->v.sclass == c->v.sclass)) { - printk(KERN_ERR "SELinux: dup genfs " - "entry (%s,%s)\n", - newgenfs->fstype, c->u.name); - goto bad_newc; - } - len = strlen(newc->u.name); - len2 = strlen(c->u.name); - if (len > len2) - break; - } - - newc->next = c; - if (l) - l->next = newc; - else - newgenfs->head = newc; - } - } rc = range_read(p, fp); if (rc) @@ -2227,8 +2257,6 @@ int policydb_read(struct policydb *p, void *fp) rc = 0; out: return rc; -bad_newc: - ocontext_destroy(newc, OCON_FSUSE); bad: if (!rc) rc = -EINVAL; -- cgit v1.2.3-70-g09d2 From 692a8a231b212dfc68f612956d63f34abf098e0f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 21 Jul 2010 12:51:03 -0400 Subject: SELinux: break ocontext reading into a separate function Move the reading of ocontext type data out of policydb_read() in a separate function ocontext_read() Signed-off-by: Eric Paris Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- security/selinux/ss/policydb.c | 244 ++++++++++++++++++++++------------------- 1 file changed, 133 insertions(+), 111 deletions(-) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 4ea073eaf8b..674ddfe0ba0 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1901,6 +1901,136 @@ out: return rc; } +static int ocontext_read(struct policydb *p, struct policydb_compat_info *info, + void *fp) +{ + int i, j, rc; + u32 nel, len; + __le32 buf[3]; + struct ocontext *l, *c; + u32 nodebuf[8]; + + for (i = 0; i < info->ocon_num; i++) { + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + nel = le32_to_cpu(buf[0]); + + l = NULL; + for (j = 0; j < nel; j++) { + rc = -ENOMEM; + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) + goto out; + if (l) + l->next = c; + else + p->ocontexts[i] = c; + l = c; + + switch (i) { + case OCON_ISID: + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + + c->sid[0] = le32_to_cpu(buf[0]); + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_FS: + case OCON_NETIF: + rc = next_entry(buf, fp, sizeof(u32)); + if (rc) + goto out; + len = le32_to_cpu(buf[0]); + + rc = -ENOMEM; + c->u.name = kmalloc(len + 1, GFP_KERNEL); + if (!c->u.name) + goto out; + + rc = next_entry(c->u.name, fp, len); + if (rc) + goto out; + + c->u.name[len] = 0; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + rc = context_read_and_validate(&c->context[1], p, fp); + if (rc) + goto out; + break; + case OCON_PORT: + rc = next_entry(buf, fp, sizeof(u32)*3); + if (rc) + goto out; + c->u.port.protocol = le32_to_cpu(buf[0]); + c->u.port.low_port = le32_to_cpu(buf[1]); + c->u.port.high_port = le32_to_cpu(buf[2]); + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_NODE: + rc = next_entry(nodebuf, fp, sizeof(u32) * 2); + if (rc) + goto out; + c->u.node.addr = nodebuf[0]; /* network order */ + c->u.node.mask = nodebuf[1]; /* network order */ + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_FSUSE: + rc = next_entry(buf, fp, sizeof(u32)*2); + if (rc) + goto out; + + rc = -EINVAL; + c->v.behavior = le32_to_cpu(buf[0]); + if (c->v.behavior > SECURITY_FS_USE_NONE) + goto out; + + rc = -ENOMEM; + len = le32_to_cpu(buf[1]); + c->u.name = kmalloc(len + 1, GFP_KERNEL); + if (!c->u.name) + goto out; + + rc = next_entry(c->u.name, fp, len); + if (rc) + goto out; + c->u.name[len] = 0; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + case OCON_NODE6: { + int k; + + rc = next_entry(nodebuf, fp, sizeof(u32) * 8); + if (rc) + goto out; + for (k = 0; k < 4; k++) + c->u.node6.addr[k] = nodebuf[k]; + for (k = 0; k < 4; k++) + c->u.node6.mask[k] = nodebuf[k+4]; + rc = context_read_and_validate(&c->context[0], p, fp); + if (rc) + goto out; + break; + } + } + } + } + rc = 0; +out: + return rc; +} + /* * Read the configuration data from a policy database binary * representation file into a policy database structure. @@ -1909,10 +2039,8 @@ int policydb_read(struct policydb *p, void *fp) { struct role_allow *ra, *lra; struct role_trans *tr, *ltr; - struct ocontext *l, *c; int i, j, rc; __le32 buf[4]; - u32 nodebuf[8]; u32 len, nprim, nel; char *policydb_str; @@ -2117,115 +2245,9 @@ int policydb_read(struct policydb *p, void *fp) if (!p->process_trans_perms) goto bad; - for (i = 0; i < info->ocon_num; i++) { - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - nel = le32_to_cpu(buf[0]); - l = NULL; - for (j = 0; j < nel; j++) { - c = kzalloc(sizeof(*c), GFP_KERNEL); - if (!c) { - rc = -ENOMEM; - goto bad; - } - if (l) - l->next = c; - else - p->ocontexts[i] = c; - l = c; - rc = -EINVAL; - switch (i) { - case OCON_ISID: - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - c->sid[0] = le32_to_cpu(buf[0]); - rc = context_read_and_validate(&c->context[0], p, fp); - if (rc) - goto bad; - break; - case OCON_FS: - case OCON_NETIF: - rc = next_entry(buf, fp, sizeof(u32)); - if (rc < 0) - goto bad; - len = le32_to_cpu(buf[0]); - c->u.name = kmalloc(len + 1, GFP_KERNEL); - if (!c->u.name) { - rc = -ENOMEM; - goto bad; - } - rc = next_entry(c->u.name, fp, len); - if (rc < 0) - goto bad; - c->u.name[len] = 0; - rc = context_read_and_validate(&c->context[0], p, fp); - if (rc) - goto bad; - rc = context_read_and_validate(&c->context[1], p, fp); - if (rc) - goto bad; - break; - case OCON_PORT: - rc = next_entry(buf, fp, sizeof(u32)*3); - if (rc < 0) - goto bad; - c->u.port.protocol = le32_to_cpu(buf[0]); - c->u.port.low_port = le32_to_cpu(buf[1]); - c->u.port.high_port = le32_to_cpu(buf[2]); - rc = context_read_and_validate(&c->context[0], p, fp); - if (rc) - goto bad; - break; - case OCON_NODE: - rc = next_entry(nodebuf, fp, sizeof(u32) * 2); - if (rc < 0) - goto bad; - c->u.node.addr = nodebuf[0]; /* network order */ - c->u.node.mask = nodebuf[1]; /* network order */ - rc = context_read_and_validate(&c->context[0], p, fp); - if (rc) - goto bad; - break; - case OCON_FSUSE: - rc = next_entry(buf, fp, sizeof(u32)*2); - if (rc < 0) - goto bad; - c->v.behavior = le32_to_cpu(buf[0]); - if (c->v.behavior > SECURITY_FS_USE_NONE) - goto bad; - len = le32_to_cpu(buf[1]); - c->u.name = kmalloc(len + 1, GFP_KERNEL); - if (!c->u.name) { - rc = -ENOMEM; - goto bad; - } - rc = next_entry(c->u.name, fp, len); - if (rc < 0) - goto bad; - c->u.name[len] = 0; - rc = context_read_and_validate(&c->context[0], p, fp); - if (rc) - goto bad; - break; - case OCON_NODE6: { - int k; - - rc = next_entry(nodebuf, fp, sizeof(u32) * 8); - if (rc < 0) - goto bad; - for (k = 0; k < 4; k++) - c->u.node6.addr[k] = nodebuf[k]; - for (k = 0; k < 4; k++) - c->u.node6.mask[k] = nodebuf[k+4]; - if (context_read_and_validate(&c->context[0], p, fp)) - goto bad; - break; - } - } - } - } + rc = ocontext_read(p, info, fp); + if (rc) + goto bad; rc = genfs_read(p, fp); if (rc) -- cgit v1.2.3-70-g09d2 From 9cfcac810e8993fa7a5bfd24b1a21f1dbbb03a7b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 23 Jul 2010 11:43:51 -0400 Subject: vfs: re-introduce MAY_CHDIR Currently MAY_ACCESS means that filesystems must check the permissions right then and not rely on cached results or the results of future operations on the object. This can be because of a call to sys_access() or because of a call to chdir() which needs to check search without relying on any future operations inside that dir. I plan to use MAY_ACCESS for other purposes in the security system, so I split the MAY_ACCESS and the MAY_CHDIR cases. Signed-off-by: Eric Paris Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- fs/fuse/dir.c | 2 +- fs/nfs/dir.c | 2 +- fs/open.c | 6 +++--- include/linux/fs.h | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 3cdc5f78a40..431be0795b6 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -1016,7 +1016,7 @@ static int fuse_permission(struct inode *inode, int mask) exist. So if permissions are revoked this won't be noticed immediately, only after the attribute timeout has expired */ - } else if (mask & MAY_ACCESS) { + } else if (mask & (MAY_ACCESS | MAY_CHDIR)) { err = fuse_access(inode, mask); } else if ((mask & MAY_EXEC) && S_ISREG(inode->i_mode)) { if (!(inode->i_mode & S_IXUGO)) { diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index e60416d3f81..832e9e23932 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c @@ -1953,7 +1953,7 @@ int nfs_permission(struct inode *inode, int mask) if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) goto out; /* Is this sys_access() ? */ - if (mask & MAY_ACCESS) + if (mask & (MAY_ACCESS | MAY_CHDIR)) goto force_lookup; switch (inode->i_mode & S_IFMT) { diff --git a/fs/open.c b/fs/open.c index a54ed85209c..0d1fa3dc0ef 100644 --- a/fs/open.c +++ b/fs/open.c @@ -366,7 +366,7 @@ SYSCALL_DEFINE1(chdir, const char __user *, filename) if (error) goto out; - error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); + error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; @@ -395,7 +395,7 @@ SYSCALL_DEFINE1(fchdir, unsigned int, fd) if (!S_ISDIR(inode->i_mode)) goto out_putf; - error = inode_permission(inode, MAY_EXEC | MAY_ACCESS); + error = inode_permission(inode, MAY_EXEC | MAY_CHDIR); if (!error) set_fs_pwd(current->fs, &file->f_path); out_putf: @@ -413,7 +413,7 @@ SYSCALL_DEFINE1(chroot, const char __user *, filename) if (error) goto out; - error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_ACCESS); + error = inode_permission(path.dentry->d_inode, MAY_EXEC | MAY_CHDIR); if (error) goto dput_and_out; diff --git a/include/linux/fs.h b/include/linux/fs.h index 68ca1b0491a..7d94b72f034 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -53,6 +53,7 @@ struct inodes_stat_t { #define MAY_APPEND 8 #define MAY_ACCESS 16 #define MAY_OPEN 32 +#define MAY_CHDIR 64 /* * flags in file.f_mode. Note that FMODE_READ and FMODE_WRITE must correspond -- cgit v1.2.3-70-g09d2 From d09ca73979460b96d5d4684d588b188be9a1f57d Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 23 Jul 2010 11:43:57 -0400 Subject: security: make LSMs explicitly mask off permissions SELinux needs to pass the MAY_ACCESS flag so it can handle auditting correctly. Presently the masking of MAY_* flags is done in the VFS. In order to allow LSMs to decide what flags they care about and what flags they don't just pass them all and the each LSM mask off what they don't need. This patch should contain no functional changes to either the VFS or any LSM. Signed-off-by: Eric Paris Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- fs/namei.c | 3 +-- security/selinux/hooks.c | 2 ++ security/smack/smack_lsm.c | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index fe34c2b879f..42d2d28fb82 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -282,8 +282,7 @@ int inode_permission(struct inode *inode, int mask) if (retval) return retval; - return security_inode_permission(inode, - mask & (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND)); + return security_inode_permission(inode, mask); } /** diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 85338f0c048..0c98846f188 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2645,6 +2645,8 @@ static int selinux_inode_permission(struct inode *inode, int mask) { const struct cred *cred = current_cred(); + mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); + if (!mask) { /* No permission to check. Existence test. */ return 0; diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 07abc9ce72f..9192ba366a4 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -598,6 +598,8 @@ static int smack_inode_rename(struct inode *old_inode, static int smack_inode_permission(struct inode *inode, int mask) { struct smk_audit_info ad; + + mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); /* * No permission to check. Existence test. Yup, it's there. */ -- cgit v1.2.3-70-g09d2 From b782e0a68d17894d9a618ffea55b33639faa6bb4 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 23 Jul 2010 11:44:03 -0400 Subject: SELinux: special dontaudit for access checks Currently there are a number of applications (nautilus being the main one) which calls access() on files in order to determine how they should be displayed. It is normal and expected that nautilus will want to see if files are executable or if they are really read/write-able. access() should return the real permission. SELinux policy checks are done in access() and can result in lots of AVC denials as policy denies RWX on files which DAC allows. Currently SELinux must dontaudit actual attempts to read/write/execute a file in order to silence these messages (and not flood the logs.) But dontaudit rules like that can hide real attacks. This patch addes a new common file permission audit_access. This permission is special in that it is meaningless and should never show up in an allow rule. Instead the only place this permission has meaning is in a dontaudit rule like so: dontaudit nautilus_t sbin_t:file audit_access With such a rule if nautilus just checks access() we will still get denied and thus userspace will still get the correct answer but we will not log the denial. If nautilus attempted to actually perform one of the forbidden actions (rather than just querying access(2) about it) we would still log a denial. This type of dontaudit rule should be used sparingly, as it could be a method for an attacker to probe the system permissions without detection. Signed-off-by: Eric Paris Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- include/linux/lsm_audit.h | 5 +++++ security/selinux/avc.c | 24 ++++++++++++++++++++++-- security/selinux/hooks.c | 20 +++++++++++++++----- security/selinux/include/classmap.h | 2 +- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 6907251d520..788f0ab937a 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -90,6 +90,11 @@ struct common_audit_data { u32 requested; u32 audited; u32 denied; + /* + * auditdeny is a bit tricky and unintuitive. See the + * comments in avc.c for it's meaning and usage. + */ + u32 auditdeny; struct av_decision *avd; int result; } selinux_audit_data; diff --git a/security/selinux/avc.c b/security/selinux/avc.c index 3662b0f15ec..9da6420e205 100644 --- a/security/selinux/avc.c +++ b/security/selinux/avc.c @@ -488,9 +488,29 @@ void avc_audit(u32 ssid, u32 tsid, struct common_audit_data stack_data; u32 denied, audited; denied = requested & ~avd->allowed; - if (denied) + if (denied) { audited = denied & avd->auditdeny; - else if (result) + /* + * a->selinux_audit_data.auditdeny is TRICKY! Setting a bit in + * this field means that ANY denials should NOT be audited if + * the policy contains an explicit dontaudit rule for that + * permission. Take notice that this is unrelated to the + * actual permissions that were denied. As an example lets + * assume: + * + * denied == READ + * avd.auditdeny & ACCESS == 0 (not set means explicit rule) + * selinux_audit_data.auditdeny & ACCESS == 1 + * + * We will NOT audit the denial even though the denied + * permission was READ and the auditdeny checks were for + * ACCESS + */ + if (a && + a->selinux_audit_data.auditdeny && + !(a->selinux_audit_data.auditdeny & avd->auditdeny)) + audited = 0; + } else if (result) audited = denied = requested; else audited = requested & avd->auditallow; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 0c98846f188..650947a72a2 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2644,16 +2644,26 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na static int selinux_inode_permission(struct inode *inode, int mask) { const struct cred *cred = current_cred(); + struct common_audit_data ad; + u32 perms; + bool from_access; + from_access = mask & MAY_ACCESS; mask &= (MAY_READ|MAY_WRITE|MAY_EXEC|MAY_APPEND); - if (!mask) { - /* No permission to check. Existence test. */ + /* No permission to check. Existence test. */ + if (!mask) return 0; - } - return inode_has_perm(cred, inode, - file_mask_to_av(inode->i_mode, mask), NULL); + COMMON_AUDIT_DATA_INIT(&ad, FS); + ad.u.fs.inode = inode; + + if (from_access) + ad.selinux_audit_data.auditdeny |= FILE__AUDIT_ACCESS; + + perms = file_mask_to_av(inode->i_mode, mask); + + return inode_has_perm(cred, inode, perms, &ad); } static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 8b32e959bb2..d64603e10db 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -2,7 +2,7 @@ "getattr", "setattr", "lock", "relabelfrom", "relabelto", "append" #define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \ - "rename", "execute", "swapon", "quotaon", "mounton" + "rename", "execute", "swapon", "quotaon", "mounton", "audit_access" #define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \ "listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \ -- cgit v1.2.3-70-g09d2 From 49b7b8de46d293113a0a0bb026ff7bd833c73367 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 23 Jul 2010 11:44:09 -0400 Subject: selinux: place open in the common file perms kernel can dynamically remap perms. Drop the open lookup table and put open in the common file perms. Signed-off-by: Eric Paris Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- security/selinux/hooks.c | 24 +++--------------------- security/selinux/include/classmap.h | 15 ++++++++------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 650947a72a2..9b40f4c0ac7 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -1776,27 +1776,9 @@ static inline u32 open_file_to_av(struct file *file) { u32 av = file_to_av(file); - if (selinux_policycap_openperm) { - mode_t mode = file->f_path.dentry->d_inode->i_mode; - /* - * lnk files and socks do not really have an 'open' - */ - if (S_ISREG(mode)) - av |= FILE__OPEN; - else if (S_ISCHR(mode)) - av |= CHR_FILE__OPEN; - else if (S_ISBLK(mode)) - av |= BLK_FILE__OPEN; - else if (S_ISFIFO(mode)) - av |= FIFO_FILE__OPEN; - else if (S_ISDIR(mode)) - av |= DIR__OPEN; - else if (S_ISSOCK(mode)) - av |= SOCK_FILE__OPEN; - else - printk(KERN_ERR "SELinux: WARNING: inside %s with " - "unknown mode:%o\n", __func__, mode); - } + if (selinux_policycap_openperm) + av |= FILE__OPEN; + return av; } diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index d64603e10db..41990cbba76 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -2,7 +2,8 @@ "getattr", "setattr", "lock", "relabelfrom", "relabelto", "append" #define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \ - "rename", "execute", "swapon", "quotaon", "mounton", "audit_access" + "rename", "execute", "swapon", "quotaon", "mounton", "audit_access", \ + "open" #define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \ "listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \ @@ -43,22 +44,22 @@ struct security_class_mapping secclass_map[] = { "quotaget", NULL } }, { "file", { COMMON_FILE_PERMS, - "execute_no_trans", "entrypoint", "execmod", "open", NULL } }, + "execute_no_trans", "entrypoint", "execmod", NULL } }, { "dir", { COMMON_FILE_PERMS, "add_name", "remove_name", - "reparent", "search", "rmdir", "open", NULL } }, + "reparent", "search", "rmdir", NULL } }, { "fd", { "use", NULL } }, { "lnk_file", { COMMON_FILE_PERMS, NULL } }, { "chr_file", { COMMON_FILE_PERMS, - "execute_no_trans", "entrypoint", "execmod", "open", NULL } }, + "execute_no_trans", "entrypoint", "execmod", NULL } }, { "blk_file", - { COMMON_FILE_PERMS, "open", NULL } }, + { COMMON_FILE_PERMS, NULL } }, { "sock_file", - { COMMON_FILE_PERMS, "open", NULL } }, + { COMMON_FILE_PERMS, NULL } }, { "fifo_file", - { COMMON_FILE_PERMS, "open", NULL } }, + { COMMON_FILE_PERMS, NULL } }, { "socket", { COMMON_SOCK_PERMS, NULL } }, { "tcp_socket", -- cgit v1.2.3-70-g09d2 From b424485abe2b16580a178b469917a7b6ee0c152a Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Fri, 23 Jul 2010 11:44:15 -0400 Subject: SELinux: Move execmod to the common perms execmod "could" show up on non regular files and non chr files. The current implementation would actually make these checks against non-existant bits since the code assumes the execmod permission is same for all file types. To make this line up for chr files we had to define execute_no_trans and entrypoint permissions. These permissions are unreachable and only existed to to make FILE__EXECMOD and CHR_FILE__EXECMOD the same. This patch drops those needless perms as well. Signed-off-by: Eric Paris Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- security/selinux/include/classmap.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index 41990cbba76..b4c9eb4bd6f 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -3,7 +3,7 @@ #define COMMON_FILE_PERMS COMMON_FILE_SOCK_PERMS, "unlink", "link", \ "rename", "execute", "swapon", "quotaon", "mounton", "audit_access", \ - "open" + "open", "execmod" #define COMMON_SOCK_PERMS COMMON_FILE_SOCK_PERMS, "bind", "connect", \ "listen", "accept", "getopt", "setopt", "shutdown", "recvfrom", \ @@ -44,7 +44,7 @@ struct security_class_mapping secclass_map[] = { "quotaget", NULL } }, { "file", { COMMON_FILE_PERMS, - "execute_no_trans", "entrypoint", "execmod", NULL } }, + "execute_no_trans", "entrypoint", NULL } }, { "dir", { COMMON_FILE_PERMS, "add_name", "remove_name", "reparent", "search", "rmdir", NULL } }, @@ -52,8 +52,7 @@ struct security_class_mapping secclass_map[] = { { "lnk_file", { COMMON_FILE_PERMS, NULL } }, { "chr_file", - { COMMON_FILE_PERMS, - "execute_no_trans", "entrypoint", "execmod", NULL } }, + { COMMON_FILE_PERMS, NULL } }, { "blk_file", { COMMON_FILE_PERMS, NULL } }, { "sock_file", -- cgit v1.2.3-70-g09d2 From 7e3d199a4009a4094a955282daf5ecd43f2c8152 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 27 Jul 2010 10:08:29 +0900 Subject: TOMOYO: Fix quota check. Commit d74725b9 "TOMOYO: Use callback for updating entries." broke tomoyo_domain_quota_is_ok() by counting deleted entries. It needs to count non-deleted entries. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/util.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c index 150167d0cc3..9bfc1ee8222 100644 --- a/security/tomoyo/util.c +++ b/security/tomoyo/util.c @@ -911,7 +911,7 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r) if (!domain) return true; list_for_each_entry_rcu(ptr, &domain->acl_info_list, list) { - if (!ptr->is_deleted) + if (ptr->is_deleted) continue; switch (ptr->type) { u16 perm; -- cgit v1.2.3-70-g09d2 From e6f6a4cc955d626ed26562d98de5766bf1f73526 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 27 Jul 2010 17:17:06 +0900 Subject: TOMOYO: Update version to 2.3.0 Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- Documentation/tomoyo.txt | 10 +++++----- MAINTAINERS | 2 +- security/tomoyo/common.c | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Documentation/tomoyo.txt b/Documentation/tomoyo.txt index b3a232cae7f..200a2d37cbc 100644 --- a/Documentation/tomoyo.txt +++ b/Documentation/tomoyo.txt @@ -3,8 +3,8 @@ TOMOYO is a name-based MAC extension (LSM module) for the Linux kernel. LiveCD-based tutorials are available at -http://tomoyo.sourceforge.jp/en/1.6.x/1st-step/ubuntu8.04-live/ -http://tomoyo.sourceforge.jp/en/1.6.x/1st-step/centos5-live/ . +http://tomoyo.sourceforge.jp/1.7/1st-step/ubuntu10.04-live/ +http://tomoyo.sourceforge.jp/1.7/1st-step/centos5-live/ . Though these tutorials use non-LSM version of TOMOYO, they are useful for you to know what TOMOYO is. @@ -13,12 +13,12 @@ to know what TOMOYO is. Build the kernel with CONFIG_SECURITY_TOMOYO=y and pass "security=tomoyo" on kernel's command line. -Please see http://tomoyo.sourceforge.jp/en/2.2.x/ for details. +Please see http://tomoyo.sourceforge.jp/2.3/ for details. --- Where is documentation? --- User <-> Kernel interface documentation is available at -http://tomoyo.sourceforge.jp/en/2.2.x/policy-reference.html . +http://tomoyo.sourceforge.jp/2.3/policy-reference.html . Materials we prepared for seminars and symposiums are available at http://sourceforge.jp/projects/tomoyo/docs/?category_id=532&language_id=1 . @@ -50,6 +50,6 @@ multiple LSM modules at the same time. We feel sorry that you have to give up SELinux/SMACK/AppArmor etc. when you want to use TOMOYO. We hope that LSM becomes stackable in future. Meanwhile, you can use non-LSM -version of TOMOYO, available at http://tomoyo.sourceforge.jp/en/1.6.x/ . +version of TOMOYO, available at http://tomoyo.sourceforge.jp/1.7/ . LSM version of TOMOYO is a subset of non-LSM version of TOMOYO. We are planning to port non-LSM version's functionalities to LSM versions. diff --git a/MAINTAINERS b/MAINTAINERS index 02f75fccac2..28443185646 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5605,7 +5605,7 @@ L: tomoyo-users-en@lists.sourceforge.jp (subscribers-only, for developers and us L: tomoyo-dev@lists.sourceforge.jp (subscribers-only, for developers in Japanese) L: tomoyo-users@lists.sourceforge.jp (subscribers-only, for users in Japanese) W: http://tomoyo.sourceforge.jp/ -T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.2.x/tomoyo-lsm/patches/ +T: quilt http://svn.sourceforge.jp/svnroot/tomoyo/trunk/2.3.x/tomoyo-lsm/patches/ S: Maintained F: security/tomoyo/ diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index bff63929e53..ef43995119a 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1747,7 +1747,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.3.0-pre"); + tomoyo_io_printf(head, "2.3.0"); head->r.eof = true; } } @@ -2054,6 +2054,6 @@ void tomoyo_check_profile(void) if (tomoyo_profile_version != 20090903) panic("Profile version %u is not supported.\n", tomoyo_profile_version); - printk(KERN_INFO "TOMOYO: 2.3.0-pre 2010/06/03\n"); + printk(KERN_INFO "TOMOYO: 2.3.0\n"); printk(KERN_INFO "Mandatory Access Control activated.\n"); } -- cgit v1.2.3-70-g09d2 From cdff264264254e0fabc8107a33f3bb75a95e981f Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 29 Jul 2010 14:47:57 -0700 Subject: AppArmor: misc. base functions and defines Miscellaneous functions and defines needed by AppArmor, including the base path resolution routines. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/include/apparmor.h | 92 ++++++++++++++ security/apparmor/include/path.h | 31 +++++ security/apparmor/lib.c | 133 ++++++++++++++++++++ security/apparmor/path.c | 235 +++++++++++++++++++++++++++++++++++ 4 files changed, 491 insertions(+) create mode 100644 security/apparmor/include/apparmor.h create mode 100644 security/apparmor/include/path.h create mode 100644 security/apparmor/lib.c create mode 100644 security/apparmor/path.c diff --git a/security/apparmor/include/apparmor.h b/security/apparmor/include/apparmor.h new file mode 100644 index 00000000000..38ccaea0820 --- /dev/null +++ b/security/apparmor/include/apparmor.h @@ -0,0 +1,92 @@ +/* + * AppArmor security module + * + * This file contains AppArmor basic global and lib definitions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __APPARMOR_H +#define __APPARMOR_H + +#include + +#include "match.h" + +/* Control parameters settable through module/boot flags */ +extern enum audit_mode aa_g_audit; +extern int aa_g_audit_header; +extern int aa_g_debug; +extern int aa_g_lock_policy; +extern int aa_g_logsyscall; +extern int aa_g_paranoid_load; +extern unsigned int aa_g_path_max; + +/* + * DEBUG remains global (no per profile flag) since it is mostly used in sysctl + * which is not related to profile accesses. + */ + +#define AA_DEBUG(fmt, args...) \ + do { \ + if (aa_g_debug && printk_ratelimit()) \ + printk(KERN_DEBUG "AppArmor: " fmt, ##args); \ + } while (0) + +#define AA_ERROR(fmt, args...) \ + do { \ + if (printk_ratelimit()) \ + printk(KERN_ERR "AppArmor: " fmt, ##args); \ + } while (0) + +/* Flag indicating whether initialization completed */ +extern int apparmor_initialized __initdata; + +/* fn's in lib */ +char *aa_split_fqname(char *args, char **ns_name); +void aa_info_message(const char *str); +void *kvmalloc(size_t size); +void kvfree(void *buffer); + + +/** + * aa_strneq - compare null terminated @str to a non null terminated substring + * @str: a null terminated string + * @sub: a substring, not necessarily null terminated + * @len: length of @sub to compare + * + * The @str string must be full consumed for this to be considered a match + */ +static inline bool aa_strneq(const char *str, const char *sub, int len) +{ + return !strncmp(str, sub, len) && !str[len]; +} + +/** + * aa_dfa_null_transition - step to next state after null character + * @dfa: the dfa to match against + * @start: the state of the dfa to start matching in + * + * aa_dfa_null_transition transitions to the next state after a null + * character which is not used in standard matching and is only + * used to separate pairs. + */ +static inline unsigned int aa_dfa_null_transition(struct aa_dfa *dfa, + unsigned int start) +{ + /* the null transition only needs the string's null terminator byte */ + return aa_dfa_match_len(dfa, start, "", 1); +} + +static inline bool mediated_filesystem(struct inode *inode) +{ + return !(inode->i_sb->s_flags & MS_NOUSER); +} + +#endif /* __APPARMOR_H */ diff --git a/security/apparmor/include/path.h b/security/apparmor/include/path.h new file mode 100644 index 00000000000..27b327a7fae --- /dev/null +++ b/security/apparmor/include/path.h @@ -0,0 +1,31 @@ +/* + * AppArmor security module + * + * This file contains AppArmor basic path manipulation function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_PATH_H +#define __AA_PATH_H + + +enum path_flags { + PATH_IS_DIR = 0x1, /* path is a directory */ + PATH_CONNECT_PATH = 0x4, /* connect disconnected paths to / */ + PATH_CHROOT_REL = 0x8, /* do path lookup relative to chroot */ + PATH_CHROOT_NSCONNECT = 0x10, /* connect paths that are at ns root */ + + PATH_DELEGATE_DELETED = 0x08000, /* delegate deleted files */ + PATH_MEDIATE_DELETED = 0x10000, /* mediate deleted paths */ +}; + +int aa_get_name(struct path *path, int flags, char **buffer, const char **name); + +#endif /* __AA_PATH_H */ diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c new file mode 100644 index 00000000000..6e85cdb4303 --- /dev/null +++ b/security/apparmor/lib.c @@ -0,0 +1,133 @@ +/* + * AppArmor security module + * + * This file contains basic common functions used in AppArmor + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include +#include +#include + +#include "include/audit.h" + + +/** + * aa_split_fqname - split a fqname into a profile and namespace name + * @fqname: a full qualified name in namespace profile format (NOT NULL) + * @ns_name: pointer to portion of the string containing the ns name (NOT NULL) + * + * Returns: profile name or NULL if one is not specified + * + * Split a namespace name from a profile name (see policy.c for naming + * description). If a portion of the name is missing it returns NULL for + * that portion. + * + * NOTE: may modify the @fqname string. The pointers returned point + * into the @fqname string. + */ +char *aa_split_fqname(char *fqname, char **ns_name) +{ + char *name = strim(fqname); + + *ns_name = NULL; + if (name[0] == ':') { + char *split = strchr(&name[1], ':'); + if (split) { + /* overwrite ':' with \0 */ + *split = 0; + name = skip_spaces(split + 1); + } else + /* a ns name without a following profile is allowed */ + name = NULL; + *ns_name = &name[1]; + } + if (name && *name == 0) + name = NULL; + + return name; +} + +/** + * aa_info_message - log a none profile related status message + * @str: message to log + */ +void aa_info_message(const char *str) +{ + if (audit_enabled) { + struct common_audit_data sa; + COMMON_AUDIT_DATA_INIT(&sa, NONE); + sa.aad.info = str; + aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL); + } + printk(KERN_INFO "AppArmor: %s\n", str); +} + +/** + * kvmalloc - do allocation preferring kmalloc but falling back to vmalloc + * @size: size of allocation + * + * Return: allocated buffer or NULL if failed + * + * It is possible that policy being loaded from the user is larger than + * what can be allocated by kmalloc, in those cases fall back to vmalloc. + */ +void *kvmalloc(size_t size) +{ + void *buffer = NULL; + + if (size == 0) + return NULL; + + /* do not attempt kmalloc if we need more than 16 pages at once */ + if (size <= (16*PAGE_SIZE)) + buffer = kmalloc(size, GFP_NOIO | __GFP_NOWARN); + if (!buffer) { + /* see kvfree for why size must be at least work_struct size + * when allocated via vmalloc + */ + if (size < sizeof(struct work_struct)) + size = sizeof(struct work_struct); + buffer = vmalloc(size); + } + return buffer; +} + +/** + * do_vfree - workqueue routine for freeing vmalloced memory + * @work: data to be freed + * + * The work_struct is overlaid to the data being freed, as at the point + * the work is scheduled the data is no longer valid, be its freeing + * needs to be delayed until safe. + */ +static void do_vfree(struct work_struct *work) +{ + vfree(work); +} + +/** + * kvfree - free an allocation do by kvmalloc + * @buffer: buffer to free (MAYBE_NULL) + * + * Free a buffer allocated by kvmalloc + */ +void kvfree(void *buffer) +{ + if (is_vmalloc_addr(buffer)) { + /* Data is no longer valid so just use the allocated space + * as the work_struct + */ + struct work_struct *work = (struct work_struct *) buffer; + INIT_WORK(work, do_vfree); + schedule_work(work); + } else + kfree(buffer); +} diff --git a/security/apparmor/path.c b/security/apparmor/path.c new file mode 100644 index 00000000000..96bab9469d4 --- /dev/null +++ b/security/apparmor/path.c @@ -0,0 +1,235 @@ +/* + * AppArmor security module + * + * This file contains AppArmor function for pathnames + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/path.h" +#include "include/policy.h" + + +/* modified from dcache.c */ +static int prepend(char **buffer, int buflen, const char *str, int namelen) +{ + buflen -= namelen; + if (buflen < 0) + return -ENAMETOOLONG; + *buffer -= namelen; + memcpy(*buffer, str, namelen); + return 0; +} + +#define CHROOT_NSCONNECT (PATH_CHROOT_REL | PATH_CHROOT_NSCONNECT) + +/** + * d_namespace_path - lookup a name associated with a given path + * @path: path to lookup (NOT NULL) + * @buf: buffer to store path to (NOT NULL) + * @buflen: length of @buf + * @name: Returns - pointer for start of path name with in @buf (NOT NULL) + * @flags: flags controlling path lookup + * + * Handle path name lookup. + * + * Returns: %0 else error code if path lookup fails + * When no error the path name is returned in @name which points to + * to a position in @buf + */ +static int d_namespace_path(struct path *path, char *buf, int buflen, + char **name, int flags) +{ + struct path root, tmp; + char *res; + int deleted, connected; + int error = 0; + + /* Get the root we want to resolve too */ + if (flags & PATH_CHROOT_REL) { + /* resolve paths relative to chroot */ + read_lock(¤t->fs->lock); + root = current->fs->root; + /* released below */ + path_get(&root); + read_unlock(¤t->fs->lock); + } else { + /* resolve paths relative to namespace */ + root.mnt = current->nsproxy->mnt_ns->root; + root.dentry = root.mnt->mnt_root; + /* released below */ + path_get(&root); + } + + spin_lock(&dcache_lock); + /* There is a race window between path lookup here and the + * need to strip the " (deleted) string that __d_path applies + * Detect the race and relookup the path + * + * The stripping of (deleted) is a hack that could be removed + * with an updated __d_path + */ + do { + tmp = root; + deleted = d_unlinked(path->dentry); + res = __d_path(path, &tmp, buf, buflen); + + } while (deleted != d_unlinked(path->dentry)); + spin_unlock(&dcache_lock); + + *name = res; + /* handle error conditions - and still allow a partial path to + * be returned. + */ + if (IS_ERR(res)) { + error = PTR_ERR(res); + *name = buf; + goto out; + } + if (deleted) { + /* On some filesystems, newly allocated dentries appear to the + * security_path hooks as a deleted dentry except without an + * inode allocated. + * + * Remove the appended deleted text and return as string for + * normal mediation, or auditing. The (deleted) string is + * guaranteed to be added in this case, so just strip it. + */ + buf[buflen - 11] = 0; /* - (len(" (deleted)") +\0) */ + + if (path->dentry->d_inode && !(flags & PATH_MEDIATE_DELETED)) { + error = -ENOENT; + goto out; + } + } + + /* Determine if the path is connected to the expected root */ + connected = tmp.dentry == root.dentry && tmp.mnt == root.mnt; + + /* If the path is not connected, + * check if it is a sysctl and handle specially else remove any + * leading / that __d_path may have returned. + * Unless + * specifically directed to connect the path, + * OR + * if in a chroot and doing chroot relative paths and the path + * resolves to the namespace root (would be connected outside + * of chroot) and specifically directed to connect paths to + * namespace root. + */ + if (!connected) { + /* is the disconnect path a sysctl? */ + if (tmp.dentry->d_sb->s_magic == PROC_SUPER_MAGIC && + strncmp(*name, "/sys/", 5) == 0) { + /* TODO: convert over to using a per namespace + * control instead of hard coded /proc + */ + error = prepend(name, *name - buf, "/proc", 5); + } else if (!(flags & PATH_CONNECT_PATH) && + !(((flags & CHROOT_NSCONNECT) == CHROOT_NSCONNECT) && + (tmp.mnt == current->nsproxy->mnt_ns->root && + tmp.dentry == tmp.mnt->mnt_root))) { + /* disconnected path, don't return pathname starting + * with '/' + */ + error = -ESTALE; + if (*res == '/') + *name = res + 1; + } + } + +out: + path_put(&root); + + return error; +} + +/** + * get_name_to_buffer - get the pathname to a buffer ensure dir / is appended + * @path: path to get name for (NOT NULL) + * @flags: flags controlling path lookup + * @buffer: buffer to put name in (NOT NULL) + * @size: size of buffer + * @name: Returns - contains position of path name in @buffer (NOT NULL) + * + * Returns: %0 else error on failure + */ +static int get_name_to_buffer(struct path *path, int flags, char *buffer, + int size, char **name) +{ + int adjust = (flags & PATH_IS_DIR) ? 1 : 0; + int error = d_namespace_path(path, buffer, size - adjust, name, flags); + + if (!error && (flags & PATH_IS_DIR) && (*name)[1] != '\0') + /* + * Append "/" to the pathname. The root directory is a special + * case; it already ends in slash. + */ + strcpy(&buffer[size - 2], "/"); + + return error; +} + +/** + * aa_get_name - compute the pathname of a file + * @path: path the file (NOT NULL) + * @flags: flags controlling path name generation + * @buffer: buffer that aa_get_name() allocated (NOT NULL) + * @name: Returns - the generated path name if !error (NOT NULL) + * + * @name is a pointer to the beginning of the pathname (which usually differs + * from the beginning of the buffer), or NULL. If there is an error @name + * may contain a partial or invalid name that can be used for audit purposes, + * but it can not be used for mediation. + * + * We need PATH_IS_DIR to indicate whether the file is a directory or not + * because the file may not yet exist, and so we cannot check the inode's + * file type. + * + * Returns: %0 else error code if could retrieve name + */ +int aa_get_name(struct path *path, int flags, char **buffer, const char **name) +{ + char *buf, *str = NULL; + int size = 256; + int error; + + *name = NULL; + *buffer = NULL; + for (;;) { + /* freed by caller */ + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + error = get_name_to_buffer(path, flags, buf, size, &str); + if (error != -ENAMETOOLONG) + break; + + kfree(buf); + size <<= 1; + if (size > aa_g_path_max) + return -ENAMETOOLONG; + } + *buffer = buf; + *name = str; + + return error; +} -- cgit v1.2.3-70-g09d2 From 67012e8209df95a8290d135753ff5145431a666e Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 29 Jul 2010 14:47:58 -0700 Subject: AppArmor: basic auditing infrastructure. Update lsm_audit for AppArmor specific data, and add the core routines for AppArmor uses for auditing. Signed-off-by: John Johansen Signed-off-by: James Morris --- include/linux/lsm_audit.h | 27 +++++ security/apparmor/audit.c | 215 ++++++++++++++++++++++++++++++++++++++ security/apparmor/include/audit.h | 123 ++++++++++++++++++++++ 3 files changed, 365 insertions(+) create mode 100644 security/apparmor/audit.c create mode 100644 security/apparmor/include/audit.h diff --git a/include/linux/lsm_audit.h b/include/linux/lsm_audit.h index 788f0ab937a..112a5503335 100644 --- a/include/linux/lsm_audit.h +++ b/include/linux/lsm_audit.h @@ -98,6 +98,33 @@ struct common_audit_data { struct av_decision *avd; int result; } selinux_audit_data; +#endif +#ifdef CONFIG_SECURITY_APPARMOR + struct { + int error; + int op; + int type; + void *profile; + const char *name; + const char *info; + union { + void *target; + struct { + long pos; + void *target; + } iface; + struct { + int rlim; + unsigned long max; + } rlim; + struct { + const char *target; + u32 request; + u32 denied; + uid_t ouid; + } fs; + }; + } apparmor_audit_data; #endif }; /* these callback will be implemented by a specific LSM */ diff --git a/security/apparmor/audit.c b/security/apparmor/audit.c new file mode 100644 index 00000000000..96502b22b26 --- /dev/null +++ b/security/apparmor/audit.c @@ -0,0 +1,215 @@ +/* + * AppArmor security module + * + * This file contains AppArmor auditing functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/policy.h" + +const char *op_table[] = { + "null", + + "sysctl", + "capable", + + "unlink", + "mkdir", + "rmdir", + "mknod", + "truncate", + "link", + "symlink", + "rename_src", + "rename_dest", + "chmod", + "chown", + "getattr", + "open", + + "file_perm", + "file_lock", + "file_mmap", + "file_mprotect", + + "create", + "post_create", + "bind", + "connect", + "listen", + "accept", + "sendmsg", + "recvmsg", + "getsockname", + "getpeername", + "getsockopt", + "setsockopt", + "socket_shutdown", + + "ptrace", + + "exec", + "change_hat", + "change_profile", + "change_onexec", + + "setprocattr", + "setrlimit", + + "profile_replace", + "profile_load", + "profile_remove" +}; + +const char *audit_mode_names[] = { + "normal", + "quiet_denied", + "quiet", + "noquiet", + "all" +}; + +static char *aa_audit_type[] = { + "AUDIT", + "ALLOWED", + "DENIED", + "HINT", + "STATUS", + "ERROR", + "KILLED" +}; + +/* + * Currently AppArmor auditing is fed straight into the audit framework. + * + * TODO: + * netlink interface for complain mode + * user auditing, - send user auditing to netlink interface + * system control of whether user audit messages go to system log + */ + +/** + * audit_base - core AppArmor function. + * @ab: audit buffer to fill (NOT NULL) + * @ca: audit structure containing data to audit (NOT NULL) + * + * Record common AppArmor audit data from @sa + */ +static void audit_pre(struct audit_buffer *ab, void *ca) +{ + struct common_audit_data *sa = ca; + struct task_struct *tsk = sa->tsk ? sa->tsk : current; + + if (aa_g_audit_header) { + audit_log_format(ab, "apparmor="); + audit_log_string(ab, aa_audit_type[sa->aad.type]); + } + + if (sa->aad.op) { + audit_log_format(ab, " operation="); + audit_log_string(ab, op_table[sa->aad.op]); + } + + if (sa->aad.info) { + audit_log_format(ab, " info="); + audit_log_string(ab, sa->aad.info); + if (sa->aad.error) + audit_log_format(ab, " error=%d", sa->aad.error); + } + + if (sa->aad.profile) { + struct aa_profile *profile = sa->aad.profile; + pid_t pid; + rcu_read_lock(); + pid = tsk->real_parent->pid; + rcu_read_unlock(); + audit_log_format(ab, " parent=%d", pid); + if (profile->ns != root_ns) { + audit_log_format(ab, " namespace="); + audit_log_untrustedstring(ab, profile->ns->base.hname); + } + audit_log_format(ab, " profile="); + audit_log_untrustedstring(ab, profile->base.hname); + } + + if (sa->aad.name) { + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, sa->aad.name); + } +} + +/** + * aa_audit_msg - Log a message to the audit subsystem + * @sa: audit event structure (NOT NULL) + * @cb: optional callback fn for type specific fields (MAYBE NULL) + */ +void aa_audit_msg(int type, struct common_audit_data *sa, + void (*cb) (struct audit_buffer *, void *)) +{ + sa->aad.type = type; + sa->lsm_pre_audit = audit_pre; + sa->lsm_post_audit = cb; + common_lsm_audit(sa); +} + +/** + * aa_audit - Log a profile based audit event to the audit subsystem + * @type: audit type for the message + * @profile: profile to check against (NOT NULL) + * @gfp: allocation flags to use + * @sa: audit event (NOT NULL) + * @cb: optional callback fn for type specific fields (MAYBE NULL) + * + * Handle default message switching based off of audit mode flags + * + * Returns: error on failure + */ +int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, + struct common_audit_data *sa, + void (*cb) (struct audit_buffer *, void *)) +{ + BUG_ON(!profile); + + if (type == AUDIT_APPARMOR_AUTO) { + if (likely(!sa->aad.error)) { + if (AUDIT_MODE(profile) != AUDIT_ALL) + return 0; + type = AUDIT_APPARMOR_AUDIT; + } else if (COMPLAIN_MODE(profile)) + type = AUDIT_APPARMOR_ALLOWED; + else + type = AUDIT_APPARMOR_DENIED; + } + if (AUDIT_MODE(profile) == AUDIT_QUIET || + (type == AUDIT_APPARMOR_DENIED && + AUDIT_MODE(profile) == AUDIT_QUIET)) + return sa->aad.error; + + if (KILL_MODE(profile) && type == AUDIT_APPARMOR_DENIED) + type = AUDIT_APPARMOR_KILL; + + if (!unconfined(profile)) + sa->aad.profile = profile; + + aa_audit_msg(type, sa, cb); + + if (sa->aad.type == AUDIT_APPARMOR_KILL) + (void)send_sig_info(SIGKILL, NULL, sa->tsk ? sa->tsk : current); + + if (sa->aad.type == AUDIT_APPARMOR_ALLOWED) + return complain_error(sa->aad.error); + + return sa->aad.error; +} diff --git a/security/apparmor/include/audit.h b/security/apparmor/include/audit.h new file mode 100644 index 00000000000..1951786d32e --- /dev/null +++ b/security/apparmor/include/audit.h @@ -0,0 +1,123 @@ +/* + * AppArmor security module + * + * This file contains AppArmor auditing function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_AUDIT_H +#define __AA_AUDIT_H + +#include +#include +#include +#include +#include + +#include "file.h" + +struct aa_profile; + +extern const char *audit_mode_names[]; +#define AUDIT_MAX_INDEX 5 + +#define AUDIT_APPARMOR_AUTO 0 /* auto choose audit message type */ + +enum audit_mode { + AUDIT_NORMAL, /* follow normal auditing of accesses */ + AUDIT_QUIET_DENIED, /* quiet all denied access messages */ + AUDIT_QUIET, /* quiet all messages */ + AUDIT_NOQUIET, /* do not quiet audit messages */ + AUDIT_ALL /* audit all accesses */ +}; + +enum audit_type { + AUDIT_APPARMOR_AUDIT, + AUDIT_APPARMOR_ALLOWED, + AUDIT_APPARMOR_DENIED, + AUDIT_APPARMOR_HINT, + AUDIT_APPARMOR_STATUS, + AUDIT_APPARMOR_ERROR, + AUDIT_APPARMOR_KILL +}; + +extern const char *op_table[]; +enum aa_ops { + OP_NULL, + + OP_SYSCTL, + OP_CAPABLE, + + OP_UNLINK, + OP_MKDIR, + OP_RMDIR, + OP_MKNOD, + OP_TRUNC, + OP_LINK, + OP_SYMLINK, + OP_RENAME_SRC, + OP_RENAME_DEST, + OP_CHMOD, + OP_CHOWN, + OP_GETATTR, + OP_OPEN, + + OP_FPERM, + OP_FLOCK, + OP_FMMAP, + OP_FMPROT, + + OP_CREATE, + OP_POST_CREATE, + OP_BIND, + OP_CONNECT, + OP_LISTEN, + OP_ACCEPT, + OP_SENDMSG, + OP_RECVMSG, + OP_GETSOCKNAME, + OP_GETPEERNAME, + OP_GETSOCKOPT, + OP_SETSOCKOPT, + OP_SOCK_SHUTDOWN, + + OP_PTRACE, + + OP_EXEC, + OP_CHANGE_HAT, + OP_CHANGE_PROFILE, + OP_CHANGE_ONEXEC, + + OP_SETPROCATTR, + OP_SETRLIMIT, + + OP_PROF_REPL, + OP_PROF_LOAD, + OP_PROF_RM, +}; + + +/* define a short hand for apparmor_audit_data portion of common_audit_data */ +#define aad apparmor_audit_data + +void aa_audit_msg(int type, struct common_audit_data *sa, + void (*cb) (struct audit_buffer *, void *)); +int aa_audit(int type, struct aa_profile *profile, gfp_t gfp, + struct common_audit_data *sa, + void (*cb) (struct audit_buffer *, void *)); + +static inline int complain_error(int error) +{ + if (error == -EPERM || error == -EACCES) + return 0; + return error; +} + +#endif /* __AA_AUDIT_H */ -- cgit v1.2.3-70-g09d2 From c75afcd153f6147d3b094f45a1d87e5df7f4f053 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 29 Jul 2010 14:47:59 -0700 Subject: AppArmor: contexts used in attaching policy to system objects AppArmor contexts attach profiles and state to tasks, files, etc. when a direct profile reference is not sufficient. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/context.c | 216 ++++++++++++++++++++++++++++++++++++ security/apparmor/include/context.h | 154 +++++++++++++++++++++++++ 2 files changed, 370 insertions(+) create mode 100644 security/apparmor/context.c create mode 100644 security/apparmor/include/context.h diff --git a/security/apparmor/context.c b/security/apparmor/context.c new file mode 100644 index 00000000000..8a9b5027c81 --- /dev/null +++ b/security/apparmor/context.c @@ -0,0 +1,216 @@ +/* + * AppArmor security module + * + * This file contains AppArmor functions used to manipulate object security + * contexts. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * + * AppArmor sets confinement on every task, via the the aa_task_cxt and + * the aa_task_cxt.profile, both of which are required and are not allowed + * to be NULL. The aa_task_cxt is not reference counted and is unique + * to each cred (which is reference count). The profile pointed to by + * the task_cxt is reference counted. + * + * TODO + * If a task uses change_hat it currently does not return to the old + * cred or task context but instead creates a new one. Ideally the task + * should return to the previous cred if it has not been modified. + * + */ + +#include "include/context.h" +#include "include/policy.h" + +/** + * aa_alloc_task_context - allocate a new task_cxt + * @flags: gfp flags for allocation + * + * Returns: allocated buffer or NULL on failure + */ +struct aa_task_cxt *aa_alloc_task_context(gfp_t flags) +{ + return kzalloc(sizeof(struct aa_task_cxt), flags); +} + +/** + * aa_free_task_context - free a task_cxt + * @cxt: task_cxt to free (MAYBE NULL) + */ +void aa_free_task_context(struct aa_task_cxt *cxt) +{ + if (cxt) { + aa_put_profile(cxt->profile); + aa_put_profile(cxt->previous); + aa_put_profile(cxt->onexec); + + kzfree(cxt); + } +} + +/** + * aa_dup_task_context - duplicate a task context, incrementing reference counts + * @new: a blank task context (NOT NULL) + * @old: the task context to copy (NOT NULL) + */ +void aa_dup_task_context(struct aa_task_cxt *new, const struct aa_task_cxt *old) +{ + *new = *old; + aa_get_profile(new->profile); + aa_get_profile(new->previous); + aa_get_profile(new->onexec); +} + +/** + * aa_replace_current_profile - replace the current tasks profiles + * @profile: new profile (NOT NULL) + * + * Returns: 0 or error on failure + */ +int aa_replace_current_profile(struct aa_profile *profile) +{ + struct aa_task_cxt *cxt = current_cred()->security; + struct cred *new; + BUG_ON(!profile); + + if (cxt->profile == profile) + return 0; + + new = prepare_creds(); + if (!new) + return -ENOMEM; + + cxt = new->security; + if (unconfined(profile) || (cxt->profile->ns != profile->ns)) { + /* if switching to unconfined or a different profile namespace + * clear out context state + */ + aa_put_profile(cxt->previous); + aa_put_profile(cxt->onexec); + cxt->previous = NULL; + cxt->onexec = NULL; + cxt->token = 0; + } + /* be careful switching cxt->profile, when racing replacement it + * is possible that cxt->profile->replacedby is the reference keeping + * @profile valid, so make sure to get its reference before dropping + * the reference on cxt->profile */ + aa_get_profile(profile); + aa_put_profile(cxt->profile); + cxt->profile = profile; + + commit_creds(new); + return 0; +} + +/** + * aa_set_current_onexec - set the tasks change_profile to happen onexec + * @profile: system profile to set at exec (MAYBE NULL to clear value) + * + * Returns: 0 or error on failure + */ +int aa_set_current_onexec(struct aa_profile *profile) +{ + struct aa_task_cxt *cxt; + struct cred *new = prepare_creds(); + if (!new) + return -ENOMEM; + + cxt = new->security; + aa_get_profile(profile); + aa_put_profile(cxt->onexec); + cxt->onexec = profile; + + commit_creds(new); + return 0; +} + +/** + * aa_set_current_hat - set the current tasks hat + * @profile: profile to set as the current hat (NOT NULL) + * @token: token value that must be specified to change from the hat + * + * Do switch of tasks hat. If the task is currently in a hat + * validate the token to match. + * + * Returns: 0 or error on failure + */ +int aa_set_current_hat(struct aa_profile *profile, u64 token) +{ + struct aa_task_cxt *cxt; + struct cred *new = prepare_creds(); + if (!new) + return -ENOMEM; + BUG_ON(!profile); + + cxt = new->security; + if (!cxt->previous) { + /* transfer refcount */ + cxt->previous = cxt->profile; + cxt->token = token; + } else if (cxt->token == token) { + aa_put_profile(cxt->profile); + } else { + /* previous_profile && cxt->token != token */ + abort_creds(new); + return -EACCES; + } + cxt->profile = aa_get_profile(aa_newest_version(profile)); + /* clear exec on switching context */ + aa_put_profile(cxt->onexec); + cxt->onexec = NULL; + + commit_creds(new); + return 0; +} + +/** + * aa_restore_previous_profile - exit from hat context restoring the profile + * @token: the token that must be matched to exit hat context + * + * Attempt to return out of a hat to the previous profile. The token + * must match the stored token value. + * + * Returns: 0 or error of failure + */ +int aa_restore_previous_profile(u64 token) +{ + struct aa_task_cxt *cxt; + struct cred *new = prepare_creds(); + if (!new) + return -ENOMEM; + + cxt = new->security; + if (cxt->token != token) { + abort_creds(new); + return -EACCES; + } + /* ignore restores when there is no saved profile */ + if (!cxt->previous) { + abort_creds(new); + return 0; + } + + aa_put_profile(cxt->profile); + cxt->profile = aa_newest_version(cxt->previous); + BUG_ON(!cxt->profile); + if (unlikely(cxt->profile != cxt->previous)) { + aa_get_profile(cxt->profile); + aa_put_profile(cxt->previous); + } + /* clear exec && prev information when restoring to previous context */ + cxt->previous = NULL; + cxt->token = 0; + aa_put_profile(cxt->onexec); + cxt->onexec = NULL; + + commit_creds(new); + return 0; +} diff --git a/security/apparmor/include/context.h b/security/apparmor/include/context.h new file mode 100644 index 00000000000..a9cbee4d9e4 --- /dev/null +++ b/security/apparmor/include/context.h @@ -0,0 +1,154 @@ +/* + * AppArmor security module + * + * This file contains AppArmor contexts used to associate "labels" to objects. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_CONTEXT_H +#define __AA_CONTEXT_H + +#include +#include +#include + +#include "policy.h" + +/* struct aa_file_cxt - the AppArmor context the file was opened in + * @perms: the permission the file was opened with + * + * The file_cxt could currently be directly stored in file->f_security + * as the profile reference is now stored in the f_cred. However the + * cxt struct will expand in the future so we keep the struct. + */ +struct aa_file_cxt { + u16 allow; +}; + +/** + * aa_alloc_file_context - allocate file_cxt + * @gfp: gfp flags for allocation + * + * Returns: file_cxt or NULL on failure + */ +static inline struct aa_file_cxt *aa_alloc_file_context(gfp_t gfp) +{ + return kzalloc(sizeof(struct aa_file_cxt), gfp); +} + +/** + * aa_free_file_context - free a file_cxt + * @cxt: file_cxt to free (MAYBE_NULL) + */ +static inline void aa_free_file_context(struct aa_file_cxt *cxt) +{ + if (cxt) + kzfree(cxt); +} + +/** + * struct aa_task_cxt - primary label for confined tasks + * @profile: the current profile (NOT NULL) + * @exec: profile to transition to on next exec (MAYBE NULL) + * @previous: profile the task may return to (MAYBE NULL) + * @token: magic value the task must know for returning to @previous_profile + * + * Contains the task's current profile (which could change due to + * change_hat). Plus the hat_magic needed during change_hat. + * + * TODO: make so a task can be confined by a stack of contexts + */ +struct aa_task_cxt { + struct aa_profile *profile; + struct aa_profile *onexec; + struct aa_profile *previous; + u64 token; +}; + +struct aa_task_cxt *aa_alloc_task_context(gfp_t flags); +void aa_free_task_context(struct aa_task_cxt *cxt); +void aa_dup_task_context(struct aa_task_cxt *new, + const struct aa_task_cxt *old); +int aa_replace_current_profile(struct aa_profile *profile); +int aa_set_current_onexec(struct aa_profile *profile); +int aa_set_current_hat(struct aa_profile *profile, u64 token); +int aa_restore_previous_profile(u64 cookie); + +/** + * __aa_task_is_confined - determine if @task has any confinement + * @task: task to check confinement of (NOT NULL) + * + * If @task != current needs to be called in RCU safe critical section + */ +static inline bool __aa_task_is_confined(struct task_struct *task) +{ + struct aa_task_cxt *cxt = __task_cred(task)->security; + + BUG_ON(!cxt || !cxt->profile); + if (unconfined(aa_newest_version(cxt->profile))) + return 0; + + return 1; +} + +/** + * aa_cred_profile - obtain cred's profiles + * @cred: cred to obtain profiles from (NOT NULL) + * + * Returns: confining profile + * + * does NOT increment reference count + */ +static inline struct aa_profile *aa_cred_profile(const struct cred *cred) +{ + struct aa_task_cxt *cxt = cred->security; + BUG_ON(!cxt || !cxt->profile); + return aa_newest_version(cxt->profile); +} + +/** + * __aa_current_profile - find the current tasks confining profile + * + * Returns: up to date confining profile or the ns unconfined profile (NOT NULL) + * + * This fn will not update the tasks cred to the most up to date version + * of the profile so it is safe to call when inside of locks. + */ +static inline struct aa_profile *__aa_current_profile(void) +{ + return aa_cred_profile(current_cred()); +} + +/** + * aa_current_profile - find the current tasks confining profile and do updates + * + * Returns: up to date confining profile or the ns unconfined profile (NOT NULL) + * + * This fn will update the tasks cred structure if the profile has been + * replaced. Not safe to call inside locks + */ +static inline struct aa_profile *aa_current_profile(void) +{ + const struct aa_task_cxt *cxt = current_cred()->security; + struct aa_profile *profile; + BUG_ON(!cxt || !cxt->profile); + + profile = aa_newest_version(cxt->profile); + /* + * Whether or not replacement succeeds, use newest profile so + * there is no need to update it after replacement. + */ + if (unlikely((cxt->profile != profile))) + aa_replace_current_profile(profile); + + return profile; +} + +#endif /* __AA_CONTEXT_H */ -- cgit v1.2.3-70-g09d2 From e06f75a6a2b43bd3a7a197bd21466f9da130e4af Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 29 Jul 2010 14:48:01 -0700 Subject: AppArmor: dfa match engine A basic dfa matching engine based off the dfa engine in the Dragon Book. It uses simple row comb compression with a check field. This allows AppArmor to do pattern matching in linear time, and also avoids stack issues that an nfa based engine may have. The dfa engine uses a byte based comparison, with all values being valid. Any potential character encoding are handled user side when the dfa tables are created. By convention AppArmor uses \0 to separate two dependent path matches since \0 is not a valid path character (this is done in the link permission check). The dfa tables are generated in user space and are verified at load time to be internally consistent. There are several future improvements planned for the dfa engine: * The dfa engine may be converted to a hybrid nfa-dfa engine, with a fixed size limited stack. This would allow for size time tradeoffs, by inserting limited nfa states to help control state explosion that can occur with dfas. * The dfa engine may pickup the ability to do limited dynamic variable matching, instead of fixing all variables at policy load time. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/include/match.h | 132 ++++++++++++++ security/apparmor/match.c | 353 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 485 insertions(+) create mode 100644 security/apparmor/include/match.h create mode 100644 security/apparmor/match.c diff --git a/security/apparmor/include/match.h b/security/apparmor/include/match.h new file mode 100644 index 00000000000..734a6d35112 --- /dev/null +++ b/security/apparmor/include/match.h @@ -0,0 +1,132 @@ +/* + * AppArmor security module + * + * This file contains AppArmor policy dfa matching engine definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_MATCH_H +#define __AA_MATCH_H + +#include + +#define DFA_NOMATCH 0 +#define DFA_START 1 + +#define DFA_VALID_PERM_MASK 0xffffffff +#define DFA_VALID_PERM2_MASK 0xffffffff + +/** + * The format used for transition tables is based on the GNU flex table + * file format (--tables-file option; see Table File Format in the flex + * info pages and the flex sources for documentation). The magic number + * used in the header is 0x1B5E783D insted of 0xF13C57B1 though, because + * the YY_ID_CHK (check) and YY_ID_DEF (default) tables are used + * slightly differently (see the apparmor-parser package). + */ + +#define YYTH_MAGIC 0x1B5E783D +#define YYTH_DEF_RECURSE 0x1 /* DEF Table is recursive */ + +struct table_set_header { + u32 th_magic; /* YYTH_MAGIC */ + u32 th_hsize; + u32 th_ssize; + u16 th_flags; + char th_version[]; +}; + +/* The YYTD_ID are one less than flex table mappings. The flex id + * has 1 subtracted at table load time, this allows us to directly use the + * ID's as indexes. + */ +#define YYTD_ID_ACCEPT 0 +#define YYTD_ID_BASE 1 +#define YYTD_ID_CHK 2 +#define YYTD_ID_DEF 3 +#define YYTD_ID_EC 4 +#define YYTD_ID_META 5 +#define YYTD_ID_ACCEPT2 6 +#define YYTD_ID_NXT 7 +#define YYTD_ID_TSIZE 8 + +#define YYTD_DATA8 1 +#define YYTD_DATA16 2 +#define YYTD_DATA32 4 +#define YYTD_DATA64 8 + +/* Each ACCEPT2 table gets 6 dedicated flags, YYTD_DATAX define the + * first flags + */ +#define ACCEPT1_FLAGS(X) ((X) & 0x3f) +#define ACCEPT2_FLAGS(X) ACCEPT1_FLAGS((X) >> YYTD_ID_ACCEPT2) +#define TO_ACCEPT1_FLAG(X) ACCEPT1_FLAGS(X) +#define TO_ACCEPT2_FLAG(X) (ACCEPT1_FLAGS(X) << YYTD_ID_ACCEPT2) +#define DFA_FLAG_VERIFY_STATES 0x1000 + +struct table_header { + u16 td_id; + u16 td_flags; + u32 td_hilen; + u32 td_lolen; + char td_data[]; +}; + +#define DEFAULT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_DEF]->td_data)) +#define BASE_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_BASE]->td_data)) +#define NEXT_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_NXT]->td_data)) +#define CHECK_TABLE(DFA) ((u16 *)((DFA)->tables[YYTD_ID_CHK]->td_data)) +#define EQUIV_TABLE(DFA) ((u8 *)((DFA)->tables[YYTD_ID_EC]->td_data)) +#define ACCEPT_TABLE(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT]->td_data)) +#define ACCEPT_TABLE2(DFA) ((u32 *)((DFA)->tables[YYTD_ID_ACCEPT2]->td_data)) + +struct aa_dfa { + struct kref count; + u16 flags; + struct table_header *tables[YYTD_ID_TSIZE]; +}; + +#define byte_to_byte(X) (X) + +#define UNPACK_ARRAY(TABLE, BLOB, LEN, TYPE, NTOHX) \ + do { \ + typeof(LEN) __i; \ + TYPE *__t = (TYPE *) TABLE; \ + TYPE *__b = (TYPE *) BLOB; \ + for (__i = 0; __i < LEN; __i++) { \ + __t[__i] = NTOHX(__b[__i]); \ + } \ + } while (0) + +static inline size_t table_size(size_t len, size_t el_size) +{ + return ALIGN(sizeof(struct table_header) + len * el_size, 8); +} + +struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags); +unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, + const char *str, int len); +unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, + const char *str); +void aa_dfa_free_kref(struct kref *kref); + +/** + * aa_put_dfa - put a dfa refcount + * @dfa: dfa to put refcount (MAYBE NULL) + * + * Requires: if @dfa != NULL that a valid refcount be held + */ +static inline void aa_put_dfa(struct aa_dfa *dfa) +{ + if (dfa) + kref_put(&dfa->count, aa_dfa_free_kref); +} + +#endif /* __AA_MATCH_H */ diff --git a/security/apparmor/match.c b/security/apparmor/match.c new file mode 100644 index 00000000000..5cb4dc1f699 --- /dev/null +++ b/security/apparmor/match.c @@ -0,0 +1,353 @@ +/* + * AppArmor security module + * + * This file contains AppArmor dfa based regular expression matching engine + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/match.h" + +/** + * unpack_table - unpack a dfa table (one of accept, default, base, next check) + * @blob: data to unpack (NOT NULL) + * @bsize: size of blob + * + * Returns: pointer to table else NULL on failure + * + * NOTE: must be freed by kvfree (not kmalloc) + */ +static struct table_header *unpack_table(char *blob, size_t bsize) +{ + struct table_header *table = NULL; + struct table_header th; + size_t tsize; + + if (bsize < sizeof(struct table_header)) + goto out; + + /* loaded td_id's start at 1, subtract 1 now to avoid doing + * it every time we use td_id as an index + */ + th.td_id = be16_to_cpu(*(u16 *) (blob)) - 1; + th.td_flags = be16_to_cpu(*(u16 *) (blob + 2)); + th.td_lolen = be32_to_cpu(*(u32 *) (blob + 8)); + blob += sizeof(struct table_header); + + if (!(th.td_flags == YYTD_DATA16 || th.td_flags == YYTD_DATA32 || + th.td_flags == YYTD_DATA8)) + goto out; + + tsize = table_size(th.td_lolen, th.td_flags); + if (bsize < tsize) + goto out; + + table = kvmalloc(tsize); + if (table) { + *table = th; + if (th.td_flags == YYTD_DATA8) + UNPACK_ARRAY(table->td_data, blob, th.td_lolen, + u8, byte_to_byte); + else if (th.td_flags == YYTD_DATA16) + UNPACK_ARRAY(table->td_data, blob, th.td_lolen, + u16, be16_to_cpu); + else if (th.td_flags == YYTD_DATA32) + UNPACK_ARRAY(table->td_data, blob, th.td_lolen, + u32, be32_to_cpu); + else + goto fail; + } + +out: + /* if table was vmalloced make sure the page tables are synced + * before it is used, as it goes live to all cpus. + */ + if (is_vmalloc_addr(table)) + vm_unmap_aliases(); + return table; +fail: + kvfree(table); + return NULL; +} + +/** + * verify_dfa - verify that transitions and states in the tables are in bounds. + * @dfa: dfa to test (NOT NULL) + * @flags: flags controlling what type of accept table are acceptable + * + * Assumes dfa has gone through the first pass verification done by unpacking + * NOTE: this does not valid accept table values + * + * Returns: %0 else error code on failure to verify + */ +static int verify_dfa(struct aa_dfa *dfa, int flags) +{ + size_t i, state_count, trans_count; + int error = -EPROTO; + + /* check that required tables exist */ + if (!(dfa->tables[YYTD_ID_DEF] && + dfa->tables[YYTD_ID_BASE] && + dfa->tables[YYTD_ID_NXT] && dfa->tables[YYTD_ID_CHK])) + goto out; + + /* accept.size == default.size == base.size */ + state_count = dfa->tables[YYTD_ID_BASE]->td_lolen; + if (ACCEPT1_FLAGS(flags)) { + if (!dfa->tables[YYTD_ID_ACCEPT]) + goto out; + if (state_count != dfa->tables[YYTD_ID_ACCEPT]->td_lolen) + goto out; + } + if (ACCEPT2_FLAGS(flags)) { + if (!dfa->tables[YYTD_ID_ACCEPT2]) + goto out; + if (state_count != dfa->tables[YYTD_ID_ACCEPT2]->td_lolen) + goto out; + } + if (state_count != dfa->tables[YYTD_ID_DEF]->td_lolen) + goto out; + + /* next.size == chk.size */ + trans_count = dfa->tables[YYTD_ID_NXT]->td_lolen; + if (trans_count != dfa->tables[YYTD_ID_CHK]->td_lolen) + goto out; + + /* if equivalence classes then its table size must be 256 */ + if (dfa->tables[YYTD_ID_EC] && + dfa->tables[YYTD_ID_EC]->td_lolen != 256) + goto out; + + if (flags & DFA_FLAG_VERIFY_STATES) { + for (i = 0; i < state_count; i++) { + if (DEFAULT_TABLE(dfa)[i] >= state_count) + goto out; + /* TODO: do check that DEF state recursion terminates */ + if (BASE_TABLE(dfa)[i] + 255 >= trans_count) { + printk(KERN_ERR "AppArmor DFA next/check upper " + "bounds error\n"); + goto out; + } + } + + for (i = 0; i < trans_count; i++) { + if (NEXT_TABLE(dfa)[i] >= state_count) + goto out; + if (CHECK_TABLE(dfa)[i] >= state_count) + goto out; + } + } + + error = 0; +out: + return error; +} + +/** + * dfa_free - free a dfa allocated by aa_dfa_unpack + * @dfa: the dfa to free (MAYBE NULL) + * + * Requires: reference count to dfa == 0 + */ +static void dfa_free(struct aa_dfa *dfa) +{ + if (dfa) { + int i; + + for (i = 0; i < ARRAY_SIZE(dfa->tables); i++) { + kvfree(dfa->tables[i]); + dfa->tables[i] = NULL; + } + kfree(dfa); + } +} + +/** + * aa_dfa_free_kref - free aa_dfa by kref (called by aa_put_dfa) + * @kr: kref callback for freeing of a dfa (NOT NULL) + */ +void aa_dfa_free_kref(struct kref *kref) +{ + struct aa_dfa *dfa = container_of(kref, struct aa_dfa, count); + dfa_free(dfa); +} + +/** + * aa_dfa_unpack - unpack the binary tables of a serialized dfa + * @blob: aligned serialized stream of data to unpack (NOT NULL) + * @size: size of data to unpack + * @flags: flags controlling what type of accept tables are acceptable + * + * Unpack a dfa that has been serialized. To find information on the dfa + * format look in Documentation/apparmor.txt + * Assumes the dfa @blob stream has been aligned on a 8 byte boundry + * + * Returns: an unpacked dfa ready for matching or ERR_PTR on failure + */ +struct aa_dfa *aa_dfa_unpack(void *blob, size_t size, int flags) +{ + int hsize; + int error = -ENOMEM; + char *data = blob; + struct table_header *table = NULL; + struct aa_dfa *dfa = kzalloc(sizeof(struct aa_dfa), GFP_KERNEL); + if (!dfa) + goto fail; + + kref_init(&dfa->count); + + error = -EPROTO; + + /* get dfa table set header */ + if (size < sizeof(struct table_set_header)) + goto fail; + + if (ntohl(*(u32 *) data) != YYTH_MAGIC) + goto fail; + + hsize = ntohl(*(u32 *) (data + 4)); + if (size < hsize) + goto fail; + + dfa->flags = ntohs(*(u16 *) (data + 12)); + data += hsize; + size -= hsize; + + while (size > 0) { + table = unpack_table(data, size); + if (!table) + goto fail; + + switch (table->td_id) { + case YYTD_ID_ACCEPT: + if (!(table->td_flags & ACCEPT1_FLAGS(flags))) + goto fail; + break; + case YYTD_ID_ACCEPT2: + if (!(table->td_flags & ACCEPT2_FLAGS(flags))) + goto fail; + break; + case YYTD_ID_BASE: + if (table->td_flags != YYTD_DATA32) + goto fail; + break; + case YYTD_ID_DEF: + case YYTD_ID_NXT: + case YYTD_ID_CHK: + if (table->td_flags != YYTD_DATA16) + goto fail; + break; + case YYTD_ID_EC: + if (table->td_flags != YYTD_DATA8) + goto fail; + break; + default: + goto fail; + } + /* check for duplicate table entry */ + if (dfa->tables[table->td_id]) + goto fail; + dfa->tables[table->td_id] = table; + data += table_size(table->td_lolen, table->td_flags); + size -= table_size(table->td_lolen, table->td_flags); + table = NULL; + } + + error = verify_dfa(dfa, flags); + if (error) + goto fail; + + return dfa; + +fail: + kvfree(table); + dfa_free(dfa); + return ERR_PTR(error); +} + +/** + * aa_dfa_match_len - traverse @dfa to find state @str stops at + * @dfa: the dfa to match @str against (NOT NULL) + * @start: the state of the dfa to start matching in + * @str: the string of bytes to match against the dfa (NOT NULL) + * @len: length of the string of bytes to match + * + * aa_dfa_match_len will match @str against the dfa and return the state it + * finished matching in. The final state can be used to look up the accepting + * label, or as the start state of a continuing match. + * + * This function will happily match again the 0 byte and only finishes + * when @len input is consumed. + * + * Returns: final state reached after input is consumed + */ +unsigned int aa_dfa_match_len(struct aa_dfa *dfa, unsigned int start, + const char *str, int len) +{ + u16 *def = DEFAULT_TABLE(dfa); + u32 *base = BASE_TABLE(dfa); + u16 *next = NEXT_TABLE(dfa); + u16 *check = CHECK_TABLE(dfa); + unsigned int state = start, pos; + + if (state == 0) + return 0; + + /* current state is , matching character *str */ + if (dfa->tables[YYTD_ID_EC]) { + /* Equivalence class table defined */ + u8 *equiv = EQUIV_TABLE(dfa); + /* default is direct to next state */ + for (; len; len--) { + pos = base[state] + equiv[(u8) *str++]; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + } + } else { + /* default is direct to next state */ + for (; len; len--) { + pos = base[state] + (u8) *str++; + if (check[pos] == state) + state = next[pos]; + else + state = def[state]; + } + } + + return state; +} + +/** + * aa_dfa_next_state - traverse @dfa to find state @str stops at + * @dfa: the dfa to match @str against (NOT NULL) + * @start: the state of the dfa to start matching in + * @str: the null terminated string of bytes to match against the dfa (NOT NULL) + * + * aa_dfa_next_state will match @str against the dfa and return the state it + * finished matching in. The final state can be used to look up the accepting + * label, or as the start state of a continuing match. + * + * Returns: final state reached after input is consumed + */ +unsigned int aa_dfa_match(struct aa_dfa *dfa, unsigned int start, + const char *str) +{ + return aa_dfa_match_len(dfa, start, str, strlen(str)); +} -- cgit v1.2.3-70-g09d2 From 63e2b423771ab0bc7ad4d407f3f6517c6d05cdc0 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 29 Jul 2010 14:48:03 -0700 Subject: AppArmor: userspace interfaces The /proc//attr/* interface is used for process introspection and commands. While the apparmorfs interface is used for global introspection and loading and removing policy. The interface currently only contains the files necessary for loading policy, and will be extended in the future to include sysfs style single per file introspection inteface. The old AppArmor 2.4 interface files have been removed into a compatibility patch, that distros can use to maintain backwards compatibility. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/apparmorfs.c | 239 +++++++++++++++++++++++++++++++++ security/apparmor/include/apparmorfs.h | 20 +++ security/apparmor/include/procattr.h | 26 ++++ security/apparmor/procattr.c | 170 +++++++++++++++++++++++ 4 files changed, 455 insertions(+) create mode 100644 security/apparmor/apparmorfs.c create mode 100644 security/apparmor/include/apparmorfs.h create mode 100644 security/apparmor/include/procattr.h create mode 100644 security/apparmor/procattr.c diff --git a/security/apparmor/apparmorfs.c b/security/apparmor/apparmorfs.c new file mode 100644 index 00000000000..7320331b44a --- /dev/null +++ b/security/apparmor/apparmorfs.c @@ -0,0 +1,239 @@ +/* + * AppArmor security module + * + * This file contains AppArmor /sys/kernel/security/apparmor interface functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include +#include +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/apparmorfs.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/policy.h" + +/** + * aa_simple_write_to_buffer - common routine for getting policy from user + * @op: operation doing the user buffer copy + * @userbuf: user buffer to copy data from (NOT NULL) + * @alloc_size: size of user buffer + * @copy_size: size of data to copy from user buffer + * @pos: position write is at in the file (NOT NULL) + * + * Returns: kernel buffer containing copy of user buffer data or an + * ERR_PTR on failure. + */ +static char *aa_simple_write_to_buffer(int op, const char __user *userbuf, + size_t alloc_size, size_t copy_size, + loff_t *pos) +{ + char *data; + + if (*pos != 0) + /* only writes from pos 0, that is complete writes */ + return ERR_PTR(-ESPIPE); + + /* + * Don't allow profile load/replace/remove from profiles that don't + * have CAP_MAC_ADMIN + */ + if (!aa_may_manage_policy(op)) + return ERR_PTR(-EACCES); + + /* freed by caller to simple_write_to_buffer */ + data = kvmalloc(alloc_size); + if (data == NULL) + return ERR_PTR(-ENOMEM); + + if (copy_from_user(data, userbuf, copy_size)) { + kvfree(data); + return ERR_PTR(-EFAULT); + } + + return data; +} + + +/* .load file hook fn to load policy */ +static ssize_t profile_load(struct file *f, const char __user *buf, size_t size, + loff_t *pos) +{ + char *data; + ssize_t error; + + data = aa_simple_write_to_buffer(OP_PROF_LOAD, buf, size, size, pos); + + error = PTR_ERR(data); + if (!IS_ERR(data)) { + error = aa_replace_profiles(data, size, PROF_ADD); + kvfree(data); + } + + return error; +} + +static const struct file_operations aa_fs_profile_load = { + .write = profile_load +}; + +/* .replace file hook fn to load and/or replace policy */ +static ssize_t profile_replace(struct file *f, const char __user *buf, + size_t size, loff_t *pos) +{ + char *data; + ssize_t error; + + data = aa_simple_write_to_buffer(OP_PROF_REPL, buf, size, size, pos); + error = PTR_ERR(data); + if (!IS_ERR(data)) { + error = aa_replace_profiles(data, size, PROF_REPLACE); + kvfree(data); + } + + return error; +} + +static const struct file_operations aa_fs_profile_replace = { + .write = profile_replace +}; + +/* .remove file hook fn to remove loaded policy */ +static ssize_t profile_remove(struct file *f, const char __user *buf, + size_t size, loff_t *pos) +{ + char *data; + ssize_t error; + + /* + * aa_remove_profile needs a null terminated string so 1 extra + * byte is allocated and the copied data is null terminated. + */ + data = aa_simple_write_to_buffer(OP_PROF_RM, buf, size + 1, size, pos); + + error = PTR_ERR(data); + if (!IS_ERR(data)) { + data[size] = 0; + error = aa_remove_profiles(data, size); + kvfree(data); + } + + return error; +} + +static const struct file_operations aa_fs_profile_remove = { + .write = profile_remove +}; + +/** Base file system setup **/ + +static struct dentry *aa_fs_dentry __initdata; + +static void __init aafs_remove(const char *name) +{ + struct dentry *dentry; + + dentry = lookup_one_len(name, aa_fs_dentry, strlen(name)); + if (!IS_ERR(dentry)) { + securityfs_remove(dentry); + dput(dentry); + } +} + +/** + * aafs_create - create an entry in the apparmor filesystem + * @name: name of the entry (NOT NULL) + * @mask: file permission mask of the file + * @fops: file operations for the file (NOT NULL) + * + * Used aafs_remove to remove entries created with this fn. + */ +static int __init aafs_create(const char *name, int mask, + const struct file_operations *fops) +{ + struct dentry *dentry; + + dentry = securityfs_create_file(name, S_IFREG | mask, aa_fs_dentry, + NULL, fops); + + return IS_ERR(dentry) ? PTR_ERR(dentry) : 0; +} + +/** + * aa_destroy_aafs - cleanup and free aafs + * + * releases dentries allocated by aa_create_aafs + */ +void __init aa_destroy_aafs(void) +{ + if (aa_fs_dentry) { + aafs_remove(".remove"); + aafs_remove(".replace"); + aafs_remove(".load"); + + securityfs_remove(aa_fs_dentry); + aa_fs_dentry = NULL; + } +} + +/** + * aa_create_aafs - create the apparmor security filesystem + * + * dentries created here are released by aa_destroy_aafs + * + * Returns: error on failure + */ +int __init aa_create_aafs(void) +{ + int error; + + if (!apparmor_initialized) + return 0; + + if (aa_fs_dentry) { + AA_ERROR("%s: AppArmor securityfs already exists\n", __func__); + return -EEXIST; + } + + aa_fs_dentry = securityfs_create_dir("apparmor", NULL); + if (IS_ERR(aa_fs_dentry)) { + error = PTR_ERR(aa_fs_dentry); + aa_fs_dentry = NULL; + goto error; + } + + error = aafs_create(".load", 0640, &aa_fs_profile_load); + if (error) + goto error; + error = aafs_create(".replace", 0640, &aa_fs_profile_replace); + if (error) + goto error; + error = aafs_create(".remove", 0640, &aa_fs_profile_remove); + if (error) + goto error; + + /* TODO: add support for apparmorfs_null and apparmorfs_mnt */ + + /* Report that AppArmor fs is enabled */ + aa_info_message("AppArmor Filesystem Enabled"); + return 0; + +error: + aa_destroy_aafs(); + AA_ERROR("Error creating AppArmor securityfs\n"); + return error; +} + +fs_initcall(aa_create_aafs); diff --git a/security/apparmor/include/apparmorfs.h b/security/apparmor/include/apparmorfs.h new file mode 100644 index 00000000000..cb1e93a114d --- /dev/null +++ b/security/apparmor/include/apparmorfs.h @@ -0,0 +1,20 @@ +/* + * AppArmor security module + * + * This file contains AppArmor filesystem definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_APPARMORFS_H +#define __AA_APPARMORFS_H + +extern void __init aa_destroy_aafs(void); + +#endif /* __AA_APPARMORFS_H */ diff --git a/security/apparmor/include/procattr.h b/security/apparmor/include/procattr.h new file mode 100644 index 00000000000..544aa6b766a --- /dev/null +++ b/security/apparmor/include/procattr.h @@ -0,0 +1,26 @@ +/* + * AppArmor security module + * + * This file contains AppArmor /proc//attr/ interface function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_PROCATTR_H +#define __AA_PROCATTR_H + +#define AA_DO_TEST 1 +#define AA_ONEXEC 1 + +int aa_getprocattr(struct aa_profile *profile, char **string); +int aa_setprocattr_changehat(char *args, size_t size, int test); +int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test); +int aa_setprocattr_permipc(char *fqname); + +#endif /* __AA_PROCATTR_H */ diff --git a/security/apparmor/procattr.c b/security/apparmor/procattr.c new file mode 100644 index 00000000000..04a2cf8d1b6 --- /dev/null +++ b/security/apparmor/procattr.c @@ -0,0 +1,170 @@ +/* + * AppArmor security module + * + * This file contains AppArmor /proc//attr/ interface functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include "include/apparmor.h" +#include "include/context.h" +#include "include/policy.h" +#include "include/domain.h" + + +/** + * aa_getprocattr - Return the profile information for @profile + * @profile: the profile to print profile info about (NOT NULL) + * @string: Returns - string containing the profile info (NOT NULL) + * + * Returns: length of @string on success else error on failure + * + * Requires: profile != NULL + * + * Creates a string containing the namespace_name://profile_name for + * @profile. + * + * Returns: size of string placed in @string else error code on failure + */ +int aa_getprocattr(struct aa_profile *profile, char **string) +{ + char *str; + int len = 0, mode_len = 0, ns_len = 0, name_len; + const char *mode_str = profile_mode_names[profile->mode]; + const char *ns_name = NULL; + struct aa_namespace *ns = profile->ns; + struct aa_namespace *current_ns = __aa_current_profile()->ns; + char *s; + + if (!aa_ns_visible(current_ns, ns)) + return -EACCES; + + ns_name = aa_ns_name(current_ns, ns); + ns_len = strlen(ns_name); + + /* if the visible ns_name is > 0 increase size for : :// seperator */ + if (ns_len) + ns_len += 4; + + /* unconfined profiles don't have a mode string appended */ + if (!unconfined(profile)) + mode_len = strlen(mode_str) + 3; /* + 3 for _() */ + + name_len = strlen(profile->base.hname); + len = mode_len + ns_len + name_len + 1; /* + 1 for \n */ + s = str = kmalloc(len + 1, GFP_KERNEL); /* + 1 \0 */ + if (!str) + return -ENOMEM; + + if (ns_len) { + /* skip over prefix current_ns->base.hname and separating // */ + sprintf(s, ":%s://", ns_name); + s += ns_len; + } + if (unconfined(profile)) + /* mode string not being appended */ + sprintf(s, "%s\n", profile->base.hname); + else + sprintf(s, "%s (%s)\n", profile->base.hname, mode_str); + *string = str; + + /* NOTE: len does not include \0 of string, not saved as part of file */ + return len; +} + +/** + * split_token_from_name - separate a string of form ^ + * @op: operation being checked + * @args: string to parse (NOT NULL) + * @token: stores returned parsed token value (NOT NULL) + * + * Returns: start position of name after token else NULL on failure + */ +static char *split_token_from_name(int op, char *args, u64 * token) +{ + char *name; + + *token = simple_strtoull(args, &name, 16); + if ((name == args) || *name != '^') { + AA_ERROR("%s: Invalid input '%s'", op_table[op], args); + return ERR_PTR(-EINVAL); + } + + name++; /* skip ^ */ + if (!*name) + name = NULL; + return name; +} + +/** + * aa_setprocattr_chagnehat - handle procattr interface to change_hat + * @args: args received from writing to /proc//attr/current (NOT NULL) + * @size: size of the args + * @test: true if this is a test of change_hat permissions + * + * Returns: %0 or error code if change_hat fails + */ +int aa_setprocattr_changehat(char *args, size_t size, int test) +{ + char *hat; + u64 token; + const char *hats[16]; /* current hard limit on # of names */ + int count = 0; + + hat = split_token_from_name(OP_CHANGE_HAT, args, &token); + if (IS_ERR(hat)) + return PTR_ERR(hat); + + if (!hat && !token) { + AA_ERROR("change_hat: Invalid input, NULL hat and NULL magic"); + return -EINVAL; + } + + if (hat) { + /* set up hat name vector, args guaranteed null terminated + * at args[size] by setprocattr. + * + * If there are multiple hat names in the buffer each is + * separated by a \0. Ie. userspace writes them pre tokenized + */ + char *end = args + size; + for (count = 0; (hat < end) && count < 16; ++count) { + char *next = hat + strlen(hat) + 1; + hats[count] = hat; + hat = next; + } + } + + AA_DEBUG("%s: Magic 0x%llx Hat '%s'\n", + __func__, token, hat ? hat : NULL); + + return aa_change_hat(hats, count, token, test); +} + +/** + * aa_setprocattr_changeprofile - handle procattr interface to changeprofile + * @fqname: args received from writting to /proc//attr/current (NOT NULL) + * @onexec: true if change_profile should be delayed until exec + * @test: true if this is a test of change_profile permissions + * + * Returns: %0 or error code if change_profile fails + */ +int aa_setprocattr_changeprofile(char *fqname, bool onexec, int test) +{ + char *name, *ns_name; + + name = aa_split_fqname(fqname, &ns_name); + return aa_change_profile(ns_name, name, onexec, test); +} + +int aa_setprocattr_permipc(char *fqname) +{ + /* TODO: add ipc permission querying */ + return -ENOTSUPP; +} -- cgit v1.2.3-70-g09d2 From 6380bd8ddf613b29f478396308b591867d401de4 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 29 Jul 2010 14:48:04 -0700 Subject: AppArmor: file enforcement routines AppArmor does files enforcement via pathname matching. Matching is done at file open using a dfa match engine. Permission is against the final file object not parent directories, ie. the traversal of directories as part of the file match is implicitly allowed. In the case of nonexistant files (creation) permissions are checked against the target file not the directory. eg. In case of creating the file /dir/new, permissions are checked against the match /dir/new not against /dir/. The permissions for matches are currently stored in the dfa accept table, but this will change to allow for dfa reuse and also to allow for sharing of wider accept states. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/file.c | 457 +++++++++++++++++++++++++++++++++++++++ security/apparmor/include/file.h | 217 +++++++++++++++++++ 2 files changed, 674 insertions(+) create mode 100644 security/apparmor/file.c create mode 100644 security/apparmor/include/file.h diff --git a/security/apparmor/file.c b/security/apparmor/file.c new file mode 100644 index 00000000000..7312db74121 --- /dev/null +++ b/security/apparmor/file.c @@ -0,0 +1,457 @@ +/* + * AppArmor security module + * + * This file contains AppArmor mediation of files + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/file.h" +#include "include/match.h" +#include "include/path.h" +#include "include/policy.h" + +struct file_perms nullperms; + + +/** + * audit_file_mask - convert mask to permission string + * @buffer: buffer to write string to (NOT NULL) + * @mask: permission mask to convert + */ +static void audit_file_mask(struct audit_buffer *ab, u32 mask) +{ + char str[10]; + + char *m = str; + + if (mask & AA_EXEC_MMAP) + *m++ = 'm'; + if (mask & (MAY_READ | AA_MAY_META_READ)) + *m++ = 'r'; + if (mask & (MAY_WRITE | AA_MAY_META_WRITE | AA_MAY_CHMOD | + AA_MAY_CHOWN)) + *m++ = 'w'; + else if (mask & MAY_APPEND) + *m++ = 'a'; + if (mask & AA_MAY_CREATE) + *m++ = 'c'; + if (mask & AA_MAY_DELETE) + *m++ = 'd'; + if (mask & AA_MAY_LINK) + *m++ = 'l'; + if (mask & AA_MAY_LOCK) + *m++ = 'k'; + if (mask & MAY_EXEC) + *m++ = 'x'; + *m = '\0'; + + audit_log_string(ab, str); +} + +/** + * file_audit_cb - call back for file specific audit fields + * @ab: audit_buffer (NOT NULL) + * @va: audit struct to audit values of (NOT NULL) + */ +static void file_audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + uid_t fsuid = current_fsuid(); + + if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) { + audit_log_format(ab, " requested_mask="); + audit_file_mask(ab, sa->aad.fs.request); + } + if (sa->aad.fs.denied & AA_AUDIT_FILE_MASK) { + audit_log_format(ab, " denied_mask="); + audit_file_mask(ab, sa->aad.fs.denied); + } + if (sa->aad.fs.request & AA_AUDIT_FILE_MASK) { + audit_log_format(ab, " fsuid=%d", fsuid); + audit_log_format(ab, " ouid=%d", sa->aad.fs.ouid); + } + + if (sa->aad.fs.target) { + audit_log_format(ab, " target="); + audit_log_untrustedstring(ab, sa->aad.fs.target); + } +} + +/** + * aa_audit_file - handle the auditing of file operations + * @profile: the profile being enforced (NOT NULL) + * @perms: the permissions computed for the request (NOT NULL) + * @gfp: allocation flags + * @op: operation being mediated + * @request: permissions requested + * @name: name of object being mediated (MAYBE NULL) + * @target: name of target (MAYBE NULL) + * @ouid: object uid + * @info: extra information message (MAYBE NULL) + * @error: 0 if operation allowed else failure error code + * + * Returns: %0 or error on failure + */ +int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, + gfp_t gfp, int op, u32 request, const char *name, + const char *target, uid_t ouid, const char *info, int error) +{ + int type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + COMMON_AUDIT_DATA_INIT(&sa, NONE); + sa.aad.op = op, + sa.aad.fs.request = request; + sa.aad.name = name; + sa.aad.fs.target = target; + sa.aad.fs.ouid = ouid; + sa.aad.info = info; + sa.aad.error = error; + + if (likely(!sa.aad.error)) { + u32 mask = perms->audit; + + if (unlikely(AUDIT_MODE(profile) == AUDIT_ALL)) + mask = 0xffff; + + /* mask off perms that are not being force audited */ + sa.aad.fs.request &= mask; + + if (likely(!sa.aad.fs.request)) + return 0; + type = AUDIT_APPARMOR_AUDIT; + } else { + /* only report permissions that were denied */ + sa.aad.fs.request = sa.aad.fs.request & ~perms->allow; + + if (sa.aad.fs.request & perms->kill) + type = AUDIT_APPARMOR_KILL; + + /* quiet known rejects, assumes quiet and kill do not overlap */ + if ((sa.aad.fs.request & perms->quiet) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) + sa.aad.fs.request &= ~perms->quiet; + + if (!sa.aad.fs.request) + return COMPLAIN_MODE(profile) ? 0 : sa.aad.error; + } + + sa.aad.fs.denied = sa.aad.fs.request & ~perms->allow; + return aa_audit(type, profile, gfp, &sa, file_audit_cb); +} + +/** + * map_old_perms - map old file perms layout to the new layout + * @old: permission set in old mapping + * + * Returns: new permission mapping + */ +static u32 map_old_perms(u32 old) +{ + u32 new = old & 0xf; + if (old & MAY_READ) + new |= AA_MAY_META_READ; + if (old & MAY_WRITE) + new |= AA_MAY_META_WRITE | AA_MAY_CREATE | AA_MAY_DELETE | + AA_MAY_CHMOD | AA_MAY_CHOWN; + if (old & 0x10) + new |= AA_MAY_LINK; + /* the old mapping lock and link_subset flags where overlaid + * and use was determined by part of a pair that they were in + */ + if (old & 0x20) + new |= AA_MAY_LOCK | AA_LINK_SUBSET; + if (old & 0x40) /* AA_EXEC_MMAP */ + new |= AA_EXEC_MMAP; + + new |= AA_MAY_META_READ; + + return new; +} + +/** + * compute_perms - convert dfa compressed perms to internal perms + * @dfa: dfa to compute perms for (NOT NULL) + * @state: state in dfa + * @cond: conditions to consider (NOT NULL) + * + * TODO: convert from dfa + state to permission entry, do computation conversion + * at load time. + * + * Returns: computed permission set + */ +static struct file_perms compute_perms(struct aa_dfa *dfa, unsigned int state, + struct path_cond *cond) +{ + struct file_perms perms; + + /* FIXME: change over to new dfa format + * currently file perms are encoded in the dfa, new format + * splits the permissions from the dfa. This mapping can be + * done at profile load + */ + perms.kill = 0; + + if (current_fsuid() == cond->uid) { + perms.allow = map_old_perms(dfa_user_allow(dfa, state)); + perms.audit = map_old_perms(dfa_user_audit(dfa, state)); + perms.quiet = map_old_perms(dfa_user_quiet(dfa, state)); + perms.xindex = dfa_user_xindex(dfa, state); + } else { + perms.allow = map_old_perms(dfa_other_allow(dfa, state)); + perms.audit = map_old_perms(dfa_other_audit(dfa, state)); + perms.quiet = map_old_perms(dfa_other_quiet(dfa, state)); + perms.xindex = dfa_other_xindex(dfa, state); + } + + /* change_profile wasn't determined by ownership in old mapping */ + if (ACCEPT_TABLE(dfa)[state] & 0x80000000) + perms.allow |= AA_MAY_CHANGE_PROFILE; + + return perms; +} + +/** + * aa_str_perms - find permission that match @name + * @dfa: to match against (MAYBE NULL) + * @state: state to start matching in + * @name: string to match against dfa (NOT NULL) + * @cond: conditions to consider for permission set computation (NOT NULL) + * @perms: Returns - the permissions found when matching @name + * + * Returns: the final state in @dfa when beginning @start and walking @name + */ +unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, + const char *name, struct path_cond *cond, + struct file_perms *perms) +{ + unsigned int state; + if (!dfa) { + *perms = nullperms; + return DFA_NOMATCH; + } + + state = aa_dfa_match(dfa, start, name); + *perms = compute_perms(dfa, state, cond); + + return state; +} + +/** + * is_deleted - test if a file has been completely unlinked + * @dentry: dentry of file to test for deletion (NOT NULL) + * + * Returns: %1 if deleted else %0 + */ +static inline bool is_deleted(struct dentry *dentry) +{ + if (d_unlinked(dentry) && dentry->d_inode->i_nlink == 0) + return 1; + return 0; +} + +/** + * aa_path_perm - do permissions check & audit for @path + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @path: path to check permissions of (NOT NULL) + * @flags: any additional path flags beyond what the profile specifies + * @request: requested permissions + * @cond: conditional info for this request (NOT NULL) + * + * Returns: %0 else error if access denied or other error + */ +int aa_path_perm(int op, struct aa_profile *profile, struct path *path, + int flags, u32 request, struct path_cond *cond) +{ + char *buffer = NULL; + struct file_perms perms = {}; + const char *name, *info = NULL; + int error; + + flags |= profile->path_flags | (S_ISDIR(cond->mode) ? PATH_IS_DIR : 0); + error = aa_get_name(path, flags, &buffer, &name); + if (error) { + if (error == -ENOENT && is_deleted(path->dentry)) { + /* Access to open files that are deleted are + * give a pass (implicit delegation) + */ + error = 0; + perms.allow = request; + } else if (error == -ENOENT) + info = "Failed name lookup - deleted entry"; + else if (error == -ESTALE) + info = "Failed name lookup - disconnected path"; + else if (error == -ENAMETOOLONG) + info = "Failed name lookup - name too long"; + else + info = "Failed name lookup"; + } else { + aa_str_perms(profile->file.dfa, profile->file.start, name, cond, + &perms); + if (request & ~perms.allow) + error = -EACCES; + } + error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, name, + NULL, cond->uid, info, error); + kfree(buffer); + + return error; +} + +/** + * xindex_is_subset - helper for aa_path_link + * @link: link permission set + * @target: target permission set + * + * test target x permissions are equal OR a subset of link x permissions + * this is done as part of the subset test, where a hardlink must have + * a subset of permissions that the target has. + * + * Returns: %1 if subset else %0 + */ +static inline bool xindex_is_subset(u32 link, u32 target) +{ + if (((link & ~AA_X_UNSAFE) != (target & ~AA_X_UNSAFE)) || + ((link & AA_X_UNSAFE) && !(target & AA_X_UNSAFE))) + return 0; + + return 1; +} + +/** + * aa_path_link - Handle hard link permission check + * @profile: the profile being enforced (NOT NULL) + * @old_dentry: the target dentry (NOT NULL) + * @new_dir: directory the new link will be created in (NOT NULL) + * @new_dentry: the link being created (NOT NULL) + * + * Handle the permission test for a link & target pair. Permission + * is encoded as a pair where the link permission is determined + * first, and if allowed, the target is tested. The target test + * is done from the point of the link match (not start of DFA) + * making the target permission dependent on the link permission match. + * + * The subset test if required forces that permissions granted + * on link are a subset of the permission granted to target. + * + * Returns: %0 if allowed else error + */ +int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, + struct path *new_dir, struct dentry *new_dentry) +{ + struct path link = { new_dir->mnt, new_dentry }; + struct path target = { new_dir->mnt, old_dentry }; + struct path_cond cond = { + old_dentry->d_inode->i_uid, + old_dentry->d_inode->i_mode + }; + char *buffer = NULL, *buffer2 = NULL; + const char *lname, *tname = NULL, *info = NULL; + struct file_perms lperms, perms; + u32 request = AA_MAY_LINK; + unsigned int state; + int error; + + lperms = nullperms; + + /* buffer freed below, lname is pointer in buffer */ + error = aa_get_name(&link, profile->path_flags, &buffer, &lname); + if (error) + goto audit; + + /* buffer2 freed below, tname is pointer in buffer2 */ + error = aa_get_name(&target, profile->path_flags, &buffer2, &tname); + if (error) + goto audit; + + error = -EACCES; + /* aa_str_perms - handles the case of the dfa being NULL */ + state = aa_str_perms(profile->file.dfa, profile->file.start, lname, + &cond, &lperms); + + if (!(lperms.allow & AA_MAY_LINK)) + goto audit; + + /* test to see if target can be paired with link */ + state = aa_dfa_null_transition(profile->file.dfa, state); + aa_str_perms(profile->file.dfa, state, tname, &cond, &perms); + + /* force audit/quiet masks for link are stored in the second entry + * in the link pair. + */ + lperms.audit = perms.audit; + lperms.quiet = perms.quiet; + lperms.kill = perms.kill; + + if (!(perms.allow & AA_MAY_LINK)) { + info = "target restricted"; + goto audit; + } + + /* done if link subset test is not required */ + if (!(perms.allow & AA_LINK_SUBSET)) + goto done_tests; + + /* Do link perm subset test requiring allowed permission on link are a + * subset of the allowed permissions on target. + */ + aa_str_perms(profile->file.dfa, profile->file.start, tname, &cond, + &perms); + + /* AA_MAY_LINK is not considered in the subset test */ + request = lperms.allow & ~AA_MAY_LINK; + lperms.allow &= perms.allow | AA_MAY_LINK; + + request |= AA_AUDIT_FILE_MASK & (lperms.allow & ~perms.allow); + if (request & ~lperms.allow) { + goto audit; + } else if ((lperms.allow & MAY_EXEC) && + !xindex_is_subset(lperms.xindex, perms.xindex)) { + lperms.allow &= ~MAY_EXEC; + request |= MAY_EXEC; + info = "link not subset of target"; + goto audit; + } + +done_tests: + error = 0; + +audit: + error = aa_audit_file(profile, &lperms, GFP_KERNEL, OP_LINK, request, + lname, tname, cond.uid, info, error); + kfree(buffer); + kfree(buffer2); + + return error; +} + +/** + * aa_file_perm - do permission revalidation check & audit for @file + * @op: operation being checked + * @profile: profile being enforced (NOT NULL) + * @file: file to revalidate access permissions on (NOT NULL) + * @request: requested permissions + * + * Returns: %0 if access allowed else error + */ +int aa_file_perm(int op, struct aa_profile *profile, struct file *file, + u32 request) +{ + struct path_cond cond = { + .uid = file->f_path.dentry->d_inode->i_uid, + .mode = file->f_path.dentry->d_inode->i_mode + }; + + return aa_path_perm(op, profile, &file->f_path, PATH_DELEGATE_DELETED, + request, &cond); +} diff --git a/security/apparmor/include/file.h b/security/apparmor/include/file.h new file mode 100644 index 00000000000..be36feabb16 --- /dev/null +++ b/security/apparmor/include/file.h @@ -0,0 +1,217 @@ +/* + * AppArmor security module + * + * This file contains AppArmor file mediation function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_FILE_H +#define __AA_FILE_H + +#include + +#include "domain.h" +#include "match.h" + +struct aa_profile; + +/* + * We use MAY_EXEC, MAY_WRITE, MAY_READ, MAY_APPEND and the following flags + * for profile permissions + */ +#define AA_MAY_CREATE 0x0010 +#define AA_MAY_DELETE 0x0020 +#define AA_MAY_META_WRITE 0x0040 +#define AA_MAY_META_READ 0x0080 + +#define AA_MAY_CHMOD 0x0100 +#define AA_MAY_CHOWN 0x0200 +#define AA_MAY_LOCK 0x0400 +#define AA_EXEC_MMAP 0x0800 + +#define AA_MAY_LINK 0x1000 +#define AA_LINK_SUBSET AA_MAY_LOCK /* overlaid */ +#define AA_MAY_ONEXEC 0x40000000 /* exec allows onexec */ +#define AA_MAY_CHANGE_PROFILE 0x80000000 +#define AA_MAY_CHANGEHAT 0x80000000 /* ctrl auditing only */ + +#define AA_AUDIT_FILE_MASK (MAY_READ | MAY_WRITE | MAY_EXEC | MAY_APPEND |\ + AA_MAY_CREATE | AA_MAY_DELETE | \ + AA_MAY_META_READ | AA_MAY_META_WRITE | \ + AA_MAY_CHMOD | AA_MAY_CHOWN | AA_MAY_LOCK | \ + AA_EXEC_MMAP | AA_MAY_LINK) + +/* + * The xindex is broken into 3 parts + * - index - an index into either the exec name table or the variable table + * - exec type - which determines how the executable name and index are used + * - flags - which modify how the destination name is applied + */ +#define AA_X_INDEX_MASK 0x03ff + +#define AA_X_TYPE_MASK 0x0c00 +#define AA_X_TYPE_SHIFT 10 +#define AA_X_NONE 0x0000 +#define AA_X_NAME 0x0400 /* use executable name px */ +#define AA_X_TABLE 0x0800 /* use a specified name ->n# */ + +#define AA_X_UNSAFE 0x1000 +#define AA_X_CHILD 0x2000 /* make >AA_X_NONE apply to children */ +#define AA_X_INHERIT 0x4000 +#define AA_X_UNCONFINED 0x8000 + +/* AA_SECURE_X_NEEDED - is passed in the bprm->unsafe field */ +#define AA_SECURE_X_NEEDED 0x8000 + +/* need to make conditional which ones are being set */ +struct path_cond { + uid_t uid; + umode_t mode; +}; + +/* struct file_perms - file permission + * @allow: mask of permissions that are allowed + * @audit: mask of permissions to force an audit message for + * @quiet: mask of permissions to quiet audit messages for + * @kill: mask of permissions that when matched will kill the task + * @xindex: exec transition index if @allow contains MAY_EXEC + * + * The @audit and @queit mask should be mutually exclusive. + */ +struct file_perms { + u32 allow; + u32 audit; + u32 quiet; + u32 kill; + u16 xindex; +}; + +extern struct file_perms nullperms; + +#define COMBINED_PERM_MASK(X) ((X).allow | (X).audit | (X).quiet | (X).kill) + +/* FIXME: split perms from dfa and match this to description + * also add delegation info. + */ +static inline u16 dfa_map_xindex(u16 mask) +{ + u16 old_index = (mask >> 10) & 0xf; + u16 index = 0; + + if (mask & 0x100) + index |= AA_X_UNSAFE; + if (mask & 0x200) + index |= AA_X_INHERIT; + if (mask & 0x80) + index |= AA_X_UNCONFINED; + + if (old_index == 1) { + index |= AA_X_UNCONFINED; + } else if (old_index == 2) { + index |= AA_X_NAME; + } else if (old_index == 3) { + index |= AA_X_NAME | AA_X_CHILD; + } else { + index |= AA_X_TABLE; + index |= old_index - 4; + } + + return index; +} + +/* + * map old dfa inline permissions to new format + */ +#define dfa_user_allow(dfa, state) (((ACCEPT_TABLE(dfa)[state]) & 0x7f) | \ + ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) +#define dfa_user_audit(dfa, state) ((ACCEPT_TABLE2(dfa)[state]) & 0x7f) +#define dfa_user_quiet(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 7) & 0x7f) +#define dfa_user_xindex(dfa, state) \ + (dfa_map_xindex(ACCEPT_TABLE(dfa)[state] & 0x3fff)) + +#define dfa_other_allow(dfa, state) ((((ACCEPT_TABLE(dfa)[state]) >> 14) & \ + 0x7f) | \ + ((ACCEPT_TABLE(dfa)[state]) & 0x80000000)) +#define dfa_other_audit(dfa, state) (((ACCEPT_TABLE2(dfa)[state]) >> 14) & 0x7f) +#define dfa_other_quiet(dfa, state) \ + ((((ACCEPT_TABLE2(dfa)[state]) >> 7) >> 14) & 0x7f) +#define dfa_other_xindex(dfa, state) \ + dfa_map_xindex((ACCEPT_TABLE(dfa)[state] >> 14) & 0x3fff) + +int aa_audit_file(struct aa_profile *profile, struct file_perms *perms, + gfp_t gfp, int op, u32 request, const char *name, + const char *target, uid_t ouid, const char *info, int error); + +/** + * struct aa_file_rules - components used for file rule permissions + * @dfa: dfa to match path names and conditionals against + * @perms: permission table indexed by the matched state accept entry of @dfa + * @trans: transition table for indexed by named x transitions + * + * File permission are determined by matching a path against @dfa and then + * then using the value of the accept entry for the matching state as + * an index into @perms. If a named exec transition is required it is + * looked up in the transition table. + */ +struct aa_file_rules { + unsigned int start; + struct aa_dfa *dfa; + /* struct perms perms; */ + struct aa_domain trans; + /* TODO: add delegate table */ +}; + +unsigned int aa_str_perms(struct aa_dfa *dfa, unsigned int start, + const char *name, struct path_cond *cond, + struct file_perms *perms); + +int aa_path_perm(int op, struct aa_profile *profile, struct path *path, + int flags, u32 request, struct path_cond *cond); + +int aa_path_link(struct aa_profile *profile, struct dentry *old_dentry, + struct path *new_dir, struct dentry *new_dentry); + +int aa_file_perm(int op, struct aa_profile *profile, struct file *file, + u32 request); + +static inline void aa_free_file_rules(struct aa_file_rules *rules) +{ + aa_put_dfa(rules->dfa); + aa_free_domain_entries(&rules->trans); +} + +#define ACC_FMODE(x) (("\000\004\002\006"[(x)&O_ACCMODE]) | (((x) << 1) & 0x40)) + +/* from namei.c */ +#define MAP_OPEN_FLAGS(x) ((((x) + 1) & O_ACCMODE) ? (x) + 1 : (x)) + +/** + * aa_map_file_perms - map file flags to AppArmor permissions + * @file: open file to map flags to AppArmor permissions + * + * Returns: apparmor permission set for the file + */ +static inline u32 aa_map_file_to_perms(struct file *file) +{ + int flags = MAP_OPEN_FLAGS(file->f_flags); + u32 perms = ACC_FMODE(file->f_mode); + + if ((flags & O_APPEND) && (perms & MAY_WRITE)) + perms = (perms & ~MAY_WRITE) | MAY_APPEND; + /* trunc implies write permission */ + if (flags & O_TRUNC) + perms |= MAY_WRITE; + if (flags & O_CREAT) + perms |= AA_MAY_CREATE; + + return perms; +} + +#endif /* __AA_FILE_H */ -- cgit v1.2.3-70-g09d2 From 898127c34ec03291c86f4ff3856d79e9e18952bc Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 29 Jul 2010 14:48:06 -0700 Subject: AppArmor: functions for domain transitions AppArmor routines for controling domain transitions, which can occur at exec or through self directed change_profile/change_hat calls. Unconfined tasks are checked at exec against the profiles in the confining profile namespace to determine if a profile should be attached to the task. Confined tasks execs are controlled by the profile which provides rules determining which execs are allowed and if so which profiles should be transitioned to. Self directed domain transitions allow a task to request transition to a given profile. If the transition is allowed then the profile will be applied, either immeditately or at exec time depending on the request. Immeditate self directed transitions have several security limitations but have uses in setting up stub transition profiles and other limited cases. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/domain.c | 823 +++++++++++++++++++++++++++++++++++++ security/apparmor/include/domain.h | 36 ++ 2 files changed, 859 insertions(+) create mode 100644 security/apparmor/domain.c create mode 100644 security/apparmor/include/domain.h diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c new file mode 100644 index 00000000000..08bbe6397a7 --- /dev/null +++ b/security/apparmor/domain.c @@ -0,0 +1,823 @@ +/* + * AppArmor security module + * + * This file contains AppArmor policy attachment and domain transitions + * + * Copyright (C) 2002-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "include/audit.h" +#include "include/apparmorfs.h" +#include "include/context.h" +#include "include/domain.h" +#include "include/file.h" +#include "include/ipc.h" +#include "include/match.h" +#include "include/path.h" +#include "include/policy.h" + +/** + * aa_free_domain_entries - free entries in a domain table + * @domain: the domain table to free (MAYBE NULL) + */ +void aa_free_domain_entries(struct aa_domain *domain) +{ + int i; + if (domain) { + if (!domain->table) + return; + + for (i = 0; i < domain->size; i++) + kzfree(domain->table[i]); + kzfree(domain->table); + domain->table = NULL; + } +} + +/** + * may_change_ptraced_domain - check if can change profile on ptraced task + * @task: task we want to change profile of (NOT NULL) + * @to_profile: profile to change to (NOT NULL) + * + * Check if the task is ptraced and if so if the tracing task is allowed + * to trace the new domain + * + * Returns: %0 or error if change not allowed + */ +static int may_change_ptraced_domain(struct task_struct *task, + struct aa_profile *to_profile) +{ + struct task_struct *tracer; + struct cred *cred = NULL; + struct aa_profile *tracerp = NULL; + int error = 0; + + rcu_read_lock(); + tracer = tracehook_tracer_task(task); + if (tracer) { + /* released below */ + cred = get_task_cred(tracer); + tracerp = aa_cred_profile(cred); + } + rcu_read_unlock(); + + /* not ptraced */ + if (!tracer || unconfined(tracerp)) + goto out; + + error = aa_may_ptrace(tracer, tracerp, to_profile, PTRACE_MODE_ATTACH); + +out: + if (cred) + put_cred(cred); + + return error; +} + +/** + * change_profile_perms - find permissions for change_profile + * @profile: the current profile (NOT NULL) + * @ns: the namespace being switched to (NOT NULL) + * @name: the name of the profile to change to (NOT NULL) + * @request: requested perms + * @start: state to start matching in + * + * Returns: permission set + */ +static struct file_perms change_profile_perms(struct aa_profile *profile, + struct aa_namespace *ns, + const char *name, u32 request, + unsigned int start) +{ + struct file_perms perms; + struct path_cond cond = { }; + unsigned int state; + + if (unconfined(profile)) { + perms.allow = AA_MAY_CHANGE_PROFILE | AA_MAY_ONEXEC; + perms.audit = perms.quiet = perms.kill = 0; + return perms; + } else if (!profile->file.dfa) { + return nullperms; + } else if ((ns == profile->ns)) { + /* try matching against rules with out namespace prepended */ + aa_str_perms(profile->file.dfa, start, name, &cond, &perms); + if (COMBINED_PERM_MASK(perms) & request) + return perms; + } + + /* try matching with namespace name and then profile */ + state = aa_dfa_match(profile->file.dfa, start, ns->base.name); + state = aa_dfa_match_len(profile->file.dfa, state, ":", 1); + aa_str_perms(profile->file.dfa, state, name, &cond, &perms); + + return perms; +} + +/** + * __attach_match_ - find an attachment match + * @name - to match against (NOT NULL) + * @head - profile list to walk (NOT NULL) + * + * Do a linear search on the profiles in the list. There is a matching + * preference where an exact match is preferred over a name which uses + * expressions to match, and matching expressions with the greatest + * xmatch_len are preferred. + * + * Requires: @head not be shared or have appropriate locks held + * + * Returns: profile or NULL if no match found + */ +static struct aa_profile *__attach_match(const char *name, + struct list_head *head) +{ + int len = 0; + struct aa_profile *profile, *candidate = NULL; + + list_for_each_entry(profile, head, base.list) { + if (profile->flags & PFLAG_NULL) + continue; + if (profile->xmatch && profile->xmatch_len > len) { + unsigned int state = aa_dfa_match(profile->xmatch, + DFA_START, name); + u32 perm = dfa_user_allow(profile->xmatch, state); + /* any accepting state means a valid match. */ + if (perm & MAY_EXEC) { + candidate = profile; + len = profile->xmatch_len; + } + } else if (!strcmp(profile->base.name, name)) + /* exact non-re match, no more searching required */ + return profile; + } + + return candidate; +} + +/** + * find_attach - do attachment search for unconfined processes + * @ns: the current namespace (NOT NULL) + * @list: list to search (NOT NULL) + * @name: the executable name to match against (NOT NULL) + * + * Returns: profile or NULL if no match found + */ +static struct aa_profile *find_attach(struct aa_namespace *ns, + struct list_head *list, const char *name) +{ + struct aa_profile *profile; + + read_lock(&ns->lock); + profile = aa_get_profile(__attach_match(name, list)); + read_unlock(&ns->lock); + + return profile; +} + +/** + * separate_fqname - separate the namespace and profile names + * @fqname: the fqname name to split (NOT NULL) + * @ns_name: the namespace name if it exists (NOT NULL) + * + * This is the xtable equivalent routine of aa_split_fqname. It finds the + * split in an xtable fqname which contains an embedded \0 instead of a : + * if a namespace is specified. This is done so the xtable is constant and + * isn't re-split on every lookup. + * + * Either the profile or namespace name may be optional but if the namespace + * is specified the profile name termination must be present. This results + * in the following possible encodings: + * profile_name\0 + * :ns_name\0profile_name\0 + * :ns_name\0\0 + * + * NOTE: the xtable fqname is pre-validated at load time in unpack_trans_table + * + * Returns: profile name if it is specified else NULL + */ +static const char *separate_fqname(const char *fqname, const char **ns_name) +{ + const char *name; + + if (fqname[0] == ':') { + /* In this case there is guaranteed to be two \0 terminators + * in the string. They are verified at load time by + * by unpack_trans_table + */ + *ns_name = fqname + 1; /* skip : */ + name = *ns_name + strlen(*ns_name) + 1; + if (!*name) + name = NULL; + } else { + *ns_name = NULL; + name = fqname; + } + + return name; +} + +static const char *next_name(int xtype, const char *name) +{ + return NULL; +} + +/** + * x_table_lookup - lookup an x transition name via transition table + * @profile: current profile (NOT NULL) + * @xindex: index into x transition table + * + * Returns: refcounted profile, or NULL on failure (MAYBE NULL) + */ +static struct aa_profile *x_table_lookup(struct aa_profile *profile, u32 xindex) +{ + struct aa_profile *new_profile = NULL; + struct aa_namespace *ns = profile->ns; + u32 xtype = xindex & AA_X_TYPE_MASK; + int index = xindex & AA_X_INDEX_MASK; + const char *name; + + /* index is guaranteed to be in range, validated at load time */ + for (name = profile->file.trans.table[index]; !new_profile && name; + name = next_name(xtype, name)) { + struct aa_namespace *new_ns; + const char *xname = NULL; + + new_ns = NULL; + if (xindex & AA_X_CHILD) { + /* release by caller */ + new_profile = aa_find_child(profile, name); + continue; + } else if (*name == ':') { + /* switching namespace */ + const char *ns_name; + xname = name = separate_fqname(name, &ns_name); + if (!xname) + /* no name so use profile name */ + xname = profile->base.hname; + if (*ns_name == '@') { + /* TODO: variable support */ + ; + } + /* released below */ + new_ns = aa_find_namespace(ns, ns_name); + if (!new_ns) + continue; + } else if (*name == '@') { + /* TODO: variable support */ + continue; + } else { + /* basic namespace lookup */ + xname = name; + } + + /* released by caller */ + new_profile = aa_lookup_profile(new_ns ? new_ns : ns, xname); + aa_put_namespace(new_ns); + } + + /* released by caller */ + return new_profile; +} + +/** + * x_to_profile - get target profile for a given xindex + * @profile: current profile (NOT NULL) + * @name: name to lookup (NOT NULL) + * @xindex: index into x transition table + * + * find profile for a transition index + * + * Returns: refcounted profile or NULL if not found available + */ +static struct aa_profile *x_to_profile(struct aa_profile *profile, + const char *name, u32 xindex) +{ + struct aa_profile *new_profile = NULL; + struct aa_namespace *ns = profile->ns; + u32 xtype = xindex & AA_X_TYPE_MASK; + + switch (xtype) { + case AA_X_NONE: + /* fail exec unless ix || ux fallback - handled by caller */ + return NULL; + case AA_X_NAME: + if (xindex & AA_X_CHILD) + /* released by caller */ + new_profile = find_attach(ns, &profile->base.profiles, + name); + else + /* released by caller */ + new_profile = find_attach(ns, &ns->base.profiles, + name); + break; + case AA_X_TABLE: + /* released by caller */ + new_profile = x_table_lookup(profile, xindex); + break; + } + + /* released by caller */ + return new_profile; +} + +/** + * apparmor_bprm_set_creds - set the new creds on the bprm struct + * @bprm: binprm for the exec (NOT NULL) + * + * Returns: %0 or error on failure + */ +int apparmor_bprm_set_creds(struct linux_binprm *bprm) +{ + struct aa_task_cxt *cxt; + struct aa_profile *profile, *new_profile = NULL; + struct aa_namespace *ns; + char *buffer = NULL; + unsigned int state; + struct file_perms perms = {}; + struct path_cond cond = { + bprm->file->f_path.dentry->d_inode->i_uid, + bprm->file->f_path.dentry->d_inode->i_mode + }; + const char *name = NULL, *target = NULL, *info = NULL; + int error = cap_bprm_set_creds(bprm); + if (error) + return error; + + if (bprm->cred_prepared) + return 0; + + cxt = bprm->cred->security; + BUG_ON(!cxt); + + profile = aa_get_profile(aa_newest_version(cxt->profile)); + /* + * get the namespace from the replacement profile as replacement + * can change the namespace + */ + ns = profile->ns; + state = profile->file.start; + + /* buffer freed below, name is pointer into buffer */ + error = aa_get_name(&bprm->file->f_path, profile->path_flags, &buffer, + &name); + if (error) { + if (profile->flags & + (PFLAG_IX_ON_NAME_ERROR | PFLAG_UNCONFINED)) + error = 0; + info = "Exec failed name resolution"; + name = bprm->filename; + goto audit; + } + + /* Test for onexec first as onexec directives override other + * x transitions. + */ + if (unconfined(profile)) { + /* unconfined task */ + if (cxt->onexec) + /* change_profile on exec already been granted */ + new_profile = aa_get_profile(cxt->onexec); + else + new_profile = find_attach(ns, &ns->base.profiles, name); + if (!new_profile) + goto cleanup; + goto apply; + } + + /* find exec permissions for name */ + state = aa_str_perms(profile->file.dfa, state, name, &cond, &perms); + if (cxt->onexec) { + struct file_perms cp; + info = "change_profile onexec"; + if (!(perms.allow & AA_MAY_ONEXEC)) + goto audit; + + /* test if this exec can be paired with change_profile onexec. + * onexec permission is linked to exec with a standard pairing + * exec\0change_profile + */ + state = aa_dfa_null_transition(profile->file.dfa, state); + cp = change_profile_perms(profile, cxt->onexec->ns, name, + AA_MAY_ONEXEC, state); + + if (!(cp.allow & AA_MAY_ONEXEC)) + goto audit; + new_profile = aa_get_profile(aa_newest_version(cxt->onexec)); + goto apply; + } + + if (perms.allow & MAY_EXEC) { + /* exec permission determine how to transition */ + new_profile = x_to_profile(profile, name, perms.xindex); + if (!new_profile) { + if (perms.xindex & AA_X_INHERIT) { + /* (p|c|n)ix - don't change profile but do + * use the newest version, which was picked + * up above when getting profile + */ + info = "ix fallback"; + new_profile = aa_get_profile(profile); + goto x_clear; + } else if (perms.xindex & AA_X_UNCONFINED) { + new_profile = aa_get_profile(ns->unconfined); + info = "ux fallback"; + } else { + error = -ENOENT; + info = "profile not found"; + } + } + } else if (COMPLAIN_MODE(profile)) { + /* no exec permission - are we in learning mode */ + new_profile = aa_new_null_profile(profile, 0); + if (!new_profile) { + error = -ENOMEM; + info = "could not create null profile"; + } else { + error = -EACCES; + target = new_profile->base.hname; + } + perms.xindex |= AA_X_UNSAFE; + } else + /* fail exec */ + error = -EACCES; + + if (!new_profile) + goto audit; + + if (bprm->unsafe & LSM_UNSAFE_SHARE) { + /* FIXME: currently don't mediate shared state */ + ; + } + + if (bprm->unsafe & (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) { + error = may_change_ptraced_domain(current, new_profile); + if (error) { + aa_put_profile(new_profile); + goto audit; + } + } + + /* Determine if secure exec is needed. + * Can be at this point for the following reasons: + * 1. unconfined switching to confined + * 2. confined switching to different confinement + * 3. confined switching to unconfined + * + * Cases 2 and 3 are marked as requiring secure exec + * (unless policy specified "unsafe exec") + * + * bprm->unsafe is used to cache the AA_X_UNSAFE permission + * to avoid having to recompute in secureexec + */ + if (!(perms.xindex & AA_X_UNSAFE)) { + AA_DEBUG("scrubbing environment variables for %s profile=%s\n", + name, new_profile->base.hname); + bprm->unsafe |= AA_SECURE_X_NEEDED; + } +apply: + target = new_profile->base.hname; + /* when transitioning profiles clear unsafe personality bits */ + bprm->per_clear |= PER_CLEAR_ON_SETID; + +x_clear: + aa_put_profile(cxt->profile); + /* transfer new profile reference will be released when cxt is freed */ + cxt->profile = new_profile; + + /* clear out all temporary/transitional state from the context */ + aa_put_profile(cxt->previous); + aa_put_profile(cxt->onexec); + cxt->previous = NULL; + cxt->onexec = NULL; + cxt->token = 0; + +audit: + error = aa_audit_file(profile, &perms, GFP_KERNEL, OP_EXEC, MAY_EXEC, + name, target, cond.uid, info, error); + +cleanup: + aa_put_profile(profile); + kfree(buffer); + + return error; +} + +/** + * apparmor_bprm_secureexec - determine if secureexec is needed + * @bprm: binprm for exec (NOT NULL) + * + * Returns: %1 if secureexec is needed else %0 + */ +int apparmor_bprm_secureexec(struct linux_binprm *bprm) +{ + int ret = cap_bprm_secureexec(bprm); + + /* the decision to use secure exec is computed in set_creds + * and stored in bprm->unsafe. + */ + if (!ret && (bprm->unsafe & AA_SECURE_X_NEEDED)) + ret = 1; + + return ret; +} + +/** + * apparmor_bprm_committing_creds - do task cleanup on committing new creds + * @bprm: binprm for the exec (NOT NULL) + */ +void apparmor_bprm_committing_creds(struct linux_binprm *bprm) +{ + struct aa_profile *profile = __aa_current_profile(); + struct aa_task_cxt *new_cxt = bprm->cred->security; + + /* bail out if unconfined or not changing profile */ + if ((new_cxt->profile == profile) || + (unconfined(new_cxt->profile))) + return; + + current->pdeath_signal = 0; + + /* reset soft limits and set hard limits for the new profile */ + __aa_transition_rlimits(profile, new_cxt->profile); +} + +/** + * apparmor_bprm_commited_cred - do cleanup after new creds committed + * @bprm: binprm for the exec (NOT NULL) + */ +void apparmor_bprm_committed_creds(struct linux_binprm *bprm) +{ + /* TODO: cleanup signals - ipc mediation */ + return; +} + +/* + * Functions for self directed profile change + */ + +/** + * new_compound_name - create an hname with @n2 appended to @n1 + * @n1: base of hname (NOT NULL) + * @n2: name to append (NOT NULL) + * + * Returns: new name or NULL on error + */ +static char *new_compound_name(const char *n1, const char *n2) +{ + char *name = kmalloc(strlen(n1) + strlen(n2) + 3, GFP_KERNEL); + if (name) + sprintf(name, "%s//%s", n1, n2); + return name; +} + +/** + * aa_change_hat - change hat to/from subprofile + * @hats: vector of hat names to try changing into (MAYBE NULL if @count == 0) + * @count: number of hat names in @hats + * @token: magic value to validate the hat change + * @permtest: true if this is just a permission test + * + * Change to the first profile specified in @hats that exists, and store + * the @hat_magic in the current task context. If the count == 0 and the + * @token matches that stored in the current task context, return to the + * top level profile. + * + * Returns %0 on success, error otherwise. + */ +int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) +{ + const struct cred *cred; + struct aa_task_cxt *cxt; + struct aa_profile *profile, *previous_profile, *hat = NULL; + char *name = NULL; + int i; + struct file_perms perms = {}; + const char *target = NULL, *info = NULL; + int error = 0; + + /* released below */ + cred = get_current_cred(); + cxt = cred->security; + profile = aa_cred_profile(cred); + previous_profile = cxt->previous; + + if (unconfined(profile)) { + info = "unconfined"; + error = -EPERM; + goto audit; + } + + if (count) { + /* attempting to change into a new hat or switch to a sibling */ + struct aa_profile *root; + root = PROFILE_IS_HAT(profile) ? profile->parent : profile; + + /* find first matching hat */ + for (i = 0; i < count && !hat; i++) + /* released below */ + hat = aa_find_child(root, hats[i]); + if (!hat) { + if (!COMPLAIN_MODE(root) || permtest) { + if (list_empty(&root->base.profiles)) + error = -ECHILD; + else + error = -ENOENT; + goto out; + } + + /* + * In complain mode and failed to match any hats. + * Audit the failure is based off of the first hat + * supplied. This is done due how userspace + * interacts with change_hat. + * + * TODO: Add logging of all failed hats + */ + + /* freed below */ + name = new_compound_name(root->base.hname, hats[0]); + target = name; + /* released below */ + hat = aa_new_null_profile(profile, 1); + if (!hat) { + info = "failed null profile create"; + error = -ENOMEM; + goto audit; + } + } else { + target = hat->base.hname; + if (!PROFILE_IS_HAT(hat)) { + info = "target not hat"; + error = -EPERM; + goto audit; + } + } + + error = may_change_ptraced_domain(current, hat); + if (error) { + info = "ptraced"; + error = -EPERM; + goto audit; + } + + if (!permtest) { + error = aa_set_current_hat(hat, token); + if (error == -EACCES) + /* kill task in case of brute force attacks */ + perms.kill = AA_MAY_CHANGEHAT; + else if (name && !error) + /* reset error for learning of new hats */ + error = -ENOENT; + } + } else if (previous_profile) { + /* Return to saved profile. Kill task if restore fails + * to avoid brute force attacks + */ + target = previous_profile->base.hname; + error = aa_restore_previous_profile(token); + perms.kill = AA_MAY_CHANGEHAT; + } else + /* ignore restores when there is no saved profile */ + goto out; + +audit: + if (!permtest) + error = aa_audit_file(profile, &perms, GFP_KERNEL, + OP_CHANGE_HAT, AA_MAY_CHANGEHAT, NULL, + target, 0, info, error); + +out: + aa_put_profile(hat); + kfree(name); + put_cred(cred); + + return error; +} + +/** + * aa_change_profile - perform a one-way profile transition + * @ns_name: name of the profile namespace to change to (MAYBE NULL) + * @hname: name of profile to change to (MAYBE NULL) + * @onexec: whether this transition is to take place immediately or at exec + * @permtest: true if this is just a permission test + * + * Change to new profile @name. Unlike with hats, there is no way + * to change back. If @name isn't specified the current profile name is + * used. + * If @onexec then the transition is delayed until + * the next exec. + * + * Returns %0 on success, error otherwise. + */ +int aa_change_profile(const char *ns_name, const char *hname, bool onexec, + bool permtest) +{ + const struct cred *cred; + struct aa_task_cxt *cxt; + struct aa_profile *profile, *target = NULL; + struct aa_namespace *ns = NULL; + struct file_perms perms = {}; + const char *name = NULL, *info = NULL; + int op, error = 0; + u32 request; + + if (!hname && !ns_name) + return -EINVAL; + + if (onexec) { + request = AA_MAY_ONEXEC; + op = OP_CHANGE_ONEXEC; + } else { + request = AA_MAY_CHANGE_PROFILE; + op = OP_CHANGE_PROFILE; + } + + cred = get_current_cred(); + cxt = cred->security; + profile = aa_cred_profile(cred); + + if (ns_name) { + /* released below */ + ns = aa_find_namespace(profile->ns, ns_name); + if (!ns) { + /* we don't create new namespace in complain mode */ + name = ns_name; + info = "namespace not found"; + error = -ENOENT; + goto audit; + } + } else + /* released below */ + ns = aa_get_namespace(profile->ns); + + /* if the name was not specified, use the name of the current profile */ + if (!hname) { + if (unconfined(profile)) + hname = ns->unconfined->base.hname; + else + hname = profile->base.hname; + } + + perms = change_profile_perms(profile, ns, hname, request, + profile->file.start); + if (!(perms.allow & request)) { + error = -EACCES; + goto audit; + } + + /* released below */ + target = aa_lookup_profile(ns, hname); + if (!target) { + info = "profile not found"; + error = -ENOENT; + if (permtest || !COMPLAIN_MODE(profile)) + goto audit; + /* released below */ + target = aa_new_null_profile(profile, 0); + if (!target) { + info = "failed null profile create"; + error = -ENOMEM; + goto audit; + } + } + + /* check if tracing task is allowed to trace target domain */ + error = may_change_ptraced_domain(current, target); + if (error) { + info = "ptrace prevents transition"; + goto audit; + } + + if (permtest) + goto audit; + + if (onexec) + error = aa_set_current_onexec(target); + else + error = aa_replace_current_profile(target); + +audit: + if (!permtest) + error = aa_audit_file(profile, &perms, GFP_KERNEL, op, request, + name, hname, 0, info, error); + + aa_put_namespace(ns); + aa_put_profile(target); + put_cred(cred); + + return error; +} diff --git a/security/apparmor/include/domain.h b/security/apparmor/include/domain.h new file mode 100644 index 00000000000..de04464f0a3 --- /dev/null +++ b/security/apparmor/include/domain.h @@ -0,0 +1,36 @@ +/* + * AppArmor security module + * + * This file contains AppArmor security domain transition function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include +#include + +#ifndef __AA_DOMAIN_H +#define __AA_DOMAIN_H + +struct aa_domain { + int size; + char **table; +}; + +int apparmor_bprm_set_creds(struct linux_binprm *bprm); +int apparmor_bprm_secureexec(struct linux_binprm *bprm); +void apparmor_bprm_committing_creds(struct linux_binprm *bprm); +void apparmor_bprm_committed_creds(struct linux_binprm *bprm); + +void aa_free_domain_entries(struct aa_domain *domain); +int aa_change_hat(const char *hats[], int count, u64 token, bool permtest); +int aa_change_profile(const char *ns_name, const char *name, bool onexec, + bool permtest); + +#endif /* __AA_DOMAIN_H */ -- cgit v1.2.3-70-g09d2 From c1c124e91e7c6d5a600c98f6fb5b443c403a14f4 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 29 Jul 2010 14:48:09 -0700 Subject: AppArmor: update Maintainer and Documentation Signed-off-by: John Johansen Signed-off-by: James Morris --- Documentation/apparmor.txt | 39 +++++++++++++++++++++++++++++++++++++ Documentation/kernel-parameters.txt | 8 ++++++++ MAINTAINERS | 8 ++++++++ 3 files changed, 55 insertions(+) create mode 100644 Documentation/apparmor.txt diff --git a/Documentation/apparmor.txt b/Documentation/apparmor.txt new file mode 100644 index 00000000000..93c1fd7d063 --- /dev/null +++ b/Documentation/apparmor.txt @@ -0,0 +1,39 @@ +--- What is AppArmor? --- + +AppArmor is MAC style security extension for the Linux kernel. It implements +a task centered policy, with task "profiles" being created and loaded +from user space. Tasks on the system that do not have a profile defined for +them run in an unconfined state which is equivalent to standard Linux DAC +permissions. + +--- How to enable/disable --- + +set CONFIG_SECURITY_APPARMOR=y + +If AppArmor should be selected as the default security module then + set CONFIG_DEFAULT_SECURITY="apparmor" + and CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE=1 + +Build the kernel + +If AppArmor is not the default security module it can be enabled by passing +security=apparmor on the kernel's command line. + +If AppArmor is the default security module it can be disabled by passing +apparmor=0, security=XXXX (where XXX is valid security module), on the +kernel's command line + +For AppArmor to enforce any restrictions beyond standard Linux DAC permissions +policy must be loaded into the kernel from user space (see the Documentation +and tools links). + +--- Documentation --- + +Documentation can be found on the wiki. + +--- Links --- + +Mailing List - apparmor@lists.ubuntu.com +Wiki - http://apparmor.wiki.kernel.org/ +User space tools - https://launchpad.net/apparmor +Kernel module - git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 2b2407d9a6d..b61f89fa01c 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -93,6 +93,7 @@ parameter is applicable: Documentation/scsi/. SECURITY Different security models are enabled. SELINUX SELinux support is enabled. + APPARMOR AppArmor support is enabled. SERIAL Serial support is enabled. SH SuperH architecture is enabled. SMP The kernel is an SMP kernel. @@ -2312,6 +2313,13 @@ and is between 256 and 4096 characters. It is defined in the file If enabled at boot time, /selinux/disable can be used later to disable prior to initial policy load. + apparmor= [APPARMOR] Disable or enable AppArmor at boot time + Format: { "0" | "1" } + See security/apparmor/Kconfig help text + 0 -- disable. + 1 -- enable. + Default value is set via kernel config option. + serialnumber [BUGS=X86-32] shapers= [NET] diff --git a/MAINTAINERS b/MAINTAINERS index 28443185646..33606bb91f1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -5061,6 +5061,14 @@ S: Supported F: include/linux/selinux* F: security/selinux/ +APPARMOR SECURITY MODULE +M: John Johansen +L: apparmor@lists.ubuntu.com (subscribers-only, general discussion) +W: apparmor.wiki.kernel.org +T: git git://git.kernel.org/pub/scm/linux/kernel/git/jj/apparmor-dev.git +S: Supported +F: security/apparmor/ + SENSABLE PHANTOM M: Jiri Slaby S: Maintained -- cgit v1.2.3-70-g09d2 From f9ad1af53d5232a89a1ff1827102843999975dfa Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 29 Jul 2010 14:48:08 -0700 Subject: AppArmor: Enable configuring and building of the AppArmor security module Kconfig and Makefiles to enable configuration and building of AppArmor. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/Kconfig | 6 ++++++ security/Makefile | 2 ++ 2 files changed, 8 insertions(+) diff --git a/security/Kconfig b/security/Kconfig index 226b9556b25..bd72ae62349 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -140,6 +140,7 @@ config LSM_MMAP_MIN_ADDR source security/selinux/Kconfig source security/smack/Kconfig source security/tomoyo/Kconfig +source security/apparmor/Kconfig source security/integrity/ima/Kconfig @@ -148,6 +149,7 @@ choice default DEFAULT_SECURITY_SELINUX if SECURITY_SELINUX default DEFAULT_SECURITY_SMACK if SECURITY_SMACK default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO + default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR default DEFAULT_SECURITY_DAC help @@ -163,6 +165,9 @@ choice config DEFAULT_SECURITY_TOMOYO bool "TOMOYO" if SECURITY_TOMOYO=y + config DEFAULT_SECURITY_APPARMOR + bool "AppArmor" if SECURITY_APPARMOR=y + config DEFAULT_SECURITY_DAC bool "Unix Discretionary Access Controls" @@ -173,6 +178,7 @@ config DEFAULT_SECURITY default "selinux" if DEFAULT_SECURITY_SELINUX default "smack" if DEFAULT_SECURITY_SMACK default "tomoyo" if DEFAULT_SECURITY_TOMOYO + default "apparmor" if DEFAULT_SECURITY_APPARMOR default "" if DEFAULT_SECURITY_DAC endmenu diff --git a/security/Makefile b/security/Makefile index da20a193c8d..8bb0fe9e1ca 100644 --- a/security/Makefile +++ b/security/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_KEYS) += keys/ subdir-$(CONFIG_SECURITY_SELINUX) += selinux subdir-$(CONFIG_SECURITY_SMACK) += smack subdir-$(CONFIG_SECURITY_TOMOYO) += tomoyo +subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor # always enable default capabilities obj-y += commoncap.o @@ -19,6 +20,7 @@ obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o obj-$(CONFIG_AUDIT) += lsm_audit.o obj-$(CONFIG_SECURITY_TOMOYO) += tomoyo/built-in.o +obj-$(CONFIG_SECURITY_APPARMOR) += apparmor/built-in.o obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o # Object integrity file lists -- cgit v1.2.3-70-g09d2 From b5e95b48685e3481139a5634d14d630d12c7d5ce Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 29 Jul 2010 14:48:07 -0700 Subject: AppArmor: LSM interface, and security module initialization AppArmor hooks to interface with the LSM, module parameters and module initialization. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/lsm.c | 939 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 939 insertions(+) create mode 100644 security/apparmor/lsm.c diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c new file mode 100644 index 00000000000..7daf0d52803 --- /dev/null +++ b/security/apparmor/lsm.c @@ -0,0 +1,939 @@ +/* + * AppArmor security module + * + * This file contains AppArmor LSM hooks. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "include/apparmor.h" +#include "include/apparmorfs.h" +#include "include/audit.h" +#include "include/capability.h" +#include "include/context.h" +#include "include/file.h" +#include "include/ipc.h" +#include "include/path.h" +#include "include/policy.h" +#include "include/procattr.h" + +/* Flag indicating whether initialization completed */ +int apparmor_initialized __initdata; + +/* + * LSM hook functions + */ + +/* + * free the associated aa_task_cxt and put its profiles + */ +static void apparmor_cred_free(struct cred *cred) +{ + aa_free_task_context(cred->security); + cred->security = NULL; +} + +/* + * allocate the apparmor part of blank credentials + */ +static int apparmor_cred_alloc_blank(struct cred *cred, gfp_t gfp) +{ + /* freed by apparmor_cred_free */ + struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); + if (!cxt) + return -ENOMEM; + + cred->security = cxt; + return 0; +} + +/* + * prepare new aa_task_cxt for modification by prepare_cred block + */ +static int apparmor_cred_prepare(struct cred *new, const struct cred *old, + gfp_t gfp) +{ + /* freed by apparmor_cred_free */ + struct aa_task_cxt *cxt = aa_alloc_task_context(gfp); + if (!cxt) + return -ENOMEM; + + aa_dup_task_context(cxt, old->security); + new->security = cxt; + return 0; +} + +/* + * transfer the apparmor data to a blank set of creds + */ +static void apparmor_cred_transfer(struct cred *new, const struct cred *old) +{ + const struct aa_task_cxt *old_cxt = old->security; + struct aa_task_cxt *new_cxt = new->security; + + aa_dup_task_context(new_cxt, old_cxt); +} + +static int apparmor_ptrace_access_check(struct task_struct *child, + unsigned int mode) +{ + int error = cap_ptrace_access_check(child, mode); + if (error) + return error; + + return aa_ptrace(current, child, mode); +} + +static int apparmor_ptrace_traceme(struct task_struct *parent) +{ + int error = cap_ptrace_traceme(parent); + if (error) + return error; + + return aa_ptrace(parent, current, PTRACE_MODE_ATTACH); +} + +/* Derived from security/commoncap.c:cap_capget */ +static int apparmor_capget(struct task_struct *target, kernel_cap_t *effective, + kernel_cap_t *inheritable, kernel_cap_t *permitted) +{ + struct aa_profile *profile; + const struct cred *cred; + + rcu_read_lock(); + cred = __task_cred(target); + profile = aa_cred_profile(cred); + + *effective = cred->cap_effective; + *inheritable = cred->cap_inheritable; + *permitted = cred->cap_permitted; + + if (!unconfined(profile)) { + *effective = cap_intersect(*effective, profile->caps.allow); + *permitted = cap_intersect(*permitted, profile->caps.allow); + } + rcu_read_unlock(); + + return 0; +} + +static int apparmor_capable(struct task_struct *task, const struct cred *cred, + int cap, int audit) +{ + struct aa_profile *profile; + /* cap_capable returns 0 on success, else -EPERM */ + int error = cap_capable(task, cred, cap, audit); + if (!error) { + profile = aa_cred_profile(cred); + if (!unconfined(profile)) + error = aa_capable(task, profile, cap, audit); + } + return error; +} + +/** + * common_perm - basic common permission check wrapper fn for paths + * @op: operation being checked + * @path: path to check permission of (NOT NULL) + * @mask: requested permissions mask + * @cond: conditional info for the permission request (NOT NULL) + * + * Returns: %0 else error code if error or permission denied + */ +static int common_perm(int op, struct path *path, u32 mask, + struct path_cond *cond) +{ + struct aa_profile *profile; + int error = 0; + + profile = __aa_current_profile(); + if (!unconfined(profile)) + error = aa_path_perm(op, profile, path, 0, mask, cond); + + return error; +} + +/** + * common_perm_dir_dentry - common permission wrapper when path is dir, dentry + * @op: operation being checked + * @dir: directory of the dentry (NOT NULL) + * @dentry: dentry to check (NOT NULL) + * @mask: requested permissions mask + * @cond: conditional info for the permission request (NOT NULL) + * + * Returns: %0 else error code if error or permission denied + */ +static int common_perm_dir_dentry(int op, struct path *dir, + struct dentry *dentry, u32 mask, + struct path_cond *cond) +{ + struct path path = { dir->mnt, dentry }; + + return common_perm(op, &path, mask, cond); +} + +/** + * common_perm_mnt_dentry - common permission wrapper when mnt, dentry + * @op: operation being checked + * @mnt: mount point of dentry (NOT NULL) + * @dentry: dentry to check (NOT NULL) + * @mask: requested permissions mask + * + * Returns: %0 else error code if error or permission denied + */ +static int common_perm_mnt_dentry(int op, struct vfsmount *mnt, + struct dentry *dentry, u32 mask) +{ + struct path path = { mnt, dentry }; + struct path_cond cond = { dentry->d_inode->i_uid, + dentry->d_inode->i_mode + }; + + return common_perm(op, &path, mask, &cond); +} + +/** + * common_perm_rm - common permission wrapper for operations doing rm + * @op: operation being checked + * @dir: directory that the dentry is in (NOT NULL) + * @dentry: dentry being rm'd (NOT NULL) + * @mask: requested permission mask + * + * Returns: %0 else error code if error or permission denied + */ +static int common_perm_rm(int op, struct path *dir, + struct dentry *dentry, u32 mask) +{ + struct inode *inode = dentry->d_inode; + struct path_cond cond = { }; + + if (!inode || !dir->mnt || !mediated_filesystem(inode)) + return 0; + + cond.uid = inode->i_uid; + cond.mode = inode->i_mode; + + return common_perm_dir_dentry(op, dir, dentry, mask, &cond); +} + +/** + * common_perm_create - common permission wrapper for operations doing create + * @op: operation being checked + * @dir: directory that dentry will be created in (NOT NULL) + * @dentry: dentry to create (NOT NULL) + * @mask: request permission mask + * @mode: created file mode + * + * Returns: %0 else error code if error or permission denied + */ +static int common_perm_create(int op, struct path *dir, struct dentry *dentry, + u32 mask, umode_t mode) +{ + struct path_cond cond = { current_fsuid(), mode }; + + if (!dir->mnt || !mediated_filesystem(dir->dentry->d_inode)) + return 0; + + return common_perm_dir_dentry(op, dir, dentry, mask, &cond); +} + +static int apparmor_path_unlink(struct path *dir, struct dentry *dentry) +{ + return common_perm_rm(OP_UNLINK, dir, dentry, AA_MAY_DELETE); +} + +static int apparmor_path_mkdir(struct path *dir, struct dentry *dentry, + int mode) +{ + return common_perm_create(OP_MKDIR, dir, dentry, AA_MAY_CREATE, + S_IFDIR); +} + +static int apparmor_path_rmdir(struct path *dir, struct dentry *dentry) +{ + return common_perm_rm(OP_RMDIR, dir, dentry, AA_MAY_DELETE); +} + +static int apparmor_path_mknod(struct path *dir, struct dentry *dentry, + int mode, unsigned int dev) +{ + return common_perm_create(OP_MKNOD, dir, dentry, AA_MAY_CREATE, mode); +} + +static int apparmor_path_truncate(struct path *path, loff_t length, + unsigned int time_attrs) +{ + struct path_cond cond = { path->dentry->d_inode->i_uid, + path->dentry->d_inode->i_mode + }; + + if (!path->mnt || !mediated_filesystem(path->dentry->d_inode)) + return 0; + + return common_perm(OP_TRUNC, path, MAY_WRITE | AA_MAY_META_WRITE, + &cond); +} + +static int apparmor_path_symlink(struct path *dir, struct dentry *dentry, + const char *old_name) +{ + return common_perm_create(OP_SYMLINK, dir, dentry, AA_MAY_CREATE, + S_IFLNK); +} + +static int apparmor_path_link(struct dentry *old_dentry, struct path *new_dir, + struct dentry *new_dentry) +{ + struct aa_profile *profile; + int error = 0; + + if (!mediated_filesystem(old_dentry->d_inode)) + return 0; + + profile = aa_current_profile(); + if (!unconfined(profile)) + error = aa_path_link(profile, old_dentry, new_dir, new_dentry); + return error; +} + +static int apparmor_path_rename(struct path *old_dir, struct dentry *old_dentry, + struct path *new_dir, struct dentry *new_dentry) +{ + struct aa_profile *profile; + int error = 0; + + if (!mediated_filesystem(old_dentry->d_inode)) + return 0; + + profile = aa_current_profile(); + if (!unconfined(profile)) { + struct path old_path = { old_dir->mnt, old_dentry }; + struct path new_path = { new_dir->mnt, new_dentry }; + struct path_cond cond = { old_dentry->d_inode->i_uid, + old_dentry->d_inode->i_mode + }; + + error = aa_path_perm(OP_RENAME_SRC, profile, &old_path, 0, + MAY_READ | AA_MAY_META_READ | MAY_WRITE | + AA_MAY_META_WRITE | AA_MAY_DELETE, + &cond); + if (!error) + error = aa_path_perm(OP_RENAME_DEST, profile, &new_path, + 0, MAY_WRITE | AA_MAY_META_WRITE | + AA_MAY_CREATE, &cond); + + } + return error; +} + +static int apparmor_path_chmod(struct dentry *dentry, struct vfsmount *mnt, + mode_t mode) +{ + if (!mediated_filesystem(dentry->d_inode)) + return 0; + + return common_perm_mnt_dentry(OP_CHMOD, mnt, dentry, AA_MAY_CHMOD); +} + +static int apparmor_path_chown(struct path *path, uid_t uid, gid_t gid) +{ + struct path_cond cond = { path->dentry->d_inode->i_uid, + path->dentry->d_inode->i_mode + }; + + if (!mediated_filesystem(path->dentry->d_inode)) + return 0; + + return common_perm(OP_CHOWN, path, AA_MAY_CHOWN, &cond); +} + +static int apparmor_inode_getattr(struct vfsmount *mnt, struct dentry *dentry) +{ + if (!mediated_filesystem(dentry->d_inode)) + return 0; + + return common_perm_mnt_dentry(OP_GETATTR, mnt, dentry, + AA_MAY_META_READ); +} + +static int apparmor_dentry_open(struct file *file, const struct cred *cred) +{ + struct aa_file_cxt *fcxt = file->f_security; + struct aa_profile *profile; + int error = 0; + + if (!mediated_filesystem(file->f_path.dentry->d_inode)) + return 0; + + /* If in exec, permission is handled by bprm hooks. + * Cache permissions granted by the previous exec check, with + * implicit read and executable mmap which are required to + * actually execute the image. + */ + if (current->in_execve) { + fcxt->allow = MAY_EXEC | MAY_READ | AA_EXEC_MMAP; + return 0; + } + + profile = aa_cred_profile(cred); + if (!unconfined(profile)) { + struct inode *inode = file->f_path.dentry->d_inode; + struct path_cond cond = { inode->i_uid, inode->i_mode }; + + error = aa_path_perm(OP_OPEN, profile, &file->f_path, 0, + aa_map_file_to_perms(file), &cond); + /* todo cache full allowed permissions set and state */ + fcxt->allow = aa_map_file_to_perms(file); + } + + return error; +} + +static int apparmor_file_alloc_security(struct file *file) +{ + /* freed by apparmor_file_free_security */ + file->f_security = aa_alloc_file_context(GFP_KERNEL); + if (!file->f_security) + return -ENOMEM; + return 0; + +} + +static void apparmor_file_free_security(struct file *file) +{ + struct aa_file_cxt *cxt = file->f_security; + + aa_free_file_context(cxt); +} + +static int common_file_perm(int op, struct file *file, u32 mask) +{ + struct aa_file_cxt *fcxt = file->f_security; + struct aa_profile *profile, *fprofile = aa_cred_profile(file->f_cred); + int error = 0; + + BUG_ON(!fprofile); + + if (!file->f_path.mnt || + !mediated_filesystem(file->f_path.dentry->d_inode)) + return 0; + + profile = __aa_current_profile(); + + /* revalidate access, if task is unconfined, or the cached cred + * doesn't match or if the request is for more permissions than + * was granted. + * + * Note: the test for !unconfined(fprofile) is to handle file + * delegation from unconfined tasks + */ + if (!unconfined(profile) && !unconfined(fprofile) && + ((fprofile != profile) || (mask & ~fcxt->allow))) + error = aa_file_perm(op, profile, file, mask); + + return error; +} + +static int apparmor_file_permission(struct file *file, int mask) +{ + return common_file_perm(OP_FPERM, file, mask); +} + +static int apparmor_file_lock(struct file *file, unsigned int cmd) +{ + u32 mask = AA_MAY_LOCK; + + if (cmd == F_WRLCK) + mask |= MAY_WRITE; + + return common_file_perm(OP_FLOCK, file, mask); +} + +static int common_mmap(int op, struct file *file, unsigned long prot, + unsigned long flags) +{ + struct dentry *dentry; + int mask = 0; + + if (!file || !file->f_security) + return 0; + + if (prot & PROT_READ) + mask |= MAY_READ; + /* + * Private mappings don't require write perms since they don't + * write back to the files + */ + if ((prot & PROT_WRITE) && !(flags & MAP_PRIVATE)) + mask |= MAY_WRITE; + if (prot & PROT_EXEC) + mask |= AA_EXEC_MMAP; + + dentry = file->f_path.dentry; + return common_file_perm(op, file, mask); +} + +static int apparmor_file_mmap(struct file *file, unsigned long reqprot, + unsigned long prot, unsigned long flags, + unsigned long addr, unsigned long addr_only) +{ + int rc = 0; + + /* do DAC check */ + rc = cap_file_mmap(file, reqprot, prot, flags, addr, addr_only); + if (rc || addr_only) + return rc; + + return common_mmap(OP_FMMAP, file, prot, flags); +} + +static int apparmor_file_mprotect(struct vm_area_struct *vma, + unsigned long reqprot, unsigned long prot) +{ + return common_mmap(OP_FMPROT, vma->vm_file, prot, + !(vma->vm_flags & VM_SHARED) ? MAP_PRIVATE : 0); +} + +static int apparmor_getprocattr(struct task_struct *task, char *name, + char **value) +{ + int error = -ENOENT; + struct aa_profile *profile; + /* released below */ + const struct cred *cred = get_task_cred(task); + struct aa_task_cxt *cxt = cred->security; + profile = aa_cred_profile(cred); + + if (strcmp(name, "current") == 0) + error = aa_getprocattr(aa_newest_version(cxt->profile), + value); + else if (strcmp(name, "prev") == 0 && cxt->previous) + error = aa_getprocattr(aa_newest_version(cxt->previous), + value); + else if (strcmp(name, "exec") == 0 && cxt->onexec) + error = aa_getprocattr(aa_newest_version(cxt->onexec), + value); + else + error = -EINVAL; + + put_cred(cred); + + return error; +} + +static int apparmor_setprocattr(struct task_struct *task, char *name, + void *value, size_t size) +{ + char *command, *args = value; + size_t arg_size; + int error; + + if (size == 0) + return -EINVAL; + /* args points to a PAGE_SIZE buffer, AppArmor requires that + * the buffer must be null terminated or have size <= PAGE_SIZE -1 + * so that AppArmor can null terminate them + */ + if (args[size - 1] != '\0') { + if (size == PAGE_SIZE) + return -EINVAL; + args[size] = '\0'; + } + + /* task can only write its own attributes */ + if (current != task) + return -EACCES; + + args = value; + args = strim(args); + command = strsep(&args, " "); + if (!args) + return -EINVAL; + args = skip_spaces(args); + if (!*args) + return -EINVAL; + + arg_size = size - (args - (char *) value); + if (strcmp(name, "current") == 0) { + if (strcmp(command, "changehat") == 0) { + error = aa_setprocattr_changehat(args, arg_size, + !AA_DO_TEST); + } else if (strcmp(command, "permhat") == 0) { + error = aa_setprocattr_changehat(args, arg_size, + AA_DO_TEST); + } else if (strcmp(command, "changeprofile") == 0) { + error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, + !AA_DO_TEST); + } else if (strcmp(command, "permprofile") == 0) { + error = aa_setprocattr_changeprofile(args, !AA_ONEXEC, + AA_DO_TEST); + } else if (strcmp(command, "permipc") == 0) { + error = aa_setprocattr_permipc(args); + } else { + struct common_audit_data sa; + COMMON_AUDIT_DATA_INIT(&sa, NONE); + sa.aad.op = OP_SETPROCATTR; + sa.aad.info = name; + sa.aad.error = -EINVAL; + return aa_audit(AUDIT_APPARMOR_DENIED, NULL, GFP_KERNEL, + &sa, NULL); + } + } else if (strcmp(name, "exec") == 0) { + error = aa_setprocattr_changeprofile(args, AA_ONEXEC, + !AA_DO_TEST); + } else { + /* only support the "current" and "exec" process attributes */ + return -EINVAL; + } + if (!error) + error = size; + return error; +} + +static int apparmor_task_setrlimit(unsigned int resource, + struct rlimit *new_rlim) +{ + struct aa_profile *profile = aa_current_profile(); + int error = 0; + + if (!unconfined(profile)) + error = aa_task_setrlimit(profile, resource, new_rlim); + + return error; +} + +static struct security_operations apparmor_ops = { + .name = "apparmor", + + .ptrace_access_check = apparmor_ptrace_access_check, + .ptrace_traceme = apparmor_ptrace_traceme, + .capget = apparmor_capget, + .capable = apparmor_capable, + + .path_link = apparmor_path_link, + .path_unlink = apparmor_path_unlink, + .path_symlink = apparmor_path_symlink, + .path_mkdir = apparmor_path_mkdir, + .path_rmdir = apparmor_path_rmdir, + .path_mknod = apparmor_path_mknod, + .path_rename = apparmor_path_rename, + .path_chmod = apparmor_path_chmod, + .path_chown = apparmor_path_chown, + .path_truncate = apparmor_path_truncate, + .dentry_open = apparmor_dentry_open, + .inode_getattr = apparmor_inode_getattr, + + .file_permission = apparmor_file_permission, + .file_alloc_security = apparmor_file_alloc_security, + .file_free_security = apparmor_file_free_security, + .file_mmap = apparmor_file_mmap, + .file_mprotect = apparmor_file_mprotect, + .file_lock = apparmor_file_lock, + + .getprocattr = apparmor_getprocattr, + .setprocattr = apparmor_setprocattr, + + .cred_alloc_blank = apparmor_cred_alloc_blank, + .cred_free = apparmor_cred_free, + .cred_prepare = apparmor_cred_prepare, + .cred_transfer = apparmor_cred_transfer, + + .bprm_set_creds = apparmor_bprm_set_creds, + .bprm_committing_creds = apparmor_bprm_committing_creds, + .bprm_committed_creds = apparmor_bprm_committed_creds, + .bprm_secureexec = apparmor_bprm_secureexec, + + .task_setrlimit = apparmor_task_setrlimit, +}; + +/* + * AppArmor sysfs module parameters + */ + +static int param_set_aabool(const char *val, struct kernel_param *kp); +static int param_get_aabool(char *buffer, struct kernel_param *kp); +#define param_check_aabool(name, p) __param_check(name, p, int) + +static int param_set_aauint(const char *val, struct kernel_param *kp); +static int param_get_aauint(char *buffer, struct kernel_param *kp); +#define param_check_aauint(name, p) __param_check(name, p, int) + +static int param_set_aalockpolicy(const char *val, struct kernel_param *kp); +static int param_get_aalockpolicy(char *buffer, struct kernel_param *kp); +#define param_check_aalockpolicy(name, p) __param_check(name, p, int) + +static int param_set_audit(const char *val, struct kernel_param *kp); +static int param_get_audit(char *buffer, struct kernel_param *kp); +#define param_check_audit(name, p) __param_check(name, p, int) + +static int param_set_mode(const char *val, struct kernel_param *kp); +static int param_get_mode(char *buffer, struct kernel_param *kp); +#define param_check_mode(name, p) __param_check(name, p, int) + +/* Flag values, also controllable via /sys/module/apparmor/parameters + * We define special types as we want to do additional mediation. + */ + +/* AppArmor global enforcement switch - complain, enforce, kill */ +enum profile_mode aa_g_profile_mode = APPARMOR_ENFORCE; +module_param_call(mode, param_set_mode, param_get_mode, + &aa_g_profile_mode, S_IRUSR | S_IWUSR); + +/* Debug mode */ +int aa_g_debug; +module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); + +/* Audit mode */ +enum audit_mode aa_g_audit; +module_param_call(audit, param_set_audit, param_get_audit, + &aa_g_audit, S_IRUSR | S_IWUSR); + +/* Determines if audit header is included in audited messages. This + * provides more context if the audit daemon is not running + */ +int aa_g_audit_header = 1; +module_param_named(audit_header, aa_g_audit_header, aabool, + S_IRUSR | S_IWUSR); + +/* lock out loading/removal of policy + * TODO: add in at boot loading of policy, which is the only way to + * load policy, if lock_policy is set + */ +int aa_g_lock_policy; +module_param_named(lock_policy, aa_g_lock_policy, aalockpolicy, + S_IRUSR | S_IWUSR); + +/* Syscall logging mode */ +int aa_g_logsyscall; +module_param_named(logsyscall, aa_g_logsyscall, aabool, S_IRUSR | S_IWUSR); + +/* Maximum pathname length before accesses will start getting rejected */ +unsigned int aa_g_path_max = 2 * PATH_MAX; +module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR); + +/* Determines how paranoid loading of policy is and how much verification + * on the loaded policy is done. + */ +int aa_g_paranoid_load = 1; +module_param_named(paranoid_load, aa_g_paranoid_load, aabool, + S_IRUSR | S_IWUSR); + +/* Boot time disable flag */ +static unsigned int apparmor_enabled = CONFIG_SECURITY_APPARMOR_BOOTPARAM_VALUE; +module_param_named(enabled, apparmor_enabled, aabool, S_IRUSR); + +static int __init apparmor_enabled_setup(char *str) +{ + unsigned long enabled; + int error = strict_strtoul(str, 0, &enabled); + if (!error) + apparmor_enabled = enabled ? 1 : 0; + return 1; +} + +__setup("apparmor=", apparmor_enabled_setup); + +/* set global flag turning off the ability to load policy */ +static int param_set_aalockpolicy(const char *val, struct kernel_param *kp) +{ + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + if (aa_g_lock_policy) + return -EACCES; + return param_set_bool(val, kp); +} + +static int param_get_aalockpolicy(char *buffer, struct kernel_param *kp) +{ + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + return param_get_bool(buffer, kp); +} + +static int param_set_aabool(const char *val, struct kernel_param *kp) +{ + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + return param_set_bool(val, kp); +} + +static int param_get_aabool(char *buffer, struct kernel_param *kp) +{ + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + return param_get_bool(buffer, kp); +} + +static int param_set_aauint(const char *val, struct kernel_param *kp) +{ + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + return param_set_uint(val, kp); +} + +static int param_get_aauint(char *buffer, struct kernel_param *kp) +{ + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + return param_get_uint(buffer, kp); +} + +static int param_get_audit(char *buffer, struct kernel_param *kp) +{ + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + + if (!apparmor_enabled) + return -EINVAL; + + return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]); +} + +static int param_set_audit(const char *val, struct kernel_param *kp) +{ + int i; + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + + if (!apparmor_enabled) + return -EINVAL; + + if (!val) + return -EINVAL; + + for (i = 0; i < AUDIT_MAX_INDEX; i++) { + if (strcmp(val, audit_mode_names[i]) == 0) { + aa_g_audit = i; + return 0; + } + } + + return -EINVAL; +} + +static int param_get_mode(char *buffer, struct kernel_param *kp) +{ + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + + if (!apparmor_enabled) + return -EINVAL; + + return sprintf(buffer, "%s", profile_mode_names[aa_g_profile_mode]); +} + +static int param_set_mode(const char *val, struct kernel_param *kp) +{ + int i; + if (!capable(CAP_MAC_ADMIN)) + return -EPERM; + + if (!apparmor_enabled) + return -EINVAL; + + if (!val) + return -EINVAL; + + for (i = 0; i < APPARMOR_NAMES_MAX_INDEX; i++) { + if (strcmp(val, profile_mode_names[i]) == 0) { + aa_g_profile_mode = i; + return 0; + } + } + + return -EINVAL; +} + +/* + * AppArmor init functions + */ + +/** + * set_init_cxt - set a task context and profile on the first task. + * + * TODO: allow setting an alternate profile than unconfined + */ +static int __init set_init_cxt(void) +{ + struct cred *cred = (struct cred *)current->real_cred; + struct aa_task_cxt *cxt; + + cxt = aa_alloc_task_context(GFP_KERNEL); + if (!cxt) + return -ENOMEM; + + cxt->profile = aa_get_profile(root_ns->unconfined); + cred->security = cxt; + + return 0; +} + +static int __init apparmor_init(void) +{ + int error; + + if (!apparmor_enabled || !security_module_enable(&apparmor_ops)) { + aa_info_message("AppArmor disabled by boot time parameter"); + apparmor_enabled = 0; + return 0; + } + + error = aa_alloc_root_ns(); + if (error) { + AA_ERROR("Unable to allocate default profile namespace\n"); + goto alloc_out; + } + + error = set_init_cxt(); + if (error) { + AA_ERROR("Failed to set context on init task\n"); + goto register_security_out; + } + + error = register_security(&apparmor_ops); + if (error) { + AA_ERROR("Unable to register AppArmor\n"); + goto register_security_out; + } + + /* Report that AppArmor successfully initialized */ + apparmor_initialized = 1; + if (aa_g_profile_mode == APPARMOR_COMPLAIN) + aa_info_message("AppArmor initialized: complain mode enabled"); + else if (aa_g_profile_mode == APPARMOR_KILL) + aa_info_message("AppArmor initialized: kill mode enabled"); + else + aa_info_message("AppArmor initialized"); + + return error; + +register_security_out: + aa_free_root_ns(); + +alloc_out: + aa_destroy_aafs(); + + apparmor_enabled = 0; + return error; + +} + +security_initcall(apparmor_init); -- cgit v1.2.3-70-g09d2 From 0ed3b28ab8bf460a3a026f3f1782bf4c53840184 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 29 Jul 2010 14:48:05 -0700 Subject: AppArmor: mediation of non file objects ipc: AppArmor ipc is currently limited to mediation done by file mediation and basic ptrace tests. Improved mediation is a wip. rlimits: AppArmor provides basic abilities to set and control rlimits at a per profile level. Only resources specified in a profile are controled or set. AppArmor rules set the hard limit to a value <= to the current hard limit (ie. they can not currently raise hard limits), and if necessary will lower the soft limit to the new hard limit value. AppArmor does not track resource limits to reset them when a profile is left so that children processes inherit the limits set by the parent even if they are not confined by the same profile. Capabilities: AppArmor provides a per profile mask of capabilities, that will further restrict. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/capability.c | 141 +++++++++++++++++++++++++++++++++ security/apparmor/include/capability.h | 45 +++++++++++ security/apparmor/include/ipc.h | 28 +++++++ security/apparmor/include/resource.h | 46 +++++++++++ security/apparmor/ipc.c | 114 ++++++++++++++++++++++++++ security/apparmor/resource.c | 134 +++++++++++++++++++++++++++++++ 6 files changed, 508 insertions(+) create mode 100644 security/apparmor/capability.c create mode 100644 security/apparmor/include/capability.h create mode 100644 security/apparmor/include/ipc.h create mode 100644 security/apparmor/include/resource.h create mode 100644 security/apparmor/ipc.c create mode 100644 security/apparmor/resource.c diff --git a/security/apparmor/capability.c b/security/apparmor/capability.c new file mode 100644 index 00000000000..9982c48def4 --- /dev/null +++ b/security/apparmor/capability.c @@ -0,0 +1,141 @@ +/* + * AppArmor security module + * + * This file contains AppArmor capability mediation functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/capability.h" +#include "include/context.h" +#include "include/policy.h" +#include "include/audit.h" + +/* + * Table of capability names: we generate it from capabilities.h. + */ +#include "capability_names.h" + +struct audit_cache { + struct aa_profile *profile; + kernel_cap_t caps; +}; + +static DEFINE_PER_CPU(struct audit_cache, audit_cache); + +/** + * audit_cb - call back for capability components of audit struct + * @ab - audit buffer (NOT NULL) + * @va - audit struct to audit data from (NOT NULL) + */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + audit_log_format(ab, " capname="); + audit_log_untrustedstring(ab, capability_names[sa->u.cap]); +} + +/** + * audit_caps - audit a capability + * @profile: profile confining task (NOT NULL) + * @task: task capability test was performed against (NOT NULL) + * @cap: capability tested + * @error: error code returned by test + * + * Do auditing of capability and handle, audit/complain/kill modes switching + * and duplicate message elimination. + * + * Returns: 0 or sa->error on success, error code on failure + */ +static int audit_caps(struct aa_profile *profile, struct task_struct *task, + int cap, int error) +{ + struct audit_cache *ent; + int type = AUDIT_APPARMOR_AUTO; + struct common_audit_data sa; + COMMON_AUDIT_DATA_INIT(&sa, CAP); + sa.tsk = task; + sa.u.cap = cap; + sa.aad.op = OP_CAPABLE; + sa.aad.error = error; + + if (likely(!error)) { + /* test if auditing is being forced */ + if (likely((AUDIT_MODE(profile) != AUDIT_ALL) && + !cap_raised(profile->caps.audit, cap))) + return 0; + type = AUDIT_APPARMOR_AUDIT; + } else if (KILL_MODE(profile) || + cap_raised(profile->caps.kill, cap)) { + type = AUDIT_APPARMOR_KILL; + } else if (cap_raised(profile->caps.quiet, cap) && + AUDIT_MODE(profile) != AUDIT_NOQUIET && + AUDIT_MODE(profile) != AUDIT_ALL) { + /* quiet auditing */ + return error; + } + + /* Do simple duplicate message elimination */ + ent = &get_cpu_var(audit_cache); + if (profile == ent->profile && cap_raised(ent->caps, cap)) { + put_cpu_var(audit_cache); + if (COMPLAIN_MODE(profile)) + return complain_error(error); + return error; + } else { + aa_put_profile(ent->profile); + ent->profile = aa_get_profile(profile); + cap_raise(ent->caps, cap); + } + put_cpu_var(audit_cache); + + return aa_audit(type, profile, GFP_ATOMIC, &sa, audit_cb); +} + +/** + * profile_capable - test if profile allows use of capability @cap + * @profile: profile being enforced (NOT NULL, NOT unconfined) + * @cap: capability to test if allowed + * + * Returns: 0 if allowed else -EPERM + */ +static int profile_capable(struct aa_profile *profile, int cap) +{ + return cap_raised(profile->caps.allow, cap) ? 0 : -EPERM; +} + +/** + * aa_capable - test permission to use capability + * @task: task doing capability test against (NOT NULL) + * @profile: profile confining @task (NOT NULL) + * @cap: capability to be tested + * @audit: whether an audit record should be generated + * + * Look up capability in profile capability set. + * + * Returns: 0 on success, or else an error code. + */ +int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, + int audit) +{ + int error = profile_capable(profile, cap); + + if (!audit) { + if (COMPLAIN_MODE(profile)) + return complain_error(error); + return error; + } + + return audit_caps(profile, task, cap, error); +} diff --git a/security/apparmor/include/capability.h b/security/apparmor/include/capability.h new file mode 100644 index 00000000000..c24d2959ea0 --- /dev/null +++ b/security/apparmor/include/capability.h @@ -0,0 +1,45 @@ +/* + * AppArmor security module + * + * This file contains AppArmor capability mediation definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_CAPABILITY_H +#define __AA_CAPABILITY_H + +#include + +struct aa_profile; + +/* aa_caps - confinement data for capabilities + * @allowed: capabilities mask + * @audit: caps that are to be audited + * @quiet: caps that should not be audited + * @kill: caps that when requested will result in the task being killed + * @extended: caps that are subject finer grained mediation + */ +struct aa_caps { + kernel_cap_t allow; + kernel_cap_t audit; + kernel_cap_t quiet; + kernel_cap_t kill; + kernel_cap_t extended; +}; + +int aa_capable(struct task_struct *task, struct aa_profile *profile, int cap, + int audit); + +static inline void aa_free_cap_rules(struct aa_caps *caps) +{ + /* NOP */ +} + +#endif /* __AA_CAPBILITY_H */ diff --git a/security/apparmor/include/ipc.h b/security/apparmor/include/ipc.h new file mode 100644 index 00000000000..aeda0fbc8b2 --- /dev/null +++ b/security/apparmor/include/ipc.h @@ -0,0 +1,28 @@ +/* + * AppArmor security module + * + * This file contains AppArmor ipc mediation function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_IPC_H +#define __AA_IPC_H + +#include + +struct aa_profile; + +int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, + struct aa_profile *tracee, unsigned int mode); + +int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, + unsigned int mode); + +#endif /* __AA_IPC_H */ diff --git a/security/apparmor/include/resource.h b/security/apparmor/include/resource.h new file mode 100644 index 00000000000..3c88be94649 --- /dev/null +++ b/security/apparmor/include/resource.h @@ -0,0 +1,46 @@ +/* + * AppArmor security module + * + * This file contains AppArmor resource limits function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_RESOURCE_H +#define __AA_RESOURCE_H + +#include +#include + +struct aa_profile; + +/* struct aa_rlimit - rlimit settings for the profile + * @mask: which hard limits to set + * @limits: rlimit values that override task limits + * + * AppArmor rlimits are used to set confined task rlimits. Only the + * limits specified in @mask will be controlled by apparmor. + */ +struct aa_rlimit { + unsigned int mask; + struct rlimit limits[RLIM_NLIMITS]; +}; + +int aa_map_resource(int resource); +int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, + struct rlimit *new_rlim); + +void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new); + +static inline void aa_free_rlimit_rules(struct aa_rlimit *rlims) +{ + /* NOP */ +} + +#endif /* __AA_RESOURCE_H */ diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c new file mode 100644 index 00000000000..9013a78a166 --- /dev/null +++ b/security/apparmor/ipc.c @@ -0,0 +1,114 @@ +/* + * AppArmor security module + * + * This file contains AppArmor ipc mediation + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include +#include + +#include "include/audit.h" +#include "include/capability.h" +#include "include/context.h" +#include "include/policy.h" + +/* call back to audit ptrace fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + audit_log_format(ab, " target="); + audit_log_untrustedstring(ab, sa->aad.target); +} + +/** + * aa_audit_ptrace - do auditing for ptrace + * @profile: profile being enforced (NOT NULL) + * @target: profile being traced (NOT NULL) + * @error: error condition + * + * Returns: %0 or error code + */ +static int aa_audit_ptrace(struct aa_profile *profile, + struct aa_profile *target, int error) +{ + struct common_audit_data sa; + COMMON_AUDIT_DATA_INIT(&sa, NONE); + sa.aad.op = OP_PTRACE; + sa.aad.target = target; + sa.aad.error = error; + + return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_ATOMIC, &sa, + audit_cb); +} + +/** + * aa_may_ptrace - test if tracer task can trace the tracee + * @tracer_task: task who will do the tracing (NOT NULL) + * @tracer: profile of the task doing the tracing (NOT NULL) + * @tracee: task to be traced + * @mode: whether PTRACE_MODE_READ || PTRACE_MODE_ATTACH + * + * Returns: %0 else error code if permission denied or error + */ +int aa_may_ptrace(struct task_struct *tracer_task, struct aa_profile *tracer, + struct aa_profile *tracee, unsigned int mode) +{ + /* TODO: currently only based on capability, not extended ptrace + * rules, + * Test mode for PTRACE_MODE_READ || PTRACE_MODE_ATTACH + */ + + if (unconfined(tracer) || tracer == tracee) + return 0; + /* log this capability request */ + return aa_capable(tracer_task, tracer, CAP_SYS_PTRACE, 1); +} + +/** + * aa_ptrace - do ptrace permission check and auditing + * @tracer: task doing the tracing (NOT NULL) + * @tracee: task being traced (NOT NULL) + * @mode: ptrace mode either PTRACE_MODE_READ || PTRACE_MODE_ATTACH + * + * Returns: %0 else error code if permission denied or error + */ +int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, + unsigned int mode) +{ + /* + * tracer can ptrace tracee when + * - tracer is unconfined || + * - tracer is in complain mode + * - tracer has rules allowing it to trace tracee currently this is: + * - confined by the same profile || + * - tracer profile has CAP_SYS_PTRACE + */ + + struct aa_profile *tracer_p; + /* cred released below */ + const struct cred *cred = get_task_cred(tracer); + int error = 0; + tracer_p = aa_cred_profile(cred); + + if (!unconfined(tracer_p)) { + /* lcred released below */ + struct cred *lcred = get_task_cred(tracee); + struct aa_profile *tracee_p = aa_cred_profile(lcred); + + error = aa_may_ptrace(tracer, tracer_p, tracee_p, mode); + error = aa_audit_ptrace(tracer_p, tracee_p, error); + + put_cred(lcred); + } + put_cred(cred); + + return error; +} diff --git a/security/apparmor/resource.c b/security/apparmor/resource.c new file mode 100644 index 00000000000..4a368f1fd36 --- /dev/null +++ b/security/apparmor/resource.c @@ -0,0 +1,134 @@ +/* + * AppArmor security module + * + * This file contains AppArmor resource mediation and attachment + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#include + +#include "include/audit.h" +#include "include/resource.h" +#include "include/policy.h" + +/* + * Table of rlimit names: we generate it from resource.h. + */ +#include "rlim_names.h" + +/* audit callback for resource specific fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + + audit_log_format(ab, " rlimit=%s value=%lu", + rlim_names[sa->aad.rlim.rlim], sa->aad.rlim.max); +} + +/** + * audit_resource - audit setting resource limit + * @profile: profile being enforced (NOT NULL) + * @resoure: rlimit being auditing + * @value: value being set + * @error: error value + * + * Returns: 0 or sa->error else other error code on failure + */ +static int audit_resource(struct aa_profile *profile, unsigned int resource, + unsigned long value, int error) +{ + struct common_audit_data sa; + + COMMON_AUDIT_DATA_INIT(&sa, NONE); + sa.aad.op = OP_SETRLIMIT, + sa.aad.rlim.rlim = resource; + sa.aad.rlim.max = value; + sa.aad.error = error; + return aa_audit(AUDIT_APPARMOR_AUTO, profile, GFP_KERNEL, &sa, + audit_cb); +} + +/** + * aa_map_resouce - map compiled policy resource to internal # + * @resource: flattened policy resource number + * + * Returns: resource # for the current architecture. + * + * rlimit resource can vary based on architecture, map the compiled policy + * resource # to the internal representation for the architecture. + */ +int aa_map_resource(int resource) +{ + return rlim_map[resource]; +} + +/** + * aa_task_setrlimit - test permission to set an rlimit + * @profile - profile confining the task (NOT NULL) + * @resource - the resource being set + * @new_rlim - the new resource limit (NOT NULL) + * + * Control raising the processes hard limit. + * + * Returns: 0 or error code if setting resource failed + */ +int aa_task_setrlimit(struct aa_profile *profile, unsigned int resource, + struct rlimit *new_rlim) +{ + int error = 0; + + if (profile->rlimits.mask & (1 << resource) && + new_rlim->rlim_max > profile->rlimits.limits[resource].rlim_max) + + error = audit_resource(profile, resource, new_rlim->rlim_max, + -EACCES); + + return error; +} + +/** + * __aa_transition_rlimits - apply new profile rlimits + * @old: old profile on task (NOT NULL) + * @new: new profile with rlimits to apply (NOT NULL) + */ +void __aa_transition_rlimits(struct aa_profile *old, struct aa_profile *new) +{ + unsigned int mask = 0; + struct rlimit *rlim, *initrlim; + int i; + + /* for any rlimits the profile controlled reset the soft limit + * to the less of the tasks hard limit and the init tasks soft limit + */ + if (old->rlimits.mask) { + for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) { + if (old->rlimits.mask & mask) { + rlim = current->signal->rlim + i; + initrlim = init_task.signal->rlim + i; + rlim->rlim_cur = min(rlim->rlim_max, + initrlim->rlim_cur); + } + } + } + + /* set any new hard limits as dictated by the new profile */ + if (!new->rlimits.mask) + return; + for (i = 0, mask = 1; i < RLIM_NLIMITS; i++, mask <<= 1) { + if (!(new->rlimits.mask & mask)) + continue; + + rlim = current->signal->rlim + i; + rlim->rlim_max = min(rlim->rlim_max, + new->rlimits.limits[i].rlim_max); + /* soft limit should not exceed hard limit */ + rlim->rlim_cur = min(rlim->rlim_cur, rlim->rlim_max); + } +} -- cgit v1.2.3-70-g09d2 From 736ec752d95e91e77cc0e8c97c057ab076ac2f51 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 29 Jul 2010 14:48:02 -0700 Subject: AppArmor: policy routines for loading and unpacking policy AppArmor policy is loaded in a platform independent flattened binary stream. Verify and unpack the data converting it to the internal format needed for enforcement. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/include/policy_unpack.h | 20 + security/apparmor/policy_unpack.c | 703 ++++++++++++++++++++++++++++++ 2 files changed, 723 insertions(+) create mode 100644 security/apparmor/include/policy_unpack.h create mode 100644 security/apparmor/policy_unpack.c diff --git a/security/apparmor/include/policy_unpack.h b/security/apparmor/include/policy_unpack.h new file mode 100644 index 00000000000..a2dcccac45a --- /dev/null +++ b/security/apparmor/include/policy_unpack.h @@ -0,0 +1,20 @@ +/* + * AppArmor security module + * + * This file contains AppArmor policy loading interface function definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __POLICY_INTERFACE_H +#define __POLICY_INTERFACE_H + +struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns); + +#endif /* __POLICY_INTERFACE_H */ diff --git a/security/apparmor/policy_unpack.c b/security/apparmor/policy_unpack.c new file mode 100644 index 00000000000..eb3700e9fd3 --- /dev/null +++ b/security/apparmor/policy_unpack.c @@ -0,0 +1,703 @@ +/* + * AppArmor security module + * + * This file contains AppArmor functions for unpacking policy loaded from + * userspace. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * AppArmor uses a serialized binary format for loading policy. + * To find policy format documentation look in Documentation/apparmor.txt + * All policy is validated before it is used. + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/audit.h" +#include "include/context.h" +#include "include/match.h" +#include "include/policy.h" +#include "include/policy_unpack.h" +#include "include/sid.h" + +/* + * The AppArmor interface treats data as a type byte followed by the + * actual data. The interface has the notion of a a named entry + * which has a name (AA_NAME typecode followed by name string) followed by + * the entries typecode and data. Named types allow for optional + * elements and extensions to be added and tested for without breaking + * backwards compatibility. + */ + +enum aa_code { + AA_U8, + AA_U16, + AA_U32, + AA_U64, + AA_NAME, /* same as string except it is items name */ + AA_STRING, + AA_BLOB, + AA_STRUCT, + AA_STRUCTEND, + AA_LIST, + AA_LISTEND, + AA_ARRAY, + AA_ARRAYEND, +}; + +/* + * aa_ext is the read of the buffer containing the serialized profile. The + * data is copied into a kernel buffer in apparmorfs and then handed off to + * the unpack routines. + */ +struct aa_ext { + void *start; + void *end; + void *pos; /* pointer to current position in the buffer */ + u32 version; +}; + +/* audit callback for unpack fields */ +static void audit_cb(struct audit_buffer *ab, void *va) +{ + struct common_audit_data *sa = va; + if (sa->aad.iface.target) { + struct aa_profile *name = sa->aad.iface.target; + audit_log_format(ab, " name="); + audit_log_untrustedstring(ab, name->base.hname); + } + if (sa->aad.iface.pos) + audit_log_format(ab, " offset=%ld", sa->aad.iface.pos); +} + +/** + * audit_iface - do audit message for policy unpacking/load/replace/remove + * @new: profile if it has been allocated (MAYBE NULL) + * @name: name of the profile being manipulated (MAYBE NULL) + * @info: any extra info about the failure (MAYBE NULL) + * @e: buffer position info (NOT NULL) + * @error: error code + * + * Returns: %0 or error + */ +static int audit_iface(struct aa_profile *new, const char *name, + const char *info, struct aa_ext *e, int error) +{ + struct aa_profile *profile = __aa_current_profile(); + struct common_audit_data sa; + COMMON_AUDIT_DATA_INIT(&sa, NONE); + sa.aad.iface.pos = e->pos - e->start; + sa.aad.iface.target = new; + sa.aad.name = name; + sa.aad.info = info; + sa.aad.error = error; + + return aa_audit(AUDIT_APPARMOR_STATUS, profile, GFP_KERNEL, &sa, + audit_cb); +} + +/* test if read will be in packed data bounds */ +static bool inbounds(struct aa_ext *e, size_t size) +{ + return (size <= e->end - e->pos); +} + +/** + * aa_u16_chunck - test and do bounds checking for a u16 size based chunk + * @e: serialized data read head (NOT NULL) + * @chunk: start address for chunk of data (NOT NULL) + * + * Returns: the size of chunk found with the read head at the end of the chunk. + */ +static size_t unpack_u16_chunk(struct aa_ext *e, char **chunk) +{ + size_t size = 0; + + if (!inbounds(e, sizeof(u16))) + return 0; + size = le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + if (!inbounds(e, size)) + return 0; + *chunk = e->pos; + e->pos += size; + return size; +} + +/* unpack control byte */ +static bool unpack_X(struct aa_ext *e, enum aa_code code) +{ + if (!inbounds(e, 1)) + return 0; + if (*(u8 *) e->pos != code) + return 0; + e->pos++; + return 1; +} + +/** + * unpack_nameX - check is the next element is of type X with a name of @name + * @e: serialized data extent information (NOT NULL) + * @code: type code + * @name: name to match to the serialized element. (MAYBE NULL) + * + * check that the next serialized data element is of type X and has a tag + * name @name. If @name is specified then there must be a matching + * name element in the stream. If @name is NULL any name element will be + * skipped and only the typecode will be tested. + * + * Returns 1 on success (both type code and name tests match) and the read + * head is advanced past the headers + * + * Returns: 0 if either match fails, the read head does not move + */ +static bool unpack_nameX(struct aa_ext *e, enum aa_code code, const char *name) +{ + /* + * May need to reset pos if name or type doesn't match + */ + void *pos = e->pos; + /* + * Check for presence of a tagname, and if present name size + * AA_NAME tag value is a u16. + */ + if (unpack_X(e, AA_NAME)) { + char *tag = NULL; + size_t size = unpack_u16_chunk(e, &tag); + /* if a name is specified it must match. otherwise skip tag */ + if (name && (!size || strcmp(name, tag))) + goto fail; + } else if (name) { + /* if a name is specified and there is no name tag fail */ + goto fail; + } + + /* now check if type code matches */ + if (unpack_X(e, code)) + return 1; + +fail: + e->pos = pos; + return 0; +} + +static bool unpack_u32(struct aa_ext *e, u32 *data, const char *name) +{ + if (unpack_nameX(e, AA_U32, name)) { + if (!inbounds(e, sizeof(u32))) + return 0; + if (data) + *data = le32_to_cpu(get_unaligned((u32 *) e->pos)); + e->pos += sizeof(u32); + return 1; + } + return 0; +} + +static bool unpack_u64(struct aa_ext *e, u64 *data, const char *name) +{ + if (unpack_nameX(e, AA_U64, name)) { + if (!inbounds(e, sizeof(u64))) + return 0; + if (data) + *data = le64_to_cpu(get_unaligned((u64 *) e->pos)); + e->pos += sizeof(u64); + return 1; + } + return 0; +} + +static size_t unpack_array(struct aa_ext *e, const char *name) +{ + if (unpack_nameX(e, AA_ARRAY, name)) { + int size; + if (!inbounds(e, sizeof(u16))) + return 0; + size = (int)le16_to_cpu(get_unaligned((u16 *) e->pos)); + e->pos += sizeof(u16); + return size; + } + return 0; +} + +static size_t unpack_blob(struct aa_ext *e, char **blob, const char *name) +{ + if (unpack_nameX(e, AA_BLOB, name)) { + u32 size; + if (!inbounds(e, sizeof(u32))) + return 0; + size = le32_to_cpu(get_unaligned((u32 *) e->pos)); + e->pos += sizeof(u32); + if (inbounds(e, (size_t) size)) { + *blob = e->pos; + e->pos += size; + return size; + } + } + return 0; +} + +static int unpack_str(struct aa_ext *e, const char **string, const char *name) +{ + char *src_str; + size_t size = 0; + void *pos = e->pos; + *string = NULL; + if (unpack_nameX(e, AA_STRING, name)) { + size = unpack_u16_chunk(e, &src_str); + if (size) { + /* strings are null terminated, length is size - 1 */ + if (src_str[size - 1] != 0) + goto fail; + *string = src_str; + } + } + return size; + +fail: + e->pos = pos; + return 0; +} + +static int unpack_strdup(struct aa_ext *e, char **string, const char *name) +{ + const char *tmp; + void *pos = e->pos; + int res = unpack_str(e, &tmp, name); + *string = NULL; + + if (!res) + return 0; + + *string = kmemdup(tmp, res, GFP_KERNEL); + if (!*string) { + e->pos = pos; + return 0; + } + + return res; +} + +/** + * verify_accept - verify the accept tables of a dfa + * @dfa: dfa to verify accept tables of (NOT NULL) + * @flags: flags governing dfa + * + * Returns: 1 if valid accept tables else 0 if error + */ +static bool verify_accept(struct aa_dfa *dfa, int flags) +{ + int i; + + /* verify accept permissions */ + for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { + int mode = ACCEPT_TABLE(dfa)[i]; + + if (mode & ~DFA_VALID_PERM_MASK) + return 0; + + if (ACCEPT_TABLE2(dfa)[i] & ~DFA_VALID_PERM2_MASK) + return 0; + } + return 1; +} + +/** + * unpack_dfa - unpack a file rule dfa + * @e: serialized data extent information (NOT NULL) + * + * returns dfa or ERR_PTR or NULL if no dfa + */ +static struct aa_dfa *unpack_dfa(struct aa_ext *e) +{ + char *blob = NULL; + size_t size; + struct aa_dfa *dfa = NULL; + + size = unpack_blob(e, &blob, "aadfa"); + if (size) { + /* + * The dfa is aligned with in the blob to 8 bytes + * from the beginning of the stream. + */ + size_t sz = blob - (char *)e->start; + size_t pad = ALIGN(sz, 8) - sz; + int flags = TO_ACCEPT1_FLAG(YYTD_DATA32) | + TO_ACCEPT2_FLAG(YYTD_DATA32); + + + if (aa_g_paranoid_load) + flags |= DFA_FLAG_VERIFY_STATES; + + dfa = aa_dfa_unpack(blob + pad, size - pad, flags); + + if (IS_ERR(dfa)) + return dfa; + + if (!verify_accept(dfa, flags)) + goto fail; + } + + return dfa; + +fail: + aa_put_dfa(dfa); + return ERR_PTR(-EPROTO); +} + +/** + * unpack_trans_table - unpack a profile transition table + * @e: serialized data extent information (NOT NULL) + * @profile: profile to add the accept table to (NOT NULL) + * + * Returns: 1 if table succesfully unpacked + */ +static bool unpack_trans_table(struct aa_ext *e, struct aa_profile *profile) +{ + void *pos = e->pos; + + /* exec table is optional */ + if (unpack_nameX(e, AA_STRUCT, "xtable")) { + int i, size; + + size = unpack_array(e, NULL); + /* currently 4 exec bits and entries 0-3 are reserved iupcx */ + if (size > 16 - 4) + goto fail; + profile->file.trans.table = kzalloc(sizeof(char *) * size, + GFP_KERNEL); + if (!profile->file.trans.table) + goto fail; + + profile->file.trans.size = size; + for (i = 0; i < size; i++) { + char *str; + int c, j, size = unpack_strdup(e, &str, NULL); + /* unpack_strdup verifies that the last character is + * null termination byte. + */ + if (!size) + goto fail; + profile->file.trans.table[i] = str; + /* verify that name doesn't start with space */ + if (isspace(*str)) + goto fail; + + /* count internal # of internal \0 */ + for (c = j = 0; j < size - 2; j++) { + if (!str[j]) + c++; + } + if (*str == ':') { + /* beginning with : requires an embedded \0, + * verify that exactly 1 internal \0 exists + * trailing \0 already verified by unpack_strdup + */ + if (c != 1) + goto fail; + /* first character after : must be valid */ + if (!str[1]) + goto fail; + } else if (c) + /* fail - all other cases with embedded \0 */ + goto fail; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + goto fail; + } + return 1; + +fail: + aa_free_domain_entries(&profile->file.trans); + e->pos = pos; + return 0; +} + +static bool unpack_rlimits(struct aa_ext *e, struct aa_profile *profile) +{ + void *pos = e->pos; + + /* rlimits are optional */ + if (unpack_nameX(e, AA_STRUCT, "rlimits")) { + int i, size; + u32 tmp = 0; + if (!unpack_u32(e, &tmp, NULL)) + goto fail; + profile->rlimits.mask = tmp; + + size = unpack_array(e, NULL); + if (size > RLIM_NLIMITS) + goto fail; + for (i = 0; i < size; i++) { + u64 tmp = 0; + int a = aa_map_resource(i); + if (!unpack_u64(e, &tmp, NULL)) + goto fail; + profile->rlimits.limits[a].rlim_max = tmp; + } + if (!unpack_nameX(e, AA_ARRAYEND, NULL)) + goto fail; + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + goto fail; + } + return 1; + +fail: + e->pos = pos; + return 0; +} + +/** + * unpack_profile - unpack a serialized profile + * @e: serialized data extent information (NOT NULL) + * + * NOTE: unpack profile sets audit struct if there is a failure + */ +static struct aa_profile *unpack_profile(struct aa_ext *e) +{ + struct aa_profile *profile = NULL; + const char *name = NULL; + int error = -EPROTO; + kernel_cap_t tmpcap; + u32 tmp; + + /* check that we have the right struct being passed */ + if (!unpack_nameX(e, AA_STRUCT, "profile")) + goto fail; + if (!unpack_str(e, &name, NULL)) + goto fail; + + profile = aa_alloc_profile(name); + if (!profile) + return ERR_PTR(-ENOMEM); + + /* profile renaming is optional */ + (void) unpack_str(e, &profile->rename, "rename"); + + /* xmatch is optional and may be NULL */ + profile->xmatch = unpack_dfa(e); + if (IS_ERR(profile->xmatch)) { + error = PTR_ERR(profile->xmatch); + profile->xmatch = NULL; + goto fail; + } + /* xmatch_len is not optional if xmatch is set */ + if (profile->xmatch) { + if (!unpack_u32(e, &tmp, NULL)) + goto fail; + profile->xmatch_len = tmp; + } + + /* per profile debug flags (complain, audit) */ + if (!unpack_nameX(e, AA_STRUCT, "flags")) + goto fail; + if (!unpack_u32(e, &tmp, NULL)) + goto fail; + if (tmp) + profile->flags |= PFLAG_HAT; + if (!unpack_u32(e, &tmp, NULL)) + goto fail; + if (tmp) + profile->mode = APPARMOR_COMPLAIN; + if (!unpack_u32(e, &tmp, NULL)) + goto fail; + if (tmp) + profile->audit = AUDIT_ALL; + + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + goto fail; + + /* path_flags is optional */ + if (unpack_u32(e, &profile->path_flags, "path_flags")) + profile->path_flags |= profile->flags & PFLAG_MEDIATE_DELETED; + else + /* set a default value if path_flags field is not present */ + profile->path_flags = PFLAG_MEDIATE_DELETED; + + if (!unpack_u32(e, &(profile->caps.allow.cap[0]), NULL)) + goto fail; + if (!unpack_u32(e, &(profile->caps.audit.cap[0]), NULL)) + goto fail; + if (!unpack_u32(e, &(profile->caps.quiet.cap[0]), NULL)) + goto fail; + if (!unpack_u32(e, &tmpcap.cap[0], NULL)) + goto fail; + + if (unpack_nameX(e, AA_STRUCT, "caps64")) { + /* optional upper half of 64 bit caps */ + if (!unpack_u32(e, &(profile->caps.allow.cap[1]), NULL)) + goto fail; + if (!unpack_u32(e, &(profile->caps.audit.cap[1]), NULL)) + goto fail; + if (!unpack_u32(e, &(profile->caps.quiet.cap[1]), NULL)) + goto fail; + if (!unpack_u32(e, &(tmpcap.cap[1]), NULL)) + goto fail; + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + goto fail; + } + + if (unpack_nameX(e, AA_STRUCT, "capsx")) { + /* optional extended caps mediation mask */ + if (!unpack_u32(e, &(profile->caps.extended.cap[0]), NULL)) + goto fail; + if (!unpack_u32(e, &(profile->caps.extended.cap[1]), NULL)) + goto fail; + } + + if (!unpack_rlimits(e, profile)) + goto fail; + + /* get file rules */ + profile->file.dfa = unpack_dfa(e); + if (IS_ERR(profile->file.dfa)) { + error = PTR_ERR(profile->file.dfa); + profile->file.dfa = NULL; + goto fail; + } + + if (!unpack_u32(e, &profile->file.start, "dfa_start")) + /* default start state */ + profile->file.start = DFA_START; + + if (!unpack_trans_table(e, profile)) + goto fail; + + if (!unpack_nameX(e, AA_STRUCTEND, NULL)) + goto fail; + + return profile; + +fail: + if (profile) + name = NULL; + else if (!name) + name = "unknown"; + audit_iface(profile, name, "failed to unpack profile", e, error); + aa_put_profile(profile); + + return ERR_PTR(error); +} + +/** + * verify_head - unpack serialized stream header + * @e: serialized data read head (NOT NULL) + * @ns: Returns - namespace if one is specified else NULL (NOT NULL) + * + * Returns: error or 0 if header is good + */ +static int verify_header(struct aa_ext *e, const char **ns) +{ + int error = -EPROTONOSUPPORT; + /* get the interface version */ + if (!unpack_u32(e, &e->version, "version")) { + audit_iface(NULL, NULL, "invalid profile format", e, error); + return error; + } + + /* check that the interface version is currently supported */ + if (e->version != 5) { + audit_iface(NULL, NULL, "unsupported interface version", e, + error); + return error; + } + + /* read the namespace if present */ + if (!unpack_str(e, ns, "namespace")) + *ns = NULL; + + return 0; +} + +static bool verify_xindex(int xindex, int table_size) +{ + int index, xtype; + xtype = xindex & AA_X_TYPE_MASK; + index = xindex & AA_X_INDEX_MASK; + if (xtype == AA_X_TABLE && index > table_size) + return 0; + return 1; +} + +/* verify dfa xindexes are in range of transition tables */ +static bool verify_dfa_xindex(struct aa_dfa *dfa, int table_size) +{ + int i; + for (i = 0; i < dfa->tables[YYTD_ID_ACCEPT]->td_lolen; i++) { + if (!verify_xindex(dfa_user_xindex(dfa, i), table_size)) + return 0; + if (!verify_xindex(dfa_other_xindex(dfa, i), table_size)) + return 0; + } + return 1; +} + +/** + * verify_profile - Do post unpack analysis to verify profile consistency + * @profile: profile to verify (NOT NULL) + * + * Returns: 0 if passes verification else error + */ +static int verify_profile(struct aa_profile *profile) +{ + if (aa_g_paranoid_load) { + if (profile->file.dfa && + !verify_dfa_xindex(profile->file.dfa, + profile->file.trans.size)) { + audit_iface(profile, NULL, "Invalid named transition", + NULL, -EPROTO); + return -EPROTO; + } + } + + return 0; +} + +/** + * aa_unpack - unpack packed binary profile data loaded from user space + * @udata: user data copied to kmem (NOT NULL) + * @size: the size of the user data + * @ns: Returns namespace profile is in if specified else NULL (NOT NULL) + * + * Unpack user data and return refcounted allocated profile or ERR_PTR + * + * Returns: profile else error pointer if fails to unpack + */ +struct aa_profile *aa_unpack(void *udata, size_t size, const char **ns) +{ + struct aa_profile *profile = NULL; + int error; + struct aa_ext e = { + .start = udata, + .end = udata + size, + .pos = udata, + }; + + error = verify_header(&e, ns); + if (error) + return ERR_PTR(error); + + profile = unpack_profile(&e); + if (IS_ERR(profile)) + return profile; + + error = verify_profile(profile); + if (error) { + aa_put_profile(profile); + profile = ERR_PTR(error); + } + + /* return refcount */ + return profile; +} -- cgit v1.2.3-70-g09d2 From c88d4c7b049e87998ac0a9f455aa545cc895ef92 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 29 Jul 2010 14:48:00 -0700 Subject: AppArmor: core policy routines The basic routines and defines for AppArmor policy. AppArmor policy is defined by a few basic components. profiles - the basic unit of confinement contain all the information to enforce policy on a task Profiles tend to be named after an executable that they will attach to but this is not required. namespaces - a container for a set of profiles that will be used during attachment and transitions between profiles. sids - which provide a unique id for each profile Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/include/policy.h | 305 ++++++++++ security/apparmor/include/sid.h | 24 + security/apparmor/policy.c | 1184 ++++++++++++++++++++++++++++++++++++ security/apparmor/sid.c | 55 ++ 4 files changed, 1568 insertions(+) create mode 100644 security/apparmor/include/policy.h create mode 100644 security/apparmor/include/sid.h create mode 100644 security/apparmor/policy.c create mode 100644 security/apparmor/sid.c diff --git a/security/apparmor/include/policy.h b/security/apparmor/include/policy.h new file mode 100644 index 00000000000..aeda5cf5690 --- /dev/null +++ b/security/apparmor/include/policy.h @@ -0,0 +1,305 @@ +/* + * AppArmor security module + * + * This file contains AppArmor policy definitions. + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_POLICY_H +#define __AA_POLICY_H + +#include +#include +#include +#include +#include +#include + +#include "apparmor.h" +#include "audit.h" +#include "capability.h" +#include "domain.h" +#include "file.h" +#include "resource.h" + +extern const char *profile_mode_names[]; +#define APPARMOR_NAMES_MAX_INDEX 3 + +#define COMPLAIN_MODE(_profile) \ + ((aa_g_profile_mode == APPARMOR_COMPLAIN) || \ + ((_profile)->mode == APPARMOR_COMPLAIN)) + +#define KILL_MODE(_profile) \ + ((aa_g_profile_mode == APPARMOR_KILL) || \ + ((_profile)->mode == APPARMOR_KILL)) + +#define PROFILE_IS_HAT(_profile) ((_profile)->flags & PFLAG_HAT) + +/* + * FIXME: currently need a clean way to replace and remove profiles as a + * set. It should be done at the namespace level. + * Either, with a set of profiles loaded at the namespace level or via + * a mark and remove marked interface. + */ +enum profile_mode { + APPARMOR_ENFORCE, /* enforce access rules */ + APPARMOR_COMPLAIN, /* allow and log access violations */ + APPARMOR_KILL, /* kill task on access violation */ +}; + +enum profile_flags { + PFLAG_HAT = 1, /* profile is a hat */ + PFLAG_UNCONFINED = 2, /* profile is an unconfined profile */ + PFLAG_NULL = 4, /* profile is null learning profile */ + PFLAG_IX_ON_NAME_ERROR = 8, /* fallback to ix on name lookup fail */ + PFLAG_IMMUTABLE = 0x10, /* don't allow changes/replacement */ + PFLAG_USER_DEFINED = 0x20, /* user based profile - lower privs */ + PFLAG_NO_LIST_REF = 0x40, /* list doesn't keep profile ref */ + PFLAG_OLD_NULL_TRANS = 0x100, /* use // as the null transition */ + + /* These flags must correspond with PATH_flags */ + PFLAG_MEDIATE_DELETED = 0x10000, /* mediate instead delegate deleted */ +}; + +struct aa_profile; + +/* struct aa_policy - common part of both namespaces and profiles + * @name: name of the object + * @hname - The hierarchical name + * @count: reference count of the obj + * @list: list policy object is on + * @profiles: head of the profiles list contained in the object + */ +struct aa_policy { + char *name; + char *hname; + struct kref count; + struct list_head list; + struct list_head profiles; +}; + +/* struct aa_ns_acct - accounting of profiles in namespace + * @max_size: maximum space allowed for all profiles in namespace + * @max_count: maximum number of profiles that can be in this namespace + * @size: current size of profiles + * @count: current count of profiles (includes null profiles) + */ +struct aa_ns_acct { + int max_size; + int max_count; + int size; + int count; +}; + +/* struct aa_namespace - namespace for a set of profiles + * @base: common policy + * @parent: parent of namespace + * @lock: lock for modifying the object + * @acct: accounting for the namespace + * @unconfined: special unconfined profile for the namespace + * @sub_ns: list of namespaces under the current namespace. + * + * An aa_namespace defines the set profiles that are searched to determine + * which profile to attach to a task. Profiles can not be shared between + * aa_namespaces and profile names within a namespace are guaranteed to be + * unique. When profiles in separate namespaces have the same name they + * are NOT considered to be equivalent. + * + * Namespaces are hierarchical and only namespaces and profiles below the + * current namespace are visible. + * + * Namespace names must be unique and can not contain the characters :/\0 + * + * FIXME TODO: add vserver support of namespaces (can it all be done in + * userspace?) + */ +struct aa_namespace { + struct aa_policy base; + struct aa_namespace *parent; + rwlock_t lock; + struct aa_ns_acct acct; + struct aa_profile *unconfined; + struct list_head sub_ns; +}; + +/* struct aa_profile - basic confinement data + * @base - base components of the profile (name, refcount, lists, lock ...) + * @parent: parent of profile + * @ns: namespace the profile is in + * @replacedby: is set to the profile that replaced this profile + * @rename: optional profile name that this profile renamed + * @xmatch: optional extended matching for unconfined executables names + * @xmatch_len: xmatch prefix len, used to determine xmatch priority + * @sid: the unique security id number of this profile + * @audit: the auditing mode of the profile + * @mode: the enforcement mode of the profile + * @flags: flags controlling profile behavior + * @path_flags: flags controlling path generation behavior + * @size: the memory consumed by this profiles rules + * @file: The set of rules governing basic file access and domain transitions + * @caps: capabilities for the profile + * @rlimits: rlimits for the profile + * + * The AppArmor profile contains the basic confinement data. Each profile + * has a name, and exists in a namespace. The @name and @exec_match are + * used to determine profile attachment against unconfined tasks. All other + * attachments are determined by profile X transition rules. + * + * The @replacedby field is write protected by the profile lock. Reads + * are assumed to be atomic, and are done without locking. + * + * Profiles have a hierarchy where hats and children profiles keep + * a reference to their parent. + * + * Profile names can not begin with a : and can not contain the \0 + * character. If a profile name begins with / it will be considered when + * determining profile attachment on "unconfined" tasks. + */ +struct aa_profile { + struct aa_policy base; + struct aa_profile *parent; + + struct aa_namespace *ns; + struct aa_profile *replacedby; + const char *rename; + + struct aa_dfa *xmatch; + int xmatch_len; + u32 sid; + enum audit_mode audit; + enum profile_mode mode; + u32 flags; + u32 path_flags; + int size; + + struct aa_file_rules file; + struct aa_caps caps; + struct aa_rlimit rlimits; +}; + +extern struct aa_namespace *root_ns; +extern enum profile_mode aa_g_profile_mode; + +void aa_add_profile(struct aa_policy *common, struct aa_profile *profile); + +bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view); +const char *aa_ns_name(struct aa_namespace *parent, struct aa_namespace *child); +int aa_alloc_root_ns(void); +void aa_free_root_ns(void); +void aa_free_namespace_kref(struct kref *kref); + +struct aa_namespace *aa_find_namespace(struct aa_namespace *root, + const char *name); + +static inline struct aa_policy *aa_get_common(struct aa_policy *c) +{ + if (c) + kref_get(&c->count); + + return c; +} + +/** + * aa_get_namespace - increment references count on @ns + * @ns: namespace to increment reference count of (MAYBE NULL) + * + * Returns: pointer to @ns, if @ns is NULL returns NULL + * Requires: @ns must be held with valid refcount when called + */ +static inline struct aa_namespace *aa_get_namespace(struct aa_namespace *ns) +{ + if (ns) + kref_get(&(ns->base.count)); + + return ns; +} + +/** + * aa_put_namespace - decrement refcount on @ns + * @ns: namespace to put reference of + * + * Decrement reference count of @ns and if no longer in use free it + */ +static inline void aa_put_namespace(struct aa_namespace *ns) +{ + if (ns) + kref_put(&ns->base.count, aa_free_namespace_kref); +} + +struct aa_profile *aa_alloc_profile(const char *name); +struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat); +void aa_free_profile_kref(struct kref *kref); +struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name); +struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *name); +struct aa_profile *aa_match_profile(struct aa_namespace *ns, const char *name); + +ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace); +ssize_t aa_remove_profiles(char *name, size_t size); + +#define PROF_ADD 1 +#define PROF_REPLACE 0 + +#define unconfined(X) ((X)->flags & PFLAG_UNCONFINED) + +/** + * aa_newest_version - find the newest version of @profile + * @profile: the profile to check for newer versions of (NOT NULL) + * + * Returns: newest version of @profile, if @profile is the newest version + * return @profile. + * + * NOTE: the profile returned is not refcounted, The refcount on @profile + * must be held until the caller decides what to do with the returned newest + * version. + */ +static inline struct aa_profile *aa_newest_version(struct aa_profile *profile) +{ + while (profile->replacedby) + profile = profile->replacedby; + + return profile; +} + +/** + * aa_get_profile - increment refcount on profile @p + * @p: profile (MAYBE NULL) + * + * Returns: pointer to @p if @p is NULL will return NULL + * Requires: @p must be held with valid refcount when called + */ +static inline struct aa_profile *aa_get_profile(struct aa_profile *p) +{ + if (p) + kref_get(&(p->base.count)); + + return p; +} + +/** + * aa_put_profile - decrement refcount on profile @p + * @p: profile (MAYBE NULL) + */ +static inline void aa_put_profile(struct aa_profile *p) +{ + if (p) + kref_put(&p->base.count, aa_free_profile_kref); +} + +static inline int AUDIT_MODE(struct aa_profile *profile) +{ + if (aa_g_audit != AUDIT_NORMAL) + return aa_g_audit; + + return profile->audit; +} + +bool aa_may_manage_policy(int op); + +#endif /* __AA_POLICY_H */ diff --git a/security/apparmor/include/sid.h b/security/apparmor/include/sid.h new file mode 100644 index 00000000000..020db35c301 --- /dev/null +++ b/security/apparmor/include/sid.h @@ -0,0 +1,24 @@ +/* + * AppArmor security module + * + * This file contains AppArmor security identifier (sid) definitions + * + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + */ + +#ifndef __AA_SID_H +#define __AA_SID_H + +#include + +struct aa_profile; + +u32 aa_alloc_sid(void); +void aa_free_sid(u32 sid); + +#endif /* __AA_SID_H */ diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c new file mode 100644 index 00000000000..3cdc1ad0787 --- /dev/null +++ b/security/apparmor/policy.c @@ -0,0 +1,1184 @@ +/* + * AppArmor security module + * + * This file contains AppArmor policy manipulation functions + * + * Copyright (C) 1998-2008 Novell/SUSE + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * + * AppArmor policy is based around profiles, which contain the rules a + * task is confined by. Every task in the system has a profile attached + * to it determined either by matching "unconfined" tasks against the + * visible set of profiles or by following a profiles attachment rules. + * + * Each profile exists in a profile namespace which is a container of + * visible profiles. Each namespace contains a special "unconfined" profile, + * which doesn't enforce any confinement on a task beyond DAC. + * + * Namespace and profile names can be written together in either + * of two syntaxes. + * :namespace:profile - used by kernel interfaces for easy detection + * namespace://profile - used by policy + * + * Profile names can not start with : or @ or ^ and may not contain \0 + * + * Reserved profile names + * unconfined - special automatically generated unconfined profile + * inherit - special name to indicate profile inheritance + * null-XXXX-YYYY - special automatically generated learning profiles + * + * Namespace names may not start with / or @ and may not contain \0 or : + * Reserved namespace names + * user-XXXX - user defined profiles + * + * a // in a profile or namespace name indicates a hierarchical name with the + * name before the // being the parent and the name after the child. + * + * Profile and namespace hierarchies serve two different but similar purposes. + * The namespace contains the set of visible profiles that are considered + * for attachment. The hierarchy of namespaces allows for virtualizing + * the namespace so that for example a chroot can have its own set of profiles + * which may define some local user namespaces. + * The profile hierarchy severs two distinct purposes, + * - it allows for sub profiles or hats, which allows an application to run + * subprograms under its own profile with different restriction than it + * self, and not have it use the system profile. + * eg. if a mail program starts an editor, the policy might make the + * restrictions tighter on the editor tighter than the mail program, + * and definitely different than general editor restrictions + * - it allows for binary hierarchy of profiles, so that execution history + * is preserved. This feature isn't exploited by AppArmor reference policy + * but is allowed. NOTE: this is currently suboptimal because profile + * aliasing is not currently implemented so that a profile for each + * level must be defined. + * eg. /bin/bash///bin/ls as a name would indicate /bin/ls was started + * from /bin/bash + * + * A profile or namespace name that can contain one or more // separators + * is referred to as an hname (hierarchical). + * eg. /bin/bash//bin/ls + * + * An fqname is a name that may contain both namespace and profile hnames. + * eg. :ns:/bin/bash//bin/ls + * + * NOTES: + * - locking of profile lists is currently fairly coarse. All profile + * lists within a namespace use the namespace lock. + * FIXME: move profile lists to using rcu_lists + */ + +#include +#include +#include + +#include "include/apparmor.h" +#include "include/capability.h" +#include "include/context.h" +#include "include/file.h" +#include "include/ipc.h" +#include "include/match.h" +#include "include/path.h" +#include "include/policy.h" +#include "include/policy_unpack.h" +#include "include/resource.h" +#include "include/sid.h" + + +/* root profile namespace */ +struct aa_namespace *root_ns; + +const char *profile_mode_names[] = { + "enforce", + "complain", + "kill", +}; + +/** + * hname_tail - find the last component of an hname + * @name: hname to find the base profile name component of (NOT NULL) + * + * Returns: the tail (base profile name) name component of an hname + */ +static const char *hname_tail(const char *hname) +{ + char *split; + hname = strim((char *)hname); + for (split = strstr(hname, "//"); split; split = strstr(hname, "//")) + hname = split + 2; + + return hname; +} + +/** + * policy_init - initialize a policy structure + * @policy: policy to initialize (NOT NULL) + * @prefix: prefix name if any is required. (MAYBE NULL) + * @name: name of the policy, init will make a copy of it (NOT NULL) + * + * Note: this fn creates a copy of strings passed in + * + * Returns: true if policy init successful + */ +static bool policy_init(struct aa_policy *policy, const char *prefix, + const char *name) +{ + /* freed by policy_free */ + if (prefix) { + policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3, + GFP_KERNEL); + if (policy->hname) + sprintf(policy->hname, "%s//%s", prefix, name); + } else + policy->hname = kstrdup(name, GFP_KERNEL); + if (!policy->hname) + return 0; + /* base.name is a substring of fqname */ + policy->name = (char *)hname_tail(policy->hname); + INIT_LIST_HEAD(&policy->list); + INIT_LIST_HEAD(&policy->profiles); + kref_init(&policy->count); + + return 1; +} + +/** + * policy_destroy - free the elements referenced by @policy + * @policy: policy that is to have its elements freed (NOT NULL) + */ +static void policy_destroy(struct aa_policy *policy) +{ + /* still contains profiles -- invalid */ + if (!list_empty(&policy->profiles)) { + AA_ERROR("%s: internal error, " + "policy '%s' still contains profiles\n", + __func__, policy->name); + BUG(); + } + if (!list_empty(&policy->list)) { + AA_ERROR("%s: internal error, policy '%s' still on list\n", + __func__, policy->name); + BUG(); + } + + /* don't free name as its a subset of hname */ + kzfree(policy->hname); +} + +/** + * __policy_find - find a policy by @name on a policy list + * @head: list to search (NOT NULL) + * @name: name to search for (NOT NULL) + * + * Requires: correct locks for the @head list be held + * + * Returns: unrefcounted policy that match @name or NULL if not found + */ +static struct aa_policy *__policy_find(struct list_head *head, const char *name) +{ + struct aa_policy *policy; + + list_for_each_entry(policy, head, list) { + if (!strcmp(policy->name, name)) + return policy; + } + return NULL; +} + +/** + * __policy_strn_find - find a policy that's name matches @len chars of @str + * @head: list to search (NOT NULL) + * @str: string to search for (NOT NULL) + * @len: length of match required + * + * Requires: correct locks for the @head list be held + * + * Returns: unrefcounted policy that match @str or NULL if not found + * + * if @len == strlen(@strlen) then this is equiv to __policy_find + * other wise it allows searching for policy by a partial match of name + */ +static struct aa_policy *__policy_strn_find(struct list_head *head, + const char *str, int len) +{ + struct aa_policy *policy; + + list_for_each_entry(policy, head, list) { + if (aa_strneq(policy->name, str, len)) + return policy; + } + + return NULL; +} + +/* + * Routines for AppArmor namespaces + */ + +static const char *hidden_ns_name = "---"; +/** + * aa_ns_visible - test if @view is visible from @curr + * @curr: namespace to treat as the parent (NOT NULL) + * @view: namespace to test if visible from @curr (NOT NULL) + * + * Returns: true if @view is visible from @curr else false + */ +bool aa_ns_visible(struct aa_namespace *curr, struct aa_namespace *view) +{ + if (curr == view) + return true; + + for ( ; view; view = view->parent) { + if (view->parent == curr) + return true; + } + return false; +} + +/** + * aa_na_name - Find the ns name to display for @view from @curr + * @curr - current namespace (NOT NULL) + * @view - namespace attempting to view (NOT NULL) + * + * Returns: name of @view visible from @curr + */ +const char *aa_ns_name(struct aa_namespace *curr, struct aa_namespace *view) +{ + /* if view == curr then the namespace name isn't displayed */ + if (curr == view) + return ""; + + if (aa_ns_visible(curr, view)) { + /* at this point if a ns is visible it is in a view ns + * thus the curr ns.hname is a prefix of its name. + * Only output the virtualized portion of the name + * Add + 2 to skip over // separating curr hname prefix + * from the visible tail of the views hname + */ + return view->base.hname + strlen(curr->base.hname) + 2; + } else + return hidden_ns_name; +} + +/** + * alloc_namespace - allocate, initialize and return a new namespace + * @prefix: parent namespace name (MAYBE NULL) + * @name: a preallocated name (NOT NULL) + * + * Returns: refcounted namespace or NULL on failure. + */ +static struct aa_namespace *alloc_namespace(const char *prefix, + const char *name) +{ + struct aa_namespace *ns; + + ns = kzalloc(sizeof(*ns), GFP_KERNEL); + AA_DEBUG("%s(%p)\n", __func__, ns); + if (!ns) + return NULL; + if (!policy_init(&ns->base, prefix, name)) + goto fail_ns; + + INIT_LIST_HEAD(&ns->sub_ns); + rwlock_init(&ns->lock); + + /* released by free_namespace */ + ns->unconfined = aa_alloc_profile("unconfined"); + if (!ns->unconfined) + goto fail_unconfined; + + ns->unconfined->sid = aa_alloc_sid(); + ns->unconfined->flags = PFLAG_UNCONFINED | PFLAG_IX_ON_NAME_ERROR | + PFLAG_IMMUTABLE; + + /* + * released by free_namespace, however __remove_namespace breaks + * the cyclic references (ns->unconfined, and unconfined->ns) and + * replaces with refs to parent namespace unconfined + */ + ns->unconfined->ns = aa_get_namespace(ns); + + return ns; + +fail_unconfined: + kzfree(ns->base.name); +fail_ns: + kzfree(ns); + return NULL; +} + +/** + * free_namespace - free a profile namespace + * @ns: the namespace to free (MAYBE NULL) + * + * Requires: All references to the namespace must have been put, if the + * namespace was referenced by a profile confining a task, + */ +static void free_namespace(struct aa_namespace *ns) +{ + if (!ns) + return; + + policy_destroy(&ns->base); + aa_put_namespace(ns->parent); + + if (ns->unconfined && ns->unconfined->ns == ns) + ns->unconfined->ns = NULL; + + aa_put_profile(ns->unconfined); + kzfree(ns); +} + +/** + * aa_free_namespace_kref - free aa_namespace by kref (see aa_put_namespace) + * @kr: kref callback for freeing of a namespace (NOT NULL) + */ +void aa_free_namespace_kref(struct kref *kref) +{ + free_namespace(container_of(kref, struct aa_namespace, base.count)); +} + +/** + * __aa_find_namespace - find a namespace on a list by @name + * @head: list to search for namespace on (NOT NULL) + * @name: name of namespace to look for (NOT NULL) + * + * Returns: unrefcounted namespace + * + * Requires: ns lock be held + */ +static struct aa_namespace *__aa_find_namespace(struct list_head *head, + const char *name) +{ + return (struct aa_namespace *)__policy_find(head, name); +} + +/** + * aa_find_namespace - look up a profile namespace on the namespace list + * @root: namespace to search in (NOT NULL) + * @name: name of namespace to find (NOT NULL) + * + * Returns: a refcounted namespace on the list, or NULL if no namespace + * called @name exists. + * + * refcount released by caller + */ +struct aa_namespace *aa_find_namespace(struct aa_namespace *root, + const char *name) +{ + struct aa_namespace *ns = NULL; + + read_lock(&root->lock); + ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); + read_unlock(&root->lock); + + return ns; +} + +/** + * aa_prepare_namespace - find an existing or create a new namespace of @name + * @name: the namespace to find or add (MAYBE NULL) + * + * Returns: refcounted namespace or NULL if failed to create one + */ +static struct aa_namespace *aa_prepare_namespace(const char *name) +{ + struct aa_namespace *ns, *root; + + root = aa_current_profile()->ns; + + write_lock(&root->lock); + + /* if name isn't specified the profile is loaded to the current ns */ + if (!name) { + /* released by caller */ + ns = aa_get_namespace(root); + goto out; + } + + /* try and find the specified ns and if it doesn't exist create it */ + /* released by caller */ + ns = aa_get_namespace(__aa_find_namespace(&root->sub_ns, name)); + if (!ns) { + /* namespace not found */ + struct aa_namespace *new_ns; + write_unlock(&root->lock); + new_ns = alloc_namespace(root->base.hname, name); + if (!new_ns) + return NULL; + write_lock(&root->lock); + /* test for race when new_ns was allocated */ + ns = __aa_find_namespace(&root->sub_ns, name); + if (!ns) { + /* add parent ref */ + new_ns->parent = aa_get_namespace(root); + + list_add(&new_ns->base.list, &root->sub_ns); + /* add list ref */ + ns = aa_get_namespace(new_ns); + } else { + /* raced so free the new one */ + free_namespace(new_ns); + /* get reference on namespace */ + aa_get_namespace(ns); + } + } +out: + write_unlock(&root->lock); + + /* return ref */ + return ns; +} + +/** + * __list_add_profile - add a profile to a list + * @list: list to add it to (NOT NULL) + * @profile: the profile to add (NOT NULL) + * + * refcount @profile, should be put by __list_remove_profile + * + * Requires: namespace lock be held, or list not be shared + */ +static void __list_add_profile(struct list_head *list, + struct aa_profile *profile) +{ + list_add(&profile->base.list, list); + /* get list reference */ + aa_get_profile(profile); +} + +/** + * __list_remove_profile - remove a profile from the list it is on + * @profile: the profile to remove (NOT NULL) + * + * remove a profile from the list, warning generally removal should + * be done with __replace_profile as most profile removals are + * replacements to the unconfined profile. + * + * put @profile list refcount + * + * Requires: namespace lock be held, or list not have been live + */ +static void __list_remove_profile(struct aa_profile *profile) +{ + list_del_init(&profile->base.list); + if (!(profile->flags & PFLAG_NO_LIST_REF)) + /* release list reference */ + aa_put_profile(profile); +} + +/** + * __replace_profile - replace @old with @new on a list + * @old: profile to be replaced (NOT NULL) + * @new: profile to replace @old with (NOT NULL) + * + * Will duplicate and refcount elements that @new inherits from @old + * and will inherit @old children. + * + * refcount @new for list, put @old list refcount + * + * Requires: namespace list lock be held, or list not be shared + */ +static void __replace_profile(struct aa_profile *old, struct aa_profile *new) +{ + struct aa_policy *policy; + struct aa_profile *child, *tmp; + + if (old->parent) + policy = &old->parent->base; + else + policy = &old->ns->base; + + /* released when @new is freed */ + new->parent = aa_get_profile(old->parent); + new->ns = aa_get_namespace(old->ns); + new->sid = old->sid; + __list_add_profile(&policy->profiles, new); + /* inherit children */ + list_for_each_entry_safe(child, tmp, &old->base.profiles, base.list) { + aa_put_profile(child->parent); + child->parent = aa_get_profile(new); + /* list refcount transferred to @new*/ + list_move(&child->base.list, &new->base.profiles); + } + + /* released by free_profile */ + old->replacedby = aa_get_profile(new); + __list_remove_profile(old); +} + +static void __profile_list_release(struct list_head *head); + +/** + * __remove_profile - remove old profile, and children + * @profile: profile to be replaced (NOT NULL) + * + * Requires: namespace list lock be held, or list not be shared + */ +static void __remove_profile(struct aa_profile *profile) +{ + /* release any children lists first */ + __profile_list_release(&profile->base.profiles); + /* released by free_profile */ + profile->replacedby = aa_get_profile(profile->ns->unconfined); + __list_remove_profile(profile); +} + +/** + * __profile_list_release - remove all profiles on the list and put refs + * @head: list of profiles (NOT NULL) + * + * Requires: namespace lock be held + */ +static void __profile_list_release(struct list_head *head) +{ + struct aa_profile *profile, *tmp; + list_for_each_entry_safe(profile, tmp, head, base.list) + __remove_profile(profile); +} + +static void __ns_list_release(struct list_head *head); + +/** + * destroy_namespace - remove everything contained by @ns + * @ns: namespace to have it contents removed (NOT NULL) + */ +static void destroy_namespace(struct aa_namespace *ns) +{ + if (!ns) + return; + + write_lock(&ns->lock); + /* release all profiles in this namespace */ + __profile_list_release(&ns->base.profiles); + + /* release all sub namespaces */ + __ns_list_release(&ns->sub_ns); + + write_unlock(&ns->lock); +} + +/** + * __remove_namespace - remove a namespace and all its children + * @ns: namespace to be removed (NOT NULL) + * + * Requires: ns->parent->lock be held and ns removed from parent. + */ +static void __remove_namespace(struct aa_namespace *ns) +{ + struct aa_profile *unconfined = ns->unconfined; + + /* remove ns from namespace list */ + list_del_init(&ns->base.list); + + /* + * break the ns, unconfined profile cyclic reference and forward + * all new unconfined profiles requests to the parent namespace + * This will result in all confined tasks that have a profile + * being removed, inheriting the parent->unconfined profile. + */ + if (ns->parent) + ns->unconfined = aa_get_profile(ns->parent->unconfined); + + destroy_namespace(ns); + + /* release original ns->unconfined ref */ + aa_put_profile(unconfined); + /* release ns->base.list ref, from removal above */ + aa_put_namespace(ns); +} + +/** + * __ns_list_release - remove all profile namespaces on the list put refs + * @head: list of profile namespaces (NOT NULL) + * + * Requires: namespace lock be held + */ +static void __ns_list_release(struct list_head *head) +{ + struct aa_namespace *ns, *tmp; + list_for_each_entry_safe(ns, tmp, head, base.list) + __remove_namespace(ns); + +} + +/** + * aa_alloc_root_ns - allocate the root profile namespace + * + * Returns: %0 on success else error + * + */ +int __init aa_alloc_root_ns(void) +{ + /* released by aa_free_root_ns - used as list ref*/ + root_ns = alloc_namespace(NULL, "root"); + if (!root_ns) + return -ENOMEM; + + return 0; +} + + /** + * aa_free_root_ns - free the root profile namespace + */ +void __init aa_free_root_ns(void) + { + struct aa_namespace *ns = root_ns; + root_ns = NULL; + + destroy_namespace(ns); + aa_put_namespace(ns); +} + +/** + * aa_alloc_profile - allocate, initialize and return a new profile + * @hname: name of the profile (NOT NULL) + * + * Returns: refcount profile or NULL on failure + */ +struct aa_profile *aa_alloc_profile(const char *hname) +{ + struct aa_profile *profile; + + /* freed by free_profile - usually through aa_put_profile */ + profile = kzalloc(sizeof(*profile), GFP_KERNEL); + if (!profile) + return NULL; + + if (!policy_init(&profile->base, NULL, hname)) { + kzfree(profile); + return NULL; + } + + /* refcount released by caller */ + return profile; +} + +/** + * aa_new_null_profile - create a new null-X learning profile + * @parent: profile that caused this profile to be created (NOT NULL) + * @hat: true if the null- learning profile is a hat + * + * Create a null- complain mode profile used in learning mode. The name of + * the profile is unique and follows the format of parent//null-sid. + * + * null profiles are added to the profile list but the list does not + * hold a count on them so that they are automatically released when + * not in use. + * + * Returns: new refcounted profile else NULL on failure + */ +struct aa_profile *aa_new_null_profile(struct aa_profile *parent, int hat) +{ + struct aa_profile *profile = NULL; + char *name; + u32 sid = aa_alloc_sid(); + + /* freed below */ + name = kmalloc(strlen(parent->base.hname) + 2 + 7 + 8, GFP_KERNEL); + if (!name) + goto fail; + sprintf(name, "%s//null-%x", parent->base.hname, sid); + + profile = aa_alloc_profile(name); + kfree(name); + if (!profile) + goto fail; + + profile->sid = sid; + profile->mode = APPARMOR_COMPLAIN; + profile->flags = PFLAG_NULL; + if (hat) + profile->flags |= PFLAG_HAT; + + /* released on free_profile */ + profile->parent = aa_get_profile(parent); + profile->ns = aa_get_namespace(parent->ns); + + write_lock(&profile->ns->lock); + __list_add_profile(&parent->base.profiles, profile); + write_unlock(&profile->ns->lock); + + /* refcount released by caller */ + return profile; + +fail: + aa_free_sid(sid); + return NULL; +} + +/** + * free_profile - free a profile + * @profile: the profile to free (MAYBE NULL) + * + * Free a profile, its hats and null_profile. All references to the profile, + * its hats and null_profile must have been put. + * + * If the profile was referenced from a task context, free_profile() will + * be called from an rcu callback routine, so we must not sleep here. + */ +static void free_profile(struct aa_profile *profile) +{ + AA_DEBUG("%s(%p)\n", __func__, profile); + + if (!profile) + return; + + if (!list_empty(&profile->base.list)) { + AA_ERROR("%s: internal error, " + "profile '%s' still on ns list\n", + __func__, profile->base.name); + BUG(); + } + + /* free children profiles */ + policy_destroy(&profile->base); + aa_put_profile(profile->parent); + + aa_put_namespace(profile->ns); + kzfree(profile->rename); + + aa_free_file_rules(&profile->file); + aa_free_cap_rules(&profile->caps); + aa_free_rlimit_rules(&profile->rlimits); + + aa_free_sid(profile->sid); + aa_put_dfa(profile->xmatch); + + aa_put_profile(profile->replacedby); + + kzfree(profile); +} + +/** + * aa_free_profile_kref - free aa_profile by kref (called by aa_put_profile) + * @kr: kref callback for freeing of a profile (NOT NULL) + */ +void aa_free_profile_kref(struct kref *kref) +{ + struct aa_profile *p = container_of(kref, struct aa_profile, + base.count); + + free_profile(p); +} + +/* TODO: profile accounting - setup in remove */ + +/** + * __find_child - find a profile on @head list with a name matching @name + * @head: list to search (NOT NULL) + * @name: name of profile (NOT NULL) + * + * Requires: ns lock protecting list be held + * + * Returns: unrefcounted profile ptr, or NULL if not found + */ +static struct aa_profile *__find_child(struct list_head *head, const char *name) +{ + return (struct aa_profile *)__policy_find(head, name); +} + +/** + * __strn_find_child - find a profile on @head list using substring of @name + * @head: list to search (NOT NULL) + * @name: name of profile (NOT NULL) + * @len: length of @name substring to match + * + * Requires: ns lock protecting list be held + * + * Returns: unrefcounted profile ptr, or NULL if not found + */ +static struct aa_profile *__strn_find_child(struct list_head *head, + const char *name, int len) +{ + return (struct aa_profile *)__policy_strn_find(head, name, len); +} + +/** + * aa_find_child - find a profile by @name in @parent + * @parent: profile to search (NOT NULL) + * @name: profile name to search for (NOT NULL) + * + * Returns: a refcounted profile or NULL if not found + */ +struct aa_profile *aa_find_child(struct aa_profile *parent, const char *name) +{ + struct aa_profile *profile; + + read_lock(&parent->ns->lock); + profile = aa_get_profile(__find_child(&parent->base.profiles, name)); + read_unlock(&parent->ns->lock); + + /* refcount released by caller */ + return profile; +} + +/** + * __lookup_parent - lookup the parent of a profile of name @hname + * @ns: namespace to lookup profile in (NOT NULL) + * @hname: hierarchical profile name to find parent of (NOT NULL) + * + * Lookups up the parent of a fully qualified profile name, the profile + * that matches hname does not need to exist, in general this + * is used to load a new profile. + * + * Requires: ns->lock be held + * + * Returns: unrefcounted policy or NULL if not found + */ +static struct aa_policy *__lookup_parent(struct aa_namespace *ns, + const char *hname) +{ + struct aa_policy *policy; + struct aa_profile *profile = NULL; + char *split; + + policy = &ns->base; + + for (split = strstr(hname, "//"); split;) { + profile = __strn_find_child(&policy->profiles, hname, + split - hname); + if (!profile) + return NULL; + policy = &profile->base; + hname = split + 2; + split = strstr(hname, "//"); + } + if (!profile) + return &ns->base; + return &profile->base; +} + +/** + * __lookup_profile - lookup the profile matching @hname + * @base: base list to start looking up profile name from (NOT NULL) + * @hname: hierarchical profile name (NOT NULL) + * + * Requires: ns->lock be held + * + * Returns: unrefcounted profile pointer or NULL if not found + * + * Do a relative name lookup, recursing through profile tree. + */ +static struct aa_profile *__lookup_profile(struct aa_policy *base, + const char *hname) +{ + struct aa_profile *profile = NULL; + char *split; + + for (split = strstr(hname, "//"); split;) { + profile = __strn_find_child(&base->profiles, hname, + split - hname); + if (!profile) + return NULL; + + base = &profile->base; + hname = split + 2; + split = strstr(hname, "//"); + } + + profile = __find_child(&base->profiles, hname); + + return profile; +} + +/** + * aa_lookup_profile - find a profile by its full or partial name + * @ns: the namespace to start from (NOT NULL) + * @hname: name to do lookup on. Does not contain namespace prefix (NOT NULL) + * + * Returns: refcounted profile or NULL if not found + */ +struct aa_profile *aa_lookup_profile(struct aa_namespace *ns, const char *hname) +{ + struct aa_profile *profile; + + read_lock(&ns->lock); + profile = aa_get_profile(__lookup_profile(&ns->base, hname)); + read_unlock(&ns->lock); + + /* refcount released by caller */ + return profile; +} + +/** + * replacement_allowed - test to see if replacement is allowed + * @profile: profile to test if it can be replaced (MAYBE NULL) + * @noreplace: true if replacement shouldn't be allowed but addition is okay + * @info: Returns - info about why replacement failed (NOT NULL) + * + * Returns: %0 if replacement allowed else error code + */ +static int replacement_allowed(struct aa_profile *profile, int noreplace, + const char **info) +{ + if (profile) { + if (profile->flags & PFLAG_IMMUTABLE) { + *info = "cannot replace immutible profile"; + return -EPERM; + } else if (noreplace) { + *info = "profile already exists"; + return -EEXIST; + } + } + return 0; +} + +/** + * __add_new_profile - simple wrapper around __list_add_profile + * @ns: namespace that profile is being added to (NOT NULL) + * @policy: the policy container to add the profile to (NOT NULL) + * @profile: profile to add (NOT NULL) + * + * add a profile to a list and do other required basic allocations + */ +static void __add_new_profile(struct aa_namespace *ns, struct aa_policy *policy, + struct aa_profile *profile) +{ + if (policy != &ns->base) + /* released on profile replacement or free_profile */ + profile->parent = aa_get_profile((struct aa_profile *) policy); + __list_add_profile(&policy->profiles, profile); + /* released on free_profile */ + profile->sid = aa_alloc_sid(); + profile->ns = aa_get_namespace(ns); +} + +/** + * aa_audit_policy - Do auditing of policy changes + * @op: policy operation being performed + * @gfp: memory allocation flags + * @name: name of profile being manipulated (NOT NULL) + * @info: any extra information to be audited (MAYBE NULL) + * @error: error code + * + * Returns: the error to be returned after audit is done + */ +static int audit_policy(int op, gfp_t gfp, const char *name, const char *info, + int error) +{ + struct common_audit_data sa; + COMMON_AUDIT_DATA_INIT(&sa, NONE); + sa.aad.op = op; + sa.aad.name = name; + sa.aad.info = info; + sa.aad.error = error; + + return aa_audit(AUDIT_APPARMOR_STATUS, __aa_current_profile(), gfp, + &sa, NULL); +} + +/** + * aa_may_manage_policy - can the current task manage policy + * @op: the policy manipulation operation being done + * + * Returns: true if the task is allowed to manipulate policy + */ +bool aa_may_manage_policy(int op) +{ + /* check if loading policy is locked out */ + if (aa_g_lock_policy) { + audit_policy(op, GFP_KERNEL, NULL, "policy_locked", -EACCES); + return 0; + } + + if (!capable(CAP_MAC_ADMIN)) { + audit_policy(op, GFP_KERNEL, NULL, "not policy admin", -EACCES); + return 0; + } + + return 1; +} + +/** + * aa_replace_profiles - replace profile(s) on the profile list + * @udata: serialized data stream (NOT NULL) + * @size: size of the serialized data stream + * @noreplace: true if only doing addition, no replacement allowed + * + * unpack and replace a profile on the profile list and uses of that profile + * by any aa_task_cxt. If the profile does not exist on the profile list + * it is added. + * + * Returns: size of data consumed else error code on failure. + */ +ssize_t aa_replace_profiles(void *udata, size_t size, bool noreplace) +{ + struct aa_policy *policy; + struct aa_profile *old_profile = NULL, *new_profile = NULL; + struct aa_profile *rename_profile = NULL; + struct aa_namespace *ns = NULL; + const char *ns_name, *name = NULL, *info = NULL; + int op = OP_PROF_REPL; + ssize_t error; + + /* released below */ + new_profile = aa_unpack(udata, size, &ns_name); + if (IS_ERR(new_profile)) { + error = PTR_ERR(new_profile); + new_profile = NULL; + goto fail; + } + + /* released below */ + ns = aa_prepare_namespace(ns_name); + if (!ns) { + info = "failed to prepare namespace"; + error = -ENOMEM; + name = ns_name; + goto fail; + } + + name = new_profile->base.hname; + + write_lock(&ns->lock); + /* no ref on policy only use inside lock */ + policy = __lookup_parent(ns, new_profile->base.hname); + + if (!policy) { + info = "parent does not exist"; + error = -ENOENT; + goto audit; + } + + old_profile = __find_child(&policy->profiles, new_profile->base.name); + /* released below */ + aa_get_profile(old_profile); + + if (new_profile->rename) { + rename_profile = __lookup_profile(&ns->base, + new_profile->rename); + /* released below */ + aa_get_profile(rename_profile); + + if (!rename_profile) { + info = "profile to rename does not exist"; + name = new_profile->rename; + error = -ENOENT; + goto audit; + } + } + + error = replacement_allowed(old_profile, noreplace, &info); + if (error) + goto audit; + + error = replacement_allowed(rename_profile, noreplace, &info); + if (error) + goto audit; + +audit: + if (!old_profile && !rename_profile) + op = OP_PROF_LOAD; + + error = audit_policy(op, GFP_ATOMIC, name, info, error); + + if (!error) { + if (rename_profile) + __replace_profile(rename_profile, new_profile); + if (old_profile) { + /* when there are both rename and old profiles + * inherit old profiles sid + */ + if (rename_profile) + aa_free_sid(new_profile->sid); + __replace_profile(old_profile, new_profile); + } + if (!(old_profile || rename_profile)) + __add_new_profile(ns, policy, new_profile); + } + write_unlock(&ns->lock); + +out: + aa_put_namespace(ns); + aa_put_profile(rename_profile); + aa_put_profile(old_profile); + aa_put_profile(new_profile); + if (error) + return error; + return size; + +fail: + error = audit_policy(op, GFP_KERNEL, name, info, error); + goto out; +} + +/** + * aa_remove_profiles - remove profile(s) from the system + * @fqname: name of the profile or namespace to remove (NOT NULL) + * @size: size of the name + * + * Remove a profile or sub namespace from the current namespace, so that + * they can not be found anymore and mark them as replaced by unconfined + * + * NOTE: removing confinement does not restore rlimits to preconfinemnet values + * + * Returns: size of data consume else error code if fails + */ +ssize_t aa_remove_profiles(char *fqname, size_t size) +{ + struct aa_namespace *root, *ns = NULL; + struct aa_profile *profile = NULL; + const char *name = fqname, *info = NULL; + ssize_t error = 0; + + if (*fqname == 0) { + info = "no profile specified"; + error = -ENOENT; + goto fail; + } + + root = aa_current_profile()->ns; + + if (fqname[0] == ':') { + char *ns_name; + name = aa_split_fqname(fqname, &ns_name); + if (ns_name) { + /* released below */ + ns = aa_find_namespace(root, ns_name); + if (!ns) { + info = "namespace does not exist"; + error = -ENOENT; + goto fail; + } + } + } else + /* released below */ + ns = aa_get_namespace(root); + + write_lock(&ns->lock); + if (!name) { + /* remove namespace - can only happen if fqname[0] == ':' */ + __remove_namespace(ns); + } else { + /* remove profile */ + profile = aa_get_profile(__lookup_profile(&ns->base, name)); + if (!profile) { + error = -ENOENT; + info = "profile does not exist"; + goto fail_ns_lock; + } + name = profile->base.hname; + __remove_profile(profile); + } + write_unlock(&ns->lock); + + /* don't fail removal if audit fails */ + (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); + aa_put_namespace(ns); + aa_put_profile(profile); + return size; + +fail_ns_lock: + write_unlock(&ns->lock); + aa_put_namespace(ns); + +fail: + (void) audit_policy(OP_PROF_RM, GFP_KERNEL, name, info, error); + return error; +} diff --git a/security/apparmor/sid.c b/security/apparmor/sid.c new file mode 100644 index 00000000000..f0b34f76ebe --- /dev/null +++ b/security/apparmor/sid.c @@ -0,0 +1,55 @@ +/* + * AppArmor security module + * + * This file contains AppArmor security identifier (sid) manipulation fns + * + * Copyright 2009-2010 Canonical Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * + * AppArmor allocates a unique sid for every profile loaded. If a profile + * is replaced it receives the sid of the profile it is replacing. + * + * The sid value of 0 is invalid. + */ + +#include +#include +#include + +#include "include/sid.h" + +/* global counter from which sids are allocated */ +static u32 global_sid; +static DEFINE_SPINLOCK(sid_lock); + +/* TODO FIXME: add sid to profile mapping, and sid recycling */ + +/** + * aa_alloc_sid - allocate a new sid for a profile + */ +u32 aa_alloc_sid(void) +{ + u32 sid; + + /* + * TODO FIXME: sid recycling - part of profile mapping table + */ + spin_lock(&sid_lock); + sid = (++global_sid); + spin_unlock(&sid_lock); + return sid; +} + +/** + * aa_free_sid - free a sid + * @sid: sid to free + */ +void aa_free_sid(u32 sid) +{ + ; /* NOP ATM */ +} -- cgit v1.2.3-70-g09d2 From 4d6ec10bb4461fdc9a9ab94ef32934e13564e873 Mon Sep 17 00:00:00 2001 From: James Morris Date: Fri, 30 Jul 2010 09:02:04 +1000 Subject: AppArmor: update path_truncate method to latest version Remove extraneous path_truncate arguments from the AppArmor hook, as they've been removed from the LSM API. Signed-off-by: James Morris --- security/apparmor/lsm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 7daf0d52803..8db33a8b50c 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -278,8 +278,7 @@ static int apparmor_path_mknod(struct path *dir, struct dentry *dentry, return common_perm_create(OP_MKNOD, dir, dentry, AA_MAY_CREATE, mode); } -static int apparmor_path_truncate(struct path *path, loff_t length, - unsigned int time_attrs) +static int apparmor_path_truncate(struct path *path) { struct path_cond cond = { path->dentry->d_inode->i_uid, path->dentry->d_inode->i_mode -- cgit v1.2.3-70-g09d2 From 484ca79c653121d3c79fffb86e1deea724f2e20b Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 29 Jul 2010 14:29:55 +0900 Subject: TOMOYO: Use pathname specified by policy rather than execve() Commit c9e69318 "TOMOYO: Allow wildcard for execute permission." changed execute permission and domainname to accept wildcards. But tomoyo_find_next_domain() was using pathname passed to execve() rather than pathname specified by the execute permission. As a result, processes were not able to transit to domains which contain wildcards in their domainnames. This patch passes pathname specified by the execute permission back to tomoyo_find_next_domain() so that processes can transit to domains which contain wildcards in their domainnames. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/common.h | 14 +++++++++----- security/tomoyo/domain.c | 15 ++++++++++++++- security/tomoyo/file.c | 26 +++++++++++++++++--------- security/tomoyo/group.c | 14 +++++++------- security/tomoyo/mount.c | 2 +- 5 files changed, 48 insertions(+), 23 deletions(-) diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 2ffad613855..04454cb7b24 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -246,6 +246,8 @@ struct tomoyo_request_info { union { struct { const struct tomoyo_path_info *filename; + /* For using wildcards at tomoyo_find_next_domain(). */ + const struct tomoyo_path_info *matched_path; u8 operation; } path; struct { @@ -718,8 +720,9 @@ bool tomoyo_domain_quota_is_ok(struct tomoyo_request_info *r); /* Print out of memory warning message. */ void tomoyo_warn_oom(const char *function); /* Check whether the given name matches the given name_union. */ -bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, - const struct tomoyo_name_union *ptr); +const struct tomoyo_path_info * +tomoyo_compare_name_union(const struct tomoyo_path_info *name, + const struct tomoyo_name_union *ptr); /* Check whether the given number matches the given number_union. */ bool tomoyo_compare_number_union(const unsigned long value, const struct tomoyo_number_union *ptr); @@ -736,8 +739,9 @@ bool tomoyo_domain_def(const unsigned char *buffer); bool tomoyo_parse_name_union(const char *filename, struct tomoyo_name_union *ptr); /* Check whether the given filename matches the given path_group. */ -bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, - const struct tomoyo_group *group); +const struct tomoyo_path_info * +tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, + const struct tomoyo_group *group); /* Check whether the given value matches the given number_group. */ bool tomoyo_number_matches_group(const unsigned long min, const unsigned long max, @@ -879,7 +883,7 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size, const struct tomoyo_acl_head *)); void tomoyo_check_acl(struct tomoyo_request_info *r, - bool (*check_entry) (const struct tomoyo_request_info *, + bool (*check_entry) (struct tomoyo_request_info *, const struct tomoyo_acl_info *)); /********** External variable definitions. **********/ diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index 4e0101b0041..35388408e47 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -110,7 +110,7 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, } void tomoyo_check_acl(struct tomoyo_request_info *r, - bool (*check_entry) (const struct tomoyo_request_info *, + bool (*check_entry) (struct tomoyo_request_info *, const struct tomoyo_acl_info *)) { const struct tomoyo_domain_info *domain = r->domain; @@ -465,6 +465,19 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) goto retry; if (retval < 0) goto out; + /* + * To be able to specify domainnames with wildcards, use the + * pathname specified in the policy (which may contain + * wildcard) rather than the pathname passed to execve() + * (which never contains wildcard). + */ + if (r.param.path.matched_path) { + if (need_kfree) + kfree(rn.name); + need_kfree = false; + /* This is OK because it is read only. */ + rn = *r.param.path.matched_path; + } /* Calculate domain to transit to. */ switch (tomoyo_transition_type(old_domain->domainname, &rn)) { diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index f7877fa80f1..9d32f182301 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -95,12 +95,15 @@ void tomoyo_put_name_union(struct tomoyo_name_union *ptr) tomoyo_put_name(ptr->filename); } -bool tomoyo_compare_name_union(const struct tomoyo_path_info *name, - const struct tomoyo_name_union *ptr) +const struct tomoyo_path_info * +tomoyo_compare_name_union(const struct tomoyo_path_info *name, + const struct tomoyo_name_union *ptr) { if (ptr->is_group) return tomoyo_path_matches_group(name, ptr->group); - return tomoyo_path_matches_pattern(name, ptr->filename); + if (tomoyo_path_matches_pattern(name, ptr->filename)) + return ptr->filename; + return NULL; } void tomoyo_put_number_union(struct tomoyo_number_union *ptr) @@ -504,16 +507,21 @@ int tomoyo_write_no_rewrite(char *data, const bool is_delete) return tomoyo_update_no_rewrite_entry(data, is_delete); } -static bool tomoyo_check_path_acl(const struct tomoyo_request_info *r, +static bool tomoyo_check_path_acl(struct tomoyo_request_info *r, const struct tomoyo_acl_info *ptr) { const struct tomoyo_path_acl *acl = container_of(ptr, typeof(*acl), head); - return (acl->perm & (1 << r->param.path.operation)) && - tomoyo_compare_name_union(r->param.path.filename, &acl->name); + if (acl->perm & (1 << r->param.path.operation)) { + r->param.path.matched_path = + tomoyo_compare_name_union(r->param.path.filename, + &acl->name); + return r->param.path.matched_path != NULL; + } + return false; } -static bool tomoyo_check_path_number_acl(const struct tomoyo_request_info *r, +static bool tomoyo_check_path_number_acl(struct tomoyo_request_info *r, const struct tomoyo_acl_info *ptr) { const struct tomoyo_path_number_acl *acl = @@ -525,7 +533,7 @@ static bool tomoyo_check_path_number_acl(const struct tomoyo_request_info *r, &acl->name); } -static bool tomoyo_check_path2_acl(const struct tomoyo_request_info *r, +static bool tomoyo_check_path2_acl(struct tomoyo_request_info *r, const struct tomoyo_acl_info *ptr) { const struct tomoyo_path2_acl *acl = @@ -536,7 +544,7 @@ static bool tomoyo_check_path2_acl(const struct tomoyo_request_info *r, &acl->name2); } -static bool tomoyo_check_mkdev_acl(const struct tomoyo_request_info *r, +static bool tomoyo_check_mkdev_acl(struct tomoyo_request_info *r, const struct tomoyo_acl_info *ptr) { const struct tomoyo_mkdev_acl *acl = diff --git a/security/tomoyo/group.c b/security/tomoyo/group.c index 3f0a2abf65c..e94352ce723 100644 --- a/security/tomoyo/group.c +++ b/security/tomoyo/group.c @@ -80,24 +80,24 @@ int tomoyo_write_group(char *data, const bool is_delete, const u8 type) * @pathname: The name of pathname. * @group: Pointer to "struct tomoyo_path_group". * - * Returns true if @pathname matches pathnames in @group, false otherwise. + * Returns matched member's pathname if @pathname matches pathnames in @group, + * NULL otherwise. * * Caller holds tomoyo_read_lock(). */ -bool tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, - const struct tomoyo_group *group) +const struct tomoyo_path_info * +tomoyo_path_matches_group(const struct tomoyo_path_info *pathname, + const struct tomoyo_group *group) { struct tomoyo_path_group *member; - bool matched = false; list_for_each_entry_rcu(member, &group->member_list, head.list) { if (member->head.is_deleted) continue; if (!tomoyo_path_matches_pattern(pathname, member->member_name)) continue; - matched = true; - break; + return member->member_name; } - return matched; + return NULL; } /** diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c index cfeff871908..82bf8c2390b 100644 --- a/security/tomoyo/mount.c +++ b/security/tomoyo/mount.c @@ -60,7 +60,7 @@ static int tomoyo_audit_mount_log(struct tomoyo_request_info *r) flags); } -static bool tomoyo_check_mount_acl(const struct tomoyo_request_info *r, +static bool tomoyo_check_mount_acl(struct tomoyo_request_info *r, const struct tomoyo_acl_info *ptr) { const struct tomoyo_mount_acl *acl = -- cgit v1.2.3-70-g09d2 From 016d825fe02cd20fd8803ca37a1e6d428fe878f6 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Fri, 30 Jul 2010 13:46:33 +1000 Subject: AppArmor: Enable configuring and building of the AppArmor security module Kconfig and Makefiles to enable configuration and building of AppArmor. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/.gitignore | 5 +++++ security/apparmor/Kconfig | 31 +++++++++++++++++++++++++++++++ security/apparmor/Makefile | 24 ++++++++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 security/apparmor/.gitignore create mode 100644 security/apparmor/Kconfig create mode 100644 security/apparmor/Makefile diff --git a/security/apparmor/.gitignore b/security/apparmor/.gitignore new file mode 100644 index 00000000000..0a0a99f3b08 --- /dev/null +++ b/security/apparmor/.gitignore @@ -0,0 +1,5 @@ +# +# Generated include files +# +af_names.h +capability_names.h diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig new file mode 100644 index 00000000000..72555b9ca7d --- /dev/null +++ b/security/apparmor/Kconfig @@ -0,0 +1,31 @@ +config SECURITY_APPARMOR + bool "AppArmor support" + depends on SECURITY + select AUDIT + select SECURITY_PATH + select SECURITYFS + select SECURITY_NETWORK + default n + help + This enables the AppArmor security module. + Required userspace tools (if they are not included in your + distribution) and further information may be found at + http://apparmor.wiki.kernel.org + + If you are unsure how to answer this question, answer N. + +config SECURITY_APPARMOR_BOOTPARAM_VALUE + int "AppArmor boot parameter default value" + depends on SECURITY_APPARMOR + range 0 1 + default 1 + help + This option sets the default value for the kernel parameter + 'apparmor', which allows AppArmor to be enabled or disabled + at boot. If this option is set to 0 (zero), the AppArmor + kernel parameter will default to 0, disabling AppArmor at + boot. If this option is set to 1 (one), the AppArmor + kernel parameter will default to 1, enabling AppArmor at + boot. + + If you are unsure how to answer this question, answer 1. diff --git a/security/apparmor/Makefile b/security/apparmor/Makefile new file mode 100644 index 00000000000..f204869399e --- /dev/null +++ b/security/apparmor/Makefile @@ -0,0 +1,24 @@ +# Makefile for AppArmor Linux Security Module +# +obj-$(CONFIG_SECURITY_APPARMOR) += apparmor.o + +apparmor-y := apparmorfs.o audit.o capability.o context.o ipc.o lib.o match.o \ + path.o domain.o policy.o policy_unpack.o procattr.o lsm.o \ + resource.o sid.o file.o + +clean-files: capability_names.h af_names.h + +quiet_cmd_make-caps = GEN $@ +cmd_make-caps = echo "static const char *capability_names[] = {" > $@ ; sed -n -e "/CAP_FS_MASK/d" -e "s/^\#define[ \\t]\\+CAP_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ + +quiet_cmd_make-rlim = GEN $@ +cmd_make-rlim = echo "static const char *rlim_names[] = {" > $@ ; sed -n --e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+RLIMIT_\\([A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/[\\2] = \"\\1\",/p" $< | tr A-Z a-z >> $@ ; echo "};" >> $@ ; echo "static const int rlim_map[] = {" >> $@ ; sed -n -e "/AF_MAX/d" -e "s/^\# \\?define[ \\t]\\+\\(RLIMIT_[A-Z0-9_]\\+\\)[ \\t]\\+\\([0-9]\\+\\)\\(.*\\)\$$/\\1,/p" $< >> $@ ; echo "};" >> $@ + +$(obj)/capability.o : $(obj)/capability_names.h +$(obj)/resource.o : $(obj)/rlim_names.h +$(obj)/capability_names.h : $(srctree)/include/linux/capability.h + $(call cmd,make-caps) +$(obj)/af_names.h : $(srctree)/include/linux/socket.h + $(call cmd,make-af) +$(obj)/rlim_names.h : $(srctree)/include/asm-generic/resource.h + $(call cmd,make-rlim) -- cgit v1.2.3-70-g09d2 From 6371dcd36f649d9d07823f31400618155a20dde1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 29 Jul 2010 23:02:34 -0400 Subject: selinux: convert the policy type_attr_map to flex_array Current selinux policy can have over 3000 types. The type_attr_map in policy is an array sized by the number of types times sizeof(struct ebitmap) (12 on x86_64). Basic math tells us the array is going to be of length 3000 x 12 = 36,000 bytes. The largest 'safe' allocation on a long running system is 16k. Most of the time a 32k allocation will work. But on long running systems a 64k allocation (what we need) can fail quite regularly. In order to deal with this I am converting the type_attr_map to use flex_arrays. Let the library code deal with breaking this into PAGE_SIZE pieces. -v2 rework some of the if(!obj) BUG() to be BUG_ON(!obj) drop flex_array_put() calls and just use a _get() object directly -v3 make apply to James' tree (drop the policydb_write changes) Signed-off-by: Eric Paris Acked-by: Stephen D. Smalley Signed-off-by: James Morris --- security/selinux/ss/policydb.c | 41 +++++++++++++++++++++++++++++++---------- security/selinux/ss/policydb.h | 4 +++- security/selinux/ss/services.c | 7 +++++-- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 674ddfe0ba0..3a29704be8c 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "security.h" #include "policydb.h" @@ -739,11 +740,17 @@ void policydb_destroy(struct policydb *p) hashtab_map(p->range_tr, range_tr_destroy, NULL); hashtab_destroy(p->range_tr); - if (p->type_attr_map) { - for (i = 0; i < p->p_types.nprim; i++) - ebitmap_destroy(&p->type_attr_map[i]); + if (p->type_attr_map_array) { + for (i = 0; i < p->p_types.nprim; i++) { + struct ebitmap *e; + + e = flex_array_get(p->type_attr_map_array, i); + if (!e) + continue; + ebitmap_destroy(e); + } + flex_array_free(p->type_attr_map_array); } - kfree(p->type_attr_map); ebitmap_destroy(&p->policycaps); ebitmap_destroy(&p->permissive_map); @@ -2257,19 +2264,33 @@ int policydb_read(struct policydb *p, void *fp) if (rc) goto bad; - p->type_attr_map = kmalloc(p->p_types.nprim * sizeof(struct ebitmap), GFP_KERNEL); - if (!p->type_attr_map) + rc = -ENOMEM; + p->type_attr_map_array = flex_array_alloc(sizeof(struct ebitmap), + p->p_types.nprim, + GFP_KERNEL | __GFP_ZERO); + if (!p->type_attr_map_array) + goto bad; + + /* preallocate so we don't have to worry about the put ever failing */ + rc = flex_array_prealloc(p->type_attr_map_array, 0, p->p_types.nprim - 1, + GFP_KERNEL | __GFP_ZERO); + if (rc) goto bad; for (i = 0; i < p->p_types.nprim; i++) { - ebitmap_init(&p->type_attr_map[i]); + struct ebitmap *e = flex_array_get(p->type_attr_map_array, i); + + BUG_ON(!e); + ebitmap_init(e); if (p->policyvers >= POLICYDB_VERSION_AVTAB) { - if (ebitmap_read(&p->type_attr_map[i], fp)) + rc = ebitmap_read(e, fp); + if (rc) goto bad; } /* add the type itself as the degenerate case */ - if (ebitmap_set_bit(&p->type_attr_map[i], i, 1)) - goto bad; + rc = ebitmap_set_bit(e, i, 1); + if (rc) + goto bad; } rc = policydb_bounds_sanity_check(p); diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index 26d9adf8542..310e94442cb 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -24,6 +24,8 @@ #ifndef _SS_POLICYDB_H_ #define _SS_POLICYDB_H_ +#include + #include "symtab.h" #include "avtab.h" #include "sidtab.h" @@ -246,7 +248,7 @@ struct policydb { struct hashtab *range_tr; /* type -> attribute reverse mapping */ - struct ebitmap *type_attr_map; + struct flex_array *type_attr_map_array; struct ebitmap policycaps; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 1de60ce90d9..9ea2feca3cd 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -50,6 +50,7 @@ #include #include #include +#include #include #include "flask.h" @@ -626,8 +627,10 @@ static void context_struct_compute_av(struct context *scontext, */ avkey.target_class = tclass; avkey.specified = AVTAB_AV; - sattr = &policydb.type_attr_map[scontext->type - 1]; - tattr = &policydb.type_attr_map[tcontext->type - 1]; + sattr = flex_array_get(policydb.type_attr_map_array, scontext->type - 1); + BUG_ON(!sattr); + tattr = flex_array_get(policydb.type_attr_map_array, tcontext->type - 1); + BUG_ON(!tattr); ebitmap_for_each_positive_bit(sattr, snode, i) { ebitmap_for_each_positive_bit(tattr, tnode, j) { avkey.source_type = i + 1; -- cgit v1.2.3-70-g09d2 From 77c80e6b2fd049848bfd1bdab67899ad3ac407a7 Mon Sep 17 00:00:00 2001 From: James Morris Date: Mon, 2 Aug 2010 15:49:00 +1000 Subject: AppArmor: fix build warnings for non-const use of get_task_cred Fix build warnings for non-const use of get_task_cred. Signed-off-by: James Morris --- security/apparmor/domain.c | 2 +- security/apparmor/ipc.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 08bbe6397a7..c825c6e0b63 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -62,7 +62,7 @@ static int may_change_ptraced_domain(struct task_struct *task, struct aa_profile *to_profile) { struct task_struct *tracer; - struct cred *cred = NULL; + const struct cred *cred = NULL; struct aa_profile *tracerp = NULL; int error = 0; diff --git a/security/apparmor/ipc.c b/security/apparmor/ipc.c index 9013a78a166..649fad88869 100644 --- a/security/apparmor/ipc.c +++ b/security/apparmor/ipc.c @@ -100,7 +100,7 @@ int aa_ptrace(struct task_struct *tracer, struct task_struct *tracee, if (!unconfined(tracer_p)) { /* lcred released below */ - struct cred *lcred = get_task_cred(tracee); + const struct cred *lcred = get_task_cred(tracee); struct aa_profile *tracee_p = aa_cred_profile(lcred); error = aa_may_ptrace(tracer, tracer_p, tracee_p, mode); -- cgit v1.2.3-70-g09d2