]> Pileus Git - ~andy/linux/blobdiff - security/tomoyo/domain.c
Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/djbw/async_tx
[~andy/linux] / security / tomoyo / domain.c
index 7893127d8770a79ce813ae2509b40d5f28b9274f..cd0f92d88bb43a35c99230c52effb311da0d5213 100644 (file)
@@ -1,9 +1,7 @@
 /*
  * security/tomoyo/domain.c
  *
- * Domain transition functions for TOMOYO.
- *
- * Copyright (C) 2005-2010  NTT DATA CORPORATION
+ * Copyright (C) 2005-2011  NTT DATA CORPORATION
  */
 
 #include "common.h"
@@ -69,7 +67,7 @@ int tomoyo_update_policy(struct tomoyo_acl_head *new_entry, const int size,
 static inline bool tomoyo_same_acl_head(const struct tomoyo_acl_info *a,
                                        const struct tomoyo_acl_info *b)
 {
-       return a->type == b->type;
+       return a->type == b->type && a->cond == b->cond;
 }
 
 /**
@@ -100,8 +98,13 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
        struct tomoyo_acl_info *entry;
        struct list_head * const list = param->list;
 
+       if (param->data[0]) {
+               new_entry->cond = tomoyo_get_condition(param);
+               if (!new_entry->cond)
+                       return -EINVAL;
+       }
        if (mutex_lock_interruptible(&tomoyo_policy_lock))
-               return error;
+               goto out;
        list_for_each_entry_rcu(entry, list, list) {
                if (!tomoyo_same_acl_head(entry, new_entry) ||
                    !check_duplicate(entry, new_entry))
@@ -122,6 +125,8 @@ int tomoyo_update_domain(struct tomoyo_acl_info *new_entry, const int size,
                }
        }
        mutex_unlock(&tomoyo_policy_lock);
+out:
+       tomoyo_put_condition(new_entry->cond);
        return error;
 }
 
@@ -148,10 +153,12 @@ retry:
        list_for_each_entry_rcu(ptr, list, list) {
                if (ptr->is_deleted || ptr->type != r->param_type)
                        continue;
-               if (check_entry(r, ptr)) {
-                       r->granted = true;
-                       return;
-               }
+               if (!check_entry(r, ptr))
+                       continue;
+               if (!tomoyo_condition(r, ptr->cond))
+                       continue;
+               r->granted = true;
+               return;
        }
        if (!retried) {
                retried = true;
@@ -173,10 +180,10 @@ LIST_HEAD(tomoyo_domain_list);
  */
 static const char *tomoyo_last_word(const char *name)
 {
-        const char *cp = strrchr(name, ' ');
-        if (cp)
-                return cp + 1;
-        return name;
+       const char *cp = strrchr(name, ' ');
+       if (cp)
+               return cp + 1;
+       return name;
 }
 
 /**
@@ -566,23 +573,27 @@ out:
  */
 int tomoyo_find_next_domain(struct linux_binprm *bprm)
 {
-       struct tomoyo_request_info r;
-       char *tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
        struct tomoyo_domain_info *old_domain = tomoyo_domain();
        struct tomoyo_domain_info *domain = NULL;
        const char *original_name = bprm->filename;
-       u8 mode;
-       bool is_enforce;
        int retval = -ENOMEM;
        bool need_kfree = false;
        bool reject_on_transition_failure = false;
        struct tomoyo_path_info rn = { }; /* real name */
-
-       mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
-       is_enforce = (mode == TOMOYO_CONFIG_ENFORCING);
-       if (!tmp)
-               goto out;
-
+       struct tomoyo_execve *ee = kzalloc(sizeof(*ee), GFP_NOFS);
+       if (!ee)
+               return -ENOMEM;
+       ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_NOFS);
+       if (!ee->tmp) {
+               kfree(ee);
+               return -ENOMEM;
+       }
+       /* 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;
+       ee->r.obj = &ee->obj;
+       ee->obj.path1 = bprm->file->f_path;
  retry:
        if (need_kfree) {
                kfree(rn.name);
@@ -616,7 +627,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
        }
 
        /* Check execute permission. */
-       retval = tomoyo_path_permission(&r, TOMOYO_TYPE_EXECUTE, &rn);
+       retval = tomoyo_path_permission(&ee->r, TOMOYO_TYPE_EXECUTE, &rn);
        if (retval == TOMOYO_RETRY_REQUEST)
                goto retry;
        if (retval < 0)
@@ -627,12 +638,12 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
         * wildcard) rather than the pathname passed to execve()
         * (which never contains wildcard).
         */
-       if (r.param.path.matched_path) {
+       if (ee->r.param.path.matched_path) {
                if (need_kfree)
                        kfree(rn.name);
                need_kfree = false;
                /* This is OK because it is read only. */
-               rn = *r.param.path.matched_path;
+               rn = *ee->r.param.path.matched_path;
        }
 
        /* Calculate domain to transit to. */
@@ -640,7 +651,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
                                       &rn)) {
        case TOMOYO_TRANSITION_CONTROL_RESET:
                /* Transit to the root of specified namespace. */
-               snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", rn.name);
+               snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "<%s>", rn.name);
                /*
                 * Make do_execve() fail if domain transition across namespaces
                 * has failed.
@@ -649,7 +660,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
                break;
        case TOMOYO_TRANSITION_CONTROL_INITIALIZE:
                /* Transit to the child of current namespace's root. */
-               snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+               snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
                         old_domain->ns->name, rn.name);
                break;
        case TOMOYO_TRANSITION_CONTROL_KEEP:
@@ -668,29 +679,30 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
                        domain = old_domain;
                } else {
                        /* Normal domain transition. */
-                       snprintf(tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+                       snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
                                 old_domain->domainname->name, rn.name);
                }
                break;
        }
        if (!domain)
-               domain = tomoyo_assign_domain(tmp, true);
+               domain = tomoyo_assign_domain(ee->tmp, true);
        if (domain)
                retval = 0;
        else if (reject_on_transition_failure) {
-               printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n", tmp);
+               printk(KERN_WARNING "ERROR: Domain '%s' not ready.\n",
+                      ee->tmp);
                retval = -ENOMEM;
-       } else if (r.mode == TOMOYO_CONFIG_ENFORCING)
+       } else if (ee->r.mode == TOMOYO_CONFIG_ENFORCING)
                retval = -ENOMEM;
        else {
                retval = 0;
                if (!old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED]) {
                        old_domain->flags[TOMOYO_DIF_TRANSITION_FAILED] = true;
-                       r.granted = false;
-                       tomoyo_write_log(&r, "%s", tomoyo_dif
+                       ee->r.granted = false;
+                       tomoyo_write_log(&ee->r, "%s", tomoyo_dif
                                         [TOMOYO_DIF_TRANSITION_FAILED]);
                        printk(KERN_WARNING
-                              "ERROR: Domain '%s' not defined.\n", tmp);
+                              "ERROR: Domain '%s' not defined.\n", ee->tmp);
                }
        }
  out:
@@ -701,6 +713,54 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm)
        bprm->cred->security = domain;
        if (need_kfree)
                kfree(rn.name);
-       kfree(tmp);
+       kfree(ee->tmp);
+       kfree(ee->dump.data);
+       kfree(ee);
        return retval;
 }
+
+/**
+ * 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_finish_execve(). */
+       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;
+}