Commit 731d37aa authored by Tetsuo Handa's avatar Tetsuo Handa Committed by James Morris

TOMOYO: Allow domain transition without execve().

To be able to split permissions for Apache's CGI programs which are executed
without execve(), add special domain transition which is performed by writing
a TOMOYO's domainname to /sys/kernel/security/tomoyo/self_domain interface.

This is an API for TOMOYO-aware userland applications. However, since I expect
TOMOYO and other LSM modules to run in parallel, this patch does not use
/proc/self/attr/ interface in order to avoid conflicts with other LSM modules
when it became possible to run multiple LSM modules in parallel.
Signed-off-by: default avatarTetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: default avatarJames Morris <jmorris@namei.org>
parent 1f067a68
...@@ -1010,6 +1010,48 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, ...@@ -1010,6 +1010,48 @@ static bool tomoyo_select_domain(struct tomoyo_io_buffer *head,
return true; return true;
} }
/**
* tomoyo_same_task_acl - Check for duplicated "struct tomoyo_task_acl" entry.
*
* @a: Pointer to "struct tomoyo_acl_info".
* @b: Pointer to "struct tomoyo_acl_info".
*
* Returns true if @a == @b, false otherwise.
*/
static bool tomoyo_same_task_acl(const struct tomoyo_acl_info *a,
const struct tomoyo_acl_info *b)
{
const struct tomoyo_task_acl *p1 = container_of(a, typeof(*p1), head);
const struct tomoyo_task_acl *p2 = container_of(b, typeof(*p2), head);
return p1->domainname == p2->domainname;
}
/**
* tomoyo_write_task - Update task related list.
*
* @param: Pointer to "struct tomoyo_acl_param".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
static int tomoyo_write_task(struct tomoyo_acl_param *param)
{
int error = -EINVAL;
if (tomoyo_str_starts(&param->data, "manual_domain_transition ")) {
struct tomoyo_task_acl e = {
.head.type = TOMOYO_TYPE_MANUAL_TASK_ACL,
.domainname = tomoyo_get_domainname(param),
};
if (e.domainname)
error = tomoyo_update_domain(&e.head, sizeof(e), param,
tomoyo_same_task_acl,
NULL);
tomoyo_put_name(e.domainname);
}
return error;
}
/** /**
* tomoyo_delete_domain - Delete a domain. * tomoyo_delete_domain - Delete a domain.
* *
...@@ -1068,11 +1110,12 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns, ...@@ -1068,11 +1110,12 @@ static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns,
static const struct { static const struct {
const char *keyword; const char *keyword;
int (*write) (struct tomoyo_acl_param *); int (*write) (struct tomoyo_acl_param *);
} tomoyo_callback[4] = { } tomoyo_callback[5] = {
{ "file ", tomoyo_write_file }, { "file ", tomoyo_write_file },
{ "network inet ", tomoyo_write_inet_network }, { "network inet ", tomoyo_write_inet_network },
{ "network unix ", tomoyo_write_unix_network }, { "network unix ", tomoyo_write_unix_network },
{ "misc ", tomoyo_write_misc }, { "misc ", tomoyo_write_misc },
{ "task ", tomoyo_write_task },
}; };
u8 i; u8 i;
...@@ -1343,6 +1386,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head, ...@@ -1343,6 +1386,12 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,
if (first) if (first)
return true; return true;
tomoyo_print_name_union(head, &ptr->name); tomoyo_print_name_union(head, &ptr->name);
} else if (acl_type == TOMOYO_TYPE_MANUAL_TASK_ACL) {
struct tomoyo_task_acl *ptr =
container_of(acl, typeof(*ptr), head);
tomoyo_set_group(head, "task ");
tomoyo_set_string(head, "manual_domain_transition ");
tomoyo_set_string(head, ptr->domainname->name);
} else if (head->r.print_transition_related_only) { } else if (head->r.print_transition_related_only) {
return true; return true;
} else if (acl_type == TOMOYO_TYPE_PATH2_ACL) { } else if (acl_type == TOMOYO_TYPE_PATH2_ACL) {
...@@ -2178,26 +2227,6 @@ static void tomoyo_read_version(struct tomoyo_io_buffer *head) ...@@ -2178,26 +2227,6 @@ static void tomoyo_read_version(struct tomoyo_io_buffer *head)
} }
} }
/**
* tomoyo_read_self_domain - Get the current process's domainname.
*
* @head: Pointer to "struct tomoyo_io_buffer".
*
* Returns the current process's domainname.
*/
static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)
{
if (!head->r.eof) {
/*
* tomoyo_domain()->domainname != NULL
* because every process belongs to a domain and
* the domain's name cannot be NULL.
*/
tomoyo_io_printf(head, "%s", tomoyo_domain()->domainname->name);
head->r.eof = true;
}
}
/* String table for /sys/kernel/security/tomoyo/stat interface. */ /* String table for /sys/kernel/security/tomoyo/stat interface. */
static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = { static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = {
[TOMOYO_STAT_POLICY_UPDATES] = "update:", [TOMOYO_STAT_POLICY_UPDATES] = "update:",
...@@ -2328,10 +2357,6 @@ int tomoyo_open_control(const u8 type, struct file *file) ...@@ -2328,10 +2357,6 @@ int tomoyo_open_control(const u8 type, struct file *file)
head->poll = tomoyo_poll_log; head->poll = tomoyo_poll_log;
head->read = tomoyo_read_log; head->read = tomoyo_read_log;
break; break;
case TOMOYO_SELFDOMAIN:
/* /sys/kernel/security/tomoyo/self_domain */
head->read = tomoyo_read_self_domain;
break;
case TOMOYO_PROCESS_STATUS: case TOMOYO_PROCESS_STATUS:
/* /sys/kernel/security/tomoyo/.process_status */ /* /sys/kernel/security/tomoyo/.process_status */
head->write = tomoyo_write_pid; head->write = tomoyo_write_pid;
......
...@@ -227,6 +227,7 @@ enum tomoyo_acl_entry_type_index { ...@@ -227,6 +227,7 @@ enum tomoyo_acl_entry_type_index {
TOMOYO_TYPE_INET_ACL, TOMOYO_TYPE_INET_ACL,
TOMOYO_TYPE_UNIX_ACL, TOMOYO_TYPE_UNIX_ACL,
TOMOYO_TYPE_ENV_ACL, TOMOYO_TYPE_ENV_ACL,
TOMOYO_TYPE_MANUAL_TASK_ACL,
}; };
/* Index numbers for access controls with one pathname. */ /* Index numbers for access controls with one pathname. */
...@@ -295,7 +296,6 @@ enum tomoyo_securityfs_interface_index { ...@@ -295,7 +296,6 @@ enum tomoyo_securityfs_interface_index {
TOMOYO_EXCEPTIONPOLICY, TOMOYO_EXCEPTIONPOLICY,
TOMOYO_PROCESS_STATUS, TOMOYO_PROCESS_STATUS,
TOMOYO_STAT, TOMOYO_STAT,
TOMOYO_SELFDOMAIN,
TOMOYO_AUDIT, TOMOYO_AUDIT,
TOMOYO_VERSION, TOMOYO_VERSION,
TOMOYO_PROFILE, TOMOYO_PROFILE,
...@@ -480,6 +480,9 @@ struct tomoyo_request_info { ...@@ -480,6 +480,9 @@ struct tomoyo_request_info {
unsigned long flags; unsigned long flags;
int need_dev; int need_dev;
} mount; } mount;
struct {
const struct tomoyo_path_info *domainname;
} task;
} param; } param;
struct tomoyo_acl_info *matched_acl; struct tomoyo_acl_info *matched_acl;
u8 param_type; u8 param_type;
...@@ -679,6 +682,15 @@ struct tomoyo_domain_info { ...@@ -679,6 +682,15 @@ struct tomoyo_domain_info {
atomic_t users; /* Number of referring credentials. */ atomic_t users; /* Number of referring credentials. */
}; };
/*
* Structure for "task manual_domain_transition" directive.
*/
struct tomoyo_task_acl {
struct tomoyo_acl_info head; /* type = TOMOYO_TYPE_MANUAL_TASK_ACL */
/* Pointer to domainname. */
const struct tomoyo_path_info *domainname;
};
/* /*
* Structure for "file execute", "file read", "file write", "file append", * Structure for "file execute", "file read", "file write", "file append",
* "file unlink", "file getattr", "file rmdir", "file truncate", * "file unlink", "file getattr", "file rmdir", "file truncate",
...@@ -935,6 +947,8 @@ const char *tomoyo_get_exe(void); ...@@ -935,6 +947,8 @@ const char *tomoyo_get_exe(void);
const char *tomoyo_yesno(const unsigned int value); const char *tomoyo_yesno(const unsigned int value);
const struct tomoyo_path_info *tomoyo_compare_name_union const struct tomoyo_path_info *tomoyo_compare_name_union
(const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr); (const struct tomoyo_path_info *name, const struct tomoyo_name_union *ptr);
const struct tomoyo_path_info *tomoyo_get_domainname
(struct tomoyo_acl_param *param);
const struct tomoyo_path_info *tomoyo_get_name(const char *name); const struct tomoyo_path_info *tomoyo_get_name(const char *name);
const struct tomoyo_path_info *tomoyo_path_matches_group const struct tomoyo_path_info *tomoyo_path_matches_group
(const struct tomoyo_path_info *pathname, const struct tomoyo_group *group); (const struct tomoyo_path_info *pathname, const struct tomoyo_group *group);
......
...@@ -7,6 +7,124 @@ ...@@ -7,6 +7,124 @@
#include <linux/security.h> #include <linux/security.h>
#include "common.h" #include "common.h"
/**
* tomoyo_check_task_acl - Check permission for task operation.
*
* @r: Pointer to "struct tomoyo_request_info".
* @ptr: Pointer to "struct tomoyo_acl_info".
*
* Returns true if granted, false otherwise.
*/
static bool tomoyo_check_task_acl(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *ptr)
{
const struct tomoyo_task_acl *acl = container_of(ptr, typeof(*acl),
head);
return !tomoyo_pathcmp(r->param.task.domainname, acl->domainname);
}
/**
* tomoyo_write_self - write() for /sys/kernel/security/tomoyo/self_domain interface.
*
* @file: Pointer to "struct file".
* @buf: Domainname to transit to.
* @count: Size of @buf.
* @ppos: Unused.
*
* Returns @count on success, negative value otherwise.
*
* If domain transition was permitted but the domain transition failed, this
* function returns error rather than terminating current thread with SIGKILL.
*/
static ssize_t tomoyo_write_self(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
char *data;
int error;
if (!count || count >= TOMOYO_EXEC_TMPSIZE - 10)
return -ENOMEM;
data = kzalloc(count + 1, GFP_NOFS);
if (!data)
return -ENOMEM;
if (copy_from_user(data, buf, count)) {
error = -EFAULT;
goto out;
}
tomoyo_normalize_line(data);
if (tomoyo_correct_domain(data)) {
const int idx = tomoyo_read_lock();
struct tomoyo_path_info name;
struct tomoyo_request_info r;
name.name = data;
tomoyo_fill_path_info(&name);
/* Check "task manual_domain_transition" permission. */
tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
r.param_type = TOMOYO_TYPE_MANUAL_TASK_ACL;
r.param.task.domainname = &name;
tomoyo_check_acl(&r, tomoyo_check_task_acl);
if (!r.granted)
error = -EPERM;
else {
struct tomoyo_domain_info *new_domain =
tomoyo_assign_domain(data, true);
if (!new_domain) {
error = -ENOENT;
} else {
struct cred *cred = prepare_creds();
if (!cred) {
error = -ENOMEM;
} else {
struct tomoyo_domain_info *old_domain =
cred->security;
cred->security = new_domain;
atomic_inc(&new_domain->users);
atomic_dec(&old_domain->users);
commit_creds(cred);
error = 0;
}
}
}
tomoyo_read_unlock(idx);
} else
error = -EINVAL;
out:
kfree(data);
return error ? error : count;
}
/**
* tomoyo_read_self - read() for /sys/kernel/security/tomoyo/self_domain interface.
*
* @file: Pointer to "struct file".
* @buf: Domainname which current thread belongs to.
* @count: Size of @buf.
* @ppos: Bytes read by now.
*
* Returns read size on success, negative value otherwise.
*/
static ssize_t tomoyo_read_self(struct file *file, char __user *buf,
size_t count, loff_t *ppos)
{
const char *domain = tomoyo_domain()->domainname->name;
loff_t len = strlen(domain);
loff_t pos = *ppos;
if (pos >= len || !count)
return 0;
len -= pos;
if (count < len)
len = count;
if (copy_to_user(buf, domain + pos, len))
return -EFAULT;
*ppos += len;
return len;
}
/* Operations for /sys/kernel/security/tomoyo/self_domain interface. */
static const struct file_operations tomoyo_self_operations = {
.write = tomoyo_write_self,
.read = tomoyo_read_self,
};
/** /**
* tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface. * tomoyo_open - open() for /sys/kernel/security/tomoyo/ interface.
* *
...@@ -135,8 +253,6 @@ static int __init tomoyo_initerface_init(void) ...@@ -135,8 +253,6 @@ static int __init tomoyo_initerface_init(void)
TOMOYO_EXCEPTIONPOLICY); TOMOYO_EXCEPTIONPOLICY);
tomoyo_create_entry("audit", 0400, tomoyo_dir, tomoyo_create_entry("audit", 0400, tomoyo_dir,
TOMOYO_AUDIT); TOMOYO_AUDIT);
tomoyo_create_entry("self_domain", 0400, tomoyo_dir,
TOMOYO_SELFDOMAIN);
tomoyo_create_entry(".process_status", 0600, tomoyo_dir, tomoyo_create_entry(".process_status", 0600, tomoyo_dir,
TOMOYO_PROCESS_STATUS); TOMOYO_PROCESS_STATUS);
tomoyo_create_entry("stat", 0644, tomoyo_dir, tomoyo_create_entry("stat", 0644, tomoyo_dir,
...@@ -147,6 +263,8 @@ static int __init tomoyo_initerface_init(void) ...@@ -147,6 +263,8 @@ static int __init tomoyo_initerface_init(void)
TOMOYO_MANAGER); TOMOYO_MANAGER);
tomoyo_create_entry("version", 0400, tomoyo_dir, tomoyo_create_entry("version", 0400, tomoyo_dir,
TOMOYO_VERSION); TOMOYO_VERSION);
securityfs_create_file("self_domain", 0666, tomoyo_dir, NULL,
&tomoyo_self_operations);
return 0; return 0;
} }
......
...@@ -158,6 +158,31 @@ char *tomoyo_read_token(struct tomoyo_acl_param *param) ...@@ -158,6 +158,31 @@ char *tomoyo_read_token(struct tomoyo_acl_param *param)
return pos; return pos;
} }
/**
* tomoyo_get_domainname - Read a domainname from a line.
*
* @param: Pointer to "struct tomoyo_acl_param".
*
* Returns a domainname on success, NULL otherwise.
*/
const struct tomoyo_path_info *tomoyo_get_domainname
(struct tomoyo_acl_param *param)
{
char *start = param->data;
char *pos = start;
while (*pos) {
if (*pos++ != ' ' || *pos++ == '/')
continue;
pos -= 2;
*pos++ = '\0';
break;
}
param->data = pos;
if (tomoyo_correct_domain(start))
return tomoyo_get_name(start);
return NULL;
}
/** /**
* tomoyo_parse_ulong - Parse an "unsigned long" value. * tomoyo_parse_ulong - Parse an "unsigned long" value.
* *
......
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