[TOMOYO #16 10/25] TOMOYO: Add conditional ACL support.
From: Tetsuo Handa
Date: Sun Oct 04 2009 - 08:57:05 EST
This patch contains code for handling "if" clause of a permission.
TOMOYO is a pathname based access control system, but also deals some of non
pathname attributes.
TOMOYO supports conditional permissions based on process's UID/GID etc. and/or
requested pathname's UID/GID.
Regarding symlink(), you can check symlink's contents.
Regarding execve(), you can check argv[]/envp[] conditions.
If you need to examine argv[]/envp[] more deeply, you can use execute handler.
Signed-off-by: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx>
---
security/tomoyo/condition.c | 1332 ++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1332 insertions(+)
--- /dev/null
+++ security-testing-2.6/security/tomoyo/condition.c
@@ -0,0 +1,1332 @@
+/*
+ * security/tomoyo/condition.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.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)
+{
+ /*
+ if exec.argc=3
+ if (argc == 3)
+ if exec.argv[1]="-c"
+ if (argc >= 2 && !strcmp(argv[1], "-c"))
+ if exec.argv[1]!="-c"
+ if (argc < 2 || strcmp(argv[1], "-c"))
+ if exec.envc=10-20
+ if (envc >= 10 && envc <= 20)
+ if exec.envc!=10-20
+ if (envc < 10 || envc > 20)
+ if exec.envp["HOME"]!=NULL
+ if (getenv("HOME"))
+ if exec.envp["HOME"]=NULL
+ if (!getenv("HOME"))
+ if exec.envp["HOME"]="/"
+ if (getenv("HOME") && !strcmp(getenv("HOME"), "/"))
+ if exec.envp["HOME"]!="/"
+ if (!getenv("HOME") || strcmp(getenv("HOME", "/"))
+ */
+ 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_KERNEL);
+ 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 (!tomoyo_is_correct_path(start, 0, 0, 0))
+ 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 (!*cp || !tomoyo_is_correct_path(cp, 0, 0, 0))
+ 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". */
+static LIST_HEAD(tomoyo_condition_list);
+
+const char *tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = {
+ [TOMOYO_TASK_UID] = "task.uid",
+ [TOMOYO_TASK_EUID] = "task.euid",
+ [TOMOYO_TASK_SUID] = "task.suid",
+ [TOMOYO_TASK_FSUID] = "task.fsuid",
+ [TOMOYO_TASK_GID] = "task.gid",
+ [TOMOYO_TASK_EGID] = "task.egid",
+ [TOMOYO_TASK_SGID] = "task.sgid",
+ [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_TASK_STATE_0] = "task.state[0]",
+ [TOMOYO_TASK_STATE_1] = "task.state[1]",
+ [TOMOYO_TASK_STATE_2] = "task.state[2]",
+ [TOMOYO_TYPE_IS_SOCKET] = "socket",
+ [TOMOYO_TYPE_IS_SYMLINK] = "symlink",
+ [TOMOYO_TYPE_IS_FILE] = "file",
+ [TOMOYO_TYPE_IS_BLOCK_DEV] = "block",
+ [TOMOYO_TYPE_IS_DIRECTORY] = "directory",
+ [TOMOYO_TYPE_IS_CHAR_DEV] = "char",
+ [TOMOYO_TYPE_IS_FIFO] = "fifo",
+ [TOMOYO_MODE_SETUID] = "setuid",
+ [TOMOYO_MODE_SETGID] = "setgid",
+ [TOMOYO_MODE_STICKY] = "sticky",
+ [TOMOYO_MODE_OWNER_READ] = "owner_read",
+ [TOMOYO_MODE_OWNER_WRITE] = "owner_write",
+ [TOMOYO_MODE_OWNER_EXECUTE] = "owner_execute",
+ [TOMOYO_MODE_GROUP_READ] = "group_read",
+ [TOMOYO_MODE_GROUP_WRITE] = "group_write",
+ [TOMOYO_MODE_GROUP_EXECUTE] = "group_execute",
+ [TOMOYO_MODE_OTHERS_READ] = "others_read",
+ [TOMOYO_MODE_OTHERS_WRITE] = "others_write",
+ [TOMOYO_MODE_OTHERS_EXECUTE] = "others_execute",
+ [TOMOYO_TASK_TYPE] = "task.type",
+ [TOMOYO_TASK_EXECUTE_HANDLER] = "execute_handler",
+ [TOMOYO_EXEC_REALPATH] = "exec.realpath",
+ [TOMOYO_SYMLINK_TARGET] = "symlink.target",
+ [TOMOYO_PATH1_UID] = "path1.uid",
+ [TOMOYO_PATH1_GID] = "path1.gid",
+ [TOMOYO_PATH1_INO] = "path1.ino",
+ [TOMOYO_PATH1_MAJOR] = "path1.major",
+ [TOMOYO_PATH1_MINOR] = "path1.minor",
+ [TOMOYO_PATH1_PERM] = "path1.perm",
+ [TOMOYO_PATH1_TYPE] = "path1.type",
+ [TOMOYO_PATH1_DEV_MAJOR] = "path1.dev_major",
+ [TOMOYO_PATH1_DEV_MINOR] = "path1.dev_minor",
+ [TOMOYO_PATH2_UID] = "path2.uid",
+ [TOMOYO_PATH2_GID] = "path2.gid",
+ [TOMOYO_PATH2_INO] = "path2.ino",
+ [TOMOYO_PATH2_MAJOR] = "path2.major",
+ [TOMOYO_PATH2_MINOR] = "path2.minor",
+ [TOMOYO_PATH2_PERM] = "path2.perm",
+ [TOMOYO_PATH2_TYPE] = "path2.type",
+ [TOMOYO_PATH2_DEV_MAJOR] = "path2.dev_major",
+ [TOMOYO_PATH2_DEV_MINOR] = "path2.dev_minor",
+ [TOMOYO_PATH1_PARENT_UID] = "path1.parent.uid",
+ [TOMOYO_PATH1_PARENT_GID] = "path1.parent.gid",
+ [TOMOYO_PATH1_PARENT_INO] = "path1.parent.ino",
+ [TOMOYO_PATH1_PARENT_PERM] = "path1.parent.perm",
+ [TOMOYO_PATH2_PARENT_UID] = "path2.parent.uid",
+ [TOMOYO_PATH2_PARENT_GID] = "path2.parent.gid",
+ [TOMOYO_PATH2_PARENT_INO] = "path2.parent.ino",
+ [TOMOYO_PATH2_PARENT_PERM] = "path2.parent.perm",
+};
+
+/**
+ * tomoyo_parse_post_condition - Parse post-condition part.
+ *
+ * @condition: String to parse.
+ * @post_state: Buffer to store post-condition part.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_post_condition(char * const condition,
+ u8 post_state[4])
+{
+ char *start = strstr(condition, "; set ");
+ if (!start)
+ return true;
+ *start = '\0';
+ start += 6;
+ while (1) {
+ int i;
+ unsigned long value;
+ while (*start == ' ')
+ start++;
+ if (!*start)
+ break;
+ if (!strncmp(start, "task.state[0]=", 14))
+ i = 0;
+ else if (!strncmp(start, "task.state[1]=", 14))
+ i = 1;
+ else if (!strncmp(start, "task.state[2]=", 14))
+ i = 2;
+ else
+ goto out;
+ start += 14;
+ if (post_state[3] & (1 << i))
+ goto out;
+ post_state[3] |= 1 << i;
+ if (!tomoyo_parse_ulong(&value, &start) || value > 255)
+ goto out;
+ post_state[i] = (u8) value;
+ }
+ return true;
+ out:
+ return false;
+}
+
+/**
+ * tomoyo_get_condition - Parse condition part.
+ *
+ * @condition: Pointer to string to parse.
+ *
+ * Returns pointer to "struct tomoyo_condition" on success, NULL otherwise.
+ */
+struct tomoyo_condition *tomoyo_get_condition(char * const condition)
+{
+ static const bool debug;
+ char *start = condition;
+ struct tomoyo_condition *entry = NULL;
+ 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;
+ u8 i;
+ bool found = false;
+ u16 condc = 0;
+ u16 numbers_count = 0;
+ u16 names_count = 0;
+ u16 argc = 0;
+ u16 envc = 0;
+ u8 post_state[4] = { 0, 0, 0, 0 };
+ char *end_of_string;
+ if (!tomoyo_parse_post_condition(start, post_state))
+ goto out;
+ start = condition;
+ if (!strncmp(start, "if ", 3))
+ start += 3;
+ else if (*start)
+ return NULL;
+ end_of_string = start + strlen(start);
+ while (1) {
+ u8 left;
+ u8 right;
+ char *word = start;
+ char *cp;
+ char *eq;
+ bool is_not = false;
+ if (!*word)
+ break;
+ cp = strchr(start, ' ');
+ if (cp) {
+ *cp = '\0';
+ start = cp + 1;
+ } else {
+ start = "";
+ }
+ if (debug)
+ printk(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;
+ if (eq > word && *(eq - 1) == '!') {
+ is_not = true;
+ eq--;
+ }
+ *eq = '\0';
+ for (left = 0; left < TOMOYO_MAX_CONDITION_KEYWORD; left++) {
+ if (strcmp(word, tomoyo_condition_keyword[left]))
+ continue;
+ break;
+ }
+ if (debug)
+ printk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__,
+ word, left);
+ if (left == TOMOYO_MAX_CONDITION_KEYWORD)
+ numbers_count++;
+ *eq = is_not ? '!' : '=';
+ word = eq + 1;
+ if (is_not)
+ word++;
+ condc++;
+ if (debug)
+ printk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__,
+ word, left);
+ if (left == TOMOYO_EXEC_REALPATH ||
+ left == TOMOYO_SYMLINK_TARGET) {
+ names_count++;
+ continue;
+ }
+ for (right = 0; right < TOMOYO_MAX_CONDITION_KEYWORD;
+ right++) {
+ if (strcmp(word, tomoyo_condition_keyword[right]))
+ continue;
+ break;
+ }
+ if (debug)
+ printk(KERN_WARNING "%u: <%s> right=%u\n", __LINE__,
+ word, right);
+ if (right == TOMOYO_MAX_CONDITION_KEYWORD)
+ numbers_count++;
+ }
+ if (debug)
+ printk(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)
+ + names_count * sizeof(struct tomoyo_name_union)
+ + argc * sizeof(struct tomoyo_argv_entry)
+ + envc * sizeof(struct tomoyo_envp_entry);
+ entry = kzalloc(size, GFP_KERNEL);
+ if (!entry)
+ return NULL;
+ atomic_set(&entry->users, 1);
+ INIT_LIST_HEAD(&entry->list);
+ for (i = 0; i < 4; i++)
+ entry->post_state[i] = post_state[i];
+ 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 = ' ';
+ start = condition;
+ if (!strncmp(start, "if ", 3))
+ start += 3;
+ else if (*start)
+ goto out;
+ while (1) {
+ u8 left;
+ u8 right;
+ char *word = start;
+ char *cp;
+ char *eq;
+ bool is_not = false;
+ if (!*word)
+ break;
+ cp = strchr(start, ' ');
+ if (cp) {
+ *cp = '\0';
+ start = cp + 1;
+ } else {
+ start = "";
+ }
+ if (debug)
+ printk(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) {
+ if (debug)
+ printk(KERN_WARNING "%u: No operator.\n",
+ __LINE__);
+ goto out;
+ }
+ if (eq > word && *(eq - 1) == '!') {
+ is_not = true;
+ eq--;
+ }
+ *eq = '\0';
+ for (left = 0; left < TOMOYO_MAX_CONDITION_KEYWORD; left++) {
+ if (strcmp(word, tomoyo_condition_keyword[left]))
+ continue;
+ break;
+ }
+ if (debug)
+ printk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__,
+ word, left);
+ if (left == TOMOYO_MAX_CONDITION_KEYWORD) {
+ left = TOMOYO_NUMBER_UNION;
+ if (!tomoyo_parse_number_union(word, numbers_p))
+ goto out;
+ if (numbers_p->is_group)
+ goto out;
+ numbers_p++;
+ numbers_count--;
+ }
+ *eq = is_not ? '!' : '=';
+ word = eq + 1;
+ if (is_not)
+ word++;
+ condc--;
+ if (debug)
+ printk(KERN_WARNING "%u: <%s> left=%u\n", __LINE__,
+ word, left);
+ if (left == TOMOYO_EXEC_REALPATH ||
+ left == TOMOYO_SYMLINK_TARGET) {
+ 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;
+ break;
+ }
+ if (right == TOMOYO_MAX_CONDITION_KEYWORD) {
+ right = TOMOYO_NUMBER_UNION;
+ if (!tomoyo_parse_number_union(word, numbers_p++))
+ goto out;
+ numbers_count--;
+ }
+ store_value:
+ condp->left = left;
+ condp->right = right;
+ condp->equals = !is_not;
+ if (debug)
+ printk(KERN_WARNING "%u: left=%u right=%u match=%u\n",
+ __LINE__, condp->left, condp->right,
+ condp->equals);
+ condp++;
+ }
+ if (debug) {
+ for (start = condition; start < end_of_string; start++)
+ if (!*start)
+ *start = ' ';
+ printk(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);
+ entry->size = size;
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_condition_list, list) {
+ if (tomoyo_memcmp(ptr, entry, offsetof(typeof(*entry), size),
+ size))
+ continue;
+ /* Same entry found. Share this entry. */
+ atomic_inc(&ptr->users);
+ found = true;
+ break;
+ }
+ if (!found) {
+ if (tomoyo_memory_ok(entry, size)) {
+ list_add_rcu(&entry->list, &tomoyo_condition_list);
+ } else {
+ found = true;
+ ptr = NULL;
+ }
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ if (found) {
+ entry->size = 0;
+ tomoyo_put_condition(entry);
+ entry = ptr;
+ }
+ return entry;
+ out:
+ if (debug)
+ printk(KERN_WARNING "%u: %s failed\n", __LINE__, __func__);
+ tomoyo_put_condition(entry);
+ return NULL;
+}
+
+/**
+ * tomoyo_get_attributes - Revalidate "struct inode".
+ *
+ * @obj: Pointer to "struct tomoyo_obj_info".
+ *
+ * Returns nothing.
+ */
+void tomoyo_get_attributes(struct tomoyo_obj_info *obj)
+{
+ struct vfsmount *mnt;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct kstat stat;
+
+ if (!obj->path1.mnt)
+ goto no_path1;
+
+ /* Get information on "path1". */
+ mnt = obj->path1.mnt;
+ dentry = obj->path1.dentry;
+ inode = dentry->d_inode;
+ if (inode) {
+ if (!inode->i_op || vfs_getattr(mnt, dentry, &stat)) {
+ /* Nothing to do. */
+ } else {
+ obj->path1_stat.uid = stat.uid;
+ obj->path1_stat.gid = stat.gid;
+ obj->path1_stat.ino = stat.ino;
+ obj->path1_stat.mode = stat.mode;
+ obj->path1_stat.dev = stat.dev;
+ obj->path1_stat.rdev = stat.rdev;
+ obj->path1_valid = true;
+ }
+ }
+
+ /* Get information on "path1.parent". */
+ dentry = dget_parent(obj->path1.dentry);
+ inode = dentry->d_inode;
+ if (inode) {
+ if (!inode->i_op || vfs_getattr(mnt, dentry, &stat)) {
+ /* Nothing to do. */
+ } else {
+ obj->path1_parent_stat.uid = stat.uid;
+ obj->path1_parent_stat.gid = stat.gid;
+ obj->path1_parent_stat.ino = stat.ino;
+ obj->path1_parent_stat.mode = stat.mode;
+ obj->path1_parent_stat.dev = stat.dev;
+ obj->path1_parent_stat.rdev = stat.rdev;
+ obj->path1_parent_valid = true;
+ }
+ }
+ dput(dentry);
+
+ no_path1:
+ mnt = obj->path2.mnt;
+ if (!mnt)
+ return;
+
+ /* Get information on "path2". */
+ dentry = obj->path2.dentry;
+ inode = dentry->d_inode;
+ if (inode) {
+ if (!inode->i_op || vfs_getattr(mnt, dentry, &stat)) {
+ /* Nothing to do. */
+ } else {
+ obj->path2_stat.uid = stat.uid;
+ obj->path2_stat.gid = stat.gid;
+ obj->path2_stat.ino = stat.ino;
+ obj->path2_stat.mode = stat.mode;
+ obj->path2_stat.dev = stat.dev;
+ obj->path2_stat.rdev = stat.rdev;
+ obj->path2_valid = true;
+ }
+ }
+
+ /* Get information on "path2.parent". */
+ dentry = dget_parent(obj->path2.dentry);
+ inode = dentry->d_inode;
+ if (inode) {
+ if (!inode->i_op || vfs_getattr(mnt, dentry, &stat)) {
+ /* Nothing to do. */
+ } else {
+ obj->path2_parent_stat.uid = stat.uid;
+ obj->path2_parent_stat.gid = stat.gid;
+ obj->path2_parent_stat.ino = stat.ino;
+ obj->path2_parent_stat.mode = stat.mode;
+ obj->path2_parent_stat.dev = stat.dev;
+ obj->path2_parent_stat.rdev = stat.rdev;
+ obj->path2_parent_valid = true;
+ }
+ }
+ dput(dentry);
+}
+
+/**
+ * tomoyo_condition - Check condition part.
+ *
+ * @r: Pointer to "struct tomoyo_request_info".
+ * @acl: Pointer to "struct tomoyo_acl_info".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_condition(struct tomoyo_request_info *r,
+ const struct tomoyo_acl_info *acl)
+{
+ const struct task_struct *task = current;
+ u32 i;
+ unsigned long left_min = 0;
+ unsigned long left_max = 0;
+ unsigned long right_min = 0;
+ 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;
+ struct tomoyo_obj_info *obj;
+ 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;
+ obj = r->obj;
+ 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;
+ bool left_is_bitop = false;
+ bool right_is_bitop = false;
+ 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_path_info *symlink;
+ struct tomoyo_execve_entry *ee;
+ struct file *file;
+ case TOMOYO_SYMLINK_TARGET:
+ symlink = obj->symlink_target;
+ if (!symlink ||
+ tomoyo_compare_name_union(symlink, ptr)
+ != match)
+ goto out;
+ break;
+ 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;
+ unsigned long value = 0;
+ bool is_bitop = false;
+ switch (index) {
+ case TOMOYO_TASK_UID:
+ value = current_uid();
+ break;
+ case TOMOYO_TASK_EUID:
+ value = current_euid();
+ break;
+ case TOMOYO_TASK_SUID:
+ value = current_suid();
+ break;
+ case TOMOYO_TASK_FSUID:
+ value = current_fsuid();
+ break;
+ case TOMOYO_TASK_GID:
+ value = current_gid();
+ break;
+ case TOMOYO_TASK_EGID:
+ value = current_egid();
+ break;
+ case TOMOYO_TASK_SGID:
+ value = current_sgid();
+ break;
+ case TOMOYO_TASK_FSGID:
+ value = current_fsgid();
+ break;
+ case TOMOYO_TASK_PID:
+ value = sys_getpid();
+ break;
+ case TOMOYO_TASK_PPID:
+ value = sys_getppid();
+ break;
+ case TOMOYO_TYPE_IS_SOCKET:
+ value = S_IFSOCK;
+ break;
+ case TOMOYO_TYPE_IS_SYMLINK:
+ value = S_IFLNK;
+ break;
+ case TOMOYO_TYPE_IS_FILE:
+ value = S_IFREG;
+ break;
+ case TOMOYO_TYPE_IS_BLOCK_DEV:
+ value = S_IFBLK;
+ break;
+ case TOMOYO_TYPE_IS_DIRECTORY:
+ value = S_IFDIR;
+ break;
+ case TOMOYO_TYPE_IS_CHAR_DEV:
+ value = S_IFCHR;
+ break;
+ case TOMOYO_TYPE_IS_FIFO:
+ value = S_IFIFO;
+ break;
+ case TOMOYO_MODE_SETUID:
+ value = S_ISUID;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_SETGID:
+ value = S_ISGID;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_STICKY:
+ value = S_ISVTX;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_OWNER_READ:
+ value = S_IRUSR;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_OWNER_WRITE:
+ value = S_IWUSR;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_OWNER_EXECUTE:
+ value = S_IXUSR;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_GROUP_READ:
+ value = S_IRGRP;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_GROUP_WRITE:
+ value = S_IWGRP;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_GROUP_EXECUTE:
+ value = S_IXGRP;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_OTHERS_READ:
+ value = S_IROTH;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_OTHERS_WRITE:
+ value = S_IWOTH;
+ is_bitop = true;
+ break;
+ case TOMOYO_MODE_OTHERS_EXECUTE:
+ value = S_IXOTH;
+ is_bitop = true;
+ 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_TASK_STATE_0:
+ value = (u8) (task->tomoyo_flags >> 24);
+ break;
+ case TOMOYO_TASK_STATE_1:
+ value = (u8) (task->tomoyo_flags >> 16);
+ break;
+ case TOMOYO_TASK_STATE_2:
+ value = (u8) (task->tomoyo_flags >> 8);
+ break;
+ case TOMOYO_TASK_TYPE:
+ value = ((u8) task->tomoyo_flags)
+ & TOMOYO_TASK_IS_EXECUTE_HANDLER;
+ break;
+ case TOMOYO_TASK_EXECUTE_HANDLER:
+ value = TOMOYO_TASK_IS_EXECUTE_HANDLER;
+ break;
+ case TOMOYO_NUMBER_UNION:
+ /* Fetch values later. */
+ break;
+ default:
+ if (!obj)
+ goto out;
+ if (!obj->validate_done) {
+ tomoyo_get_attributes(obj);
+ obj->validate_done = true;
+ }
+ switch (index) {
+ case TOMOYO_PATH1_UID:
+ if (!obj->path1_valid)
+ goto out;
+ value = obj->path1_stat.uid;
+ break;
+ case TOMOYO_PATH1_GID:
+ if (!obj->path1_valid)
+ goto out;
+ value = obj->path1_stat.gid;
+ break;
+ case TOMOYO_PATH1_INO:
+ if (!obj->path1_valid)
+ goto out;
+ value = obj->path1_stat.ino;
+ break;
+ case TOMOYO_PATH1_MAJOR:
+ if (!obj->path1_valid)
+ goto out;
+ value = MAJOR(obj->path1_stat.dev);
+ break;
+ case TOMOYO_PATH1_MINOR:
+ if (!obj->path1_valid)
+ goto out;
+ value = MINOR(obj->path1_stat.dev);
+ break;
+ case TOMOYO_PATH1_TYPE:
+ if (!obj->path1_valid)
+ goto out;
+ value = obj->path1_stat.mode & S_IFMT;
+ break;
+ case TOMOYO_PATH1_DEV_MAJOR:
+ if (!obj->path1_valid)
+ goto out;
+ value = MAJOR(obj->path1_stat.rdev);
+ break;
+ case TOMOYO_PATH1_DEV_MINOR:
+ if (!obj->path1_valid)
+ goto out;
+ value = MINOR(obj->path1_stat.rdev);
+ break;
+ case TOMOYO_PATH1_PERM:
+ if (!obj->path1_valid)
+ goto out;
+ value = obj->path1_stat.mode
+ & S_IALLUGO;
+ break;
+ case TOMOYO_PATH2_UID:
+ if (!obj->path2_valid)
+ goto out;
+ value = obj->path2_stat.uid;
+ break;
+ case TOMOYO_PATH2_GID:
+ if (!obj->path2_valid)
+ goto out;
+ value = obj->path2_stat.gid;
+ break;
+ case TOMOYO_PATH2_INO:
+ if (!obj->path2_valid)
+ goto out;
+ value = obj->path2_stat.ino;
+ break;
+ case TOMOYO_PATH2_MAJOR:
+ if (!obj->path2_valid)
+ goto out;
+ value = MAJOR(obj->path2_stat.dev);
+ break;
+ case TOMOYO_PATH2_MINOR:
+ if (!obj->path2_valid)
+ goto out;
+ value = MINOR(obj->path2_stat.dev);
+ break;
+ case TOMOYO_PATH2_TYPE:
+ if (!obj->path2_valid)
+ goto out;
+ value = obj->path2_stat.mode & S_IFMT;
+ break;
+ case TOMOYO_PATH2_DEV_MAJOR:
+ if (!obj->path2_valid)
+ goto out;
+ value = MAJOR(obj->path2_stat.rdev);
+ break;
+ case TOMOYO_PATH2_DEV_MINOR:
+ if (!obj->path2_valid)
+ goto out;
+ value = MINOR(obj->path2_stat.rdev);
+ break;
+ case TOMOYO_PATH2_PERM:
+ if (!obj->path2_valid)
+ goto out;
+ value = obj->path2_stat.mode
+ & S_IALLUGO;
+ break;
+ case TOMOYO_PATH1_PARENT_UID:
+ if (!obj->path1_parent_valid)
+ goto out;
+ value = obj->path1_parent_stat.uid;
+ break;
+ case TOMOYO_PATH1_PARENT_GID:
+ if (!obj->path1_parent_valid)
+ goto out;
+ value = obj->path1_parent_stat.gid;
+ break;
+ case TOMOYO_PATH1_PARENT_INO:
+ if (!obj->path1_parent_valid)
+ goto out;
+ value = obj->path1_parent_stat.ino;
+ break;
+ case TOMOYO_PATH1_PARENT_PERM:
+ if (!obj->path1_parent_valid)
+ goto out;
+ value = obj->path1_parent_stat.mode
+ & S_IALLUGO;
+ break;
+ case TOMOYO_PATH2_PARENT_UID:
+ if (!obj->path2_parent_valid)
+ goto out;
+ value = obj->path2_parent_stat.uid;
+ break;
+ case TOMOYO_PATH2_PARENT_GID:
+ if (!obj->path2_parent_valid)
+ goto out;
+ value = obj->path2_parent_stat.gid;
+ break;
+ case TOMOYO_PATH2_PARENT_INO:
+ if (!obj->path2_parent_valid)
+ goto out;
+ value = obj->path2_parent_stat.ino;
+ break;
+ case TOMOYO_PATH2_PARENT_PERM:
+ if (!obj->path2_parent_valid)
+ goto out;
+ value = obj->path2_parent_stat.mode
+ & S_IALLUGO;
+ break;
+ }
+ break;
+ }
+ value = value;
+ if (j) {
+ right_max = value;
+ right_min = value;
+ right_is_bitop = is_bitop;
+ } else {
+ left_max = value;
+ left_min = value;
+ left_is_bitop = is_bitop;
+ }
+ }
+ if (left == TOMOYO_NUMBER_UNION) {
+ /* Fetch values now. */
+ const struct tomoyo_number_union *ptr = numbers_p++;
+ left_min = ptr->values[0];
+ left_max = ptr->values[1];
+ }
+ if (right == TOMOYO_NUMBER_UNION) {
+ /* Fetch values now. */
+ const struct tomoyo_number_union *ptr = numbers_p++;
+ if (ptr->is_group) {
+ if (tomoyo_number_matches_group(left_min,
+ left_max,
+ ptr->group)
+ == match)
+ continue;
+ } else {
+ if ((left_min <= ptr->values[1] &&
+ left_max >= ptr->values[0]) == match)
+ continue;
+ }
+ goto out;
+ }
+ /*
+ * Bit operation is valid only when counterpart value
+ * represents permission.
+ */
+ if (left_is_bitop && right_is_bitop)
+ goto out;
+ if (left_is_bitop) {
+ switch (right) {
+ case TOMOYO_PATH1_PERM:
+ case TOMOYO_PATH1_PARENT_PERM:
+ case TOMOYO_PATH2_PERM:
+ case TOMOYO_PATH2_PARENT_PERM:
+ if (match) {
+ if ((right_max & left_max))
+ continue;
+ } else {
+ if (!(right_max & left_max))
+ continue;
+ }
+ }
+ goto out;
+ }
+ if (right_is_bitop) {
+ switch (left) {
+ case TOMOYO_PATH1_PERM:
+ case TOMOYO_PATH1_PARENT_PERM:
+ case TOMOYO_PATH2_PERM:
+ case TOMOYO_PATH2_PARENT_PERM:
+ if (match) {
+ if ((left_max & right_max))
+ continue;
+ } else {
+ if (!(left_max & right_max))
+ continue;
+ }
+ }
+ goto out;
+ }
+ /* Normal value range comparison. */
+ if (match) {
+ if (left_min <= right_max && left_max >= right_min)
+ continue;
+ } else {
+ if (left_min > right_max || left_max < right_min)
+ continue;
+ }
+ 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;
+}
--
--
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/