[TOMOYO #16 24/25] TOMOYO: Add domain transition handler.

From: Tetsuo Handa
Date: Sun Oct 04 2009 - 09:01:08 EST


This patch contains code for handling domain transition.

tomoyo_read_lock()/tomoyo_read_unlock() protects the data against the garbage
collector. I call tomoyo_read_lock() when an execve() operation starts and
call tomoyo_read_unlock() when an execve() operation finishes rather than
calling tomoyo_read_lock()/tomoyo_read_unlock() upon individual list traversal.
In this way, the pointer saved in "struct tomoyo_execve_entry" is guaranteed
to be valid. Please ignore

warning: context imbalance in 'tomoyo_start_execve' - wrong count at exit
warning: context imbalance in 'tomoyo_finish_execve' - unexpected unlock

messages when built with "C=1" option.

Signed-off-by: Tetsuo Handa <penguin-kernel@xxxxxxxxxxxxxxxxxxx>
---
security/tomoyo/new-domain.c | 1354 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1354 insertions(+)

--- /dev/null
+++ security-testing-2.6/security/tomoyo/new-domain.c
@@ -0,0 +1,1354 @@
+/*
+ * security/tomoyo/domain.c
+ *
+ * Copyright (C) 2005-2009 NTT DATA CORPORATION
+ */
+#include "internal.h"
+#include <linux/mount.h>
+#include <linux/highmem.h>
+#include <linux/fs_struct.h>
+#include <linux/file.h>
+
+/* Variables definitions.*/
+
+/* The initial domain. */
+struct tomoyo_domain_info tomoyo_kernel_domain;
+
+/*
+ * 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.
+ *
+ * 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
+ *
+ * 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.
+ */
+LIST_HEAD(tomoyo_domain_list);
+
+/**
+ * tomoyo_audit_execute_handler_log - Audit execute_handler log.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ * @is_default: True if it is "execute_handler" log.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_execute_handler_log(struct tomoyo_execve_entry *ee,
+ const bool is_default)
+{
+ struct tomoyo_request_info *r = &ee->r;
+ const char *handler = ee->handler->name;
+ r->mode = tomoyo_get_mode(r->profile, TOMOYO_MAC_FILE_EXECUTE);
+ return tomoyo_write_audit_log(true, r, "%s %s\n",
+ is_default ?
+ TOMOYO_KEYWORD_EXECUTE_HANDLER :
+ TOMOYO_KEYWORD_DENIED_EXECUTE_HANDLER,
+ handler);
+}
+
+/**
+ * tomoyo_audit_domain_creation_log - Audit domain creation log.
+ *
+ * @domain: Pointer to "struct tomoyo_domain_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_audit_domain_creation_log(struct tomoyo_domain_info *domain)
+{
+ int error;
+ struct tomoyo_request_info r;
+ tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_EXECUTE);
+ error = tomoyo_write_audit_log(false, &r, "use_profile %u\n",
+ r.profile);
+ return error;
+}
+
+/*
+ * 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.
+ *
+ * @domainname: The name of domain. May be NULL.
+ * @program: The name of program.
+ * @is_not: True if it is "no_initialize_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+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 *entry = NULL;
+ struct tomoyo_domain_initializer_entry *ptr;
+ struct tomoyo_domain_initializer_entry e = { .is_not = is_not };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ 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))
+ e.is_last_name = true;
+ else if (!tomoyo_is_correct_domain(domainname))
+ return -EINVAL;
+ e.domainname = tomoyo_get_name(domainname);
+ if (!e.domainname)
+ goto out;
+ }
+ e.program = tomoyo_get_name(program);
+ if (!e.program)
+ goto out;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
+ if (tomoyo_memcmp(ptr, &e, offsetof(typeof(e), is_not),
+ sizeof(e)))
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list,
+ &tomoyo_domain_initializer_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name(e.domainname);
+ tomoyo_put_name(e.program);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_read_domain_initializer_policy - Read "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @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;
+ list_for_each_cookie(pos, head->read_var2,
+ &tomoyo_domain_initializer_list) {
+ const char *no;
+ const char *from = "";
+ const char *domain = "";
+ struct tomoyo_domain_initializer_entry *ptr;
+ ptr = list_entry(pos, struct tomoyo_domain_initializer_entry,
+ list);
+ if (ptr->is_deleted)
+ continue;
+ no = ptr->is_not ? "no_" : "";
+ if (ptr->domainname) {
+ from = " from ";
+ domain = ptr->domainname->name;
+ }
+ done = tomoyo_io_printf(head, "%s"
+ TOMOYO_KEYWORD_INITIALIZE_DOMAIN
+ "%s%s%s\n", no, ptr->program->name,
+ from, domain);
+ if (!done)
+ break;
+ }
+ return done;
+}
+
+/**
+ * tomoyo_write_domain_initializer_policy - Write "struct tomoyo_domain_initializer_entry" list.
+ *
+ * @data: String to parse.
+ * @is_not: True if it is "no_initialize_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_domain_initializer_policy(char *data, const bool is_not,
+ const bool is_delete)
+{
+ char *cp = strstr(data, " from ");
+ if (cp) {
+ *cp = '\0';
+ return tomoyo_update_domain_initializer_entry(cp + 6, data,
+ is_not,
+ is_delete);
+ }
+ return tomoyo_update_domain_initializer_entry(NULL, data, is_not,
+ is_delete);
+}
+
+/**
+ * tomoyo_is_domain_initializer - Check whether the given program causes domainname reinitialization.
+ *
+ * @domainname: The name of domain.
+ * @program: The name of program.
+ * @last_name: The last component of @domainname.
+ *
+ * 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,
+ const struct tomoyo_path_info *program,
+ const struct tomoyo_path_info *last_name)
+{
+ struct tomoyo_domain_initializer_entry *ptr;
+ bool flag = false;
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_initializer_list, list) {
+ if (ptr->is_deleted)
+ continue;
+ if (ptr->domainname) {
+ if (!ptr->is_last_name) {
+ if (ptr->domainname != domainname)
+ continue;
+ } else {
+ if (tomoyo_pathcmp(ptr->domainname, last_name))
+ continue;
+ }
+ }
+ if (tomoyo_pathcmp(ptr->program, program))
+ continue;
+ if (ptr->is_not) {
+ flag = false;
+ break;
+ }
+ flag = true;
+ }
+ return flag;
+}
+
+/*
+ * 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.
+ *
+ * @domainname: The name of domain.
+ * @program: The name of program. May be NULL.
+ * @is_not: True if it is "no_keep_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+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 *entry = NULL;
+ struct tomoyo_domain_keeper_entry *ptr;
+ struct tomoyo_domain_keeper_entry e = { .is_not = is_not };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_is_domain_def(domainname) &&
+ tomoyo_is_correct_path(domainname, 1, -1, -1))
+ e.is_last_name = true;
+ else if (!tomoyo_is_correct_domain(domainname))
+ return -EINVAL;
+ if (program) {
+ if (!tomoyo_is_correct_path(program, 1, -1, -1))
+ return -EINVAL;
+ e.program = tomoyo_get_name(program);
+ if (!e.program)
+ goto out;
+ }
+ e.domainname = tomoyo_get_name(domainname);
+ if (!e.domainname)
+ goto out;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
+ if (tomoyo_memcmp(ptr, &e, offsetof(typeof(e), is_not),
+ sizeof(e)))
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list, &tomoyo_domain_keeper_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name(e.domainname);
+ tomoyo_put_name(e.program);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_write_domain_keeper_policy - Write "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @data: String to parse.
+ * @is_not: True if it is "no_keep_domain" entry.
+ * @is_delete: True if it is a delete request.
+ *
+ */
+int tomoyo_write_domain_keeper_policy(char *data, const bool is_not,
+ const bool is_delete)
+{
+ char *cp = strstr(data, " from ");
+ if (cp) {
+ *cp = '\0';
+ return tomoyo_update_domain_keeper_entry(cp + 6, data,
+ is_not, is_delete);
+ }
+ return tomoyo_update_domain_keeper_entry(data, NULL, is_not,
+ is_delete);
+}
+
+/**
+ * tomoyo_read_domain_keeper_policy - Read "struct tomoyo_domain_keeper_entry" list.
+ *
+ * @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;
+ list_for_each_cookie(pos, head->read_var2,
+ &tomoyo_domain_keeper_list) {
+ struct tomoyo_domain_keeper_entry *ptr;
+ const char *no;
+ const char *from = "";
+ const char *program = "";
+ ptr = list_entry(pos, struct tomoyo_domain_keeper_entry, list);
+ if (ptr->is_deleted)
+ continue;
+ no = ptr->is_not ? "no_" : "";
+ if (ptr->program) {
+ from = " from ";
+ program = ptr->program->name;
+ }
+ done = tomoyo_io_printf(head, "%s" TOMOYO_KEYWORD_KEEP_DOMAIN
+ "%s%s%s\n", no, program, from,
+ ptr->domainname->name);
+ if (!done)
+ break;
+ }
+ return done;
+}
+
+/**
+ * tomoyo_is_domain_keeper - Check whether the given program causes domain transition suppression.
+ *
+ * @domainname: The name of domain.
+ * @program: The name of program.
+ * @last_name: The last component of @domainname.
+ *
+ * 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,
+ const struct tomoyo_path_info *last_name)
+{
+ struct tomoyo_domain_keeper_entry *ptr;
+ bool flag = false;
+ list_for_each_entry_rcu(ptr, &tomoyo_domain_keeper_list, list) {
+ if (ptr->is_deleted)
+ continue;
+ if (!ptr->is_last_name) {
+ if (ptr->domainname != domainname)
+ continue;
+ } else {
+ if (tomoyo_pathcmp(ptr->domainname, last_name))
+ continue;
+ }
+ if (ptr->program && tomoyo_pathcmp(ptr->program, program))
+ continue;
+ if (ptr->is_not) {
+ flag = false;
+ break;
+ }
+ flag = true;
+ }
+ return flag;
+}
+
+/* The list for "struct tomoyo_aggregator_entry". */
+LIST_HEAD(tomoyo_aggregator_list);
+
+/**
+ * tomoyo_update_aggregator_entry - Update "struct tomoyo_aggregator_entry" list.
+ *
+ * @original_name: The original program's name.
+ * @aggregated_name: The aggregated program's name.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_update_aggregator_entry(const char *original_name,
+ const char *aggregated_name,
+ const bool is_delete)
+{
+ struct tomoyo_aggregator_entry *entry = NULL;
+ struct tomoyo_aggregator_entry *ptr;
+ struct tomoyo_aggregator_entry e = { };
+ int error = is_delete ? -ENOENT : -ENOMEM;
+ if (!tomoyo_is_correct_path(original_name, 1, 0, -1) ||
+ !tomoyo_is_correct_path(aggregated_name, 1, -1, -1))
+ return -EINVAL;
+ e.original_name = tomoyo_get_name(original_name);
+ e.aggregated_name = tomoyo_get_name(aggregated_name);
+ if (!e.original_name || !e.aggregated_name)
+ goto out;
+ if (!is_delete)
+ entry = kmalloc(sizeof(e), GFP_KERNEL);
+ mutex_lock(&tomoyo_policy_lock);
+ list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, list) {
+ if (tomoyo_memcmp(ptr, &e, offsetof(typeof(e), original_name),
+ sizeof(e)))
+ continue;
+ ptr->is_deleted = is_delete;
+ error = 0;
+ break;
+ }
+ if (!is_delete && error && tomoyo_commit_ok(entry, &e, sizeof(e))) {
+ list_add_tail_rcu(&entry->list, &tomoyo_aggregator_list);
+ entry = NULL;
+ error = 0;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ out:
+ tomoyo_put_name(e.original_name);
+ tomoyo_put_name(e.aggregated_name);
+ kfree(entry);
+ return error;
+}
+
+/**
+ * tomoyo_read_aggregator_policy - Read "struct tomoyo_aggregator_entry" list.
+ *
+ * @head: Pointer to "struct tomoyo_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+bool tomoyo_read_aggregator_policy(struct tomoyo_io_buffer *head)
+{
+ struct list_head *pos;
+ bool done = true;
+ list_for_each_cookie(pos, head->read_var2, &tomoyo_aggregator_list) {
+ struct tomoyo_aggregator_entry *ptr;
+ ptr = list_entry(pos, struct tomoyo_aggregator_entry, list);
+ if (ptr->is_deleted)
+ continue;
+ done = tomoyo_io_printf(head, TOMOYO_KEYWORD_AGGREGATOR
+ "%s %s\n", ptr->original_name->name,
+ ptr->aggregated_name->name);
+ if (!done)
+ break;
+ }
+ return done;
+}
+
+/**
+ * tomoyo_write_aggregator_policy - Write "struct tomoyo_aggregator_entry" list.
+ *
+ * @data: String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_write_aggregator_policy(char *data, const bool is_delete)
+{
+ char *w[2];
+ if (!tomoyo_tokenize(data, w, sizeof(w)) || !w[1][0])
+ return -EINVAL;
+ return tomoyo_update_aggregator_entry(w[0], w[1], 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);
+ mutex_lock(&tomoyo_policy_lock);
+ /* Is there an active domain? */
+ list_for_each_entry_rcu(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;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ return 0;
+}
+
+/**
+ * tomoyo_find_or_assign_new_domain - Create a domain.
+ *
+ * @domainname: The name of domain.
+ * @profile: Profile number to assign if the domain was newly created.
+ *
+ * Returns pointer to "struct tomoyo_domain_info" on success, NULL otherwise.
+ */
+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;
+
+ if (!tomoyo_is_correct_domain(domainname))
+ return NULL;
+ saved_domainname = tomoyo_get_name(domainname);
+ if (!saved_domainname)
+ 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, sizeof(*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;
+ }
+ mutex_unlock(&tomoyo_policy_lock);
+ tomoyo_put_name(saved_domainname);
+ kfree(entry);
+ return found ? domain : NULL;
+}
+
+/**
+ * tomoyo_find_next_domain - Find a domain.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static int tomoyo_find_next_domain(struct tomoyo_execve_entry *ee)
+{
+ struct tomoyo_request_info *r = &ee->r;
+ const struct tomoyo_path_info *handler = ee->handler;
+ struct tomoyo_domain_info *domain = NULL;
+ const char *old_domain_name = r->domain->domainname->name;
+ struct linux_binprm *bprm = ee->bprm;
+ const u32 tomoyo_flags = current->tomoyo_flags;
+ struct tomoyo_path_info rn = { }; /* real name */
+ struct tomoyo_path_info ln; /* last name */
+ int retval;
+ bool need_kfree = false;
+ ln.name = tomoyo_last_word(old_domain_name);
+ tomoyo_fill_path_info(&ln);
+ retry:
+ current->tomoyo_flags = tomoyo_flags;
+ r->cond = NULL;
+ if (need_kfree) {
+ kfree(rn.name);
+ need_kfree = false;
+ }
+
+ /* Get symlink's pathname of program. */
+ retval = tomoyo_symlink_path(bprm->filename, &rn);
+ if (retval < 0)
+ goto out;
+ need_kfree = true;
+
+ if (handler) {
+ if (tomoyo_pathcmp(&rn, handler)) {
+ /* Failed to verify execute handler. */
+ static u8 counter = 20;
+ if (counter) {
+ counter--;
+ printk(KERN_WARNING "Failed to verify: %s\n",
+ handler->name);
+ }
+ goto out;
+ }
+ } else {
+ struct tomoyo_aggregator_entry *ptr;
+ /* Check 'aggregator' directive. */
+ list_for_each_entry_rcu(ptr, &tomoyo_aggregator_list, list) {
+ if (ptr->is_deleted ||
+ !tomoyo_path_matches_pattern(&rn,
+ ptr->original_name))
+ continue;
+ kfree(rn.name);
+ need_kfree = false;
+ /* This is OK because it is read only. */
+ rn = *ptr->aggregated_name;
+ break;
+ }
+
+ /* Check execute permission. */
+ retval = tomoyo_exec_perm(r, &rn);
+ if (retval == 1)
+ goto retry;
+ if (retval < 0)
+ goto out;
+ }
+
+ /* Calculate domain to transit to. */
+ if (tomoyo_is_domain_initializer(r->domain->domainname, &rn, &ln)) {
+ /* Transit to the child of tomoyo_kernel_domain domain. */
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, ROOT_NAME " " "%s",
+ rn.name);
+ } else if (r->domain == &tomoyo_kernel_domain &&
+ !tomoyo_policy_loaded) {
+ /*
+ * Needn't to transit from kernel domain before starting
+ * /sbin/init. But transit from kernel domain if executing
+ * initializers because they might start before /sbin/init.
+ */
+ domain = r->domain;
+ } else if (tomoyo_is_domain_keeper(r->domain->domainname, &rn, &ln)) {
+ /* Keep current domain. */
+ domain = r->domain;
+ } else {
+ /* Normal domain transition. */
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s %s",
+ old_domain_name, rn.name);
+ }
+ if (domain || strlen(ee->tmp) >= TOMOYO_EXEC_TMPSIZE - 10)
+ goto done;
+ domain = tomoyo_find_domain(ee->tmp);
+ if (domain)
+ goto done;
+ if (r->mode == TOMOYO_CONFIG_ENFORCING) {
+ int error = tomoyo_supervisor(r, "# wants to create domain\n"
+ "%s\n", ee->tmp);
+ if (error == 1)
+ goto retry;
+ if (error < 0)
+ goto done;
+ }
+ domain = tomoyo_find_or_assign_new_domain(ee->tmp, r->profile);
+ if (domain)
+ tomoyo_audit_domain_creation_log(r->domain);
+ done:
+ if (!domain) {
+ printk(KERN_WARNING "ERROR: Domain '%s' not defined.\n",
+ ee->tmp);
+ if (r->mode == TOMOYO_CONFIG_ENFORCING)
+ retval = -EPERM;
+ else {
+ retval = 0;
+ r->domain->domain_transition_failed = true;
+ }
+ } else {
+ retval = 0;
+ }
+ out:
+ if (domain)
+ r->domain = domain;
+ if (need_kfree)
+ kfree(rn.name);
+ return retval;
+}
+
+/**
+ * tomoyo_environ - Check permission for environment variable names.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_environ(struct tomoyo_execve_entry *ee)
+{
+ struct tomoyo_request_info *r = &ee->r;
+ struct linux_binprm *bprm = ee->bprm;
+ char *arg_ptr = ee->tmp;
+ int arg_len = 0;
+ unsigned long pos = bprm->p;
+ int offset = pos % PAGE_SIZE;
+ int argv_count = bprm->argc;
+ int envp_count = bprm->envc;
+ /* printk(KERN_DEBUG "start %d %d\n", argv_count, envp_count); */
+ int error = -ENOMEM;
+ if (!r->mode || !envp_count)
+ return 0;
+ while (error == -ENOMEM) {
+ if (!tomoyo_dump_page(bprm, pos, &ee->dump))
+ goto out;
+ pos += PAGE_SIZE - offset;
+ /* Read. */
+ while (argv_count && offset < PAGE_SIZE) {
+ const char *kaddr = ee->dump.data;
+ if (!kaddr[offset++])
+ argv_count--;
+ }
+ if (argv_count) {
+ offset = 0;
+ continue;
+ }
+ while (offset < PAGE_SIZE) {
+ const char *kaddr = ee->dump.data;
+ const unsigned char c = kaddr[offset++];
+ if (c && arg_len < TOMOYO_EXEC_TMPSIZE - 10) {
+ if (c == '=') {
+ arg_ptr[arg_len++] = '\0';
+ } else 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;
+ if (tomoyo_env_perm(r, arg_ptr)) {
+ error = -EPERM;
+ break;
+ }
+ if (!--envp_count) {
+ error = 0;
+ break;
+ }
+ arg_len = 0;
+ }
+ offset = 0;
+ }
+ out:
+ if (r->mode != 3)
+ error = 0;
+ return error;
+}
+
+/**
+ * tomoyo_unescape - Unescape escaped string.
+ *
+ * @dest: String to unescape.
+ *
+ * Returns nothing.
+ */
+static void tomoyo_unescape(unsigned char *dest)
+{
+ unsigned char *src = dest;
+ unsigned char c;
+ unsigned char d;
+ unsigned char e;
+ while (1) {
+ c = *src++;
+ if (!c)
+ break;
+ if (c != '\\') {
+ *dest++ = c;
+ continue;
+ }
+ c = *src++;
+ if (c == '\\') {
+ *dest++ = c;
+ continue;
+ }
+ if (c < '0' || c > '3')
+ break;
+ d = *src++;
+ if (d < '0' || d > '7')
+ break;
+ e = *src++;
+ if (e < '0' || e > '7')
+ break;
+ *dest++ = ((c - '0') << 6) + ((d - '0') << 3) + (e - '0');
+ }
+ *dest = '\0';
+}
+
+/**
+ * tomoyo_root_depth - Get number of directories to strip.
+ *
+ * @dentry: Pointer to "struct dentry".
+ * @vfsmnt: Pointer to "struct vfsmount".
+ *
+ * Returns number of directories to strip.
+ */
+static inline int tomoyo_root_depth(struct dentry *dentry,
+ struct vfsmount *vfsmnt)
+{
+ int depth = 0;
+ spin_lock(&dcache_lock);
+ spin_lock(&vfsmount_lock);
+ for (;;) {
+ if (dentry == vfsmnt->mnt_root || IS_ROOT(dentry)) {
+ /* Global root? */
+ if (vfsmnt->mnt_parent == vfsmnt)
+ break;
+ dentry = vfsmnt->mnt_mountpoint;
+ vfsmnt = vfsmnt->mnt_parent;
+ continue;
+ }
+ dentry = dentry->d_parent;
+ depth++;
+ }
+ spin_unlock(&vfsmount_lock);
+ spin_unlock(&dcache_lock);
+ return depth;
+}
+
+/**
+ * tomoyo_get_root_depth - return the depth of root directory.
+ *
+ * Returns number of directories to strip.
+ */
+static int tomoyo_get_root_depth(void)
+{
+ int depth;
+ struct dentry *dentry;
+ struct vfsmount *vfsmnt;
+ struct path root;
+ read_lock(&current->fs->lock);
+ root = current->fs->root;
+ path_get(&current->fs->root);
+ dentry = root.dentry;
+ vfsmnt = root.mnt;
+ read_unlock(&current->fs->lock);
+ depth = tomoyo_root_depth(dentry, vfsmnt);
+ path_put(&root);
+ return depth;
+}
+
+/**
+ * tomoyo_try_alt_exec - Try to start execute handler.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int tomoyo_try_alt_exec(struct tomoyo_execve_entry *ee)
+{
+ /*
+ * Contents of modified bprm.
+ * The envp[] in original bprm is moved to argv[] so that
+ * the alternatively executed program won't be affected by
+ * some dangerous environment variables like LD_PRELOAD.
+ *
+ * modified bprm->argc
+ * = original bprm->argc + original bprm->envc + 7
+ * modified bprm->envc
+ * = 0
+ *
+ * modified bprm->argv[0]
+ * = the program's name specified by execute_handler
+ * modified bprm->argv[1]
+ * = tomoyo_current_domain()->domainname->name
+ * modified bprm->argv[2]
+ * = the current process's name
+ * modified bprm->argv[3]
+ * = the current process's information (e.g. uid/gid).
+ * modified bprm->argv[4]
+ * = original bprm->filename
+ * modified bprm->argv[5]
+ * = original bprm->argc in string expression
+ * modified bprm->argv[6]
+ * = original bprm->envc in string expression
+ * modified bprm->argv[7]
+ * = original bprm->argv[0]
+ * ...
+ * modified bprm->argv[bprm->argc + 6]
+ * = original bprm->argv[bprm->argc - 1]
+ * modified bprm->argv[bprm->argc + 7]
+ * = original bprm->envp[0]
+ * ...
+ * modified bprm->argv[bprm->envc + bprm->argc + 6]
+ * = original bprm->envp[bprm->envc - 1]
+ */
+ struct linux_binprm *bprm = ee->bprm;
+ struct file *filp;
+ int retval;
+ const int original_argc = bprm->argc;
+ const int original_envc = bprm->envc;
+ struct task_struct *task = current;
+
+ /* Close the requested program's dentry. */
+ ee->obj.path1.dentry = NULL;
+ ee->obj.path1.mnt = NULL;
+ ee->obj.validate_done = false;
+ allow_write_access(bprm->file);
+ fput(bprm->file);
+ bprm->file = NULL;
+
+ /* Invalidate page dump cache. */
+ ee->dump.page = NULL;
+
+ /* Move envp[] to argv[] */
+ bprm->argc += bprm->envc;
+ bprm->envc = 0;
+
+ /* Set argv[6] */
+ {
+ char *cp = ee->tmp;
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%d",
+ original_envc);
+ retval = copy_strings_kernel(1, &cp, bprm);
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /* Set argv[5] */
+ {
+ char *cp = ee->tmp;
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%d",
+ original_argc);
+ retval = copy_strings_kernel(1, &cp, bprm);
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /* Set argv[4] */
+ {
+ retval = copy_strings_kernel(1, &bprm->filename, bprm);
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /* Set argv[3] */
+ {
+ char *cp = ee->tmp;
+ const u32 tomoyo_flags = task->tomoyo_flags;
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1,
+ "pid=%d uid=%d gid=%d euid=%d egid=%d suid=%d "
+ "sgid=%d fsuid=%d fsgid=%d state[0]=%u "
+ "state[1]=%u state[2]=%u",
+ (pid_t) sys_getpid(), current_uid(), current_gid(),
+ current_euid(), current_egid(), current_suid(),
+ current_sgid(), current_fsuid(), current_fsgid(),
+ (u8) (tomoyo_flags >> 24), (u8) (tomoyo_flags >> 16),
+ (u8) (tomoyo_flags >> 8));
+ retval = copy_strings_kernel(1, &cp, bprm);
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /* Set argv[2] */
+ {
+ char *exe = (char *) tomoyo_get_exe();
+ if (exe) {
+ retval = copy_strings_kernel(1, &exe, bprm);
+ kfree(exe);
+ } else {
+ exe = ee->tmp;
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1,
+ "<unknown>");
+ retval = copy_strings_kernel(1, &exe, bprm);
+ }
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /* Set argv[1] */
+ {
+ char *cp = ee->tmp;
+ snprintf(ee->tmp, TOMOYO_EXEC_TMPSIZE - 1, "%s",
+ tomoyo_current_domain()->domainname->name);
+ retval = copy_strings_kernel(1, &cp, bprm);
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /* Set argv[0] */
+ {
+ int depth = tomoyo_get_root_depth();
+ int len = strlen(ee->handler->name) + 1;
+ char *cp = kmalloc(len, GFP_KERNEL);
+ if (!cp) {
+ retval = -ENOMEM;
+ goto out;
+ }
+ ee->handler_path = cp;
+ memmove(cp, ee->handler->name, len);
+ tomoyo_unescape(cp);
+ retval = -ENOENT;
+ if (!*cp || *cp != '/')
+ goto out;
+ /* Adjust root directory for open_exec(). */
+ while (depth) {
+ cp = strchr(cp + 1, '/');
+ if (!cp)
+ goto out;
+ depth--;
+ }
+ memmove(ee->handler_path, cp, strlen(cp) + 1);
+ cp = ee->handler_path;
+ retval = copy_strings_kernel(1, &cp, bprm);
+ if (retval < 0)
+ goto out;
+ bprm->argc++;
+ }
+
+ /*
+ * OK, now restart the process with execute handler program's dentry.
+ */
+ filp = open_exec(ee->handler_path);
+ if (IS_ERR(filp)) {
+ retval = PTR_ERR(filp);
+ goto out;
+ }
+ ee->obj.path1.dentry = filp->f_dentry;
+ ee->obj.path1.mnt = filp->f_vfsmnt;
+ bprm->file = filp;
+ bprm->filename = ee->handler_path;
+ bprm->interp = bprm->filename;
+ retval = prepare_binprm(bprm);
+ if (retval < 0)
+ goto out;
+ task->tomoyo_flags |= TOMOYO_DONT_SLEEP_ON_ENFORCE_ERROR;
+ retval = tomoyo_find_next_domain(ee);
+ task->tomoyo_flags &= ~TOMOYO_DONT_SLEEP_ON_ENFORCE_ERROR;
+ out:
+ return retval;
+}
+
+/**
+ * tomoyo_find_execute_handler - Find an execute handler.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ * @type: Type of execute handler.
+ *
+ * Returns true if found, false otherwise.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+static bool tomoyo_find_execute_handler(struct tomoyo_execve_entry *ee,
+ const u8 type)
+{
+ struct task_struct *task = current;
+ const struct tomoyo_domain_info *domain = tomoyo_current_domain();
+ struct tomoyo_acl_info *ptr;
+ bool found = false;
+ /*
+ * Don't use execute handler if the current process is
+ * marked as execute handler to avoid infinite execute handler loop.
+ */
+ if (task->tomoyo_flags & TOMOYO_TASK_IS_EXECUTE_HANDLER)
+ return false;
+ list_for_each_entry(ptr, &domain->acl_info_list, list) {
+ struct tomoyo_execute_handler_record *acl;
+ if (ptr->type != type)
+ continue;
+ acl = container_of(ptr, struct tomoyo_execute_handler_record,
+ head);
+ ee->handler = acl->handler;
+ found = true;
+ break;
+ }
+ return found;
+}
+
+/**
+ * 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_KERNEL);
+ if (!dump->data)
+ return false;
+ }
+ /* Same with get_arg_page(bprm, pos, 0) in fs/exec.c */
+#if defined(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 */
+#if defined(CONFIG_MMU)
+ put_page(page);
+#endif
+ return true;
+}
+
+/**
+ * tomoyo_start_execve - Prepare for execve() operation.
+ *
+ * @bprm: Pointer to "struct linux_binprm".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tomoyo_start_execve(struct linux_binprm *bprm)
+{
+ int retval;
+ struct task_struct *task = current;
+ struct tomoyo_execve_entry *ee;
+ if (!tomoyo_policy_loaded)
+ tomoyo_load_policy(bprm->filename);
+ ee = kzalloc(sizeof(*ee), GFP_KERNEL);
+ if (!ee)
+ return -ENOMEM;
+ ee->tmp = kzalloc(TOMOYO_EXEC_TMPSIZE, GFP_KERNEL);
+ if (!ee->tmp) {
+ kfree(ee);
+ return -ENOMEM;
+ }
+ /* This lock is released at tomoyo_finish_execve(). */
+ ee->reader_idx = tomoyo_read_lock();
+ /* ee->dump->data is allocated by tomoyo_dump_page(). */
+ ee->previous_domain = task->tomoyo_domain_info;
+ /* Clear manager flag. */
+ task->tomoyo_flags &= ~TOMOYO_TASK_IS_POLICY_MANAGER;
+ /* Tell GC that I started execve(). */
+ task->tomoyo_flags |= TOMOYO_TASK_IS_IN_EXECVE;
+ /*
+ * Make task->tomoyo_flags visible to GC before changing
+ * task->tomoyo_domain_info .
+ */
+ smp_mb();
+ bprm->cred->security = ee;
+ atomic_set(&ee->cred_users, 1);
+ 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.dentry = bprm->file->f_dentry;
+ ee->obj.path1.mnt = bprm->file->f_vfsmnt;
+ if (tomoyo_find_execute_handler(ee, TOMOYO_TYPE_EXECUTE_HANDLER)) {
+ retval = tomoyo_try_alt_exec(ee);
+ if (!retval)
+ tomoyo_audit_execute_handler_log(ee, true);
+ goto ok;
+ }
+ retval = tomoyo_find_next_domain(ee);
+ if (retval != -EPERM)
+ goto ok;
+ if (tomoyo_find_execute_handler(ee,
+ TOMOYO_TYPE_DENIED_EXECUTE_HANDLER)) {
+ retval = tomoyo_try_alt_exec(ee);
+ if (!retval)
+ tomoyo_audit_execute_handler_log(ee, false);
+ }
+ ok:
+ if (retval < 0)
+ goto out;
+ /*
+ * Proceed to the next domain in order to allow reaching via PID.
+ * It will be reverted if execve() failed. Reverting is not good.
+ * But it is better than being unable to reach via PID in interactive
+ * enforcing mode.
+ */
+ task->tomoyo_domain_info = ee->r.domain;
+ ee->r.mode = tomoyo_get_mode(ee->r.domain->profile,
+ TOMOYO_MAC_ENVIRON);
+ retval = tomoyo_environ(ee);
+ if (retval < 0)
+ goto out;
+ retval = 0;
+ out:
+ if (retval) {
+ tomoyo_finish_execve(ee, true);
+ bprm->cred->security = NULL;
+ }
+ return retval;
+}
+
+/**
+ * tomoyo_finish_execve - Clean up execve() operation.
+ *
+ * @ee: Pointer to "struct tomoyo_execve_entry".
+ * @rollback: True if need to rollback.
+ *
+ * Caller holds tomoyo_read_lock().
+ */
+void tomoyo_finish_execve(struct tomoyo_execve_entry *ee, const bool rollback)
+{
+ struct task_struct *task = current;
+ if (!ee)
+ return;
+ if (rollback) {
+ task->tomoyo_domain_info = ee->previous_domain;
+ /*
+ * Make task->tomoyo_domain_info visible to GC before changing
+ * task->tomoyo_flags .
+ */
+ smp_mb();
+ } else {
+ /* Mark the current process as execute handler. */
+ if (ee->handler)
+ task->tomoyo_flags |= TOMOYO_TASK_IS_EXECUTE_HANDLER;
+ /* Mark the current process as normal process. */
+ else
+ task->tomoyo_flags &= ~TOMOYO_TASK_IS_EXECUTE_HANDLER;
+ }
+ /* Tell GC that I finished execve(). */
+ task->tomoyo_flags &= ~TOMOYO_TASK_IS_IN_EXECVE;
+ /* This lock is acquired at tomoyo_start_execve(). */
+ tomoyo_read_unlock(ee->reader_idx);
+ kfree(ee->handler_path);
+ kfree(ee->tmp);
+ kfree(ee->dump.data);
+ kfree(ee);
+}

--
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/