diff options
author | Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> | 2011-09-16 22:54:25 +0900 |
---|---|---|
committer | James Morris <jmorris@namei.org> | 2011-09-19 10:09:59 +1000 |
commit | 6bce98edc3365a8f780ff3944ac7992544c194fe (patch) | |
tree | ee10abf2345f651d65d7f10fd385c01e0dc891b3 /security/tomoyo | |
parent | cc100551b4d92f47abebfa7c7918b2be71263b4a (diff) | |
download | lwn-6bce98edc3365a8f780ff3944ac7992544c194fe.tar.gz lwn-6bce98edc3365a8f780ff3944ac7992544c194fe.zip |
TOMOYO: Allow specifying domain transition preference.
I got an opinion that it is difficult to use exception policy's domain
transition control directives because they need to match the pathname specified
to "file execute" directives. For example, if "file execute /bin/\*\-ls\-cat"
is given, corresponding domain transition control directive needs to be like
"no_keep_domain /bin/\*\-ls\-cat from any".
If we can specify like below, it will become more convenient.
file execute /bin/ls keep exec.realpath="/bin/ls" exec.argv[0]="ls"
file execute /bin/cat keep exec.realpath="/bin/cat" exec.argv[0]="cat"
file execute /bin/\*\-ls\-cat child
file execute /usr/sbin/httpd <apache> exec.realpath="/usr/sbin/httpd" exec.argv[0]="/usr/sbin/httpd"
In above examples, "keep" works as if keep_domain is specified, "child" works
as if "no_reset_domain" and "no_initialize_domain" and "no_keep_domain" are
specified, "<apache>" causes domain transition to <apache> domain upon
successful execve() operation.
Moreover, we can also allow transition to different domains based on conditions
like below example.
<kernel> /usr/sbin/sshd
file execute /bin/bash <kernel> /usr/sbin/sshd //batch-session exec.argc=2 exec.argv[1]="-c"
file execute /bin/bash <kernel> /usr/sbin/sshd //root-session task.uid=0
file execute /bin/bash <kernel> /usr/sbin/sshd //nonroot-session task.uid!=0
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>
Diffstat (limited to 'security/tomoyo')
-rw-r--r-- | security/tomoyo/common.c | 4 | ||||
-rw-r--r-- | security/tomoyo/common.h | 4 | ||||
-rw-r--r-- | security/tomoyo/condition.c | 50 | ||||
-rw-r--r-- | security/tomoyo/domain.c | 53 | ||||
-rw-r--r-- | security/tomoyo/file.c | 38 |
5 files changed, 137 insertions, 12 deletions
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 084018351b4f..0994948f3edc 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1203,6 +1203,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, case 0: head->r.cond_index = 0; head->r.cond_step++; + if (cond->transit) { + tomoyo_set_space(head); + tomoyo_set_string(head, cond->transit->name); + } /* fall through */ case 1: { diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h index 471c9f9afc18..a2bc33fc60b6 100644 --- a/security/tomoyo/common.h +++ b/security/tomoyo/common.h @@ -615,6 +615,7 @@ struct tomoyo_execve { struct tomoyo_request_info r; struct tomoyo_obj_info obj; struct linux_binprm *bprm; + const struct tomoyo_path_info *transition; /* For dumping argv[] and envp[]. */ struct tomoyo_page_dump dump; /* For temporary use. */ @@ -650,6 +651,7 @@ struct tomoyo_condition { u16 argc; /* Number of "struct tomoyo_argv". */ u16 envc; /* Number of "struct tomoyo_envp". */ u8 grant_log; /* One of values in "enum tomoyo_grant_log". */ + const struct tomoyo_path_info *transit; /* Maybe NULL. */ /* * struct tomoyo_condition_element condition[condc]; * struct tomoyo_number_union values[numbers_count]; @@ -956,6 +958,8 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct path *path, const int flag); int tomoyo_close_control(struct tomoyo_io_buffer *head); int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env); +int tomoyo_execute_permission(struct tomoyo_request_info *r, + const struct tomoyo_path_info *filename); int tomoyo_find_next_domain(struct linux_binprm *bprm); int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, const u8 index); diff --git a/security/tomoyo/condition.c b/security/tomoyo/condition.c index 3a05eb3e2a64..b854959c0fd4 100644 --- a/security/tomoyo/condition.c +++ b/security/tomoyo/condition.c @@ -348,7 +348,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a, a->numbers_count == b->numbers_count && a->names_count == b->names_count && a->argc == b->argc && a->envc == b->envc && - a->grant_log == b->grant_log && + a->grant_log == b->grant_log && a->transit == b->transit && !memcmp(a + 1, b + 1, a->size - sizeof(*a)); } @@ -429,6 +429,46 @@ out: } /** + * tomoyo_get_transit_preference - Parse domain transition preference for execve(). + * + * @param: Pointer to "struct tomoyo_acl_param". + * @e: Pointer to "struct tomoyo_condition". + * + * Returns the condition string part. + */ +static char *tomoyo_get_transit_preference(struct tomoyo_acl_param *param, + struct tomoyo_condition *e) +{ + char * const pos = param->data; + bool flag; + if (*pos == '<') { + e->transit = tomoyo_get_domainname(param); + goto done; + } + { + char *cp = strchr(pos, ' '); + if (cp) + *cp = '\0'; + flag = tomoyo_correct_path(pos) || !strcmp(pos, "keep") || + !strcmp(pos, "initialize") || !strcmp(pos, "reset") || + !strcmp(pos, "child") || !strcmp(pos, "parent"); + if (cp) + *cp = ' '; + } + if (!flag) + return pos; + e->transit = tomoyo_get_name(tomoyo_read_token(param)); +done: + if (e->transit) + return param->data; + /* + * Return a bad read-only condition string that will let + * tomoyo_get_condition() return NULL. + */ + return "/"; +} + +/** * tomoyo_get_condition - Parse condition part. * * @param: Pointer to "struct tomoyo_acl_param". @@ -444,7 +484,8 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) struct tomoyo_argv *argv = NULL; struct tomoyo_envp *envp = NULL; struct tomoyo_condition e = { }; - char * const start_of_string = param->data; + char * const start_of_string = + tomoyo_get_transit_preference(param, &e); char * const end_of_string = start_of_string + strlen(start_of_string); char *pos; rerun: @@ -608,8 +649,9 @@ store_value: + e.envc * sizeof(struct tomoyo_envp); entry = kzalloc(e.size, GFP_NOFS); if (!entry) - return NULL; + goto out2; *entry = e; + e.transit = NULL; condp = (struct tomoyo_condition_element *) (entry + 1); numbers_p = (struct tomoyo_number_union *) (condp + e.condc); names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count); @@ -636,6 +678,8 @@ out: tomoyo_del_condition(&entry->head.list); kfree(entry); } +out2: + tomoyo_put_name(e.transit); return NULL; } diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c index a1fc6b5f6125..860390ee1fbe 100644 --- a/security/tomoyo/domain.c +++ b/security/tomoyo/domain.c @@ -102,6 +102,15 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, new_entry->cond = tomoyo_get_condition(param); if (!new_entry->cond) return -EINVAL; + /* + * Domain transition preference is allowed for only + * "file execute" entries. + */ + if (new_entry->cond->transit && + !(new_entry->type == TOMOYO_TYPE_PATH_ACL && + container_of(new_entry, struct tomoyo_path_acl, head) + ->perm == 1 << TOMOYO_TYPE_EXECUTE)) + goto out; } if (mutex_lock_interruptible(&tomoyo_policy_lock)) goto out; @@ -707,8 +716,7 @@ retry: } /* Check execute permission. */ - retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, - candidate); + retval = tomoyo_execute_permission(&ee->r, candidate); if (retval == TOMOYO_RETRY_REQUEST) goto retry; if (retval < 0) @@ -722,10 +730,45 @@ retry: if (ee->r.param.path.matched_path) candidate = ee->r.param.path.matched_path; - /* Calculate domain to transit to. */ + /* + * Check for domain transition preference if "file execute" matched. + * If preference is given, make do_execve() fail if domain transition + * has failed, for domain transition preference should be used with + * destination domain defined. + */ + if (ee->transition) { + const char *domainname = ee->transition->name; + reject_on_transition_failure = true; + if (!strcmp(domainname, "keep")) + goto force_keep_domain; + if (!strcmp(domainname, "child")) + goto force_child_domain; + if (!strcmp(domainname, "reset")) + goto force_reset_domain; + if (!strcmp(domainname, "initialize")) + goto force_initialize_domain; + if (!strcmp(domainname, "parent")) { + char *cp; + strncpy(ee->tmp, old_domain->domainname->name, + TOMOYO_EXEC_TMPSIZE - 1); + cp = strrchr(ee->tmp, ' '); + if (cp) + *cp = '\0'; + } else if (*domainname == '<') + strncpy(ee->tmp, domainname, TOMOYO_EXEC_TMPSIZE - 1); + else + snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", + old_domain->domainname->name, domainname); + goto force_jump_domain; + } + /* + * No domain transition preference specified. + * Calculate domain to transit to. + */ switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname, candidate)) { case TOMOYO_TRANSITION_CONTROL_RESET: +force_reset_domain: /* Transit to the root of specified namespace. */ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", candidate->name); @@ -736,11 +779,13 @@ retry: reject_on_transition_failure = true; break; case TOMOYO_TRANSITION_CONTROL_INITIALIZE: +force_initialize_domain: /* Transit to the child of current namespace's root. */ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", old_domain->ns->name, candidate->name); break; case TOMOYO_TRANSITION_CONTROL_KEEP: +force_keep_domain: /* Keep current domain. */ domain = old_domain; break; @@ -756,11 +801,13 @@ retry: domain = old_domain; break; } +force_child_domain: /* Normal domain transition. */ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", old_domain->domainname->name, candidate->name); break; } +force_jump_domain: if (!domain) domain = tomoyo_assign_domain(ee->tmp, true); if (domain) diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 743c35f5084a..b280c1bd652d 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -570,16 +570,42 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, do { tomoyo_check_acl(r, tomoyo_check_path_acl); error = tomoyo_audit_path_log(r); - /* - * Do not retry for execute request, for alias may have - * changed. - */ - } while (error == TOMOYO_RETRY_REQUEST && - operation != TOMOYO_TYPE_EXECUTE); + } while (error == TOMOYO_RETRY_REQUEST); return error; } /** + * tomoyo_execute_permission - Check permission for execute operation. + * + * @r: Pointer to "struct tomoyo_request_info". + * @filename: Filename to check. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +int tomoyo_execute_permission(struct tomoyo_request_info *r, + const struct tomoyo_path_info *filename) +{ + /* + * Unlike other permission checks, this check is done regardless of + * profile mode settings in order to check for domain transition + * preference. + */ + r->type = TOMOYO_MAC_FILE_EXECUTE; + r->mode = tomoyo_get_mode(r->domain->ns, r->profile, r->type); + r->param_type = TOMOYO_TYPE_PATH_ACL; + r->param.path.filename = filename; + r->param.path.operation = TOMOYO_TYPE_EXECUTE; + tomoyo_check_acl(r, tomoyo_check_path_acl); + r->ee->transition = r->matched_acl && r->matched_acl->cond ? + r->matched_acl->cond->transit : NULL; + if (r->mode != TOMOYO_CONFIG_DISABLED) + return tomoyo_audit_path_log(r); + return 0; +} + +/** * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry. * * @a: Pointer to "struct tomoyo_acl_info". |