diff options
Diffstat (limited to 'security/tomoyo/domain.c')
-rw-r--r-- | security/tomoyo/domain.c | 855 |
1 files changed, 296 insertions, 559 deletions
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index cd8ba444676..35388408e47 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" @@ -18,366 +15,191 @@ /* The initial domain. */ struct tomoyo_domain_info tomoyo_kernel_domain; -/* - * 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 "<kernel>"; echo "allow_execute /sbin/init" ) > \ - * /sys/kernel/security/tomoyo/domain_policy - * - * and is deleted by - * - * # ( echo "<kernel>"; 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 "<kernel>" > /sys/kernel/security/tomoyo/domain_policy - * - * and is deleted by - * - * # echo "delete <kernel>" > /sys/kernel/security/tomoyo/domain_policy - * - * and all domains are retrieved by - * - * # grep '^<kernel>' /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. - */ -LIST_HEAD(tomoyo_domain_list); - /** - * tomoyo_get_last_name - Get last component of a domainname. - * - * @domain: Pointer to "struct tomoyo_domain_info". - * - * Returns the last component of the domainname. - */ -const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain) -{ - const char *cp0 = domain->domainname->name; - const char *cp1 = strrchr(cp0, ' '); - - if (cp1) - return cp1 + 1; - 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. + * tomoyo_update_policy - Update an entry for exception policy. * - * 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 - * "<kernel> /usr/sbin/httpd" domain. - * - * You may specify a domainname using "from" keyword. - * "initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd" - * will cause "/usr/sbin/httpd" executed from "<kernel> /etc/rc.d/init.d/httpd" - * domain to belong to "<kernel> /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 <kernel> /etc/rc.d/init.d/httpd" - * will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain - * unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain. - */ -LIST_HEAD(tomoyo_domain_initializer_list); - -/** - * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list. - * - * @domainname: The name of domain. May be NULL. - * @program: The name of program. - * @is_not: True if it is "no_initialize_domain" entry. - * @is_delete: True if it is a delete request. + * @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(). */ -static int tomoyo_update_domain_initializer_entry(const char *domainname, - const char *program, - const bool is_not, - const bool is_delete) +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 + *)) { - struct tomoyo_domain_initializer_entry *ptr; - struct tomoyo_domain_initializer_entry e = { .is_not = is_not }; int error = is_delete ? -ENOENT : -ENOMEM; + struct tomoyo_acl_head *entry; - if (!tomoyo_is_correct_path(program, 1, -1, -1)) - return -EINVAL; /* No patterns allowed. */ - if (domainname) { - if (!tomoyo_is_domain_def(domainname) && - tomoyo_is_correct_path(domainname, 1, -1, -1)) - e.is_last_name = true; - else if (!tomoyo_is_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; if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { - if (!tomoyo_is_same_domain_initializer_entry(ptr, &e)) + return -ENOMEM; + list_for_each_entry_rcu(entry, list, list) { + if (!check_duplicate(entry, new_entry)) continue; - ptr->is_deleted = is_delete; + entry->is_deleted = is_delete; error = 0; break; } - if (!is_delete && error) { - struct tomoyo_domain_initializer_entry *entry = - tomoyo_commit_ok(&e, sizeof(e)); + if (error && !is_delete) { + entry = tomoyo_commit_ok(new_entry, size); if (entry) { - list_add_tail_rcu(&entry->list, - &tomoyo_domain_initializer_list); + list_add_tail_rcu(&entry->list, list); error = 0; } } mutex_unlock(&tomoyo_policy_lock); - out: - tomoyo_put_name(e.domainname); - tomoyo_put_name(e.program); return error; } /** - * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list. + * tomoyo_update_domain - Update an entry for domain policy. * - * @head: Pointer to "struct tomoyo_io_buffer". + * @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 true on success, false otherwise. + * Returns 0 on success, negative value otherwise. * * Caller holds tomoyo_read_lock(). */ -bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head) +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)) { - struct list_head *pos; - bool done = true; + int error = is_delete ? -ENOENT : -ENOMEM; + struct tomoyo_acl_info *entry; - list_for_each_cookie(pos, head->read_var2, - &tomoyo_domain_initializer_list) { - const char *no; - const char *from = ""; - const char *domain = ""; - struct tomoyo_domain_initializer_entry *ptr; - ptr = list_entry(pos, struct tomoyo_domain_initializer_entry, - list); - if (ptr->is_deleted) + 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; - no = ptr->is_not ? "no_" : ""; - if (ptr->domainname) { - from = " from "; - domain = ptr->domainname->name; + 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; } - 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; + mutex_unlock(&tomoyo_policy_lock); + return error; } -/** - * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list. - * - * @data: String to parse. - * @is_not: True if it is "no_initialize_domain" entry. - * @is_delete: True if it is a delete request. - * - * 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) +void tomoyo_check_acl(struct tomoyo_request_info *r, + bool (*check_entry) (struct tomoyo_request_info *, + const struct tomoyo_acl_info *)) { - char *cp = strstr(data, " from "); + const struct tomoyo_domain_info *domain = r->domain; + struct tomoyo_acl_info *ptr; - if (cp) { - *cp = '\0'; - return tomoyo_update_domain_initializer_entry(cp + 6, data, - is_not, - is_delete); + 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; + } } - return tomoyo_update_domain_initializer_entry(NULL, data, is_not, - is_delete); + r->granted = false; } +/* 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_is_domain_initializer - Check whether the given program causes domainname reinitialization. - * - * @domainname: The name of domain. - * @program: The name of program. - * @last_name: The last component of @domainname. + * tomoyo_last_word - Get last component of a domainname. * - * Returns true if executing @program reinitializes domain transition, - * false otherwise. + * @domainname: Domainname to check. * - * Caller holds tomoyo_read_lock(). + * Returns the last word of @domainname. */ -static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info * - domainname, - const struct tomoyo_path_info *program, - const struct tomoyo_path_info * - last_name) +static const char *tomoyo_last_word(const char *name) { - struct tomoyo_domain_initializer_entry *ptr; - bool flag = false; - - list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) { - if (ptr->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; + const char *cp = strrchr(name, ' '); + if (cp) + return cp + 1; + return name; } -/* - * 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 <kernel> /usr/sbin/sshd /bin/bash' > \ - * /sys/kernel/security/tomoyo/exception_policy - * - * and is deleted by - * - * # echo 'delete keep_domain <kernel> /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 - * "<kernel> /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 <kernel> /usr/sbin/sshd /bin/bash" - * will cause "/bin/pwd" executed from "<kernel> /usr/sbin/sshd /bin/bash" - * domain to remain in "<kernel> /usr/sbin/sshd /bin/bash" domain. - * - * You may add "no_" prefix to "keep_domain". - * "keep_domain <kernel> /usr/sbin/sshd /bin/bash" and - * "no_keep_domain /usr/bin/passwd from <kernel> /usr/sbin/sshd /bin/bash" will - * cause "/usr/bin/passwd" to belong to - * "<kernel> /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_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), + 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_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list. + * tomoyo_update_transition_control_entry - Update "struct tomoyo_transition_control" list. * - * @domainname: The name of domain. - * @program: The name of program. May be NULL. - * @is_not: True if it is "no_keep_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_keeper_entry(const char *domainname, - const char *program, - const bool is_not, - const bool is_delete) +static int tomoyo_update_transition_control_entry(const char *domainname, + const char *program, + const u8 type, + const bool is_delete) { - struct tomoyo_domain_keeper_entry *ptr; - struct tomoyo_domain_keeper_entry e = { .is_not = is_not }; + struct tomoyo_transition_control e = { .type = type }; int error = is_delete ? -ENOENT : -ENOMEM; - - if (!tomoyo_is_domain_def(domainname) && - tomoyo_is_correct_path(domainname, 1, -1, -1)) - 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_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; - if (mutex_lock_interruptible(&tomoyo_policy_lock)) - goto out; - list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { - if (!tomoyo_is_same_domain_keeper_entry(ptr, &e)) - continue; - ptr->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->list, - &tomoyo_domain_keeper_list); - error = 0; + if (domainname) { + if (!tomoyo_correct_domain(domainname)) { + if (!tomoyo_correct_path(domainname)) + goto out; + e.is_last_name = true; } + e.domainname = tomoyo_get_name(domainname); + if (!e.domainname) + goto out; } - mutex_unlock(&tomoyo_policy_lock); + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + &tomoyo_policy_list + [TOMOYO_ID_TRANSITION_CONTROL], + tomoyo_same_transition_control); out: tomoyo_put_name(e.domainname); tomoyo_put_name(e.program); @@ -385,219 +207,133 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname, } /** - * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list. + * tomoyo_write_transition_control - Write "struct tomoyo_transition_control" list. * * @data: String to parse. - * @is_not: True if it is "no_keep_domain" entry. * @is_delete: True if it is a delete request. + * @type: Type of this entry. * - * 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_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(). + * Returns 0 on success, negative value otherwise. */ -bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head) +int tomoyo_write_transition_control(char *data, const bool is_delete, + const u8 type) { - struct list_head *pos; - bool done = true; - - list_for_each_cookie(pos, head->read_var2, - &tomoyo_domain_keeper_list) { - struct tomoyo_domain_keeper_entry *ptr; - const char *no; - const char *from = ""; - const char *program = ""; - - ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, list); - if (ptr->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; + 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 done; + return tomoyo_update_transition_control_entry(domainname, data, type, + is_delete); } /** - * tomoyo_is_domain_keeper - Check whether the given program causes domain transition suppression. + * 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 supresses 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_is_domain_keeper(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_keeper_entry *ptr; - bool flag = false; - - list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) { - if (ptr->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; } -/* - * 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_aggregator(const struct tomoyo_acl_head *a, + const struct tomoyo_acl_head *b) +{ + 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_alias_entry - Update "struct tomoyo_alias_entry" list. + * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator" 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. + * @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_alias_entry(const char *original_name, - const char *aliased_name, - const bool is_delete) +static int tomoyo_update_aggregator_entry(const char *original_name, + const char *aggregated_name, + const bool is_delete) { - struct tomoyo_alias_entry *ptr; - struct tomoyo_alias_entry e = { }; + struct tomoyo_aggregator 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_correct_path(original_name) || + !tomoyo_correct_path(aggregated_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.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_alias_list, list) { - if (!tomoyo_is_same_alias_entry(ptr, &e)) - continue; - ptr->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->list, &tomoyo_alias_list); - error = 0; - } - } - mutex_unlock(&tomoyo_policy_lock); + error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, + &tomoyo_policy_list[TOMOYO_ID_AGGREGATOR], + tomoyo_same_aggregator); out: tomoyo_put_name(e.original_name); - tomoyo_put_name(e.aliased_name); + tomoyo_put_name(e.aggregated_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_alias_list) { - struct tomoyo_alias_entry *ptr; - - ptr = list_entry(pos, struct tomoyo_alias_entry, list); - if (ptr->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. + * tomoyo_write_aggregator - Write "struct tomoyo_aggregator" list. * * @data: String to parse. * @is_delete: True if it is a delete request. @@ -606,18 +342,18 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head) * * Caller holds tomoyo_read_lock(). */ -int tomoyo_write_alias_policy(char *data, const bool is_delete) +int tomoyo_write_aggregator(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); + return tomoyo_update_aggregator_entry(data, cp, 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. @@ -626,16 +362,15 @@ int tomoyo_write_alias_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; 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) @@ -678,116 +413,118 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char * */ int tomoyo_find_next_domain(struct linux_binprm *bprm) { - /* - * This function assumes that the size of buffer returned by - * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN. - */ - struct tomoyo_page_buffer *tmp = kzalloc(sizeof(*tmp), GFP_NOFS); + struct tomoyo_request_info r; + 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; - 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 == 3); + u8 mode; + bool is_enforce; int retval = -ENOMEM; - struct tomoyo_path_info r; /* real name */ - struct tomoyo_path_info s; /* symlink name */ - struct tomoyo_path_info l; /* last name */ - static bool initialized; + bool need_kfree = false; + struct tomoyo_path_info rn = { }; /* real name */ + mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE); + is_enforce = (mode == TOMOYO_CONFIG_ENFORCING); 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: + if (need_kfree) { + kfree(rn.name); + need_kfree = false; } - - /* Get tomoyo_realpath of program. */ + /* Get symlink's pathname of program. */ retval = -ENOENT; - /* I hope tomoyo_realpath() won't fail with -ENOMEM. */ - real_program_name = tomoyo_realpath(original_name); - if (!real_program_name) - goto out; - /* Get tomoyo_realpath of symbolic link. */ - symlink_program_name = tomoyo_realpath_nofollow(original_name); - if (!symlink_program_name) + rn.name = tomoyo_realpath_nofollow(original_name); + if (!rn.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); - - /* Check 'alias' directive. */ - if (tomoyo_pathcmp(&r, &s)) { - 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_fill_path_info(&rn); + need_kfree = true; + + /* Check 'aggregator' directive. */ + { + struct tomoyo_aggregator *ptr; + 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)) 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); + 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, &r); + retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn); + if (retval == TOMOYO_RETRY_REQUEST) + 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; + } - new_domain_name = tmp->buffer; - if (tomoyo_is_domain_initializer(old_domain->domainname, &r, &l)) { + /* 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(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1, - TOMOYO_ROOT_NAME " " "%s", real_program_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_is_domain_keeper(old_domain->domainname, &r, &l)) { + 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(new_domain_name, TOMOYO_MAX_PATHNAME_LEN + 1, - "%s %s", old_domain_name, real_program_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(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) - goto done; - domain = tomoyo_find_or_assign_new_domain(new_domain_name, - old_domain->profile); + if (is_enforce) { + int error = tomoyo_supervisor(&r, "# wants to create domain\n" + "%s\n", tmp); + if (error == TOMOYO_RETRY_REQUEST) + goto retry; + if (error < 0) + goto done; + } + domain = tomoyo_assign_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 @@ -798,8 +535,8 @@ 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(tmp); return retval; } |