All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kentaro Takeda <takedakn@nttdata.co.jp>
To: Stephen Smalley <sds@tycho.nsa.gov>,
	James Morris <jmorris@namei.org>,
	Chris Wright <chrisw@sous-sol.org>
Cc: "Serge E. Hallyn" <serue@us.ibm.com>,
	linux-security-module@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	Toshiharu Harada <haradats@nttdata.co.jp>,
	Andrew Morton <akpm@linux-foundation.org>,
	Kentaro Takeda <takedakn@nttdata.co.jp>,
	Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Subject: [TOMOYO #11 (linux-next) 08/11] Domain transition handler.
Date: Mon, 20 Oct 2008 16:34:31 +0900	[thread overview]
Message-ID: <20081020073657.085777708@nttdata.co.jp> (raw)
In-Reply-To: 20081020073423.024299308@nttdata.co.jp

This file controls domain creation/deletion/transition.

Every process belongs to a domain in TOMOYO Linux.
Domain transition occurs when execve(2) is called
and the domain is expressed as 'process invocation history',
such as '<kernel> /sbin/init /etc/init.d/rc'.
Domain information is stored in task_struct->cred->security field.

Signed-off-by: Kentaro Takeda <takedakn@nttdata.co.jp>
Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: Toshiharu Harada <haradats@nttdata.co.jp>
---
 security/tomoyo/domain.c |  865 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 865 insertions(+)

--- /dev/null
+++ linux-next/security/tomoyo/domain.c
@@ -0,0 +1,865 @@
+/*
+ * security/tomoyo/domain.c
+ *
+ * Implementation of the Domain-Based Mandatory Access Control.
+ *
+ * Copyright (C) 2005-2008  NTT DATA CORPORATION
+ *
+ * Version: 2.2.0-pre   2008/10/10
+ *
+ */
+
+#include "common.h"
+#include "tomoyo.h"
+#include "realpath.h"
+#include <linux/binfmts.h>
+
+/* Variables definitions.*/
+
+/* The initial domain. */
+struct domain_info KERNEL_DOMAIN;
+
+/*
+ * The list for "struct domain_info".
+ *
+ * The domain_list_lock mutex protects the domain_list list.
+ */
+LIST1_HEAD(domain_list);
+static DEFINE_MUTEX(domain_list_lock);
+
+/* Structure for "initialize_domain" and "no_initialize_domain" keyword. */
+struct domain_initializer_entry {
+	struct list1_head list;
+	const struct path_info *domainname;    /* This may be NULL */
+	const struct path_info *program;
+	bool is_deleted;
+	bool is_not;       /* True if this entry is "no_initialize_domain".  */
+	bool is_last_name; /* True if the domainname is tmy_get_last_name(). */
+};
+
+/* Structure for "keep_domain" and "no_keep_domain" keyword. */
+struct domain_keeper_entry {
+	struct list1_head list;
+	const struct path_info *domainname;
+	const struct path_info *program;       /* This may be NULL */
+	bool is_deleted;
+	bool is_not;       /* True if this entry is "no_keep_domain".        */
+	bool is_last_name; /* True if the domainname is tmy_get_last_name(). */
+};
+
+/* Structure for "alias" keyword. */
+struct alias_entry {
+	struct list1_head list;
+	const struct path_info *original_name;
+	const struct path_info *aliased_name;
+	bool is_deleted;
+};
+
+/**
+ * tmy_set_domain_flag - Set or clear domain's attribute flags.
+ *
+ * @domain:    Pointer to "struct domain_info".
+ * @is_delete: True if it is a delete request.
+ * @flags:     Flags to set or clear.
+ *
+ * Returns nothing.
+ */
+void tmy_set_domain_flag(struct domain_info *domain, const bool is_delete,
+			 const u8 flags)
+{
+	/* We need to serialize because this is bitfield operation. */
+	static DEFINE_SPINLOCK(lock);
+	/***** CRITICAL SECTION START *****/
+	spin_lock(&lock);
+	if (!is_delete)
+		domain->flags |= flags;
+	else
+		domain->flags &= ~flags;
+	spin_unlock(&lock);
+	/***** CRITICAL SECTION END *****/
+}
+
+/**
+ * tmy_get_last_name - Get last component of a domainname.
+ *
+ * @domain: Pointer to "struct domain_info".
+ *
+ * Returns the last component of the domainname.
+ */
+const char *tmy_get_last_name(const struct domain_info *domain)
+{
+	const char *cp0 = domain->domainname->name;
+	const char *cp1 = strrchr(cp0, ' ');
+
+	if (cp1)
+		return cp1 + 1;
+	return cp0;
+}
+
+/*
+ * The list for "struct domain_initializer_entry".
+ *
+ * This list is updated only inside update_domain_initializer_entry(), thus
+ * no global mutex exists.
+ */
+static LIST1_HEAD(domain_initializer_list);
+
+/**
+ * update_domain_initializer_entry - Update "struct 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 update_domain_initializer_entry(const char *domainname,
+					   const char *program,
+					   const bool is_not,
+					   const bool is_delete)
+{
+	struct domain_initializer_entry *new_entry;
+	struct domain_initializer_entry *ptr;
+	static DEFINE_MUTEX(lock);
+	const struct path_info *saved_program;
+	const struct path_info *saved_domainname = NULL;
+	int error = -ENOMEM;
+	bool is_last_name = false;
+
+	if (!tmy_is_correct_path(program, 1, -1, -1, __func__))
+		return -EINVAL; /* No patterns allowed. */
+	if (domainname) {
+		if (!tmy_is_domain_def(domainname) &&
+		    tmy_is_correct_path(domainname, 1, -1, -1, __func__))
+			is_last_name = true;
+		else if (!tmy_is_correct_domain(domainname, __func__))
+			return -EINVAL;
+		saved_domainname = tmy_save_name(domainname);
+		if (!saved_domainname)
+			return -ENOMEM;
+	}
+	saved_program = tmy_save_name(program);
+	if (!saved_program)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	list1_for_each_entry(ptr, &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;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tmy_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;
+	list1_add_tail(&new_entry->list, &domain_initializer_list);
+	error = 0;
+out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	tmy_update_counter(TMY_UPDATES_COUNTER_EXCEPTION_POLICY);
+	return error;
+}
+
+/**
+ * tmy_read_domain_initializer_policy - Read "struct domain_initializer_entry" list.
+ *
+ * @head: Pointer to "struct tmy_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tmy_read_domain_initializer_policy(struct tmy_io_buffer *head)
+{
+	struct list1_head *pos;
+
+	list1_for_each_cookie(pos, head->read_var2, &domain_initializer_list) {
+		const char *no;
+		const char *from = "";
+		const char *domain = "";
+		struct domain_initializer_entry *ptr;
+		ptr = list1_entry(pos, struct domain_initializer_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		no = ptr->is_not ? "no_" : "";
+		if (ptr->domainname) {
+			from = " from ";
+			domain = ptr->domainname->name;
+		}
+		if (!tmy_io_printf(head,
+				   "%s" KEYWORD_INITIALIZE_DOMAIN "%s%s%s\n",
+				   no, ptr->program->name, from, domain))
+			goto out;
+	}
+	return true;
+out:
+	return false;
+}
+
+/**
+ * tmy_write_domain_initializer_policy - Write "struct 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 tmy_write_domain_initializer_policy(char *data, const bool is_not,
+					const bool is_delete)
+{
+	char *cp = strstr(data, " from ");
+
+	if (cp) {
+		*cp = '\0';
+		return update_domain_initializer_entry(cp + 6, data, is_not,
+						       is_delete);
+	}
+	return update_domain_initializer_entry(NULL, data, is_not, is_delete);
+}
+
+/**
+ * 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.
+ */
+static bool is_domain_initializer(const struct path_info *domainname,
+				  const struct path_info *program,
+				  const struct path_info *last_name)
+{
+	struct domain_initializer_entry *ptr;
+	bool flag = false;
+
+	list1_for_each_entry(ptr,  &domain_initializer_list, list) {
+		if (ptr->is_deleted)
+			continue;
+		if (ptr->domainname) {
+			if (!ptr->is_last_name) {
+				if (ptr->domainname != domainname)
+					continue;
+			} else {
+				if (tmy_pathcmp(ptr->domainname, last_name))
+					continue;
+			}
+		}
+		if (tmy_pathcmp(ptr->program, program))
+			continue;
+		if (ptr->is_not)
+			return false;
+		flag = true;
+	}
+	return flag;
+}
+
+/*
+ * The list for "struct domain_keeper_entry".
+ *
+ * This list is updated only inside update_domain_keeper_entry(), thus
+ * no global mutex exists.
+ */
+static LIST1_HEAD(domain_keeper_list);
+
+/**
+ * update_domain_keeper_entry - Update "struct 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 update_domain_keeper_entry(const char *domainname,
+				      const char *program,
+				      const bool is_not, const bool is_delete)
+{
+	struct domain_keeper_entry *new_entry;
+	struct domain_keeper_entry *ptr;
+	const struct path_info *saved_domainname;
+	const struct path_info *saved_program = NULL;
+	static DEFINE_MUTEX(lock);
+	int error = -ENOMEM;
+	bool is_last_name = false;
+
+	if (!tmy_is_domain_def(domainname) &&
+	    tmy_is_correct_path(domainname, 1, -1, -1, __func__))
+		is_last_name = true;
+	else if (!tmy_is_correct_domain(domainname, __func__))
+		return -EINVAL;
+	if (program) {
+		if (!tmy_is_correct_path(program, 1, -1, -1, __func__))
+			return -EINVAL;
+		saved_program = tmy_save_name(program);
+		if (!saved_program)
+			return -ENOMEM;
+	}
+	saved_domainname = tmy_save_name(domainname);
+	if (!saved_domainname)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	list1_for_each_entry(ptr, &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;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tmy_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;
+	list1_add_tail(&new_entry->list, &domain_keeper_list);
+	error = 0;
+out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	tmy_update_counter(TMY_UPDATES_COUNTER_EXCEPTION_POLICY);
+	return error;
+}
+
+/**
+ * tmy_write_domain_keeper_policy - Write "struct 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 tmy_write_domain_keeper_policy(char *data, const bool is_not,
+				   const bool is_delete)
+{
+	char *cp = strstr(data, " from ");
+
+	if (cp) {
+		*cp = '\0';
+		return update_domain_keeper_entry(cp + 6, data,
+						  is_not, is_delete);
+	}
+	return update_domain_keeper_entry(data, NULL, is_not, is_delete);
+}
+
+/**
+ * tmy_read_domain_keeper_policy - Read "struct domain_keeper_entry" list.
+ *
+ * @head: Pointer to "struct tmy_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tmy_read_domain_keeper_policy(struct tmy_io_buffer *head)
+{
+	struct list1_head *pos;
+
+	list1_for_each_cookie(pos, head->read_var2, &domain_keeper_list) {
+		struct domain_keeper_entry *ptr;
+		const char *no;
+		const char *from = "";
+		const char *program = "";
+
+		ptr = list1_entry(pos, struct domain_keeper_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		no = ptr->is_not ? "no_" : "";
+		if (ptr->program) {
+			from = " from ";
+			program = ptr->program->name;
+		}
+		if (!tmy_io_printf(head,
+				   "%s" KEYWORD_KEEP_DOMAIN "%s%s%s\n", no,
+				   program, from, ptr->domainname->name))
+			goto out;
+	}
+	return true;
+out:
+	return false;
+}
+
+/**
+ * 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.
+ */
+static bool is_domain_keeper(const struct path_info *domainname,
+			     const struct path_info *program,
+			     const struct path_info *last_name)
+{
+	struct domain_keeper_entry *ptr;
+	bool flag = false;
+
+	list1_for_each_entry(ptr, &domain_keeper_list, list) {
+		if (ptr->is_deleted)
+			continue;
+		if (!ptr->is_last_name) {
+			if (ptr->domainname != domainname)
+				continue;
+		} else {
+			if (tmy_pathcmp(ptr->domainname, last_name))
+				continue;
+		}
+		if (ptr->program && tmy_pathcmp(ptr->program, program))
+			continue;
+		if (ptr->is_not)
+			return false;
+		flag = true;
+	}
+	return flag;
+}
+
+/*
+ * The list for "struct alias_entry".
+ *
+ * This list is updated only inside update_alias_entry(), thus
+ * no global mutex exists.
+ */
+static LIST1_HEAD(alias_list);
+
+/**
+ * update_alias_entry - Update "struct alias_entry" list.
+ *
+ * @original_name: The original program's real name.
+ * @aliased_name:  The symbolic program's symbolic link's name.
+ * @is_delete:     True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+static int update_alias_entry(const char *original_name,
+			      const char *aliased_name,
+			      const bool is_delete)
+{
+	struct alias_entry *new_entry;
+	struct alias_entry *ptr;
+	static DEFINE_MUTEX(lock);
+	const struct path_info *saved_original_name;
+	const struct path_info *saved_aliased_name;
+	int error = -ENOMEM;
+
+	if (!tmy_is_correct_path(original_name, 1, -1, -1, __func__) ||
+	    !tmy_is_correct_path(aliased_name, 1, -1, -1, __func__))
+		return -EINVAL; /* No patterns allowed. */
+	saved_original_name = tmy_save_name(original_name);
+	saved_aliased_name = tmy_save_name(aliased_name);
+	if (!saved_original_name || !saved_aliased_name)
+		return -ENOMEM;
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&lock);
+	list1_for_each_entry(ptr, &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;
+	}
+	if (is_delete) {
+		error = -ENOENT;
+		goto out;
+	}
+	new_entry = tmy_alloc_element(sizeof(*new_entry));
+	if (!new_entry)
+		goto out;
+	new_entry->original_name = saved_original_name;
+	new_entry->aliased_name = saved_aliased_name;
+	list1_add_tail(&new_entry->list, &alias_list);
+	error = 0;
+out:
+	mutex_unlock(&lock);
+	/***** EXCLUSIVE SECTION END *****/
+	tmy_update_counter(TMY_UPDATES_COUNTER_EXCEPTION_POLICY);
+	return error;
+}
+
+/**
+ * tmy_read_alias_policy - Read "struct alias_entry" list.
+ *
+ * @head: Pointer to "struct tmy_io_buffer".
+ *
+ * Returns true on success, false otherwise.
+ */
+bool tmy_read_alias_policy(struct tmy_io_buffer *head)
+{
+	struct list1_head *pos;
+
+	list1_for_each_cookie(pos, head->read_var2, &alias_list) {
+		struct alias_entry *ptr;
+
+		ptr = list1_entry(pos, struct alias_entry, list);
+		if (ptr->is_deleted)
+			continue;
+		if (!tmy_io_printf(head, KEYWORD_ALIAS "%s %s\n",
+				   ptr->original_name->name,
+				   ptr->aliased_name->name))
+			goto out;
+	}
+	return true;
+out:
+	return false;
+}
+
+/**
+ * tmy_write_alias_policy - Write "struct alias_entry" list.
+ *
+ * @data:      String to parse.
+ * @is_delete: True if it is a delete request.
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tmy_write_alias_policy(char *data, const bool is_delete)
+{
+	char *cp = strchr(data, ' ');
+
+	if (!cp)
+		return -EINVAL;
+	*cp++ = '\0';
+	return update_alias_entry(data, cp, is_delete);
+}
+
+/* Domain create/delete/undelete handler. */
+
+/* #define DEBUG_DOMAIN_UNDELETE */
+
+/**
+ * tmy_delete_domain - Delete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns 0.
+ */
+int tmy_delete_domain(char *domainname)
+{
+	struct domain_info *domain;
+	struct path_info name;
+
+	name.name = domainname;
+	tmy_fill_path_info(&name);
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&domain_list_lock);
+#ifdef DEBUG_DOMAIN_UNDELETE
+	printk(KERN_DEBUG "tmy_delete_domain %s\n", domainname);
+	list1_for_each_entry(domain, &domain_list, list) {
+		if (tmy_pathcmp(domain->domainname, &name))
+			continue;
+		printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);
+	}
+#endif
+	/* Is there an active domain? */
+	list1_for_each_entry(domain, &domain_list, list) {
+		struct domain_info *domain2;
+		/* Never delete KERNEL_DOMAIN */
+		if (domain == &KERNEL_DOMAIN)
+			continue;
+		if (domain->is_deleted ||
+		    tmy_pathcmp(domain->domainname, &name))
+			continue;
+		/* Mark already deleted domains as non undeletable. */
+		list1_for_each_entry(domain2, &domain_list, list) {
+			if (!domain2->is_deleted ||
+			    tmy_pathcmp(domain2->domainname, &name))
+				continue;
+#ifdef DEBUG_DOMAIN_UNDELETE
+			if (domain2->is_deleted != 255)
+				printk(KERN_DEBUG
+				       "Marked %p as non undeletable\n",
+				       domain2);
+#endif
+			domain2->is_deleted = 255;
+		}
+		/* Delete and mark active domain as undeletable. */
+		domain->is_deleted = 1;
+#ifdef DEBUG_DOMAIN_UNDELETE
+		printk(KERN_DEBUG "Marked %p as undeletable\n", domain);
+#endif
+		break;
+	}
+	mutex_unlock(&domain_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return 0;
+}
+
+/**
+ * tmy_undelete_domain - Undelete a domain.
+ *
+ * @domainname: The name of domain.
+ *
+ * Returns pointer to "struct domain_info" on success, NULL otherwise.
+ */
+struct domain_info *tmy_undelete_domain(const char *domainname)
+{
+	struct domain_info *domain;
+	struct domain_info *candidate_domain = NULL;
+	struct path_info name;
+
+	name.name = domainname;
+	tmy_fill_path_info(&name);
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&domain_list_lock);
+#ifdef DEBUG_DOMAIN_UNDELETE
+	printk(KERN_DEBUG "tmy_undelete_domain %s\n", domainname);
+	list1_for_each_entry(domain, &domain_list, list) {
+		if (tmy_pathcmp(domain->domainname, &name))
+			continue;
+		printk(KERN_DEBUG "List: %p %u\n", domain, domain->is_deleted);
+	}
+#endif
+	list1_for_each_entry(domain, &domain_list, list) {
+		if (tmy_pathcmp(&name, domain->domainname))
+			continue;
+		if (!domain->is_deleted) {
+			/* This domain is active. I can't undelete. */
+			candidate_domain = NULL;
+#ifdef DEBUG_DOMAIN_UNDELETE
+			printk(KERN_DEBUG "%p is active. I can't undelete.\n",
+			       domain);
+#endif
+			break;
+		}
+		/* Is this domain undeletable? */
+		if (domain->is_deleted == 1)
+			candidate_domain = domain;
+	}
+	if (candidate_domain) {
+		candidate_domain->is_deleted = 0;
+#ifdef DEBUG_DOMAIN_UNDELETE
+		printk(KERN_DEBUG "%p was undeleted.\n", candidate_domain);
+#endif
+	}
+	mutex_unlock(&domain_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return candidate_domain;
+}
+
+/**
+ * tmy_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 domain_info" on success, NULL otherwise.
+ */
+struct domain_info *tmy_find_or_assign_new_domain(const char *domainname,
+						  const u8 profile)
+{
+	struct domain_info *domain = NULL;
+	const struct path_info *saved_domainname;
+
+	/***** EXCLUSIVE SECTION START *****/
+	mutex_lock(&domain_list_lock);
+	domain = tmy_find_domain(domainname);
+	if (domain)
+		goto out;
+	if (!tmy_is_correct_domain(domainname, __func__))
+		goto out;
+	saved_domainname = tmy_save_name(domainname);
+	if (!saved_domainname)
+		goto out;
+	/* Can I reuse memory of deleted domain? */
+	list1_for_each_entry(domain, &domain_list, list) {
+		struct task_struct *p;
+		struct acl_info *ptr;
+		bool flag;
+		if (!domain->is_deleted ||
+		    domain->domainname != saved_domainname)
+			continue;
+		flag = false;
+		/***** CRITICAL SECTION START *****/
+		read_lock(&tasklist_lock);
+		for_each_process(p) {
+			if (tmy_real_domain(p) != domain)
+				continue;
+			flag = true;
+			break;
+		}
+		read_unlock(&tasklist_lock);
+		/***** CRITICAL SECTION END *****/
+		if (flag)
+			continue;
+#ifdef DEBUG_DOMAIN_UNDELETE
+		printk(KERN_DEBUG "Reusing %p %s\n", domain,
+		       domain->domainname->name);
+#endif
+		list1_for_each_entry(ptr, &domain->acl_info_list, list) {
+			ptr->type |= ACL_DELETED;
+		}
+		tmy_set_domain_flag(domain, true, domain->flags);
+		domain->profile = profile;
+		domain->quota_warned = false;
+		mb(); /* Avoid out-of-order execution. */
+		domain->is_deleted = 0;
+		goto out;
+	}
+	/* No memory reusable. Create using new memory. */
+	domain = tmy_alloc_element(sizeof(*domain));
+	if (domain) {
+		INIT_LIST1_HEAD(&domain->acl_info_list);
+		domain->domainname = saved_domainname;
+		domain->profile = profile;
+		list1_add_tail(&domain->list, &domain_list);
+	}
+out:
+	mutex_unlock(&domain_list_lock);
+	/***** EXCLUSIVE SECTION END *****/
+	return domain;
+}
+
+/**
+ * tmy_find_next_domain - Find a domain.
+ *
+ * @bprm:           Pointer to "struct linux_binprm".
+ * @next_domain:    Pointer to pointer to "struct domain_info".
+ *
+ * Returns 0 on success, negative value otherwise.
+ */
+int tmy_find_next_domain(struct linux_binprm *bprm,
+			 struct domain_info **next_domain)
+{
+	/*
+	 * This function assumes that the size of buffer returned by
+	 * tmy_realpath() = TMY_MAX_PATHNAME_LEN.
+	 */
+	struct tmy_page_buffer *tmp = tmy_alloc(sizeof(*tmp));
+	struct domain_info *old_domain = tmy_domain();
+	struct domain_info *domain = NULL;
+	const char *old_domain_name = old_domain->domainname->name;
+	const char *original_name = bprm->filename;
+	char *new_domain_name = NULL;
+	char *real_program_name = NULL;
+	char *symlink_program_name = NULL;
+	const u8 mode = tmy_check_flags(old_domain, TMY_TOMOYO_MAC_FOR_FILE);
+	const bool is_enforce = (mode == 3);
+	int retval = -ENOMEM;
+	struct path_info r; /* real name */
+	struct path_info s; /* symlink name */
+	struct path_info l; /* last name */
+
+	if (!tmp)
+		goto out;
+
+	{
+		/*
+		 * Built-in initializers. This is needed because policies are
+		 * not loaded until starting /sbin/init.
+		 */
+		static bool first = true;
+		if (first) {
+			update_domain_initializer_entry(NULL, "/sbin/hotplug",
+							false, false);
+			update_domain_initializer_entry(NULL, "/sbin/modprobe",
+							false, false);
+			first = false;
+		}
+	}
+
+	/* Get tmy_realpath of program. */
+	retval = -ENOENT; /* I hope tmy_realpath() won't fail with -ENOMEM. */
+	real_program_name = tmy_realpath(original_name);
+	if (!real_program_name)
+		goto out;
+	/* Get tmy_realpath of symbolic link. */
+	symlink_program_name = tmy_realpath_nofollow(original_name);
+	if (!symlink_program_name)
+		goto out;
+
+	r.name = real_program_name;
+	tmy_fill_path_info(&r);
+	s.name = symlink_program_name;
+	tmy_fill_path_info(&s);
+	l.name = tmy_get_last_name(old_domain);
+	tmy_fill_path_info(&l);
+
+	/* Check 'alias' directive. */
+	if (tmy_pathcmp(&r, &s)) {
+		struct alias_entry *ptr;
+		/* Is this program allowed to be called via symbolic links? */
+		list1_for_each_entry(ptr, &alias_list, list) {
+			if (ptr->is_deleted ||
+			    tmy_pathcmp(&r, ptr->original_name) ||
+			    tmy_pathcmp(&s, ptr->aliased_name))
+				continue;
+			memset(real_program_name, 0, TMY_MAX_PATHNAME_LEN);
+			strncpy(real_program_name, ptr->aliased_name->name,
+				TMY_MAX_PATHNAME_LEN - 1);
+			tmy_fill_path_info(&r);
+			break;
+		}
+	}
+
+	/* Check execute permission. */
+	retval = tmy_check_exec_perm(old_domain, &r, tmp);
+	if (retval < 0)
+		goto out;
+
+	new_domain_name = tmp->buffer;
+	if (is_domain_initializer(old_domain->domainname, &r, &l)) {
+		/* Transit to the child of KERNEL_DOMAIN domain. */
+		snprintf(new_domain_name, TMY_MAX_PATHNAME_LEN + 1,
+			 ROOT_NAME " " "%s", real_program_name);
+	} else if (old_domain == &KERNEL_DOMAIN && !sbin_init_started) {
+		/*
+		 * 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 = old_domain;
+	} else if (is_domain_keeper(old_domain->domainname, &r, &l)) {
+		/* Keep current domain. */
+		domain = old_domain;
+	} else {
+		/* Normal domain transition. */
+		snprintf(new_domain_name, TMY_MAX_PATHNAME_LEN + 1,
+			 "%s %s", old_domain_name, real_program_name);
+	}
+	if (domain || strlen(new_domain_name) >= TMY_MAX_PATHNAME_LEN)
+		goto done;
+	domain = tmy_find_domain(new_domain_name);
+	if (domain)
+		goto done;
+	if (is_enforce)
+		goto done;
+	domain = tmy_find_or_assign_new_domain(new_domain_name,
+					       old_domain->profile);
+done:
+	if (domain)
+		goto out;
+	printk(KERN_WARNING "TOMOYO-ERROR: Domain '%s' not defined.\n",
+	       new_domain_name);
+	if (is_enforce)
+		retval = -EPERM;
+	else
+		tmy_set_domain_flag(old_domain, false,
+				    DOMAIN_FLAGS_TRANSITION_FAILED);
+out:
+	tmy_free(real_program_name);
+	tmy_free(symlink_program_name);
+	*next_domain = domain ? domain : old_domain;
+	tmy_free(tmp);
+	return retval;
+}

--


  parent reply	other threads:[~2008-10-20  7:40 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2008-10-20  7:34 [TOMOYO #11 (linux-next) 00/11] TOMOYO Linux Kentaro Takeda
2008-10-20  7:34 ` [TOMOYO #11 (linux-next) 01/11] Introduce new LSM hooks where vfsmount is available Kentaro Takeda
2008-10-20 12:27   ` Shaya Potter
2008-10-20 19:34     ` crispin
2008-10-20 21:23       ` Shaya Potter
2008-10-23 17:57         ` Shaya Potter
2008-10-20 16:44   ` Miklos Szeredi
2008-10-21  5:09     ` Kentaro Takeda
2008-10-20  7:34 ` [TOMOYO #11 (linux-next) 02/11] Add in_execve flag into task_struct Kentaro Takeda
2008-10-20  7:34 ` [TOMOYO #11 (linux-next) 03/11] Singly linked list implementation Kentaro Takeda
2008-10-20  7:34 ` [TOMOYO #11 (linux-next) 04/11] Introduce d_realpath() Kentaro Takeda
2008-10-20  7:34 ` [TOMOYO #11 (linux-next) 05/11] Memory and pathname management functions Kentaro Takeda
2008-10-20  7:34 ` [TOMOYO #11 (linux-next) 06/11] Common functions for TOMOYO Linux Kentaro Takeda
2008-10-20  7:34 ` [TOMOYO #11 (linux-next) 07/11] File operation restriction part Kentaro Takeda
2008-10-20  7:34 ` Kentaro Takeda [this message]
2008-10-20  7:34 ` [TOMOYO #11 (linux-next) 09/11] LSM adapter functions Kentaro Takeda
2008-10-20  7:34 ` [TOMOYO #11 (linux-next) 10/11] Kconfig and Makefile Kentaro Takeda
2008-10-20  7:34 ` [TOMOYO #11 (linux-next) 11/11] MAINTAINERS info Kentaro Takeda
2008-10-27  2:18 ` [TOMOYO #11 (linux-next) 00/11] TOMOYO Linux Kentaro Takeda
2008-10-29 19:18   ` Serge E. Hallyn
2008-10-30  5:27     ` Toshiharu Harada

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20081020073657.085777708@nttdata.co.jp \
    --to=takedakn@nttdata.co.jp \
    --cc=akpm@linux-foundation.org \
    --cc=chrisw@sous-sol.org \
    --cc=haradats@nttdata.co.jp \
    --cc=jmorris@namei.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=penguin-kernel@I-love.SAKURA.ne.jp \
    --cc=sds@tycho.nsa.gov \
    --cc=serue@us.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.