TOMOYO: Add garbage collector.
[safe/jmp/linux-2.6] / security / tomoyo / domain.c
index a55a1cc..74cd0f5 100644 (file)
@@ -10,8 +10,6 @@
  */
 
 #include "common.h"
-#include "tomoyo.h"
-#include "realpath.h"
 #include <linux/binfmts.h>
 
 /* Variables definitions.*/
@@ -59,98 +57,6 @@ struct tomoyo_domain_info tomoyo_kernel_domain;
  */
 LIST_HEAD(tomoyo_domain_list);
 
-/*
- * tomoyo_domain_initializer_entry is a structure which is used for holding
- * "initialize_domain" and "no_initialize_domain" entries.
- * It has following fields.
- *
- *  (1) "list" which is linked to tomoyo_domain_initializer_list .
- *  (2) "domainname" which is "a domainname" or "the last component of a
- *      domainname". This field is NULL if "from" clause is not specified.
- *  (3) "program" which is a program's pathname.
- *  (4) "is_deleted" is a bool which is true if marked as deleted, false
- *      otherwise.
- *  (5) "is_not" is a bool which is true if "no_initialize_domain", false
- *      otherwise.
- *  (6) "is_last_name" is a bool which is true if "domainname" is "the last
- *      component of a domainname", false otherwise.
- */
-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;
-};
-
-/*
- * tomoyo_domain_keeper_entry is a structure which is used for holding
- * "keep_domain" and "no_keep_domain" entries.
- * It has following fields.
- *
- *  (1) "list" which is linked to tomoyo_domain_keeper_list .
- *  (2) "domainname" which is "a domainname" or "the last component of a
- *      domainname".
- *  (3) "program" which is a program's pathname.
- *      This field is NULL if "from" clause is not specified.
- *  (4) "is_deleted" is a bool which is true if marked as deleted, false
- *      otherwise.
- *  (5) "is_not" is a bool which is true if "no_initialize_domain", false
- *      otherwise.
- *  (6) "is_last_name" is a bool which is true if "domainname" is "the last
- *      component of a domainname", false otherwise.
- */
-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;
-};
-
-/*
- * tomoyo_alias_entry is a structure which is used for holding "alias" entries.
- * It has following fields.
- *
- *  (1) "list" which is linked to tomoyo_alias_list .
- *  (2) "original_name" which is a dereferenced pathname.
- *  (3) "aliased_name" which is a symlink's pathname.
- *  (4) "is_deleted" is a bool which is true if marked as deleted, false
- *      otherwise.
- */
-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.
- *
- * @domain:    Pointer to "struct tomoyo_domain_info".
- * @is_delete: True if it is a delete request.
- * @flags:     Flags to set or clear.
- *
- * Returns nothing.
- */
-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);
-}
-
 /**
  * tomoyo_get_last_name - Get last component of a domainname.
  *
@@ -204,7 +110,7 @@ const char *tomoyo_get_last_name(const struct tomoyo_domain_info *domain)
  * will cause "/usr/sbin/httpd" to belong to "<kernel> /usr/sbin/httpd" domain
  * unless executed from "<kernel> /etc/rc.d/init.d/httpd" domain.
  */
-static LIST_HEAD(tomoyo_domain_initializer_list);
+LIST_HEAD(tomoyo_domain_initializer_list);
 
 /**
  * tomoyo_update_domain_initializer_entry - Update "struct tomoyo_domain_initializer_entry" list.
@@ -223,11 +129,11 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
                                                  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__))
@@ -238,14 +144,15 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
                        is_last_name = true;
                else if (!tomoyo_is_correct_domain(domainname, __func__))
                        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;
-       new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+               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 ||
@@ -254,24 +161,25 @@ static int tomoyo_update_domain_initializer_entry(const char *domainname,
                        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;
        }
-       if (!tomoyo_memory_ok(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_rcu(&new_entry->list, &tomoyo_domain_initializer_list);
-       new_entry = NULL;
-       error = 0;
- out:
        mutex_unlock(&tomoyo_policy_lock);
-       kfree(new_entry);
+ out:
+       tomoyo_put_name(saved_domainname);
+       tomoyo_put_name(saved_program);
+       kfree(entry);
        return error;
 }
 
@@ -422,7 +330,7 @@ static bool tomoyo_is_domain_initializer(const struct tomoyo_path_info *
  * "<kernel> /usr/sbin/sshd /bin/bash /usr/bin/passwd" domain, unless
  * explicitly specified by "initialize_domain".
  */
-static LIST_HEAD(tomoyo_domain_keeper_list);
+LIST_HEAD(tomoyo_domain_keeper_list);
 
 /**
  * tomoyo_update_domain_keeper_entry - Update "struct tomoyo_domain_keeper_entry" list.
@@ -441,11 +349,11 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
                                             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) &&
@@ -456,14 +364,15 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
        if (program) {
                if (!tomoyo_is_correct_path(program, 1, -1, -1, __func__))
                        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;
-       new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+               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 ||
@@ -472,24 +381,24 @@ static int tomoyo_update_domain_keeper_entry(const char *domainname,
                        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;
        }
-       if (!tomoyo_memory_ok(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_rcu(&new_entry->list, &tomoyo_domain_keeper_list);
-       new_entry = NULL;
-       error = 0;
- out:
        mutex_unlock(&tomoyo_policy_lock);
-       kfree(new_entry);
+ out:
+       tomoyo_put_name(saved_domainname);
+       tomoyo_put_name(saved_program);
+       kfree(entry);
        return error;
 }
 
@@ -624,7 +533,7 @@ static bool tomoyo_is_domain_keeper(const struct tomoyo_path_info *domainname,
  * /bin/busybox and domainname which the current process will belong to after
  * execve() succeeds is calculated using /bin/cat rather than /bin/busybox .
  */
-static LIST_HEAD(tomoyo_alias_list);
+LIST_HEAD(tomoyo_alias_list);
 
 /**
  * tomoyo_update_alias_entry - Update "struct tomoyo_alias_entry" list.
@@ -641,20 +550,21 @@ 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__))
                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;
-       new_entry = kmalloc(sizeof(*new_entry), GFP_KERNEL);
+               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 ||
@@ -662,22 +572,22 @@ static int tomoyo_update_alias_entry(const char *original_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;
        }
-       if (!tomoyo_memory_ok(new_entry))
-               goto out;
-       new_entry->original_name = saved_original_name;
-       new_entry->aliased_name = saved_aliased_name;
-       list_add_tail_rcu(&new_entry->list, &tomoyo_alias_list);
-       new_entry = NULL;
-       error = 0;
- out:
        mutex_unlock(&tomoyo_policy_lock);
-       kfree(new_entry);
+ out:
+       tomoyo_put_name(saved_original_name);
+       tomoyo_put_name(saved_aliased_name);
+       kfree(entry);
        return error;
 }
 
@@ -744,32 +654,39 @@ struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
                                                            domainname,
                                                            const u8 profile)
 {
+       struct tomoyo_domain_info *entry;
        struct tomoyo_domain_info *domain;
        const struct tomoyo_path_info *saved_domainname;
+       bool found = false;
 
-       mutex_lock(&tomoyo_policy_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);
+               return NULL;
+       saved_domainname = tomoyo_get_name(domainname);
        if (!saved_domainname)
-               goto out;
-       domain = kmalloc(sizeof(*domain), GFP_KERNEL);
-       if (tomoyo_memory_ok(domain)) {
-               INIT_LIST_HEAD(&domain->acl_info_list);
-               domain->domainname = saved_domainname;
-               domain->profile = profile;
-               list_add_tail_rcu(&domain->list, &tomoyo_domain_list);
-       } else {
-               kfree(domain);
-               domain = NULL;
+               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;
+               found = true;
+               break;
+       }
+       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:
        mutex_unlock(&tomoyo_policy_lock);
-       return domain;
+       tomoyo_put_name(saved_domainname);
+       kfree(entry);
+       return found ? domain : NULL;
 }
 
 /**
@@ -787,7 +704,7 @@ 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;
@@ -896,14 +813,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:
        if (!domain)
                domain = old_domain;
+       /* Update reference count on "struct tomoyo_domain_info". */
+       atomic_inc(&domain->users);
        bprm->cred->security = domain;
-       tomoyo_free(real_program_name);
-       tomoyo_free(symlink_program_name);
-       tomoyo_free(tmp);
+       kfree(real_program_name);
+       kfree(symlink_program_name);
+       kfree(tmp);
        return retval;
 }