From 1084307ca097745ed6e40a192329b133a49271ac Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Thu, 3 Jun 2010 20:38:03 +0900 Subject: [PATCH] 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 3f94011c6411..bdf1ed7ca45b 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 d1b8d791bfff..54db39aa339b 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 50f6e7972174..a07ca6dc1a08 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 be2d3b935533..8a31f0c628b2 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;