[PATCH 2/4] TOMOYO: Add argv[]/envp[] condition support.

From: Tetsuo Handa
Date: Thu Jun 10 2010 - 08:09:48 EST


Command line parameters can change how the file is used. For example, executing
"/usr/sbin/sshd -o 'Banner /etc/shadow'" will make SSH server disclose the
content of /etc/shadow to unauthenticated SSH clients (because we have to allow
SSH server to read /etc/shadow in order to authenticate SSH clients).
Therefore, TOMOYO restricts argv[] upon execve() request in order to prevent
crackers from doing

$ sudo chmod 644 /etc/shadow
$ ssh-keygen -t rsa -f /tmp/key
$ /usr/sbin/sshd -o 'Banner /etc/shadow' -o 'Port 8022' -o 'HostKey /tmp/key'
$ ssh -p 8022 localhost

.

Environment variables can change how the system behaves. For example, executing
programs with LD_PRELOAD or LD_LIBRARY_PATH defined would alter the system's
behavior. Therefore, TOMOYO restricts envp[] upon execve() request.

This patch allows users to check argv[]/envp[] parameters passed to execve()
operation. For example,

allow_execute /bin/true if exec.argc=1 exec.argv[1]="--help" exec.envp["HOME"]!=NULL

will allow execution of /bin/true only if the command line is
"/bin/true --help" and environment variable HOME is defined.

Signed-off-by: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx>
---
security/tomoyo/common.c | 193 +++++++++++++++++
security/tomoyo/common.h | 50 ++++
security/tomoyo/condition.c | 481 +++++++++++++++++++++++++++++++++++++++++++-
security/tomoyo/domain.c | 114 ++++++++--
security/tomoyo/gc.c | 17 +
5 files changed, 819 insertions(+), 36 deletions(-)

--- security-testing-2.6.orig/security/tomoyo/common.c
+++ security-testing-2.6/security/tomoyo/common.c
@@ -18,6 +18,8 @@ static struct tomoyo_profile tomoyo_defa
.preference.enforcing_verbose = true,
.preference.learning_max_entry = 2048,
.preference.learning_verbose = false,
+ .preference.learning_exec_realpath = true,
+ .preference.learning_exec_argv0 = true,
.preference.permissive_verbose = true
};

@@ -85,7 +87,7 @@ static const char *tomoyo_yesno(const un
* Returns true on success, false otherwise.
*/
static bool tomoyo_print_name_union(struct tomoyo_io_buffer *head,
- const struct tomoyo_name_union *ptr)
+ const struct tomoyo_name_union *ptr)
{
int pos = head->read_avail;
if (pos && head->read_buf[pos - 1] == ' ')
@@ -97,6 +99,23 @@ static bool tomoyo_print_name_union(stru
}

/**
+ * tomoyo_print_name_union_quoted - Print a tomoyo_name_union with double quotes.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr: Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head,
+ const struct tomoyo_name_union *ptr)
+{
+ if (ptr->is_group)
+ return tomoyo_io_printf(head, "@%s",
+ ptr->group->group_name->name);
+ return tomoyo_io_printf(head, "\"%s\"", ptr->filename->name);
+}
+
+/**
* tomoyo_print_number_union_common - Print a tomoyo_number_union.
*
* @head: Pointer to "struct tomoyo_io_buffer".
@@ -335,6 +354,14 @@ static int tomoyo_write_profile(struct t
if (cp2)
sscanf(cp2 + 10, "%u",
&profile->preference.learning_max_entry);
+ if (strstr(cp, "exec.realpath=yes"))
+ profile->preference.learning_exec_realpath = true;
+ else if (strstr(cp, "exec.realpath=no"))
+ profile->preference.learning_exec_realpath = false;
+ if (strstr(cp, "exec.argv0=yes"))
+ profile->preference.learning_exec_argv0 = true;
+ else if (strstr(cp, "exec.argv0=no"))
+ profile->preference.learning_exec_argv0 = false;
return 0;
}
if (profile == &tomoyo_default_profile)
@@ -395,10 +422,14 @@ static int tomoyo_read_profile(struct to
goto body;
tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903");
tomoyo_io_printf(head, "PREFERENCE::learning={ verbose=%s "
- "max_entry=%u }\n",
+ "max_entry=%u exec.realpath=%s exec.argv0=%s }\n",
tomoyo_yesno(tomoyo_default_profile.preference.
learning_verbose),
- tomoyo_default_profile.preference.learning_max_entry);
+ tomoyo_default_profile.preference.learning_max_entry,
+ tomoyo_yesno(tomoyo_default_profile.preference.
+ learning_exec_realpath),
+ tomoyo_yesno(tomoyo_default_profile.preference.
+ learning_exec_argv0));
tomoyo_io_printf(head, "PREFERENCE::permissive={ verbose=%s }\n",
tomoyo_yesno(tomoyo_default_profile.preference.
permissive_verbose));
@@ -441,10 +472,16 @@ static int tomoyo_read_profile(struct to
}
if (profile->learning != &tomoyo_default_profile.preference &&
!tomoyo_io_printf(head, "%u-PREFERENCE::learning={ "
- "verbose=%s max_entry=%u }\n", index,
+ "verbose=%s max_entry=%u "
+ "exec.realpath=%s exec.argv0=%s "
+ "}\n", index,
tomoyo_yesno(profile->preference.
learning_verbose),
- profile->preference.learning_max_entry))
+ profile->preference.learning_max_entry,
+ tomoyo_yesno(profile->preference.
+ learning_exec_realpath),
+ tomoyo_yesno(profile->preference.
+ learning_exec_argv0)))
goto out;
if (profile->permissive != &tomoyo_default_profile.preference
&& !tomoyo_io_printf(head, "%u-PREFERENCE::permissive={ "
@@ -881,6 +918,9 @@ static bool tomoyo_print_condition(struc
{
const struct tomoyo_condition_element *condp;
const struct tomoyo_number_union *numbers_p;
+ const struct tomoyo_name_union *names_p;
+ const struct tomoyo_argv_entry *argv;
+ const struct tomoyo_envp_entry *envp;
u16 condc;
u16 i;
char buffer[32];
@@ -889,6 +929,10 @@ static bool tomoyo_print_condition(struc
condc = cond->condc;
condp = (const struct tomoyo_condition_element *) (cond + 1);
numbers_p = (const struct tomoyo_number_union *) (condp + condc);
+ names_p = (const struct tomoyo_name_union *)
+ (numbers_p + cond->numbers_count);
+ argv = (const struct tomoyo_argv_entry *) (names_p + cond->names_count);
+ envp = (const struct tomoyo_envp_entry *) (argv + cond->argc);
memset(buffer, 0, sizeof(buffer));
if (condc && !tomoyo_io_printf(head, "%s", " if"))
goto out;
@@ -898,6 +942,28 @@ static bool tomoyo_print_condition(struc
const u8 right = condp->right;
condp++;
switch (left) {
+ case TOMOYO_ARGV_ENTRY:
+ if (!tomoyo_io_printf(head, " exec.argv[%u]%s\"%s\"",
+ argv->index, argv->is_not ?
+ "!=" : "=", argv->value->name))
+ goto out;
+ argv++;
+ continue;
+ case TOMOYO_ENVP_ENTRY:
+ if (!tomoyo_io_printf(head, " exec.envp[\"%s\"]%s",
+ envp->name->name, envp->is_not ?
+ "!=" : "="))
+ goto out;
+ if (envp->value) {
+ if (!tomoyo_io_printf(head, "\"%s\"",
+ envp->value->name))
+ goto out;
+ } else {
+ if (!tomoyo_io_printf(head, "NULL"))
+ goto out;
+ }
+ envp++;
+ continue;
case TOMOYO_NUMBER_UNION:
if (!tomoyo_print_number_union(head, numbers_p++))
goto out;
@@ -913,6 +979,10 @@ static bool tomoyo_print_condition(struc
if (!tomoyo_io_printf(head, "%s", match ? "=" : "!="))
goto out;
switch (right) {
+ case TOMOYO_NAME_UNION:
+ if (!tomoyo_print_name_union_quoted(head, names_p++))
+ goto out;
+ break;
case TOMOYO_NUMBER_UNION:
if (!tomoyo_print_number_union_nospace(head,
numbers_p++))
@@ -1532,6 +1602,110 @@ static char *tomoyo_init_audit_log(int *
return buf;
}

+/**
+ * tomoyo_get_argv0 - Get argv[0].
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_get_argv0(struct tomoyo_execve_entry *ee)
+{
+ struct linux_binprm *bprm = ee->bprm;
+ char *arg_ptr = ee->tmp;
+ int arg_len = 0;
+ unsigned long pos = bprm->p;
+ int offset = pos % PAGE_SIZE;
+ bool done = false;
+ if (!bprm->argc)
+ goto out;
+ while (1) {
+ if (!tomoyo_dump_page(bprm, pos, &ee->dump))
+ goto out;
+ pos += PAGE_SIZE - offset;
+ /* Read. */
+ while (offset < PAGE_SIZE) {
+ const char *kaddr = ee->dump.data;
+ const unsigned char c = kaddr[offset++];
+ if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
+ if (c == '\\') {
+ arg_ptr[arg_len++] = '\\';
+ arg_ptr[arg_len++] = '\\';
+ } else if (c > ' ' && c < 127) {
+ arg_ptr[arg_len++] = c;
+ } else {
+ arg_ptr[arg_len++] = '\\';
+ arg_ptr[arg_len++] = (c >> 6) + '0';
+ arg_ptr[arg_len++]
+ = ((c >> 3) & 7) + '0';
+ arg_ptr[arg_len++] = (c & 7) + '0';
+ }
+ } else {
+ arg_ptr[arg_len] = '\0';
+ done = true;
+ break;
+ }
+ }
+ offset = 0;
+ if (done)
+ break;
+ }
+ return true;
+ out:
+ return false;
+}
+
+/**
+ * tomoyo_get_execute_condition - Get condition part for execute requests.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ *
+ * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise.
+ */
+static struct tomoyo_condition *tomoyo_get_execute_condition
+(struct tomoyo_execve_entry *ee)
+{
+ struct tomoyo_condition *cond;
+ char *buf;
+ int len = 256;
+ char *realpath = NULL;
+ char *argv0 = NULL;
+ const struct tomoyo_preference *pref = tomoyo_profile(ee->r.profile)
+ ->learning;
+ if (pref->learning_exec_realpath) {
+ struct file *file = ee->bprm->file;
+ realpath = tomoyo_realpath_from_path(&file->f_path);
+ if (realpath)
+ len += strlen(realpath) + 17;
+ }
+ if (pref->learning_exec_argv0) {
+ if (tomoyo_get_argv0(ee)) {
+ argv0 = ee->tmp;
+ len += strlen(argv0) + 16;
+ }
+ }
+ buf = kmalloc(len, GFP_NOFS);
+ if (!buf) {
+ kfree(realpath);
+ return NULL;
+ }
+ snprintf(buf, len - 1, "if");
+ if (realpath) {
+ const int pos = strlen(buf);
+ snprintf(buf + pos, len - pos - 1, " exec.realpath=\"%s\"",
+ realpath);
+ kfree(realpath);
+ }
+ if (argv0) {
+ const int pos = strlen(buf);
+ snprintf(buf + pos, len - pos - 1, " exec.argv[0]=\"%s\"",
+ argv0);
+ }
+ cond = tomoyo_get_condition(buf);
+ kfree(buf);
+ return cond;
+}
+
/* Wait queue for tomoyo_query_list. */
static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait);

@@ -1580,6 +1754,7 @@ int tomoyo_supervisor(struct tomoyo_requ
char *header;
switch (r->mode) {
char *buffer;
+ struct tomoyo_condition *cond;
case TOMOYO_CONFIG_LEARNING:
if (!tomoyo_domain_quota_is_ok(r))
return 0;
@@ -1593,7 +1768,13 @@ int tomoyo_supervisor(struct tomoyo_requ
vsnprintf(buffer, len - 1, fmt, args);
va_end(args);
tomoyo_normalize_line(buffer);
- tomoyo_write_domain_policy2(buffer, r->domain, NULL, false);
+ if (r->ee)
+ cond = tomoyo_get_execute_condition(r->ee);
+ else
+ cond = NULL;
+ tomoyo_write_domain_policy2(buffer, r->domain, cond, false);
+ tomoyo_put_condition(cond);
+ kfree(buffer);
/* fall through */
case TOMOYO_CONFIG_PERMISSIVE:
return 0;
--- security-testing-2.6.orig/security/tomoyo/common.h
+++ security-testing-2.6/security/tomoyo/common.h
@@ -193,20 +193,37 @@ enum tomoyo_conditions_index {
TOMOYO_TASK_FSGID, /* current_fsgid() */
TOMOYO_TASK_PID, /* sys_getpid() */
TOMOYO_TASK_PPID, /* sys_getppid() */
+ TOMOYO_EXEC_ARGC, /* "struct linux_binprm *"->argc */
+ TOMOYO_EXEC_ENVC, /* "struct linux_binprm *"->envc */
+ TOMOYO_EXEC_REALPATH,
TOMOYO_MAX_CONDITION_KEYWORD,
TOMOYO_NUMBER_UNION,
+ TOMOYO_NAME_UNION,
+ TOMOYO_ARGV_ENTRY,
+ TOMOYO_ENVP_ENTRY
};

#define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */

/********** Structure definitions. **********/

+/* Structure for dumping argv[] and envp[] of "struct linux_binprm". */
+struct tomoyo_page_dump {
+ struct page *page; /* Previously dumped page. */
+ char *data; /* Contents of "page". Size is PAGE_SIZE. */
+};
+
struct tomoyo_condition_element {
- /* Left hand operand. */
+ /*
+ * Left hand operand. A "struct tomoyo_argv" for TOMOYO_ARGV_ENTRY, a
+ * "struct tomoyo_envp" for TOMOYO_ENVP_ENTRY is attached to the tail
+ * of the array of this struct.
+ */
u8 left;
/*
* Right hand operand. A "struct tomoyo_number_union" for
- * TOMOYO_NUMBER_UNION is attached to the tail of the array of this
+ * TOMOYO_NUMBER_UNION, a "struct tomoyo_name_union" for
+ * TOMOYO_NAME_UNION is attached to the tail of the array of this
* struct.
*/
u8 right;
@@ -221,9 +238,15 @@ struct tomoyo_condition {
u32 size;
u16 condc;
u16 numbers_count;
+ u16 names_count;
+ u16 argc;
+ u16 envc;
/*
* struct tomoyo_condition_element condition[condc];
* struct tomoyo_number_union values[numbers_count];
+ * struct tomoyo_name_union names[names_count];
+ * struct tomoyo_argv argv[argc];
+ * struct tomoyo_envp envp[envc];
*/
};

@@ -332,7 +355,10 @@ struct tomoyo_number_group_member {
/* Structure for execve() operation. */
struct tomoyo_execve_entry {
struct tomoyo_request_info r;
+ struct linux_binprm *bprm;
int reader_idx;
+ /* For dumping argv[] and envp[]. */
+ struct tomoyo_page_dump dump;
/* For temporary use. */
char *tmp; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
};
@@ -402,6 +428,20 @@ struct tomoyo_domain_info {
atomic_t users; /* Number of referring credentials. */
};

+/* Structure for argv[]. */
+struct tomoyo_argv_entry {
+ unsigned int index;
+ const struct tomoyo_path_info *value;
+ bool is_not;
+};
+
+/* Structure for envp[]. */
+struct tomoyo_envp_entry {
+ const struct tomoyo_path_info *name;
+ const struct tomoyo_path_info *value;
+ bool is_not;
+};
+
/*
* tomoyo_path_acl is a structure which is used for holding an
* entry with one pathname operation (e.g. open(), mkdir()).
@@ -734,6 +774,8 @@ struct tomoyo_preference {
unsigned int learning_max_entry;
bool enforcing_verbose;
bool learning_verbose;
+ bool learning_exec_realpath;
+ bool learning_exec_argv0;
bool permissive_verbose;
};

@@ -754,6 +796,8 @@ extern asmlinkage long sys_getppid(void)

bool tomoyo_condition(struct tomoyo_request_info *r,
const struct tomoyo_acl_info *acl);
+bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
+ struct tomoyo_page_dump *dump);
u8 tomoyo_parse_ulong(unsigned long *result, char **str);
struct tomoyo_condition *tomoyo_get_condition(char * const condition);
void tomoyo_del_condition(struct tomoyo_condition *cond);
@@ -1197,6 +1241,8 @@ static inline bool tomoyo_is_same_condit
{
return p1->size == p2->size && p1->condc == p2->condc &&
p1->numbers_count == p2->numbers_count &&
+ p1->names_count == p2->names_count &&
+ p1->argc == p2->argc && p1->envc == p2->envc &&
!memcmp(p1 + 1, p2 + 1, p1->size - sizeof(*p1));
}

--- security-testing-2.6.orig/security/tomoyo/condition.c
+++ security-testing-2.6/security/tomoyo/condition.c
@@ -7,6 +7,364 @@
#include "common.h"
#include <linux/slab.h>

+/**
+ * tomoyo_argv - Check argv[] in "struct linux_binbrm".
+ *
+ * @index: Index number of @arg_ptr.
+ * @arg_ptr: Contents of argv[@index].
+ * @argc: Length of @argv.
+ * @argv: Pointer to "struct tomoyo_argv_entry".
+ * @checked: Set to true if @argv[@index] was found.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_argv(const unsigned int index, const char *arg_ptr,
+ const int argc, const struct tomoyo_argv_entry *argv,
+ u8 *checked)
+{
+ int i;
+ struct tomoyo_path_info arg;
+ arg.name = arg_ptr;
+ for (i = 0; i < argc; argv++, checked++, i++) {
+ bool result;
+ if (index != argv->index)
+ continue;
+ *checked = 1;
+ tomoyo_fill_path_info(&arg);
+ result = tomoyo_path_matches_pattern(&arg, argv->value);
+ if (argv->is_not)
+ result = !result;
+ if (!result)
+ return false;
+ }
+ return true;
+}
+
+/**
+ * tomoyo_envp - Check envp[] in "struct linux_binbrm".
+ *
+ * @env_name: The name of environment variable.
+ * @env_value: The value of environment variable.
+ * @envc: Length of @envp.
+ * @envp: Pointer to "struct tomoyo_envp_entry".
+ * @checked: Set to true if @envp[@env_name] was found.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_envp(const char *env_name, const char *env_value,
+ const int envc, const struct tomoyo_envp_entry *envp,
+ u8 *checked)
+{
+ int i;
+ struct tomoyo_path_info name;
+ struct tomoyo_path_info value;
+ name.name = env_name;
+ tomoyo_fill_path_info(&name);
+ value.name = env_value;
+ tomoyo_fill_path_info(&value);
+ for (i = 0; i < envc; envp++, checked++, i++) {
+ bool result;
+ if (!tomoyo_path_matches_pattern(&name, envp->name))
+ continue;
+ *checked = 1;
+ if (envp->value) {
+ result = tomoyo_path_matches_pattern(&value,
+ envp->value);
+ if (envp->is_not)
+ result = !result;
+ } else {
+ result = true;
+ if (!envp->is_not)
+ result = !result;
+ }
+ if (!result)
+ return false;
+ }
+ return true;
+}
+
+/**
+ * tomoyo_scan_bprm - Scan "struct linux_binprm".
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ * @argc: Length of @argc.
+ * @argv: Pointer to "struct tomoyo_argv_entry".
+ * @envc: Length of @envp.
+ * @envp: Poiner to "struct tomoyo_envp_entry".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_scan_bprm(struct tomoyo_execve_entry *ee, const u16 argc,
+ const struct tomoyo_argv_entry *argv,
+ const u16 envc,
+ const struct tomoyo_envp_entry *envp)
+{
+ struct linux_binprm *bprm = ee->bprm;
+ struct tomoyo_page_dump *dump = &ee->dump;
+ char *arg_ptr = ee->tmp;
+ int arg_len = 0;
+ unsigned long pos = bprm->p;
+ int offset = pos % PAGE_SIZE;
+ int argv_count = bprm->argc;
+ int envp_count = bprm->envc;
+ bool result = true;
+ u8 local_checked[32];
+ u8 *checked;
+ if (argc + envc <= sizeof(local_checked)) {
+ checked = local_checked;
+ memset(local_checked, 0, sizeof(local_checked));
+ } else {
+ checked = kzalloc(argc + envc, GFP_NOFS);
+ if (!checked)
+ return false;
+ }
+ while (argv_count || envp_count) {
+ if (!tomoyo_dump_page(bprm, pos, dump)) {
+ result = false;
+ goto out;
+ }
+ pos += PAGE_SIZE - offset;
+ while (offset < PAGE_SIZE) {
+ /* Read. */
+ struct tomoyo_path_info arg;
+ const char *kaddr = dump->data;
+ const unsigned char c = kaddr[offset++];
+ arg.name = arg_ptr;
+ if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
+ if (c == '\\') {
+ arg_ptr[arg_len++] = '\\';
+ arg_ptr[arg_len++] = '\\';
+ } else if (c > ' ' && c < 127) {
+ arg_ptr[arg_len++] = c;
+ } else {
+ arg_ptr[arg_len++] = '\\';
+ arg_ptr[arg_len++] = (c >> 6) + '0';
+ arg_ptr[arg_len++] =
+ ((c >> 3) & 7) + '0';
+ arg_ptr[arg_len++] = (c & 7) + '0';
+ }
+ } else {
+ arg_ptr[arg_len] = '\0';
+ }
+ if (c)
+ continue;
+ /* Check. */
+ if (argv_count) {
+ if (!tomoyo_argv(bprm->argc - argv_count,
+ arg_ptr, argc, argv,
+ checked)) {
+ result = false;
+ break;
+ }
+ argv_count--;
+ } else if (envp_count) {
+ char *cp = strchr(arg_ptr, '=');
+ if (cp) {
+ *cp = '\0';
+ if (!tomoyo_envp(arg_ptr, cp + 1,
+ envc, envp,
+ checked + argc)) {
+ result = false;
+ break;
+ }
+ }
+ envp_count--;
+ } else {
+ break;
+ }
+ arg_len = 0;
+ }
+ offset = 0;
+ if (!result)
+ break;
+ }
+ out:
+ if (result) {
+ int i;
+ /* Check not-yet-checked entries. */
+ for (i = 0; i < argc; i++) {
+ if (checked[i])
+ continue;
+ /*
+ * Return true only if all unchecked indexes in
+ * bprm->argv[] are not matched.
+ */
+ if (argv[i].is_not)
+ continue;
+ result = false;
+ break;
+ }
+ for (i = 0; i < envc; envp++, i++) {
+ if (checked[argc + i])
+ continue;
+ /*
+ * Return true only if all unchecked environ variables
+ * in bprm->envp[] are either undefined or not matched.
+ */
+ if ((!envp->value && !envp->is_not) ||
+ (envp->value && envp->is_not))
+ continue;
+ result = false;
+ break;
+ }
+ }
+ if (checked != local_checked)
+ kfree(checked);
+ return result;
+}
+
+static bool tomoyo_scan_exec_realpath(struct file *file,
+ const struct tomoyo_name_union *ptr,
+ const bool match)
+{
+ bool result;
+ struct tomoyo_path_info exe;
+ if (!file)
+ return false;
+ exe.name = tomoyo_realpath_from_path(&file->f_path);
+ if (!exe.name)
+ return false;
+ tomoyo_fill_path_info(&exe);
+ result = tomoyo_compare_name_union(&exe, ptr);
+ kfree(exe.name);
+ return result == match;
+}
+
+static bool tomoyo_parse_name_union_quoted(char *filename,
+ struct tomoyo_name_union *ptr)
+{
+ bool result;
+ char *cp = NULL;
+ if (*filename == '"') {
+ cp = filename + strlen(filename) - 1;
+ if (*cp != '"')
+ return false;
+ *cp = '\0';
+ filename++;
+ }
+ result = tomoyo_parse_name_union(filename, ptr);
+ if (cp)
+ *cp = '"';
+ return result;
+}
+
+/**
+ * tomoyo_get_dqword - tomoyo_get_name() for a quoted string.
+ *
+ * @start: String to save.
+ *
+ * Returns pointer to "struct tomoyo_path_info" on success, NULL otherwise.
+ */
+static const struct tomoyo_path_info *tomoyo_get_dqword(char *start)
+{
+ char *cp;
+ if (*start++ != '"')
+ return NULL;
+ cp = start;
+ while (1) {
+ const char c = *cp++;
+ if (!c)
+ return NULL;
+ if (c != '"' || *cp)
+ continue;
+ *(cp - 1) = '\0';
+ break;
+ }
+ if (*start && !tomoyo_is_correct_word(start))
+ return NULL;
+ return tomoyo_get_name(start);
+}
+
+/**
+ * tomoyo_parse_argv - Parse an argv[] condition part.
+ *
+ * @start: String to parse.
+ * @argv: Pointer to "struct tomoyo_argv_entry".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_argv(char *start, struct tomoyo_argv_entry *argv)
+{
+ unsigned long index;
+ const struct tomoyo_path_info *value;
+ bool is_not;
+ char c;
+ if (tomoyo_parse_ulong(&index, &start) != TOMOYO_VALUE_TYPE_DECIMAL)
+ goto out;
+ if (*start++ != ']')
+ goto out;
+ c = *start++;
+ if (c == '=')
+ is_not = false;
+ else if (c == '!' && *start++ == '=')
+ is_not = true;
+ else
+ goto out;
+ value = tomoyo_get_dqword(start);
+ if (!value)
+ goto out;
+ argv->index = index;
+ argv->is_not = is_not;
+ argv->value = value;
+ return true;
+ out:
+ return false;
+}
+
+/**
+ * tomoyo_parse_envp - Parse an envp[] condition part.
+ *
+ * @start: String to parse.
+ * @envp: Pointer to "struct tomoyo_envp_entry".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_envp(char *start, struct tomoyo_envp_entry *envp)
+{
+ const struct tomoyo_path_info *name;
+ const struct tomoyo_path_info *value;
+ bool is_not;
+ char *cp = start;
+ /*
+ * Since environment variable names don't
+ * contain '=', I can treat '"]=' and '"]!='
+ * sequences as delimiters.
+ */
+ while (1) {
+ if (!strncmp(start, "\"]=", 3)) {
+ is_not = false;
+ *start = '\0';
+ start += 3;
+ break;
+ } else if (!strncmp(start, "\"]!=", 4)) {
+ is_not = true;
+ *start = '\0';
+ start += 4;
+ break;
+ } else if (!*start++) {
+ goto out;
+ }
+ }
+ if (!tomoyo_is_correct_word(cp))
+ goto out;
+ name = tomoyo_get_name(cp);
+ if (!name)
+ goto out;
+ if (!strcmp(start, "NULL")) {
+ value = NULL;
+ } else {
+ value = tomoyo_get_dqword(start);
+ if (!value)
+ goto out;
+ }
+ envp->name = name;
+ envp->is_not = is_not;
+ envp->value = value;
+ return true;
+ out:
+ return false;
+}
+
/* The list for "struct tomoyo_condition". */
LIST_HEAD(tomoyo_condition_list);

@@ -21,6 +379,9 @@ const char *tomoyo_condition_keyword[TOM
[TOMOYO_TASK_FSGID] = "task.fsgid",
[TOMOYO_TASK_PID] = "task.pid",
[TOMOYO_TASK_PPID] = "task.ppid",
+ [TOMOYO_EXEC_ARGC] = "exec.argc",
+ [TOMOYO_EXEC_ENVC] = "exec.envc",
+ [TOMOYO_EXEC_REALPATH] = "exec.realpath",
};

/* #define DEBUG_CONDITION */
@@ -45,10 +406,16 @@ struct tomoyo_condition *tomoyo_get_cond
struct tomoyo_condition *ptr;
struct tomoyo_condition_element *condp;
struct tomoyo_number_union *numbers_p;
+ struct tomoyo_name_union *names_p;
+ struct tomoyo_argv_entry *argv;
+ struct tomoyo_envp_entry *envp;
u32 size;
bool found = false;
u16 condc = 0;
u16 numbers_count = 0;
+ u16 names_count = 0;
+ u16 argc = 0;
+ u16 envc = 0;
char *end_of_string;
start = condition;
if (!strncmp(start, "if ", 3))
@@ -73,6 +440,15 @@ struct tomoyo_condition *tomoyo_get_cond
start = "";
}
dprintk(KERN_WARNING "%u: <%s>\n", __LINE__, word);
+ if (!strncmp(word, "exec.argv[", 10)) {
+ argc++;
+ condc++;
+ continue;
+ } else if (!strncmp(word, "exec.envp[\"", 11)) {
+ envc++;
+ condc++;
+ continue;
+ }
eq = strchr(word, '=');
if (!eq)
goto out;
@@ -97,6 +473,10 @@ struct tomoyo_condition *tomoyo_get_cond
condc++;
dprintk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__,
word, left);
+ if (left == TOMOYO_EXEC_REALPATH) {
+ names_count++;
+ continue;
+ }
for (right = 0; right < TOMOYO_MAX_CONDITION_KEYWORD; right++) {
if (strcmp(word, tomoyo_condition_keyword[right]))
continue;
@@ -107,19 +487,29 @@ struct tomoyo_condition *tomoyo_get_cond
if (right == TOMOYO_MAX_CONDITION_KEYWORD)
numbers_count++;
}
- dprintk(KERN_DEBUG "%u: cond=%u numbers=%u\n", __LINE__,
- condc, numbers_count);
+ dprintk(KERN_DEBUG "%u: cond=%u numbers=%u names=%u ac=%u "
+ "ec=%u\n", __LINE__, condc, numbers_count, names_count,
+ argc, envc);
size = sizeof(*entry)
+ condc * sizeof(struct tomoyo_condition_element)
- + numbers_count * sizeof(struct tomoyo_number_union);
+ + numbers_count * sizeof(struct tomoyo_number_union)
+ + names_count * sizeof(struct tomoyo_name_union)
+ + argc * sizeof(struct tomoyo_argv_entry)
+ + envc * sizeof(struct tomoyo_envp_entry);
entry = kzalloc(size, GFP_NOFS);
if (!entry)
return NULL;
INIT_LIST_HEAD(&entry->list);
entry->condc = condc;
entry->numbers_count = numbers_count;
+ entry->names_count = names_count;
+ entry->argc = argc;
+ entry->envc = envc;
condp = (struct tomoyo_condition_element *) (entry + 1);
numbers_p = (struct tomoyo_number_union *) (condp + condc);
+ names_p = (struct tomoyo_name_union *) (numbers_p + numbers_count);
+ argv = (struct tomoyo_argv_entry *) (names_p + names_count);
+ envp = (struct tomoyo_envp_entry *) (argv + argc);
for (start = condition; start < end_of_string; start++)
if (!*start)
*start = ' ';
@@ -145,6 +535,25 @@ struct tomoyo_condition *tomoyo_get_cond
start = "";
}
dprintk(KERN_WARNING "%u: <%s>\n", __LINE__, word);
+ if (!strncmp(word, "exec.argv[", 10)) {
+ if (!tomoyo_parse_argv(word + 10, argv))
+ goto out;
+ argv++;
+ argc--;
+ condc--;
+ left = TOMOYO_ARGV_ENTRY;
+ right = -1;
+ goto store_value;
+ } else if (!strncmp(word, "exec.envp[\"", 11)) {
+ if (!tomoyo_parse_envp(word + 11, envp))
+ goto out;
+ envp++;
+ envc--;
+ condc--;
+ left = TOMOYO_ENVP_ENTRY;
+ right = -1;
+ goto store_value;
+ }
eq = strchr(word, '=');
if (!eq) {
dprintk(KERN_WARNING "%u: No operator.\n",
@@ -179,6 +588,13 @@ struct tomoyo_condition *tomoyo_get_cond
condc--;
dprintk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__,
word, left);
+ if (left == TOMOYO_EXEC_REALPATH) {
+ right = TOMOYO_NAME_UNION;
+ if (!tomoyo_parse_name_union_quoted(word, names_p++))
+ goto out;
+ names_count--;
+ goto store_value;
+ }
for (right = 0; right < TOMOYO_MAX_CONDITION_KEYWORD; right++) {
if (strcmp(word, tomoyo_condition_keyword[right]))
continue;
@@ -190,6 +606,7 @@ struct tomoyo_condition *tomoyo_get_cond
goto out;
numbers_count--;
}
+ store_value:
condp->left = left;
condp->right = right;
condp->equals = !is_not;
@@ -202,12 +619,16 @@ struct tomoyo_condition *tomoyo_get_cond
for (start = condition; start < end_of_string; start++)
if (!*start)
*start = ' ';
- dprintk(KERN_DEBUG "%u: <%s> cond=%u numbers=%u\n",
- __LINE__, condition, condc, numbers_count);
+ dprintk(KERN_DEBUG "%u: <%s> cond=%u numbers=%u names=%u ac=%u "
+ "ec=%u\n", __LINE__, condition, condc, numbers_count,
+ names_count, argc, envc);
+ BUG_ON(names_count);
BUG_ON(numbers_count);
+ BUG_ON(argc);
+ BUG_ON(envc);
BUG_ON(condc);
#else
- BUG_ON(numbers_count | condc);
+ BUG_ON(names_count | numbers_count | argc | envc | condc);
#endif
entry->size = size;
if (mutex_lock_interruptible(&tomoyo_policy_lock))
@@ -265,19 +686,54 @@ bool tomoyo_condition(struct tomoyo_requ
unsigned long right_max = 0;
const struct tomoyo_condition_element *condp;
const struct tomoyo_number_union *numbers_p;
+ const struct tomoyo_name_union *names_p;
+ const struct tomoyo_argv_entry *argv;
+ const struct tomoyo_envp_entry *envp;
u16 condc;
+ u16 argc;
+ u16 envc;
+ struct linux_binprm *bprm = NULL;
const struct tomoyo_condition *cond = acl->cond;
if (!cond)
return true;
condc = cond->condc;
+ argc = cond->argc;
+ envc = cond->envc;
+ if (r->ee)
+ bprm = r->ee->bprm;
+ if (!bprm && (argc || envc))
+ return false;
condp = (struct tomoyo_condition_element *) (cond + 1);
numbers_p = (const struct tomoyo_number_union *) (condp + condc);
+ names_p = (const struct tomoyo_name_union *)
+ (numbers_p + cond->numbers_count);
+ argv = (const struct tomoyo_argv_entry *) (names_p + cond->names_count);
+ envp = (const struct tomoyo_envp_entry *) (argv + argc);
for (i = 0; i < condc; i++) {
const bool match = condp->equals;
const u8 left = condp->left;
const u8 right = condp->right;
u8 j;
condp++;
+ /* Check argv[] and envp[] later. */
+ if (left == TOMOYO_ARGV_ENTRY || left == TOMOYO_ENVP_ENTRY)
+ continue;
+ /* Check string expressions. */
+ if (right == TOMOYO_NAME_UNION) {
+ const struct tomoyo_name_union *ptr = names_p++;
+ switch (left) {
+ struct tomoyo_execve_entry *ee;
+ struct file *file;
+ case TOMOYO_EXEC_REALPATH:
+ ee = r->ee;
+ file = ee ? ee->bprm->file : NULL;
+ if (!tomoyo_scan_exec_realpath(file, ptr,
+ match))
+ goto out;
+ break;
+ }
+ continue;
+ }
/* Check numeric or bit-op expressions. */
for (j = 0; j < 2; j++) {
const u8 index = j ? right : left;
@@ -313,6 +769,16 @@ bool tomoyo_condition(struct tomoyo_requ
case TOMOYO_TASK_PPID:
value = sys_getppid();
break;
+ case TOMOYO_EXEC_ARGC:
+ if (!bprm)
+ goto out;
+ value = bprm->argc;
+ break;
+ case TOMOYO_EXEC_ENVC:
+ if (!bprm)
+ goto out;
+ value = bprm->envc;
+ break;
case TOMOYO_NUMBER_UNION:
/* Fetch values later. */
break;
@@ -360,5 +826,8 @@ bool tomoyo_condition(struct tomoyo_requ
out:
return false;
}
+ /* Check argv[] and envp[] now. */
+ if (r->ee && (argc || envc))
+ return tomoyo_scan_bprm(r->ee, argc, argv, envc, envp);
return true;
}
--- security-testing-2.6.orig/security/tomoyo/domain.c
+++ security-testing-2.6/security/tomoyo/domain.c
@@ -797,34 +797,74 @@ struct tomoyo_domain_info *tomoyo_find_o
}

/**
- * tomoyo_find_next_domain - Find a domain.
+ * tomoyo_dump_page - Dump a page to buffer.
*
* @bprm: Pointer to "struct linux_binprm".
+ * @pos: Location to dump.
+ * @dump: Poiner to "struct tomoyo_page_dump".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tomoyo_dump_page(struct linux_binprm *bprm, unsigned long pos,
+ struct tomoyo_page_dump *dump)
+{
+ struct page *page;
+ /* dump->data is released by tomoyo_find_next_domain(). */
+ if (!dump->data) {
+ dump->data = kzalloc(PAGE_SIZE, GFP_NOFS);
+ if (!dump->data)
+ return false;
+ }
+ /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
+#ifdef CONFIG_MMU
+ if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page, NULL) <= 0)
+ return false;
+#else
+ page = bprm->page[pos / PAGE_SIZE];
+#endif
+ if (page != dump->page) {
+ const unsigned int offset = pos % PAGE_SIZE;
+ /*
+ * Maybe kmap()/kunmap() should be used here.
+ * But remove_arg_zero() uses kmap_atomic()/kunmap_atomic().
+ * So do I.
+ */
+ char *kaddr = kmap_atomic(page, KM_USER0);
+ dump->page = page;
+ memcpy(dump->data + offset, kaddr + offset,
+ PAGE_SIZE - offset);
+ kunmap_atomic(kaddr, KM_USER0);
+ }
+ /* Same with put_arg_page(page) in fs/exec.c */
+#ifdef CONFIG_MMU
+ put_page(page);
+#endif
+ return true;
+}
+
+/**
+ * tomoyo_find_next_domain - Find a domain.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
*
* Returns 0 on success, negative value otherwise.
*
* Caller holds tomoyo_read_lock().
*/
-int tomoyo_find_next_domain(struct linux_binprm *bprm)
+static int tomoyo_find_next_domain2(struct tomoyo_execve_entry *ee)
{
- char *tmp = kmalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
- struct tomoyo_request_info r;
+ struct tomoyo_request_info *r = &ee->r;
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;
- bool is_enforce;
+ const char *original_name = ee->bprm->filename;
+ const bool is_enforce = (r->mode == TOMOYO_CONFIG_ENFORCING);
int retval = -ENOMEM;
bool need_kfree = false;
struct tomoyo_path_info rn = { }; /* real name */
struct tomoyo_path_info sn = { }; /* symlink name */
struct tomoyo_path_info ln; /* last name */

- if (!tmp)
- return -ENOMEM;
- tomoyo_init_request_info(&r, NULL, TOMOYO_TYPE_EXECUTE);
- is_enforce = (r.mode == TOMOYO_CONFIG_ENFORCING);
-
ln.name = tomoyo_get_last_name(old_domain);
tomoyo_fill_path_info(&ln);
retry:
@@ -881,7 +921,7 @@ int tomoyo_find_next_domain(struct linux
}

/* Check execute permission. */
- retval = tomoyo_check_exec_perm(&r, &rn);
+ retval = tomoyo_check_exec_perm(r, &rn);
if (retval == TOMOYO_RETRY_REQUEST)
goto retry;
if (retval < 0)
@@ -889,7 +929,7 @@ int tomoyo_find_next_domain(struct linux

if (tomoyo_is_domain_initializer(old_domain->domainname, &rn, &ln)) {
/* Transit to the child of tomoyo_kernel_domain domain. */
- snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1,
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1,
TOMOYO_ROOT_NAME " " "%s", rn.name);
} else if (old_domain == &tomoyo_kernel_domain &&
!tomoyo_policy_loaded) {
@@ -904,28 +944,28 @@ int tomoyo_find_next_domain(struct linux
domain = old_domain;
} else {
/* Normal domain transition. */
- snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1,
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1,
"%s %s", old_domain_name, rn.name);
}
- if (domain || strlen(tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
+ if (domain || strlen(ee->tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
goto done;
- domain = tomoyo_find_domain(tmp);
+ domain = tomoyo_find_domain(ee->tmp);
if (domain)
goto done;
if (is_enforce) {
- int error = tomoyo_supervisor(&r, "# wants to create domain\n"
- "%s\n", tmp);
+ int error = tomoyo_supervisor(r, "# wants to create domain\n"
+ "%s\n", ee->tmp);
if (error == TOMOYO_RETRY_REQUEST)
goto retry;
if (error < 0)
goto done;
}
- domain = tomoyo_find_or_assign_new_domain(tmp, old_domain->profile);
+ domain = tomoyo_find_or_assign_new_domain(ee->tmp, old_domain->profile);
done:
if (domain)
goto out;
printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",
- tmp);
+ ee->tmp);
if (is_enforce)
retval = -EPERM;
else
@@ -935,10 +975,40 @@ int tomoyo_find_next_domain(struct linux
domain = old_domain;
/* Update reference count on "struct tomoyo_domain_info". */
atomic_inc(&domain->users);
- bprm->cred->security = domain;
+ ee->bprm->cred->security = domain;
if (need_kfree)
kfree(rn.name);
kfree(sn.name);
- kfree(tmp);
+ return retval;
+}
+
+/**
+ * tomoyo_find_next_domain - Find a domain.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+int tomoyo_find_next_domain(struct linux_binprm *bprm)
+{
+ int retval = -ENOMEM;
+ struct tomoyo_execve_entry *ee = kzalloc(sizeof(*ee), GFP_NOFS);
+
+ if (!ee)
+ return -ENOMEM;
+ ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
+ if (!ee->tmp)
+ goto out;
+ /* ee->dump.data is allocated by tomoyo_dump_page(). */
+ tomoyo_init_request_info(&ee->r, NULL, TOMOYO_MAC_FILE_EXECUTE);
+ ee->r.ee = ee;
+ ee->bprm = bprm;
+ retval = tomoyo_find_next_domain2(ee);
+ kfree(ee->tmp);
+ kfree(ee->dump.data);
+ out:
+ kfree(ee);
return retval;
}
--- security-testing-2.6.orig/security/tomoyo/gc.c
+++ security-testing-2.6/security/tomoyo/gc.c
@@ -194,13 +194,30 @@ void tomoyo_del_condition(struct tomoyo_
{
const u16 condc = cond->condc;
const u16 numbers_count = cond->numbers_count;
+ const u16 names_count = cond->names_count;
+ const u16 argc = cond->argc;
+ const u16 envc = cond->envc;
unsigned int i;
const struct tomoyo_condition_element *condp
= (const struct tomoyo_condition_element *) (cond + 1);
struct tomoyo_number_union *numbers_p
= (struct tomoyo_number_union *) (condp + condc);
+ struct tomoyo_name_union *names_p
+ = (struct tomoyo_name_union *) (numbers_p + numbers_count);
+ const struct tomoyo_argv_entry *argv
+ = (const struct tomoyo_argv_entry *) (names_p + names_count);
+ const struct tomoyo_envp_entry *envp
+ = (const struct tomoyo_envp_entry *) (argv + argc);
for (i = 0; i < numbers_count; i++)
tomoyo_put_number_union(numbers_p++);
+ for (i = 0; i < names_count; i++)
+ tomoyo_put_name_union(names_p++);
+ for (i = 0; i < argc; argv++, i++)
+ tomoyo_put_name(argv->value);
+ for (i = 0; i < envc; envp++, i++) {
+ tomoyo_put_name(envp->name);
+ tomoyo_put_name(envp->value);
+ }
}

static void tomoyo_del_name(const struct tomoyo_name_entry *ptr)
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/