diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index 084018351b4f0b9979b2f2f8eddeccfa2ca410a1..0994948f3edc467386c5c972cfa8a1d24d7e5de0 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 471c9f9afc185968fa7c0d4421cc91c60a4c4f6f..a2bc33fc60b64ef9b3286dd9d470f0c08d567d4a 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 3a05eb3e2a643f4b973c65173e479b89db4a319d..b854959c0fd4803c34b2a6bde0bdc3f474847bd8 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)); } @@ -428,6 +428,46 @@ static struct tomoyo_condition *tomoyo_commit_condition return entry; } +/** + * 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. * @@ -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 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) + 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 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) 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 a1fc6b5f61258c7ad9c4302d8e14ea085100d1a2..860390ee1fbe9548dae129d34b1904befa04ccb7 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 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) } /* 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 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) 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 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) 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 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) 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 743c35f5084a1125c63e04043bdfb71bb8128651..b280c1bd652d409f1def2758545afdc165dbbb37 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -570,15 +570,41 @@ 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. *