include cleanup: Update gfp.h and slab.h includes to prepare for breaking implicit...
[safe/jmp/linux-2.6] / security / tomoyo / domain.c
index aa119ca..acb8c39 100644 (file)
  */
 
 #include "common.h"
-#include "tomoyo.h"
-#include "realpath.h"
 #include <linux/binfmts.h>
+#include <linux/slab.h>
 
 /* Variables definitions.*/
 
 /* The initial domain. */
 struct tomoyo_domain_info tomoyo_kernel_domain;
 
-/* The list for "struct tomoyo_domain_info". */
-LIST_HEAD(tomoyo_domain_list);
-DECLARE_RWSEM(tomoyo_domain_list_lock);
-
-/* Structure for "initialize_domain" and "no_initialize_domain" keyword. */
-struct tomoyo_domain_initializer_entry {
-       struct list_head list;
-       const struct tomoyo_path_info *domainname;    /* This may be NULL */
-       const struct tomoyo_path_info *program;
-       bool is_deleted;
-       bool is_not;       /* True if this entry is "no_initialize_domain".  */
-       /* True if the domainname is tomoyo_get_last_name(). */
-       bool is_last_name;
-};
-
-/* Structure for "keep_domain" and "no_keep_domain" keyword. */
-struct tomoyo_domain_keeper_entry {
-       struct list_head list;
-       const struct tomoyo_path_info *domainname;
-       const struct tomoyo_path_info *program;       /* This may be NULL */
-       bool is_deleted;
-       bool is_not;       /* True if this entry is "no_keep_domain".        */
-       /* True if the domainname is tomoyo_get_last_name(). */
-       bool is_last_name;
-};
-
-/* Structure for "alias" keyword. */
-struct tomoyo_alias_entry {
-       struct list_head list;
-       const struct tomoyo_path_info *original_name;
-       const struct tomoyo_path_info *aliased_name;
-       bool is_deleted;
-};
-
-/**
- * tomoyo_set_domain_flag - Set or clear domain's attribute flags.
+/*
+ * tomoyo_domain_list is used for holding list of domains.
+ * The ->acl_info_list of "struct tomoyo_domain_info" is used for holding
+ * permissions (e.g. "allow_read /lib/libc-2.5.so") given to each domain.
  *
- * @domain:    Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
- * @flags:     Flags to set or clear.
+ * An entry is added by
+ *
+ * # ( echo "<kernel>"; echo "allow_execute /sbin/init" ) > \
+ *                                  /sys/kernel/security/tomoyo/domain_policy
+ *
+ * and is deleted by
+ *
+ * # ( echo "<kernel>"; echo "delete allow_execute /sbin/init" ) > \
+ *                                  /sys/kernel/security/tomoyo/domain_policy
+ *
+ * and all entries are retrieved by
+ *
+ * # cat /sys/kernel/security/tomoyo/domain_policy
+ *
+ * A domain is added by
+ *
+ * # echo "<kernel>" > /sys/kernel/security/tomoyo/domain_policy
+ *
+ * and is deleted by
+ *
+ * # echo "delete <kernel>" > /sys/kernel/security/tomoyo/domain_policy
+ *
+ * and all domains are retrieved by
+ *
+ * # grep '^<kernel>' /sys/kernel/security/tomoyo/domain_policy
  *
- * Returns nothing.
+ * Normally, a domainname is monotonically getting longer because a domainname
+ * which the process will belong to if an execve() operation succeeds is
+ * defined as a concatenation of "current domainname" + "pathname passed to
+ * execve()".
+ * See tomoyo_domain_initializer_list and tomoyo_domain_keeper_list for
+ * exceptions.
  */
-void tomoyo_set_domain_flag(struct tomoyo_domain_info *domain,
-                           const bool is_delete, const u8 flags)
-{
-       /* We need to serialize because this is bitfield operation. */
-       static DEFINE_SPINLOCK(lock);
-       spin_lock(&lock);
-       if (!is_delete)
-               domain->flags |= flags;
-       else
-               domain->flags &= ~flags;
-       spin_unlock(&lock);
-}
+LIST_HEAD(tomoyo_domain_list);
 
 /**
  * tomoyo_get_last_name - Get last component of a domainname.
@@ -92,9 +75,43 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain)
        return cp0;
 }
 
-/* The list for "struct tomoyo_domain_initializer_entry". */
-static LIST_HEAD(tomoyo_domain_initializer_list);
-static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
+/*
+ * tomoyo_domain_initializer_list is used for holding list of programs which
+ * triggers reinitialization of domainname. Normally, a domainname is
+ * monotonically getting longer. But sometimes, we restart daemon programs.
+ * It would be convenient for us that "a daemon started upon system boot" and
+ * "the daemon restarted from console" belong to the same domain. Thus, TOMOYO
+ * provides a way to shorten domainnames.
+ *
+ * An entry is added by
+ *
+ * # echo 'initialize_domain /usr/sbin/httpd' > \
+ *                               /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and is deleted by
+ *
+ * # echo 'delete initialize_domain /usr/sbin/httpd' > \
+ *                               /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and all entries are retrieved by
+ *
+ * # grep ^initialize_domain /sys/kernel/security/tomoyo/exception_policy
+ *
+ * In the example above, /usr/sbin/httpd will belong to
+ * "<kernel> /usr/sbin/httpd" domain.
+ *
+ * You may specify a domainname using "from" keyword.
+ * "initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd"
+ * will cause "/usr/sbin/httpd" executed from "<kernel> /etc/rc.d/init.d/httpd"
+ * domain to belong to "<kernel> /usr/sbin/httpd" domain.
+ *
+ * You may add "no_" prefix to "initialize_domain".
+ * "initialize_domain /usr/sbin/httpd" and
+ * "no_initialize_domain /usr/sbin/httpd from <kernel> /etc/rc.d/init.d/httpd"
+ * will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain
+ * unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain.
+ */
+LIST_HEAD(tomoyo_domain_initializer_list);
 
 /**
  * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
@@ -105,59 +122,65 @@ static DECLARE_RWSEM(tomoyo_domain_initializer_list_lock);
  * @is_delete:  True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_update_domain_initializer_entry(const char *domainname,
                                                  const char *program,
                                                  const bool is_not,
                                                  const bool is_delete)
 {
-       struct tomoyo_domain_initializer_entry *new_entry;
+       struct tomoyo_domain_initializer_entry *entry = NULL;
        struct tomoyo_domain_initializer_entry *ptr;
-       const struct tomoyo_path_info *saved_program;
+       const struct tomoyo_path_info *saved_program = NULL;
        const struct tomoyo_path_info *saved_domainname = NULL;
-       int error = -ENOMEM;
+       int error = is_delete ? -ENOENT : -ENOMEM;
        bool is_last_name = false;
 
-       if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+       if (!tomoyo_is_correct_path(program, 1, -1, -1))
                return -EINVAL; /* No patterns allowed. */
        if (domainname) {
                if (!tomoyo_is_domain_def(domainname) &&
-                   tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
+                   tomoyo_is_correct_path(domainname, 1, -1, -1))
                        is_last_name = true;
-               else if (!tomoyo_is_correct_domain(domainname, __func__))
+               else if (!tomoyo_is_correct_domain(domainname))
                        return -EINVAL;
-               saved_domainname = tomoyo_save_name(domainname);
+               saved_domainname = tomoyo_get_name(domainname);
                if (!saved_domainname)
-                       return -ENOMEM;
+                       goto out;
        }
-       saved_program = tomoyo_save_name(program);
+       saved_program = tomoyo_get_name(program);
        if (!saved_program)
-               return -ENOMEM;
-       down_write(&tomoyo_domain_initializer_list_lock);
-       list_for_each_entry(ptr, &tomoyo_domain_initializer_list, list) {
+               goto out;
+       if (!is_delete)
+               entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
                if (ptr->is_not != is_not ||
                    ptr->domainname != saved_domainname ||
                    ptr->program != saved_program)
                        continue;
                ptr->is_deleted = is_delete;
                error = 0;
-               goto out;
+               break;
        }
-       if (is_delete) {
-               error = -ENOENT;
-               goto out;
+       if (!is_delete && error && tomoyo_memory_ok(entry)) {
+               entry->domainname = saved_domainname;
+               saved_domainname = NULL;
+               entry->program = saved_program;
+               saved_program = NULL;
+               entry->is_not = is_not;
+               entry->is_last_name = is_last_name;
+               list_add_tail_rcu(&entry->list,
+                                 &tomoyo_domain_initializer_list);
+               entry = NULL;
+               error = 0;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
-               goto out;
-       new_entry->domainname = saved_domainname;
-       new_entry->program = saved_program;
-       new_entry->is_not = is_not;
-       new_entry->is_last_name = is_last_name;
-       list_add_tail(&new_entry->list, &tomoyo_domain_initializer_list);
-       error = 0;
+       mutex_unlock(&tomoyo_policy_lock);
  out:
-       up_write(&tomoyo_domain_initializer_list_lock);
+       tomoyo_put_name(saved_domainname);
+       tomoyo_put_name(saved_program);
+       kfree(entry);
        return error;
 }
 
@@ -167,13 +190,14 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
 {
        struct list_head *pos;
        bool done = true;
 
-       down_read(&tomoyo_domain_initializer_list_lock);
        list_for_each_cookie(pos, head->read_var2,
                             &tomoyo_domain_initializer_list) {
                const char *no;
@@ -189,15 +213,13 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
                        from = " from ";
                        domain = ptr->domainname->name;
                }
-               if (!tomoyo_io_printf(head,
-                                     "%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN
-                                     "%s%s%s\n", no, ptr->program->name, from,
-                                     domain)) {
-                       done = false;
+               done = tomoyo_io_printf(head,
+                                       "%s" TOMOYO_KEYWORD_INITIALIZE_DOMAIN
+                                       "%s%s%s\n", no, ptr->program->name,
+                                       from, domain);
+               if (!done)
                        break;
-               }
        }
-       up_read(&tomoyo_domain_initializer_list_lock);
        return done;
 }
 
@@ -209,6 +231,8 @@ bool tomoyo_read_domain_initializer_policy(struct tomoyo_io_buffer *head)
  * @is_delete: True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
                                           const bool is_delete)
@@ -234,6 +258,8 @@ int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
  *
  * Returns true if executing @program reinitializes domain transition,
  * false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
                                         domainname,
@@ -244,8 +270,7 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
        struct tomoyo_domain_initializer_entry *ptr;
        bool flag = false;
 
-       down_read(&tomoyo_domain_initializer_list_lock);
-       list_for_each_entry(ptr,  &tomoyo_domain_initializer_list, list) {
+       list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
                if (ptr->is_deleted)
                        continue;
                if (ptr->domainname) {
@@ -265,13 +290,48 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
                }
                flag = true;
        }
-       up_read(&tomoyo_domain_initializer_list_lock);
        return flag;
 }
 
-/* The list for "struct tomoyo_domain_keeper_entry". */
-static LIST_HEAD(tomoyo_domain_keeper_list);
-static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
+/*
+ * tomoyo_domain_keeper_list is used for holding list of domainnames which
+ * suppresses domain transition. Normally, a domainname is monotonically
+ * getting longer. But sometimes, we want to suppress domain transition.
+ * It would be convenient for us that programs executed from a login session
+ * belong to the same domain. Thus, TOMOYO provides a way to suppress domain
+ * transition.
+ *
+ * An entry is added by
+ *
+ * # echo 'keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \
+ *                              /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and is deleted by
+ *
+ * # echo 'delete keep_domain <kernel> /usr/sbin/sshd /bin/bash' > \
+ *                              /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and all entries are retrieved by
+ *
+ * # grep ^keep_domain /sys/kernel/security/tomoyo/exception_policy
+ *
+ * In the example above, any process which belongs to
+ * "<kernel> /usr/sbin/sshd /bin/bash" domain will remain in that domain,
+ * unless explicitly specified by "initialize_domain" or "no_keep_domain".
+ *
+ * You may specify a program using "from" keyword.
+ * "keep_domain /bin/pwd from <kernel> /usr/sbin/sshd /bin/bash"
+ * will cause "/bin/pwd" executed from "<kernel> /usr/sbin/sshd /bin/bash"
+ * domain to remain in "<kernel> /usr/sbin/sshd /bin/bash" domain.
+ *
+ * You may add "no_" prefix to "keep_domain".
+ * "keep_domain <kernel> /usr/sbin/sshd /bin/bash" and
+ * "no_keep_domain /usr/bin/passwd from <kernel> /usr/sbin/sshd /bin/bash" will
+ * cause "/usr/bin/passwd" to belong to
+ * "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless
+ * explicitly specified by "initialize_domain".
+ */
+LIST_HEAD(tomoyo_domain_keeper_list);
 
 /**
  * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
@@ -282,59 +342,64 @@ static DECLARE_RWSEM(tomoyo_domain_keeper_list_lock);
  * @is_delete:  True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_update_domain_keeper_entry(const char *domainname,
                                             const char *program,
                                             const bool is_not,
                                             const bool is_delete)
 {
-       struct tomoyo_domain_keeper_entry *new_entry;
+       struct tomoyo_domain_keeper_entry *entry = NULL;
        struct tomoyo_domain_keeper_entry *ptr;
-       const struct tomoyo_path_info *saved_domainname;
+       const struct tomoyo_path_info *saved_domainname = NULL;
        const struct tomoyo_path_info *saved_program = NULL;
-       int error = -ENOMEM;
+       int error = is_delete ? -ENOENT : -ENOMEM;
        bool is_last_name = false;
 
        if (!tomoyo_is_domain_def(domainname) &&
-           tomoyo_is_correct_path(domainname, 1, -1, -1, __func__))
+           tomoyo_is_correct_path(domainname, 1, -1, -1))
                is_last_name = true;
-       else if (!tomoyo_is_correct_domain(domainname, __func__))
+       else if (!tomoyo_is_correct_domain(domainname))
                return -EINVAL;
        if (program) {
-               if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
+               if (!tomoyo_is_correct_path(program, 1, -1, -1))
                        return -EINVAL;
-               saved_program = tomoyo_save_name(program);
+               saved_program = tomoyo_get_name(program);
                if (!saved_program)
-                       return -ENOMEM;
+                       goto out;
        }
-       saved_domainname = tomoyo_save_name(domainname);
+       saved_domainname = tomoyo_get_name(domainname);
        if (!saved_domainname)
-               return -ENOMEM;
-       down_write(&tomoyo_domain_keeper_list_lock);
-       list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+               goto out;
+       if (!is_delete)
+               entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
                if (ptr->is_not != is_not ||
                    ptr->domainname != saved_domainname ||
                    ptr->program != saved_program)
                        continue;
                ptr->is_deleted = is_delete;
                error = 0;
-               goto out;
+               break;
        }
-       if (is_delete) {
-               error = -ENOENT;
-               goto out;
+       if (!is_delete && error && tomoyo_memory_ok(entry)) {
+               entry->domainname = saved_domainname;
+               saved_domainname = NULL;
+               entry->program = saved_program;
+               saved_program = NULL;
+               entry->is_not = is_not;
+               entry->is_last_name = is_last_name;
+               list_add_tail_rcu(&entry->list, &tomoyo_domain_keeper_list);
+               entry = NULL;
+               error = 0;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
-               goto out;
-       new_entry->domainname = saved_domainname;
-       new_entry->program = saved_program;
-       new_entry->is_not = is_not;
-       new_entry->is_last_name = is_last_name;
-       list_add_tail(&new_entry->list, &tomoyo_domain_keeper_list);
-       error = 0;
+       mutex_unlock(&tomoyo_policy_lock);
  out:
-       up_write(&tomoyo_domain_keeper_list_lock);
+       tomoyo_put_name(saved_domainname);
+       tomoyo_put_name(saved_program);
+       kfree(entry);
        return error;
 }
 
@@ -345,6 +410,7 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
  * @is_not:    True if it is "no_keep_domain" entry.
  * @is_delete: True if it is a delete request.
  *
+ * Caller holds tomoyo_read_lock().
  */
 int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
                                      const bool is_delete)
@@ -365,13 +431,14 @@ int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
 {
        struct list_head *pos;
        bool done = true;
 
-       down_read(&tomoyo_domain_keeper_list_lock);
        list_for_each_cookie(pos, head->read_var2,
                             &tomoyo_domain_keeper_list) {
                struct tomoyo_domain_keeper_entry *ptr;
@@ -387,15 +454,13 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
                        from = " from ";
                        program = ptr->program->name;
                }
-               if (!tomoyo_io_printf(head,
-                                     "%s" TOMOYO_KEYWORD_KEEP_DOMAIN
-                                     "%s%s%s\n", no, program, from,
-                                     ptr->domainname->name)) {
-                       done = false;
+               done = tomoyo_io_printf(head,
+                                       "%s" TOMOYO_KEYWORD_KEEP_DOMAIN
+                                       "%s%s%s\n", no, program, from,
+                                       ptr->domainname->name);
+               if (!done)
                        break;
-               }
        }
-       up_read(&tomoyo_domain_keeper_list_lock);
        return done;
 }
 
@@ -408,6 +473,8 @@ bool tomoyo_read_domain_keeper_policy(struct tomoyo_io_buffer *head)
  *
  * Returns true if executing @program supresses domain transition,
  * false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
                                    const struct tomoyo_path_info *program,
@@ -416,8 +483,7 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
        struct tomoyo_domain_keeper_entry *ptr;
        bool flag = false;
 
-       down_read(&tomoyo_domain_keeper_list_lock);
-       list_for_each_entry(ptr, &tomoyo_domain_keeper_list, list) {
+       list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
                if (ptr->is_deleted)
                        continue;
                if (!ptr->is_last_name) {
@@ -435,13 +501,40 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
                }
                flag = true;
        }
-       up_read(&tomoyo_domain_keeper_list_lock);
        return flag;
 }
 
-/* The list for "struct tomoyo_alias_entry". */
-static LIST_HEAD(tomoyo_alias_list);
-static DECLARE_RWSEM(tomoyo_alias_list_lock);
+/*
+ * tomoyo_alias_list is used for holding list of symlink's pathnames which are
+ * allowed to be passed to an execve() request. Normally, the domainname which
+ * the current process will belong to after execve() succeeds is calculated
+ * using dereferenced pathnames. But some programs behave differently depending
+ * on the name passed to argv[0]. For busybox, calculating domainname using
+ * dereferenced pathnames will cause all programs in the busybox to belong to
+ * the same domain. Thus, TOMOYO provides a way to allow use of symlink's
+ * pathname for checking execve()'s permission and calculating domainname which
+ * the current process will belong to after execve() succeeds.
+ *
+ * An entry is added by
+ *
+ * # echo 'alias /bin/busybox /bin/cat' > \
+ *                            /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and is deleted by
+ *
+ * # echo 'delete alias /bin/busybox /bin/cat' > \
+ *                            /sys/kernel/security/tomoyo/exception_policy
+ *
+ * and all entries are retrieved by
+ *
+ * # grep ^alias /sys/kernel/security/tomoyo/exception_policy
+ *
+ * In the example above, if /bin/cat is a symlink to /bin/busybox and execution
+ * of /bin/cat is requested, permission is checked for /bin/cat rather than
+ * /bin/busybox and domainname which the current process will belong to after
+ * execve() succeeds is calculated using /bin/cat rather than /bin/busybox .
+ */
+LIST_HEAD(tomoyo_alias_list);
 
 /**
  * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
@@ -451,46 +544,51 @@ static DECLARE_RWSEM(tomoyo_alias_list_lock);
  * @is_delete:     True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 static int tomoyo_update_alias_entry(const char *original_name,
                                     const char *aliased_name,
                                     const bool is_delete)
 {
-       struct tomoyo_alias_entry *new_entry;
+       struct tomoyo_alias_entry *entry = NULL;
        struct tomoyo_alias_entry *ptr;
        const struct tomoyo_path_info *saved_original_name;
        const struct tomoyo_path_info *saved_aliased_name;
-       int error = -ENOMEM;
+       int error = is_delete ? -ENOENT : -ENOMEM;
 
-       if (!tomoyo_is_correct_path(original_name, 1, -1, -1, __func__) ||
-           !tomoyo_is_correct_path(aliased_name, 1, -1, -1, __func__))
+       if (!tomoyo_is_correct_path(original_name, 1, -1, -1) ||
+           !tomoyo_is_correct_path(aliased_name, 1, -1, -1))
                return -EINVAL; /* No patterns allowed. */
-       saved_original_name = tomoyo_save_name(original_name);
-       saved_aliased_name = tomoyo_save_name(aliased_name);
+       saved_original_name = tomoyo_get_name(original_name);
+       saved_aliased_name = tomoyo_get_name(aliased_name);
        if (!saved_original_name || !saved_aliased_name)
-               return -ENOMEM;
-       down_write(&tomoyo_alias_list_lock);
-       list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+               goto out;
+       if (!is_delete)
+               entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
                if (ptr->original_name != saved_original_name ||
                    ptr->aliased_name != saved_aliased_name)
                        continue;
                ptr->is_deleted = is_delete;
                error = 0;
-               goto out;
+               break;
        }
-       if (is_delete) {
-               error = -ENOENT;
-               goto out;
+       if (!is_delete && error && tomoyo_memory_ok(entry)) {
+               entry->original_name = saved_original_name;
+               saved_original_name = NULL;
+               entry->aliased_name = saved_aliased_name;
+               saved_aliased_name = NULL;
+               list_add_tail_rcu(&entry->list, &tomoyo_alias_list);
+               entry = NULL;
+               error = 0;
        }
-       new_entry = tomoyo_alloc_element(sizeof(*new_entry));
-       if (!new_entry)
-               goto out;
-       new_entry->original_name = saved_original_name;
-       new_entry->aliased_name = saved_aliased_name;
-       list_add_tail(&new_entry->list, &tomoyo_alias_list);
-       error = 0;
+       mutex_unlock(&tomoyo_policy_lock);
  out:
-       up_write(&tomoyo_alias_list_lock);
+       tomoyo_put_name(saved_original_name);
+       tomoyo_put_name(saved_aliased_name);
+       kfree(entry);
        return error;
 }
 
@@ -500,27 +598,26 @@ static int tomoyo_update_alias_entry(const char *original_name,
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
  * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
 {
        struct list_head *pos;
        bool done = true;
 
-       down_read(&tomoyo_alias_list_lock);
        list_for_each_cookie(pos, head->read_var2, &tomoyo_alias_list) {
                struct tomoyo_alias_entry *ptr;
 
                ptr = list_entry(pos, struct tomoyo_alias_entry, list);
                if (ptr->is_deleted)
                        continue;
-               if (!tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n",
-                                     ptr->original_name->name,
-                                     ptr->aliased_name->name)) {
-                       done = false;
+               done = tomoyo_io_printf(head, TOMOYO_KEYWORD_ALIAS "%s %s\n",
+                                       ptr->original_name->name,
+                                       ptr->aliased_name->name);
+               if (!done)
                        break;
-               }
        }
-       up_read(&tomoyo_alias_list_lock);
        return done;
 }
 
@@ -531,6 +628,8 @@ bool tomoyo_read_alias_policy(struct tomoyo_io_buffer *head)
  * @is_delete: True if it is a delete request.
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 int tomoyo_write_alias_policy(char *data, const bool is_delete)
 {
@@ -542,38 +641,6 @@ int tomoyo_write_alias_policy(char *data, const bool is_delete)
        return tomoyo_update_alias_entry(data, cp, is_delete);
 }
 
-/* Domain create/delete handler. */
-
-/**
- * tomoyo_delete_domain - Delete a domain.
- *
- * @domainname: The name of domain.
- *
- * Returns 0.
- */
-int tomoyo_delete_domain(char *domainname)
-{
-       struct tomoyo_domain_info *domain;
-       struct tomoyo_path_info name;
-
-       name.name = domainname;
-       tomoyo_fill_path_info(&name);
-       down_write(&tomoyo_domain_list_lock);
-       /* Is there an active domain? */
-       list_for_each_entry(domain, &tomoyo_domain_list, list) {
-               /* Never delete tomoyo_kernel_domain */
-               if (domain == &tomoyo_kernel_domain)
-                       continue;
-               if (domain->is_deleted ||
-                   tomoyo_pathcmp(domain->domainname, &name))
-                       continue;
-               domain->is_deleted = true;
-               break;
-       }
-       up_write(&tomoyo_domain_list_lock);
-       return 0;
-}
-
 /**
  * tomoyo_find_or_assign_new_domain - Create a domain.
  *
@@ -581,81 +648,64 @@ int tomoyo_delete_domain(char *domainname)
  * @profile:    Profile number to assign if the domain was newly created.
  *
  * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
 struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
                                                            domainname,
                                                            const u8 profile)
 {
-       struct tomoyo_domain_info *domain = NULL;
+       struct tomoyo_domain_info *entry;
+       struct tomoyo_domain_info *domain;
        const struct tomoyo_path_info *saved_domainname;
+       bool found = false;
 
-       down_write(&tomoyo_domain_list_lock);
-       domain = tomoyo_find_domain(domainname);
-       if (domain)
-               goto out;
-       if (!tomoyo_is_correct_domain(domainname, __func__))
-               goto out;
-       saved_domainname = tomoyo_save_name(domainname);
+       if (!tomoyo_is_correct_domain(domainname))
+               return NULL;
+       saved_domainname = tomoyo_get_name(domainname);
        if (!saved_domainname)
-               goto out;
-       /* Can I reuse memory of deleted domain? */
-       list_for_each_entry(domain, &tomoyo_domain_list, list) {
-               struct task_struct *p;
-               struct tomoyo_acl_info *ptr;
-               bool flag;
-               if (!domain->is_deleted ||
-                   domain->domainname != saved_domainname)
-                       continue;
-               flag = false;
-               read_lock(&tasklist_lock);
-               for_each_process(p) {
-                       if (tomoyo_real_domain(p) != domain)
-                               continue;
-                       flag = true;
-                       break;
-               }
-               read_unlock(&tasklist_lock);
-               if (flag)
+               return NULL;
+       entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+       mutex_lock(&tomoyo_policy_lock);
+       list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {
+               if (domain->is_deleted ||
+                   tomoyo_pathcmp(saved_domainname, domain->domainname))
                        continue;
-               list_for_each_entry(ptr, &domain->acl_info_list, list) {
-                       ptr->type |= TOMOYO_ACL_DELETED;
-               }
-               tomoyo_set_domain_flag(domain, true, domain->flags);
-               domain->profile = profile;
-               domain->quota_warned = false;
-               mb(); /* Avoid out-of-order execution. */
-               domain->is_deleted = false;
-               goto out;
+               found = true;
+               break;
        }
-       /* No memory reusable. Create using new memory. */
-       domain = tomoyo_alloc_element(sizeof(*domain));
-       if (domain) {
-               INIT_LIST_HEAD(&domain->acl_info_list);
-               domain->domainname = saved_domainname;
-               domain->profile = profile;
-               list_add_tail(&domain->list, &tomoyo_domain_list);
+       if (!found && tomoyo_memory_ok(entry)) {
+               INIT_LIST_HEAD(&entry->acl_info_list);
+               entry->domainname = saved_domainname;
+               saved_domainname = NULL;
+               entry->profile = profile;
+               list_add_tail_rcu(&entry->list, &tomoyo_domain_list);
+               domain = entry;
+               entry = NULL;
+               found = true;
        }
- out:
-       up_write(&tomoyo_domain_list_lock);
-       return domain;
+       mutex_unlock(&tomoyo_policy_lock);
+       tomoyo_put_name(saved_domainname);
+       kfree(entry);
+       return found ? domain : NULL;
 }
 
 /**
  * tomoyo_find_next_domain - Find a domain.
  *
- * @bprm:           Pointer to "struct linux_binprm".
- * @next_domain:    Pointer to pointer to "struct tomoyo_domain_info".
+ * @bprm: Pointer to "struct linux_binprm".
  *
  * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
  */
-int tomoyo_find_next_domain(struct linux_binprm *bprm,
-                           struct tomoyo_domain_info **next_domain)
+int tomoyo_find_next_domain(struct linux_binprm *bprm)
 {
        /*
         * This function assumes that the size of buffer returned by
         * tomoyo_realpath() = TOMOYO_MAX_PATHNAME_LEN.
         */
-       struct tomoyo_page_buffer *tmp = tomoyo_alloc(sizeof(*tmp));
+       struct tomoyo_page_buffer *tmp = kzalloc(sizeof(*tmp), GFP_KERNEL);
        struct tomoyo_domain_info *old_domain = tomoyo_domain();
        struct tomoyo_domain_info *domain = NULL;
        const char *old_domain_name = old_domain->domainname->name;
@@ -708,8 +758,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm,
        if (tomoyo_pathcmp(&r, &s)) {
                struct tomoyo_alias_entry *ptr;
                /* Is this program allowed to be called via symbolic links? */
-               down_read(&tomoyo_alias_list_lock);
-               list_for_each_entry(ptr, &tomoyo_alias_list, list) {
+               list_for_each_entry_rcu(ptr, &tomoyo_alias_list, list) {
                        if (ptr->is_deleted ||
                            tomoyo_pathcmp(&r, ptr->original_name) ||
                            tomoyo_pathcmp(&s, ptr->aliased_name))
@@ -720,11 +769,10 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm,
                        tomoyo_fill_path_info(&r);
                        break;
                }
-               up_read(&tomoyo_alias_list_lock);
        }
 
        /* Check execute permission. */
-       retval = tomoyo_check_exec_perm(old_domain, &r, tmp);
+       retval = tomoyo_check_exec_perm(old_domain, &r);
        if (retval < 0)
                goto out;
 
@@ -751,9 +799,7 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm,
        }
        if (domain || strlen(new_domain_name) >= TOMOYO_MAX_PATHNAME_LEN)
                goto done;
-       down_read(&tomoyo_domain_list_lock);
        domain = tomoyo_find_domain(new_domain_name);
-       up_read(&tomoyo_domain_list_lock);
        if (domain)
                goto done;
        if (is_enforce)
@@ -768,12 +814,15 @@ int tomoyo_find_next_domain(struct linux_binprm *bprm,
        if (is_enforce)
                retval = -EPERM;
        else
-               tomoyo_set_domain_flag(old_domain, false,
-                                      TOMOYO_DOMAIN_FLAGS_TRANSITION_FAILED);
+               old_domain->transition_failed = true;
  out:
-       tomoyo_free(real_program_name);
-       tomoyo_free(symlink_program_name);
-       *next_domain = domain ? domain : old_domain;
-       tomoyo_free(tmp);
+       if (!domain)
+               domain = old_domain;
+       /* Update reference count on "struct tomoyo_domain_info". */
+       atomic_inc(&domain->users);
+       bprm->cred->security = domain;
+       kfree(real_program_name);
+       kfree(symlink_program_name);
+       kfree(tmp);
        return retval;
 }