Commit 6bce98ed authored by Tetsuo Handa's avatar Tetsuo Handa Committed by James Morris

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: default avatarTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent cc100551
...@@ -1203,6 +1203,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, ...@@ -1203,6 +1203,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
case 0: case 0:
head->r.cond_index = 0; head->r.cond_index = 0;
head->r.cond_step++; head->r.cond_step++;
if (cond->transit) {
tomoyo_set_space(head);
tomoyo_set_string(head, cond->transit->name);
}
/* fall through */ /* fall through */
case 1: case 1:
{ {
......
...@@ -615,6 +615,7 @@ struct tomoyo_execve { ...@@ -615,6 +615,7 @@ struct tomoyo_execve {
struct tomoyo_request_info r; struct tomoyo_request_info r;
struct tomoyo_obj_info obj; struct tomoyo_obj_info obj;
struct linux_binprm *bprm; struct linux_binprm *bprm;
const struct tomoyo_path_info *transition;
/* For dumping argv[] and envp[]. */ /* For dumping argv[] and envp[]. */
struct tomoyo_page_dump dump; struct tomoyo_page_dump dump;
/* For temporary use. */ /* For temporary use. */
...@@ -650,6 +651,7 @@ struct tomoyo_condition { ...@@ -650,6 +651,7 @@ struct tomoyo_condition {
u16 argc; /* Number of "struct tomoyo_argv". */ u16 argc; /* Number of "struct tomoyo_argv". */
u16 envc; /* Number of "struct tomoyo_envp". */ u16 envc; /* Number of "struct tomoyo_envp". */
u8 grant_log; /* One of values in "enum tomoyo_grant_log". */ 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_condition_element condition[condc];
* struct tomoyo_number_union values[numbers_count]; * struct tomoyo_number_union values[numbers_count];
...@@ -956,6 +958,8 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, ...@@ -956,6 +958,8 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
struct path *path, const int flag); struct path *path, const int flag);
int tomoyo_close_control(struct tomoyo_io_buffer *head); int tomoyo_close_control(struct tomoyo_io_buffer *head);
int tomoyo_env_perm(struct tomoyo_request_info *r, const char *env); 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_find_next_domain(struct linux_binprm *bprm);
int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile, int tomoyo_get_mode(const struct tomoyo_policy_namespace *ns, const u8 profile,
const u8 index); const u8 index);
......
...@@ -348,7 +348,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a, ...@@ -348,7 +348,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a,
a->numbers_count == b->numbers_count && a->numbers_count == b->numbers_count &&
a->names_count == b->names_count && a->names_count == b->names_count &&
a->argc == b->argc && a->envc == b->envc && 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)); !memcmp(a + 1, b + 1, a->size - sizeof(*a));
} }
...@@ -428,6 +428,46 @@ static struct tomoyo_condition *tomoyo_commit_condition ...@@ -428,6 +428,46 @@ static struct tomoyo_condition *tomoyo_commit_condition
return entry; 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. * tomoyo_get_condition - Parse condition part.
* *
...@@ -444,7 +484,8 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) ...@@ -444,7 +484,8 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
struct tomoyo_argv *argv = NULL; struct tomoyo_argv *argv = NULL;
struct tomoyo_envp *envp = NULL; struct tomoyo_envp *envp = NULL;
struct tomoyo_condition e = { }; 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 * const end_of_string = start_of_string + strlen(start_of_string);
char *pos; char *pos;
rerun: rerun:
...@@ -608,8 +649,9 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param) ...@@ -608,8 +649,9 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
+ e.envc * sizeof(struct tomoyo_envp); + e.envc * sizeof(struct tomoyo_envp);
entry = kzalloc(e.size, GFP_NOFS); entry = kzalloc(e.size, GFP_NOFS);
if (!entry) if (!entry)
return NULL; goto out2;
*entry = e; *entry = e;
e.transit = NULL;
condp = (struct tomoyo_condition_element *) (entry + 1); condp = (struct tomoyo_condition_element *) (entry + 1);
numbers_p = (struct tomoyo_number_union *) (condp + e.condc); numbers_p = (struct tomoyo_number_union *) (condp + e.condc);
names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count); 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) ...@@ -636,6 +678,8 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
tomoyo_del_condition(&entry->head.list); tomoyo_del_condition(&entry->head.list);
kfree(entry); kfree(entry);
} }
out2:
tomoyo_put_name(e.transit);
return NULL; return NULL;
} }
......
...@@ -102,6 +102,15 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size, ...@@ -102,6 +102,15 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
new_entry->cond = tomoyo_get_condition(param); new_entry->cond = tomoyo_get_condition(param);
if (!new_entry->cond) if (!new_entry->cond)
return -EINVAL; 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)) if (mutex_lock_interruptible(&tomoyo_policy_lock))
goto out; goto out;
...@@ -707,8 +716,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) ...@@ -707,8 +716,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
} }
/* Check execute permission. */ /* Check execute permission. */
retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, retval = tomoyo_execute_permission(&ee->r, candidate);
candidate);
if (retval == TOMOYO_RETRY_REQUEST) if (retval == TOMOYO_RETRY_REQUEST)
goto retry; goto retry;
if (retval < 0) if (retval < 0)
...@@ -722,10 +730,45 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) ...@@ -722,10 +730,45 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
if (ee->r.param.path.matched_path) if (ee->r.param.path.matched_path)
candidate = 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, switch (tomoyo_transition_type(old_domain->ns, old_domain->domainname,
candidate)) { candidate)) {
case TOMOYO_TRANSITION_CONTROL_RESET: case TOMOYO_TRANSITION_CONTROL_RESET:
force_reset_domain:
/* Transit to the root of specified namespace. */ /* Transit to the root of specified namespace. */
snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>",
candidate->name); candidate->name);
...@@ -736,11 +779,13 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) ...@@ -736,11 +779,13 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
reject_on_transition_failure = true; reject_on_transition_failure = true;
break; break;
case TOMOYO_TRANSITION_CONTROL_INITIALIZE: case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
force_initialize_domain:
/* Transit to the child of current namespace's root. */ /* Transit to the child of current namespace's root. */
snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
old_domain->ns->name, candidate->name); old_domain->ns->name, candidate->name);
break; break;
case TOMOYO_TRANSITION_CONTROL_KEEP: case TOMOYO_TRANSITION_CONTROL_KEEP:
force_keep_domain:
/* Keep current domain. */ /* Keep current domain. */
domain = old_domain; domain = old_domain;
break; break;
...@@ -756,11 +801,13 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm) ...@@ -756,11 +801,13 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
domain = old_domain; domain = old_domain;
break; break;
} }
force_child_domain:
/* Normal domain transition. */ /* Normal domain transition. */
snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s", snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
old_domain->domainname->name, candidate->name); old_domain->domainname->name, candidate->name);
break; break;
} }
force_jump_domain:
if (!domain) if (!domain)
domain = tomoyo_assign_domain(ee->tmp, true); domain = tomoyo_assign_domain(ee->tmp, true);
if (domain) if (domain)
......
...@@ -570,15 +570,41 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation, ...@@ -570,15 +570,41 @@ int tomoyo_path_permission(struct tomoyo_request_info *r, u8 operation,
do { do {
tomoyo_check_acl(r, tomoyo_check_path_acl); tomoyo_check_acl(r, tomoyo_check_path_acl);
error = tomoyo_audit_path_log(r); error = tomoyo_audit_path_log(r);
/* } while (error == TOMOYO_RETRY_REQUEST);
* Do not retry for execute request, for alias may have
* changed.
*/
} while (error == TOMOYO_RETRY_REQUEST &&
operation != TOMOYO_TYPE_EXECUTE);
return error; 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. * tomoyo_same_path_number_acl - Check for duplicated "struct tomoyo_path_number_acl" entry.
* *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment