lkml.org 
[lkml]   [2008]   [Apr]   [4]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
Date
From
Subject[TOMOYO #7 22/30] Conditional ACL support functions.
TOMOYO supports conditional permissions based on
process's UID,GID etc. and/or requested pathname's UID/GID.
Regarding execve(), you can use argv[] and envp[] conditions.


Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp>
---
fs/tomoyo_cond.c | 1380 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1380 insertions(+)

--- /dev/null
+++ linux-2.6.25-rc8-mm1/fs/tomoyo_cond.c
@@ -0,0 +1,1380 @@
+/*
+ * fs/tomoyo_cond.c
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2008 NTT DATA CORPORATION
+ *
+ * Version: 1.6.0 2008/04/01
+ *
+ */
+
+#include <linux/ccs_common.h>
+#include <linux/realpath.h>
+
+#include <linux/tomoyo.h>
+#include <linux/highmem.h>
+#include <linux/binfmts.h>
+
+/* Structure for argv[]. */
+struct argv_entry {
+ unsigned int index;
+ const struct path_info *value;
+ bool is_not;
+};
+
+/* Structure for envp[]. */
+struct envp_entry {
+ const struct path_info *name;
+ const struct path_info *value;
+ bool is_not;
+};
+
+/**
+ * check_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 argv_entry".
+ * @checked: Set to true if @argv[@index] was found.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool check_argv(const unsigned int index, const char *arg_ptr,
+ const int argc, const struct argv_entry *argv,
+ u8 *checked)
+{
+ int i;
+ struct path_info arg;
+ arg.name = arg_ptr;
+ for (i = 0; i < argc; argv++, checked++, i++) {
+ bool result;
+ if (index != argv->index)
+ continue;
+ *checked = 1;
+ ccs_fill_path_info(&arg);
+ result = ccs_path_matches_pattern(&arg, argv->value);
+ if (argv->is_not)
+ result = !result;
+ if (!result)
+ return false;
+ }
+ return true;
+}
+
+/**
+ * check_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 envp_entry".
+ * @checked: Set to true if @envp[@env_name] was found.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool check_envp(const char *env_name, const char *env_value,
+ const int envc, const struct envp_entry *envp,
+ u8 *checked)
+{
+ int i;
+ struct path_info name;
+ struct path_info value;
+ name.name = env_name;
+ ccs_fill_path_info(&name);
+ value.name = env_value;
+ ccs_fill_path_info(&value);
+ for (i = 0; i < envc; envp++, checked++, i++) {
+ bool result;
+ if (!ccs_path_matches_pattern(&name, envp->name))
+ continue;
+ *checked = 1;
+ if (envp->value) {
+ result = ccs_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;
+}
+
+/**
+ * scan_bprm - Scan "struct linux_binprm".
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ * @argc: Length of @argc.
+ * @argv: Pointer to "struct argv_entry".
+ * @envc: Length of @envp.
+ * @envp: Poiner to "struct envp_entry".
+ * @tmp: Buffer for temporal use.
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool scan_bprm(const struct linux_binprm *bprm,
+ const u16 argc, const struct argv_entry *argv,
+ const u16 envc, const struct envp_entry *envp,
+ struct ccs_page_buffer *tmp)
+{
+ /*
+ 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", "/"))
+ */
+ char *arg_ptr = tmp->buffer;
+ int arg_len = 0;
+ unsigned long pos = bprm->p;
+ int i = pos / PAGE_SIZE;
+ 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 = ccs_alloc(argc + envc);
+ if (!checked)
+ return false;
+ }
+ while (argv_count || envp_count) {
+ struct page *page;
+ const char *kaddr;
+#ifdef CONFIG_MMU
+ if (get_user_pages(current, bprm->mm, pos, 1, 0, 1, &page,
+ NULL) <= 0) {
+ printk(KERN_DEBUG "get_user_pages() failed\n");
+ result = false;
+ goto out;
+ }
+ pos += PAGE_SIZE - offset;
+#else
+ page = bprm->page[i];
+#endif
+ /* Map. */
+ kaddr = kmap(page);
+ if (!kaddr) { /* Mapping failed. */
+#ifdef CONFIG_MMU
+ put_page(page);
+#endif
+ printk(KERN_DEBUG "kmap() failed\n");
+ result = false;
+ goto out;
+ }
+ while (offset < PAGE_SIZE) {
+ /* Read. */
+ struct path_info arg;
+ const unsigned char c = kaddr[offset++];
+ arg.name = arg_ptr;
+ if (c && arg_len < CCS_MAX_PATHNAME_LEN - 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 (!check_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 (!check_envp(arg_ptr, cp + 1,
+ envc, envp,
+ checked + argc)) {
+ result = false;
+ break;
+ }
+ }
+ envp_count--;
+ } else {
+ break;
+ }
+ arg_len = 0;
+ }
+ /* Unmap. */
+ kunmap(page);
+#ifdef CONFIG_MMU
+ put_page(page);
+#endif
+ i++;
+ offset = 0;
+ if (!result)
+ break;
+ }
+ out:
+ if (result) {
+ /* 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)
+ ccs_free(checked);
+ return result;
+}
+
+/* Value type definition. */
+#define VALUE_TYPE_DECIMAL 1
+#define VALUE_TYPE_OCTAL 2
+#define VALUE_TYPE_HEXADECIMAL 3
+
+/**
+ * parse_ulong - Parse an "unsigned long" value.
+ *
+ * @result: Pointer to "unsigned long".
+ * @str: Pointer to string to parse.
+ *
+ * Returns value type on success, 0 otherwise.
+ *
+ * The @src is updated to point the first character after the value
+ * on success.
+ */
+static u8 parse_ulong(unsigned long *result, char **str)
+{
+ const char *cp = *str;
+ char *ep;
+ int base = 10;
+ if (*cp == '0') {
+ char c = *(cp + 1);
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ cp += 2;
+ } else if (c >= '0' && c <= '7') {
+ base = 8;
+ cp++;
+ }
+ }
+ *result = simple_strtoul(cp, &ep, base);
+ if (cp == ep)
+ return 0;
+ *str = ep;
+ switch (base) {
+ case 16:
+ return VALUE_TYPE_HEXADECIMAL;
+ case 8:
+ return VALUE_TYPE_OCTAL;
+ default:
+ return VALUE_TYPE_DECIMAL;
+ }
+}
+
+
+/**
+ * print_ulong - Print an "unsigned long" value.
+ *
+ * @buffer: Pointer to buffer.
+ * @buffer_len: Size of @buffer.
+ * @value: An "unsigned long" value.
+ * @type: Type of @value.
+ *
+ * Returns nothing.
+ */
+static void print_ulong(char *buffer, const int buffer_len,
+ const unsigned long value, const int type)
+{
+ if (type == VALUE_TYPE_DECIMAL)
+ snprintf(buffer, buffer_len, "%lu", value);
+ else if (type == VALUE_TYPE_OCTAL)
+ snprintf(buffer, buffer_len, "0%lo", value);
+ else
+ snprintf(buffer, buffer_len, "0x%lX", value);
+}
+
+/* Structure for " if " and "; set" part. */
+struct condition_list {
+ struct list1_head list;
+ u16 condc;
+ u16 argc;
+ u16 envc;
+ u8 post_state[4];
+ /* "unsigned long condition[condc]" follows here. */
+ /* "struct argv_entry argv[argc]" follows here. */
+ /* "struct envp_entry envp[envc]" follows here. */
+};
+
+/**
+ * parse_argv - Parse an argv[] condition part.
+ *
+ * @start: String to parse.
+ * @argv: Pointer to "struct argv_entry".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool parse_argv(char *start, struct argv_entry *argv)
+{
+ unsigned long index;
+ const struct path_info *value;
+ bool is_not;
+ char c;
+ char *cp;
+ start += 10;
+ if (parse_ulong(&index, &start) != 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;
+ if (*start++ != '"')
+ goto out;
+ cp = strchr(start, '\0') - 1;
+ if (cp < start || *cp != '"')
+ goto out;
+ *cp = '\0';
+ if (!ccs_is_correct_path(start, 0, 0, 0, __func__))
+ goto out;
+ value = ccs_save_name(start);
+ if (!value)
+ goto out;
+ argv->index = index;
+ argv->is_not = is_not;
+ argv->value = value;
+ return true;
+ out:
+ return false;
+}
+
+/**
+ * parse_envp - Parse an envp[] condition part.
+ *
+ * @start: String to parse.
+ * @envp: Pointer to "struct envp_entry".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool parse_envp(char *start, struct envp_entry *envp)
+{
+ const struct path_info *name;
+ const struct path_info *value;
+ bool is_not;
+ char *cp;
+ start += 11;
+ 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 || !ccs_is_correct_path(cp, 0, 0, 0, __func__))
+ goto out;
+ name = ccs_save_name(cp);
+ if (!name)
+ goto out;
+ if (!strcmp(start, "NULL")) {
+ value = NULL;
+ } else {
+ if (*start++ != '"')
+ goto out;
+ cp = strchr(start, '\0') - 1;
+ if (cp < start || *cp != '"')
+ goto out;
+ *cp = '\0';
+ if (!ccs_is_correct_path(start, 0, 0, 0, __func__))
+ goto out;
+ value = ccs_save_name(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 condition_list". */
+static LIST1_HEAD(condition_list);
+
+#define TASK_UID 0
+#define TASK_EUID 1
+#define TASK_SUID 2
+#define TASK_FSUID 3
+#define TASK_GID 4
+#define TASK_EGID 5
+#define TASK_SGID 6
+#define TASK_FSGID 7
+#define TASK_PID 8
+#define TASK_PPID 9
+#define PATH1_UID 10
+#define PATH1_GID 11
+#define PATH1_INO 12
+#define PATH1_PARENT_UID 13
+#define PATH1_PARENT_GID 14
+#define PATH1_PARENT_INO 15
+#define PATH2_PARENT_UID 16
+#define PATH2_PARENT_GID 17
+#define PATH2_PARENT_INO 18
+#define EXEC_ARGC 19
+#define EXEC_ENVC 20
+#define TASK_STATE_0 21
+#define TASK_STATE_1 22
+#define TASK_STATE_2 23
+#define MAX_KEYWORD 24
+
+static struct {
+ const char *keyword;
+ const int keyword_len; /* strlen(keyword) */
+} condition_control_keyword[MAX_KEYWORD] = {
+ [TASK_UID] = { "task.uid", 8 },
+ [TASK_EUID] = { "task.euid", 9 },
+ [TASK_SUID] = { "task.suid", 9 },
+ [TASK_FSUID] = { "task.fsuid", 10 },
+ [TASK_GID] = { "task.gid", 8 },
+ [TASK_EGID] = { "task.egid", 9 },
+ [TASK_SGID] = { "task.sgid", 9 },
+ [TASK_FSGID] = { "task.fsgid", 10 },
+ [TASK_PID] = { "task.pid", 8 },
+ [TASK_PPID] = { "task.ppid", 9 },
+ [PATH1_UID] = { "path1.uid", 9 },
+ [PATH1_GID] = { "path1.gid", 9 },
+ [PATH1_INO] = { "path1.ino", 9 },
+ [PATH1_PARENT_UID] = { "path1.parent.uid", 16 },
+ [PATH1_PARENT_GID] = { "path1.parent.gid", 16 },
+ [PATH1_PARENT_INO] = { "path1.parent.ino", 16 },
+ [PATH2_PARENT_UID] = { "path2.parent.uid", 16 },
+ [PATH2_PARENT_GID] = { "path2.parent.gid", 16 },
+ [PATH2_PARENT_INO] = { "path2.parent.ino", 16 },
+ [EXEC_ARGC] = { "exec.argc", 9 },
+ [EXEC_ENVC] = { "exec.envc", 9 },
+ [TASK_STATE_0] = { "task.state[0]", 13 },
+ [TASK_STATE_1] = { "task.state[1]", 13 },
+ [TASK_STATE_2] = { "task.state[2]", 13 },
+};
+
+/**
+ * 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 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 (!parse_ulong(&value, &start) || value > 255)
+ goto out;
+ post_state[i] = (u8) value;
+ }
+ return true;
+ out:
+ return false;
+}
+
+/**
+ * find_same_condition - Search for same condition list.
+ *
+ * @new_ptr: Pointer to "struct condition_list".
+ * @size: Size of @new_ptr.
+ *
+ * Returns existing pointer to "struct condition_list" if the same entry was
+ * found, NULL if memory allocation failed, @new_ptr otherwise.
+ */
+static struct condition_list *
+find_same_condition(struct condition_list *new_ptr, const u32 size)
+{
+ static DEFINE_MUTEX(lock);
+ struct condition_list *ptr;
+ mutex_lock(&lock);
+ list1_for_each_entry(ptr, &condition_list, list) {
+ /* Don't compare if size differs. */
+ if (ptr->condc != new_ptr->condc ||
+ ptr->argc != new_ptr->argc ||
+ ptr->envc != new_ptr->envc)
+ continue;
+ /*
+ * Compare ptr and new_ptr
+ * except ptr->list and new_ptr->list.
+ */
+ if (memcmp(((u8 *) ptr) + sizeof(ptr->list),
+ ((u8 *) new_ptr) + sizeof(new_ptr->list),
+ size - sizeof(ptr->list)))
+ continue;
+ /* Same entry found. Share this entry. */
+ ccs_free(new_ptr);
+ new_ptr = ptr;
+ goto ok;
+ }
+ /* Same entry not found. Save this entry. */
+ ptr = ccs_alloc_element(size);
+ if (ptr) {
+ memmove(ptr, new_ptr, size);
+ /* Append to chain. */
+ list1_add_tail_mb(&ptr->list, &condition_list);
+ }
+ ccs_free(new_ptr);
+ new_ptr = ptr;
+ ok:
+ mutex_unlock(&lock);
+ return new_ptr;
+}
+
+/**
+ * ccs_find_or_assign_new_condition - Parse condition part.
+ *
+ * @condition: Pointer to string to parse.
+ *
+ * Returns pointer to "struct condition_list" on success, NULL otherwise.
+ */
+const struct condition_list *
+ccs_find_or_assign_new_condition(char * const condition)
+{
+ char *start = condition;
+ struct condition_list *new_ptr = NULL;
+ unsigned long *ptr;
+ struct argv_entry *argv;
+ struct envp_entry *envp;
+ u32 size;
+ u8 left, right, i;
+ unsigned long left_min = 0;
+ unsigned long left_max = 0;
+ unsigned long right_min = 0;
+ unsigned long right_max = 0;
+ u16 condc = 0;
+ u16 argc = 0;
+ u16 envc = 0;
+ u8 post_state[4] = { 0, 0, 0, 0 };
+ if (!parse_post_condition(start, post_state))
+ goto out;
+ start = condition;
+ if (!strncmp(start, "if ", 3))
+ start += 3;
+ else if (*start)
+ return NULL;
+ while (1) {
+ while (*start == ' ')
+ start++;
+ if (!*start)
+ break;
+ if (!strncmp(start, "exec.argv[", 10)) {
+ argc++;
+ start = strchr(start + 10, ' ');
+ if (!start)
+ break;
+ continue;
+ } else if (!strncmp(start, "exec.envp[\"", 11)) {
+ envc++;
+ start = strchr(start + 11, ' ');
+ if (!start)
+ break;
+ continue;
+ }
+ for (left = 0; left < MAX_KEYWORD; left++) {
+ const int len =
+ condition_control_keyword[left].keyword_len;
+ if (strncmp(start,
+ condition_control_keyword[left].keyword,
+ len))
+ continue;
+ start += len;
+ break;
+ }
+ if (left < MAX_KEYWORD)
+ goto check_operator_1;
+ if (!parse_ulong(&left_min, &start))
+ goto out;
+ condc++; /* body */
+ if (*start != '-')
+ goto check_operator_1;
+ start++;
+ if (!parse_ulong(&left_max, &start) || left_min > left_max)
+ goto out;
+ condc++; /* body */
+ check_operator_1:
+ if (strncmp(start, "!=", 2) == 0)
+ start += 2;
+ else if (*start == '=')
+ start++;
+ else
+ goto out;
+ condc++; /* header */
+ for (right = 0; right < MAX_KEYWORD; right++) {
+ const int len =
+ condition_control_keyword[right].keyword_len;
+ if (strncmp(start,
+ condition_control_keyword[right].keyword,
+ len))
+ continue;
+ start += len;
+ break;
+ }
+ if (right < MAX_KEYWORD)
+ continue;
+ if (!parse_ulong(&right_min, &start))
+ goto out;
+ condc++; /* body */
+ if (*start != '-')
+ continue;
+ start++;
+ if (!parse_ulong(&right_max, &start) || right_min > right_max)
+ goto out;
+ condc++; /* body */
+ }
+ size = sizeof(*new_ptr)
+ + condc * sizeof(unsigned long)
+ + argc * sizeof(struct argv_entry)
+ + envc * sizeof(struct envp_entry);
+ new_ptr = ccs_alloc(size);
+ if (!new_ptr)
+ return NULL;
+ for (i = 0; i < 4; i++)
+ new_ptr->post_state[i] = post_state[i];
+ new_ptr->condc = condc;
+ new_ptr->argc = argc;
+ new_ptr->envc = envc;
+ ptr = (unsigned long *) (new_ptr + 1);
+ argv = (struct argv_entry *) (ptr + condc);
+ envp = (struct envp_entry *) (argv + argc);
+ start = condition;
+ if (!strncmp(start, "if ", 3))
+ start += 3;
+ else if (*start)
+ goto out;
+ while (1) {
+ u8 match = 0;
+ u8 left_1_type = 0;
+ u8 left_2_type = 0;
+ u8 right_1_type = 0;
+ u8 right_2_type = 0;
+ while (*start == ' ')
+ start++;
+ if (!*start)
+ break;
+ if (!strncmp(start, "exec.argv[", 10)) {
+ char *cp = strchr(start + 10, ' ');
+ if (cp)
+ *cp = '\0';
+ if (!parse_argv(start, argv))
+ goto out;
+ argv++;
+ argc--;
+ if (cp)
+ *cp = ' ';
+ else
+ break;
+ start = cp;
+ continue;
+ } else if (!strncmp(start, "exec.envp[\"", 11)) {
+ char *cp = strchr(start + 11, ' ');
+ if (cp)
+ *cp = '\0';
+ if (!parse_envp(start, envp))
+ goto out;
+ envp++;
+ envc--;
+ if (cp)
+ *cp = ' ';
+ else
+ break;
+ start = cp;
+ continue;
+ }
+ for (left = 0; left < MAX_KEYWORD; left++) {
+ const int len =
+ condition_control_keyword[left].keyword_len;
+ if (strncmp(start,
+ condition_control_keyword[left].keyword,
+ len))
+ continue;
+ start += len;
+ break;
+ }
+ if (left < MAX_KEYWORD)
+ goto check_operator_2;
+ left_1_type = parse_ulong(&left_min, &start);
+ condc--; /* body */
+ if (*start != '-')
+ goto check_operator_2;
+ start++;
+ left_2_type = parse_ulong(&left_max, &start);
+ condc--; /* body */
+ left++;
+ check_operator_2:
+ if (!strncmp(start, "!=", 2)) {
+ start += 2;
+ } else if (*start == '=') {
+ match |= 1;
+ start++;
+ } else {
+ break; /* This shouldn't happen. */
+ }
+ condc--; /* header */
+ for (right = 0; right < MAX_KEYWORD; right++) {
+ const int len =
+ condition_control_keyword[right].keyword_len;
+ if (strncmp(start,
+ condition_control_keyword[right].keyword,
+ len))
+ continue;
+ start += len;
+ break;
+ }
+ if (right < MAX_KEYWORD)
+ goto store_value;
+ right_1_type = parse_ulong(&right_min, &start);
+ condc--; /* body */
+ if (*start != '-')
+ goto store_value;
+ start++;
+ right_2_type = parse_ulong(&right_max, &start);
+ condc--; /* body */
+ right++;
+ store_value:
+ *ptr = (((u32) match) << 16) |
+ (((u32) left_1_type) << 18) |
+ (((u32) left_2_type) << 20) |
+ (((u32) right_1_type) << 22) |
+ (((u32) right_2_type) << 24) |
+ (((u32) left) << 8) |
+ ((u32) right);
+ ptr++;
+ if (left >= MAX_KEYWORD) {
+ *ptr = left_min;
+ ptr++;
+ }
+ if (left == MAX_KEYWORD + 1) {
+ *ptr = left_max;
+ ptr++;
+ }
+ if (right >= MAX_KEYWORD) {
+ *ptr = right_min;
+ ptr++;
+ }
+ if (right == MAX_KEYWORD + 1) {
+ *ptr = right_max;
+ ptr++;
+ }
+ }
+ /*
+ printk(KERN_DEBUG "argc=%u envc=%u condc=%u\n", argc, envc, condc);
+ */
+ BUG_ON(argc);
+ BUG_ON(envc);
+ BUG_ON(condc);
+ return find_same_condition(new_ptr, size);
+ out:
+ ccs_free(new_ptr);
+ return NULL;
+}
+
+/**
+ * get_attributes - Revalidate "struct inode".
+ *
+ * @obj: Pointer to "struct obj_info".
+ *
+ * Returns nothing.
+ */
+static void get_attributes(struct obj_info *obj)
+{
+ struct vfsmount *mnt;
+ struct dentry *dentry;
+ struct inode *inode;
+ struct kstat stat;
+
+ mnt = obj->path1_vfsmnt;
+ 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_valid = true;
+ }
+ }
+
+ 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_valid = true;
+ }
+ }
+ dput(dentry);
+
+ mnt = obj->path2_vfsmnt;
+ if (mnt) {
+ 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_valid = true;
+ }
+ }
+ dput(dentry);
+ }
+}
+
+/**
+ * ccs_check_condition - Check condition part.
+ *
+ * @acl: Pointer to "struct acl_info".
+ * @obj: Pointer to "struct obj_info". May be NULL.
+ *
+ * Returns true on success, false otherwise.
+ */
+bool ccs_check_condition(const struct acl_info *acl, struct obj_info *obj)
+{
+ 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 unsigned long *ptr;
+ const struct argv_entry *argv;
+ const struct envp_entry *envp;
+ u16 condc;
+ u16 argc;
+ u16 envc;
+ const struct condition_list *cond = ccs_get_condition_part(acl);
+ const struct linux_binprm *bprm;
+ if (!cond)
+ return true;
+ condc = cond->condc;
+ argc = cond->argc;
+ envc = cond->envc;
+ bprm = obj ? obj->bprm : NULL;
+ if (!bprm && (argc || envc))
+ return false;
+ ptr = (unsigned long *) (cond + 1);
+ argv = (const struct argv_entry *) (ptr + condc);
+ envp = (const struct envp_entry *) (argv + argc);
+ for (i = 0; i < condc; i++) {
+ const u32 header = *ptr;
+ const bool match = (header >> 16) & 1;
+ const u8 left = header >> 8;
+ const u8 right = header;
+ ptr++;
+ if ((left >= PATH1_UID && left < MAX_KEYWORD) ||
+ (right >= PATH1_UID && right < MAX_KEYWORD)) {
+ if (!obj)
+ goto out;
+ if (!obj->validate_done) {
+ get_attributes(obj);
+ obj->validate_done = true;
+ }
+ }
+ switch (left) {
+ case TASK_UID:
+ left_max = task->uid;
+ break;
+ case TASK_EUID:
+ left_max = task->euid;
+ break;
+ case TASK_SUID:
+ left_max = task->suid;
+ break;
+ case TASK_FSUID:
+ left_max = task->fsuid;
+ break;
+ case TASK_GID:
+ left_max = task->gid;
+ break;
+ case TASK_EGID:
+ left_max = task->egid;
+ break;
+ case TASK_SGID:
+ left_max = task->sgid;
+ break;
+ case TASK_FSGID:
+ left_max = task->fsgid;
+ break;
+ case TASK_PID:
+ left_max = task->pid;
+ break;
+ case TASK_PPID:
+ left_max = sys_getppid();
+ break;
+ case PATH1_UID:
+ if (!obj->path1_valid)
+ goto out;
+ left_max = obj->path1_stat.uid;
+ break;
+ case PATH1_GID:
+ if (!obj->path1_valid)
+ goto out;
+ left_max = obj->path1_stat.gid;
+ break;
+ case PATH1_INO:
+ if (!obj->path1_valid)
+ goto out;
+ left_max = obj->path1_stat.ino;
+ break;
+ case PATH1_PARENT_UID:
+ if (!obj->path1_parent_valid)
+ goto out;
+ left_max = obj->path1_parent_stat.uid;
+ break;
+ case PATH1_PARENT_GID:
+ if (!obj->path1_parent_valid)
+ goto out;
+ left_max = obj->path1_parent_stat.gid;
+ break;
+ case PATH1_PARENT_INO:
+ if (!obj->path1_parent_valid)
+ goto out;
+ left_max = obj->path1_parent_stat.ino;
+ break;
+ case PATH2_PARENT_UID:
+ if (!obj->path2_parent_valid)
+ goto out;
+ left_max = obj->path2_parent_stat.uid;
+ break;
+ case PATH2_PARENT_GID:
+ if (!obj->path2_parent_valid)
+ goto out;
+ left_max = obj->path2_parent_stat.gid;
+ break;
+ case PATH2_PARENT_INO:
+ if (!obj->path2_parent_valid)
+ goto out;
+ left_max = obj->path2_parent_stat.ino;
+ break;
+ case EXEC_ARGC:
+ if (!bprm)
+ goto out;
+ left_max = bprm->argc;
+ i++;
+ break;
+ case EXEC_ENVC:
+ if (!bprm)
+ goto out;
+ left_max = bprm->envc;
+ i++;
+ break;
+ case TASK_STATE_0:
+ left_max = (u8) (task->tomoyo_flags >> 24);
+ break;
+ case TASK_STATE_1:
+ left_max = (u8) (task->tomoyo_flags >> 16);
+ break;
+ case TASK_STATE_2:
+ left_max = (u8) (task->tomoyo_flags >> 8);
+ break;
+ case MAX_KEYWORD:
+ left_max = *ptr;
+ ptr++;
+ i++;
+ break;
+ case MAX_KEYWORD + 1:
+ left_min = *ptr;
+ ptr++;
+ left_max = *ptr;
+ ptr++;
+ i += 2;
+ break;
+ }
+ if (left != MAX_KEYWORD + 1)
+ left_min = left_max;
+ switch (right) {
+ case TASK_UID:
+ right_max = task->uid;
+ break;
+ case TASK_EUID:
+ right_max = task->euid;
+ break;
+ case TASK_SUID:
+ right_max = task->suid;
+ break;
+ case TASK_FSUID:
+ right_max = task->fsuid;
+ break;
+ case TASK_GID:
+ right_max = task->gid;
+ break;
+ case TASK_EGID:
+ right_max = task->egid;
+ break;
+ case TASK_SGID:
+ right_max = task->sgid;
+ break;
+ case TASK_FSGID:
+ right_max = task->fsgid;
+ break;
+ case TASK_PID:
+ right_max = task->pid;
+ break;
+ case TASK_PPID:
+ right_max = sys_getppid();
+ break;
+ case PATH1_UID:
+ if (!obj->path1_valid)
+ goto out;
+ right_max = obj->path1_stat.uid;
+ break;
+ case PATH1_GID:
+ if (!obj->path1_valid)
+ goto out;
+ right_max = obj->path1_stat.gid;
+ break;
+ case PATH1_INO:
+ if (!obj->path1_valid)
+ goto out;
+ right_max = obj->path1_stat.ino;
+ break;
+ case PATH1_PARENT_UID:
+ if (!obj->path1_parent_valid)
+ goto out;
+ right_max = obj->path1_parent_stat.uid;
+ break;
+ case PATH1_PARENT_GID:
+ if (!obj->path1_parent_valid)
+ goto out;
+ right_max = obj->path1_parent_stat.gid;
+ break;
+ case PATH1_PARENT_INO:
+ if (!obj->path1_parent_valid)
+ goto out;
+ right_max = obj->path1_parent_stat.ino;
+ break;
+ case PATH2_PARENT_UID:
+ if (!obj->path2_parent_valid)
+ goto out;
+ right_max = obj->path2_parent_stat.uid;
+ break;
+ case PATH2_PARENT_GID:
+ if (!obj->path2_parent_valid)
+ goto out;
+ right_max = obj->path2_parent_stat.gid;
+ break;
+ case PATH2_PARENT_INO:
+ if (!obj->path2_parent_valid)
+ goto out;
+ right_max = obj->path2_parent_stat.ino;
+ break;
+ case EXEC_ARGC:
+ if (!bprm)
+ goto out;
+ right_max = bprm->argc;
+ i++;
+ break;
+ case EXEC_ENVC:
+ if (!bprm)
+ goto out;
+ right_max = bprm->envc;
+ i++;
+ break;
+ case TASK_STATE_0:
+ right_max = (u8) (task->tomoyo_flags >> 24);
+ break;
+ case TASK_STATE_1:
+ right_max = (u8) (task->tomoyo_flags >> 16);
+ break;
+ case TASK_STATE_2:
+ right_max = (u8) (task->tomoyo_flags >> 8);
+ break;
+ case MAX_KEYWORD:
+ right_max = *ptr;
+ ptr++;
+ i++;
+ break;
+ case MAX_KEYWORD + 1:
+ right_min = *ptr;
+ ptr++;
+ right_max = *ptr;
+ ptr++;
+ i += 2;
+ break;
+ }
+ if (right != MAX_KEYWORD + 1)
+ right_min = right_max;
+ 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;
+ }
+ if (bprm && (argc || envc))
+ return scan_bprm(bprm, argc, argv, envc, envp, obj->tmp);
+ return true;
+}
+
+/**
+ * ccs_update_condition - Update the current process's condition.
+ *
+ * @acl: Pointer to "struct acl_info".
+ *
+ * Returns nothing.
+ */
+void ccs_update_condition(const struct acl_info *acl)
+{
+ /*
+ * Don't change the lowest byte because it is reserved for
+ * TOMOYO_CHECK_READ_FOR_OPEN_EXEC / CCS_DONT_SLEEP_ON_ENFORCE_ERROR /
+ * TOMOYO_TASK_IS_EXECUTE_HANDLER.
+ */
+ const struct condition_list *ptr = ccs_get_condition_part(acl);
+ struct task_struct *task;
+ u32 tomoyo_flags = current->tomoyo_flags;
+ const u8 flags = ptr ? ptr->post_state[3] : 0;
+ if (!flags)
+ return;
+ task = current;
+ tomoyo_flags = task->tomoyo_flags;
+ if (flags & 1) {
+ tomoyo_flags &= ~0xFF000000;
+ tomoyo_flags |= ptr->post_state[0] << 24;
+ }
+ if (flags & 2) {
+ tomoyo_flags &= ~0x00FF0000;
+ tomoyo_flags |= ptr->post_state[1] << 16;
+ }
+ if (flags & 4) {
+ tomoyo_flags &= ~0x0000FF00;
+ tomoyo_flags |= ptr->post_state[2] << 8;
+ }
+ task->tomoyo_flags = tomoyo_flags;
+}
+
+/**
+ * ccs_print_condition - Print condition part.
+ *
+ * @head: Pointer to "struct ccs_io_buffer".
+ * @cond: Pointer to "struct condition_list". May be NULL.
+ *
+ * Returns true on success, false otherwise.
+ */
+bool ccs_print_condition(struct ccs_io_buffer *head,
+ const struct condition_list *cond)
+{
+ const unsigned long *ptr;
+ const struct argv_entry *argv;
+ const struct envp_entry *envp;
+ u16 condc;
+ u16 argc;
+ u16 envc;
+ u16 i;
+ u16 j;
+ char buffer[32];
+ if (!cond)
+ goto no_condition;
+ condc = cond->condc;
+ argc = cond->argc;
+ envc = cond->envc;
+ ptr = (const unsigned long *) (cond + 1);
+ argv = (const struct argv_entry *) (ptr + condc);
+ envp = (const struct envp_entry *) (argv + argc);
+ memset(buffer, 0, sizeof(buffer));
+ for (i = 0; i < condc; i++) {
+ const u32 header = *ptr;
+ const u8 match = (header >> 16) & 1;
+ const u8 left_1_type = (header >> 18) & 3;
+ const u8 left_2_type = (header >> 20) & 3;
+ const u8 right_1_type = (header >> 22) & 3;
+ const u8 right_2_type = (header >> 24) & 3;
+ const u8 left = header >> 8;
+ const u8 right = header;
+ ptr++;
+ if (!ccs_io_printf(head, "%s", i ? " " : " if "))
+ goto out;
+ if (left < MAX_KEYWORD) {
+ const char *keyword
+ = condition_control_keyword[left].keyword;
+ if (!ccs_io_printf(head, "%s", keyword))
+ goto out;
+ goto print_operator;
+ }
+ print_ulong(buffer, sizeof(buffer) - 1, *ptr, left_1_type);
+ ptr++;
+ if (!ccs_io_printf(head, "%s", buffer))
+ goto out;
+ i++;
+ if (left == MAX_KEYWORD)
+ goto print_operator;
+ print_ulong(buffer, sizeof(buffer) - 1, *ptr, left_2_type);
+ ptr++;
+ if (!ccs_io_printf(head, "-%s", buffer))
+ goto out;
+ i++;
+ print_operator:
+ if (!ccs_io_printf(head, "%s", match ? "=" : "!="))
+ goto out;
+ if (right < MAX_KEYWORD) {
+ const char *keyword
+ = condition_control_keyword[right].keyword;
+ if (!ccs_io_printf(head, "%s", keyword))
+ goto out;
+ continue;
+ }
+ print_ulong(buffer, sizeof(buffer) - 1, *ptr, right_1_type);
+ ptr++;
+ if (!ccs_io_printf(head, "%s", buffer))
+ goto out;
+ i++;
+ if (right == MAX_KEYWORD)
+ continue;
+ print_ulong(buffer, sizeof(buffer) - 1, *ptr, right_2_type);
+ ptr++;
+ if (!ccs_io_printf(head, "-%s", buffer))
+ goto out;
+ i++;
+ }
+
+ if (!argc && !envc)
+ goto post_condition;
+ if (!condc && !ccs_io_printf(head, " if"))
+ goto out;
+ for (i = 0; i < argc; argv++, i++) {
+ const char *op = argv->is_not ? "!=" : "=";
+ if (!ccs_io_printf(head, " exec.argv[%u]%s\"%s\"", argv->index,
+ op, argv->value->name))
+ goto out;
+ }
+ buffer[1] = '\0';
+ for (i = 0; i < envc; envp++, i++) {
+ const char *op = envp->is_not ? "!=" : "=";
+ const char *value = envp->value ? envp->value->name : NULL;
+ if (!ccs_io_printf(head, " exec.envp[\"%s\"]%s",
+ envp->name->name, op))
+ goto out;
+ if (value) {
+ if (!ccs_io_printf(head, "\"%s\"", value))
+ goto out;
+ } else {
+ if (!ccs_io_printf(head, "NULL"))
+ goto out;
+ }
+ }
+ post_condition:
+ i = cond->post_state[3];
+ if (!i)
+ goto no_condition;
+ if (!ccs_io_printf(head, " ; set"))
+ goto out;
+ for (j = 0; j < 3; j++) {
+ if (!(i & (1 << j)))
+ continue;
+ if (!ccs_io_printf(head, " task.state[%u]=%u", j,
+ cond->post_state[j]))
+ goto out;
+ }
+ no_condition:
+ if (ccs_io_printf(head, "\n"))
+ return true;
+ out:
+ return false;
+}
--


\
 
 \ /
  Last update: 2008-04-04 15:13    [W:0.162 / U:0.084 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site