TOMOYO: Allow using executable's realpath and symlink's target as conditions.
Tetsuo Handa [Fri, 8 Jul 2011 04:23:44 +0000 (13:23 +0900)]
This patch adds support for permission checks using executable file's realpath
upon execve() and symlink's target upon symlink(). Hooks are in the last patch
of this pathset.

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>

security/tomoyo/audit.c
security/tomoyo/common.c
security/tomoyo/common.h
security/tomoyo/condition.c
security/tomoyo/gc.c

index 4973edd..b33a20a 100644 (file)
@@ -140,6 +140,8 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
 {
        char *buf = NULL;
        const char *header = NULL;
+       char *realpath = NULL;
+       const char *symlink = NULL;
        int pos;
        const char *domainname = r->domain->domainname->name;
        header = tomoyo_print_header(r);
@@ -147,15 +149,34 @@ char *tomoyo_init_log(struct tomoyo_request_info *r, int len, const char *fmt,
                return NULL;
        /* +10 is for '\n' etc. and '\0'. */
        len += strlen(domainname) + strlen(header) + 10;
+       if (r->ee) {
+               struct file *file = r->ee->bprm->file;
+               realpath = tomoyo_realpath_from_path(&file->f_path);
+               if (!realpath)
+                       goto out;
+               /* +80 is for " exec={ realpath=\"%s\" }" */
+               len += strlen(realpath) + 80;
+       } else if (r->obj && r->obj->symlink_target) {
+               symlink = r->obj->symlink_target->name;
+               /* +18 is for " symlink.target=\"%s\"" */
+               len += 18 + strlen(symlink);
+       }
        len = tomoyo_round2(len);
        buf = kzalloc(len, GFP_NOFS);
        if (!buf)
                goto out;
        len--;
        pos = snprintf(buf, len, "%s", header);
+       if (realpath) {
+               pos += snprintf(buf + pos, len - pos,
+                               " exec={ realpath=\"%s\" }", realpath);
+       } else if (symlink)
+               pos += snprintf(buf + pos, len - pos, " symlink.target=\"%s\"",
+                               symlink);
        pos += snprintf(buf + pos, len - pos, "\n%s\n", domainname);
        vsnprintf(buf + pos, len - pos, fmt, args);
 out:
+       kfree(realpath);
        kfree(header);
        return buf;
 }
index ec02d2a..69d6b59 100644 (file)
@@ -79,6 +79,8 @@ const char * const tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = {
        [TOMOYO_MODE_OTHERS_READ]     = "others_read",
        [TOMOYO_MODE_OTHERS_WRITE]    = "others_write",
        [TOMOYO_MODE_OTHERS_EXECUTE]  = "others_execute",
+       [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",
@@ -353,6 +355,27 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head,
 }
 
 /**
+ * tomoyo_print_name_union_quoted - Print a tomoyo_name_union with a quote.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ * @ptr:  Pointer to "struct tomoyo_name_union".
+ *
+ * Returns nothing.
+ */
+static void tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head,
+                                          const struct tomoyo_name_union *ptr)
+{
+       if (ptr->group) {
+               tomoyo_set_string(head, "@");
+               tomoyo_set_string(head, ptr->group->group_name->name);
+       } else {
+               tomoyo_set_string(head, "\"");
+               tomoyo_set_string(head, ptr->filename->name);
+               tomoyo_set_string(head, "\"");
+       }
+}
+
+/**
  * tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
@@ -1101,6 +1124,9 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
                                (typeof(condp)) (cond + 1);
                        const struct tomoyo_number_union *numbers_p =
                                (typeof(numbers_p)) (condp + condc);
+                       const struct tomoyo_name_union *names_p =
+                               (typeof(names_p))
+                               (numbers_p + cond->numbers_count);
                        u16 skip;
                        for (skip = 0; skip < head->r.cond_index; skip++) {
                                const u8 left = condp->left;
@@ -1112,6 +1138,9 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
                                        break;
                                }
                                switch (right) {
+                               case TOMOYO_NAME_UNION:
+                                       names_p++;
+                                       break;
                                case TOMOYO_NUMBER_UNION:
                                        numbers_p++;
                                        break;
@@ -1138,6 +1167,10 @@ static bool tomoyo_print_condition(struct tomoyo_io_buffer *head,
                                }
                                tomoyo_set_string(head, match ? "=" : "!=");
                                switch (right) {
+                               case TOMOYO_NAME_UNION:
+                                       tomoyo_print_name_union_quoted
+                                               (head, names_p++);
+                                       break;
                                case TOMOYO_NUMBER_UNION:
                                        tomoyo_print_number_union_nospace
                                                (head, numbers_p++);
@@ -1666,6 +1699,22 @@ static DEFINE_SPINLOCK(tomoyo_query_list_lock);
 static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
 
 /**
+ * tomoyo_truncate - Truncate a line.
+ *
+ * @str: String to truncate.
+ *
+ * Returns length of truncated @str.
+ */
+static int tomoyo_truncate(char *str)
+{
+       char *start = str;
+       while (*(unsigned char *) str > (unsigned char) ' ')
+               str++;
+       *str = '\0';
+       return strlen(start) + 1;
+}
+
+/**
  * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode.
  *
  * @domain: Pointer to "struct tomoyo_domain_info".
@@ -1676,6 +1725,8 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);
 static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
 {
        char *buffer;
+       char *realpath = NULL;
+       char *symlink = NULL;
        char *cp = strchr(header, '\n');
        int len;
        if (!cp)
@@ -1685,10 +1736,25 @@ static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header)
                return;
        *cp++ = '\0';
        len = strlen(cp) + 1;
+       /* strstr() will return NULL if ordering is wrong. */
+       if (*cp == 'f') {
+               realpath = strstr(header, " exec={ realpath=\"");
+               if (realpath) {
+                       realpath += 8;
+                       len += tomoyo_truncate(realpath) + 6;
+               }
+               symlink = strstr(header, " symlink.target=\"");
+               if (symlink)
+                       len += tomoyo_truncate(symlink + 1) + 1;
+       }
        buffer = kmalloc(len, GFP_NOFS);
        if (!buffer)
                return;
        snprintf(buffer, len - 1, "%s", cp);
+       if (realpath)
+               tomoyo_addprintf(buffer, len, " exec.%s", realpath);
+       if (symlink)
+               tomoyo_addprintf(buffer, len, "%s", symlink);
        tomoyo_normalize_line(buffer);
        if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer,
                                  false))
index 5a0fced..7e56e6b 100644 (file)
@@ -73,6 +73,8 @@ enum tomoyo_conditions_index {
        TOMOYO_MODE_OTHERS_READ,     /* S_IROTH */
        TOMOYO_MODE_OTHERS_WRITE,    /* S_IWOTH */
        TOMOYO_MODE_OTHERS_EXECUTE,  /* S_IXOTH */
+       TOMOYO_EXEC_REALPATH,
+       TOMOYO_SYMLINK_TARGET,
        TOMOYO_PATH1_UID,
        TOMOYO_PATH1_GID,
        TOMOYO_PATH1_INO,
@@ -101,6 +103,7 @@ enum tomoyo_conditions_index {
        TOMOYO_PATH2_PARENT_PERM,
        TOMOYO_MAX_CONDITION_KEYWORD,
        TOMOYO_NUMBER_UNION,
+       TOMOYO_NAME_UNION,
 };
 
 
@@ -351,6 +354,11 @@ struct tomoyo_request_info {
         * NULL if not dealing files.
         */
        struct tomoyo_obj_info *obj;
+       /*
+        * For holding parameters specific to execve() request.
+        * NULL if not dealing do_execve().
+        */
+       struct tomoyo_execve *ee;
        struct tomoyo_domain_info *domain;
        /* For holding parameters. */
        union {
@@ -476,6 +484,20 @@ struct tomoyo_obj_info {
         * parent directory.
         */
        struct tomoyo_mini_stat stat[TOMOYO_MAX_PATH_STAT];
+       /*
+        * Content of symbolic link to be created. NULL for operations other
+        * than symlink().
+        */
+       struct tomoyo_path_info *symlink_target;
+};
+
+/* Structure for execve() operation. */
+struct tomoyo_execve {
+       struct tomoyo_request_info r;
+       struct tomoyo_obj_info obj;
+       struct linux_binprm *bprm;
+       /* For temporary use. */
+       char *tmp; /* Size is TOMOYO_EXEC_TMPSIZE bytes */
 };
 
 /* Structure for entries which follows "struct tomoyo_condition". */
@@ -494,9 +516,11 @@ struct tomoyo_condition {
        u32 size; /* Memory size allocated for this entry. */
        u16 condc; /* Number of conditions in this struct. */
        u16 numbers_count; /* Number of "struct tomoyo_number_union values". */
+       u16 names_count; /* Number of "struct tomoyo_name_union names". */
        /*
         * struct tomoyo_condition_element condition[condc];
         * struct tomoyo_number_union values[numbers_count];
+        * struct tomoyo_name_union names[names_count];
         */
 };
 
index ac7ebeb..790b987 100644 (file)
 LIST_HEAD(tomoyo_condition_list);
 
 /**
+ * tomoyo_scan_exec_realpath - Check "exec.realpath" parameter of "struct tomoyo_condition".
+ *
+ * @file:  Pointer to "struct file".
+ * @ptr:   Pointer to "struct tomoyo_name_union".
+ * @match: True if "exec.realpath=", false if "exec.realpath!=".
+ *
+ * Returns true on success, false otherwise.
+ */
+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;
+}
+
+/**
+ * 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 = start + strlen(start) - 1;
+       if (cp == start || *start++ != '"' || *cp != '"')
+               return NULL;
+       *cp = '\0';
+       if (*start && !tomoyo_correct_word(start))
+               return NULL;
+       return tomoyo_get_name(start);
+}
+
+/**
+ * tomoyo_parse_name_union_quoted - Parse a quoted word.
+ *
+ * @param: Pointer to "struct tomoyo_acl_param".
+ * @ptr:   Pointer to "struct tomoyo_name_union".
+ *
+ * Returns true on success, false otherwise.
+ */
+static bool tomoyo_parse_name_union_quoted(struct tomoyo_acl_param *param,
+                                          struct tomoyo_name_union *ptr)
+{
+       char *filename = param->data;
+       if (*filename == '@')
+               return tomoyo_parse_name_union(param, ptr);
+       ptr->filename = tomoyo_get_dqword(filename);
+       return ptr->filename != NULL;
+}
+
+/**
  * tomoyo_same_condition - Check for duplicated "struct tomoyo_condition" entry.
  *
  * @a: Pointer to "struct tomoyo_condition".
@@ -23,6 +85,7 @@ static inline bool tomoyo_same_condition(const struct tomoyo_condition *a,
 {
        return a->size == b->size && a->condc == b->condc &&
                a->numbers_count == b->numbers_count &&
+               a->names_count == b->names_count &&
                !memcmp(a + 1, b + 1, a->size - sizeof(*a));
 }
 
@@ -114,6 +177,7 @@ struct tomoyo_condition *tomoyo_get_condition(struct tomoyo_acl_param *param)
        struct tomoyo_condition *entry = NULL;
        struct tomoyo_condition_element *condp = NULL;
        struct tomoyo_number_union *numbers_p = NULL;
+       struct tomoyo_name_union *names_p = NULL;
        struct tomoyo_condition e = { };
        char * const start_of_string = param->data;
        char * const end_of_string = start_of_string + strlen(start_of_string);
@@ -178,6 +242,20 @@ rerun:
                        e.condc++;
                else
                        e.condc--;
+               if (left == TOMOYO_EXEC_REALPATH ||
+                   left == TOMOYO_SYMLINK_TARGET) {
+                       if (!names_p) {
+                               e.names_count++;
+                       } else {
+                               e.names_count--;
+                               right = TOMOYO_NAME_UNION;
+                               param->data = right_word;
+                               if (!tomoyo_parse_name_union_quoted(param,
+                                                                   names_p++))
+                                       goto out;
+                       }
+                       goto store_value;
+               }
                right = tomoyo_condition_type(right_word);
                if (right == TOMOYO_MAX_CONDITION_KEYWORD) {
                        if (!numbers_p) {
@@ -191,6 +269,7 @@ rerun:
                                        goto out;
                        }
                }
+store_value:
                if (!condp) {
                        dprintk(KERN_WARNING "%u: dry_run left=%u right=%u "
                                "match=%u\n", __LINE__, left, right, !is_not);
@@ -204,21 +283,23 @@ rerun:
                        condp->equals);
                condp++;
        }
-       dprintk(KERN_INFO "%u: cond=%u numbers=%u\n",
-               __LINE__, e.condc, e.numbers_count);
+       dprintk(KERN_INFO "%u: cond=%u numbers=%u names=%u\n",
+               __LINE__, e.condc, e.numbers_count, e.names_count);
        if (entry) {
-               BUG_ON(e.numbers_count | e.condc);
+               BUG_ON(e.names_count | e.numbers_count | e.condc);
                return tomoyo_commit_condition(entry);
        }
        e.size = sizeof(*entry)
                + e.condc * sizeof(struct tomoyo_condition_element)
-               + e.numbers_count * sizeof(struct tomoyo_number_union);
+               + e.numbers_count * sizeof(struct tomoyo_number_union)
+               + e.names_count * sizeof(struct tomoyo_name_union);
        entry = kzalloc(e.size, GFP_NOFS);
        if (!entry)
                return NULL;
        *entry = e;
        condp = (struct tomoyo_condition_element *) (entry + 1);
        numbers_p = (struct tomoyo_number_union *) (condp + e.condc);
+       names_p = (struct tomoyo_name_union *) (numbers_p + e.numbers_count);
        {
                bool flag = false;
                for (pos = start_of_string; pos < end_of_string; pos++) {
@@ -309,6 +390,7 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
        unsigned long max_v[2] = { 0, 0 };
        const struct tomoyo_condition_element *condp;
        const struct tomoyo_number_union *numbers_p;
+       const struct tomoyo_name_union *names_p;
        struct tomoyo_obj_info *obj;
        u16 condc;
        if (!cond)
@@ -317,6 +399,8 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
        obj = r->obj;
        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);
        for (i = 0; i < condc; i++) {
                const bool match = condp->equals;
                const u8 left = condp->left;
@@ -324,6 +408,30 @@ bool tomoyo_condition(struct tomoyo_request_info *r,
                bool is_bitop[2] = { false, false };
                u8 j;
                condp++;
+               /* 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 *ee;
+                               struct file *file;
+                       case TOMOYO_SYMLINK_TARGET:
+                               symlink = obj ? obj->symlink_target : NULL;
+                               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;
index 21fccd6..e0502b6 100644 (file)
@@ -357,13 +357,18 @@ void tomoyo_del_condition(struct list_head *element)
                                                     head.list);
        const u16 condc = cond->condc;
        const u16 numbers_count = cond->numbers_count;
+       const u16 names_count = cond->names_count;
        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);
        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++);
 }
 
 /**