TOMOYO: Update profile structure.

This patch allows users to change access control mode for per-operation basis.
This feature comes from non LSM version of TOMOYO which is designed for
permitting users to use SELinux and TOMOYO at the same time.

SELinux does not care filename in a directory whereas TOMOYO does. Change of
filename can change how the file is used. For example, renaming index.txt to
.htaccess will change how the file is used. Thus, letting SELinux to enforce
read()/write()/mmap() etc. restriction and letting TOMOYO to enforce rename()
restriction is an example usage of this feature.

What is unfortunate for me is that currently LSM does not allow users to use
SELinux and LSM version of TOMOYO at the same time...

Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp>
Signed-off-by: James Morris <jmorris@namei.org>
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c
index bdf1ed7..811adb5 100644
--- a/security/tomoyo/common.c
+++ b/security/tomoyo/common.c
@@ -9,53 +9,74 @@
 #include <linux/uaccess.h>
 #include <linux/slab.h>
 #include <linux/security.h>
-#include <linux/hardirq.h>
 #include "common.h"
 
+static struct tomoyo_profile tomoyo_default_profile = {
+	.learning = &tomoyo_default_profile.preference,
+	.permissive = &tomoyo_default_profile.preference,
+	.enforcing = &tomoyo_default_profile.preference,
+	.preference.enforcing_verbose = true,
+	.preference.learning_max_entry = 2048,
+	.preference.learning_verbose = false,
+	.preference.permissive_verbose = true
+};
+
+/* Profile version. Currently only 20090903 is defined. */
+static unsigned int tomoyo_profile_version;
+
+/* Profile table. Memory is allocated as needed. */
+static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
+
 /* String table for functionality that takes 4 modes. */
 static const char *tomoyo_mode_4[4] = {
 	"disabled", "learning", "permissive", "enforcing"
 };
-/* String table for functionality that takes 2 modes. */
-static const char *tomoyo_mode_2[4] = {
-	"disabled", "enabled", "enabled", "enabled"
-};
 
-/*
- * tomoyo_control_array is a static data which contains
- *
- *  (1) functionality name used by /sys/kernel/security/tomoyo/profile .
- *  (2) initial values for "struct tomoyo_profile".
- *  (3) max values for "struct tomoyo_profile".
- */
-static struct {
-	const char *keyword;
-	unsigned int current_value;
-	const unsigned int max_value;
-} tomoyo_control_array[TOMOYO_MAX_CONTROL_INDEX] = {
-	[TOMOYO_MAC_FOR_FILE]     = { "MAC_FOR_FILE",        0,       3 },
-	[TOMOYO_MAX_ACCEPT_ENTRY] = { "MAX_ACCEPT_ENTRY", 2048, INT_MAX },
-	[TOMOYO_VERBOSE]          = { "TOMOYO_VERBOSE",      1,       1 },
+/* String table for /sys/kernel/security/tomoyo/profile */
+static const char *tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX
+				       + TOMOYO_MAX_MAC_CATEGORY_INDEX] = {
+	[TOMOYO_MAC_FILE_EXECUTE]    = "file::execute",
+	[TOMOYO_MAC_FILE_OPEN]       = "file::open",
+	[TOMOYO_MAC_FILE_CREATE]     = "file::create",
+	[TOMOYO_MAC_FILE_UNLINK]     = "file::unlink",
+	[TOMOYO_MAC_FILE_MKDIR]      = "file::mkdir",
+	[TOMOYO_MAC_FILE_RMDIR]      = "file::rmdir",
+	[TOMOYO_MAC_FILE_MKFIFO]     = "file::mkfifo",
+	[TOMOYO_MAC_FILE_MKSOCK]     = "file::mksock",
+	[TOMOYO_MAC_FILE_TRUNCATE]   = "file::truncate",
+	[TOMOYO_MAC_FILE_SYMLINK]    = "file::symlink",
+	[TOMOYO_MAC_FILE_REWRITE]    = "file::rewrite",
+	[TOMOYO_MAC_FILE_MKBLOCK]    = "file::mkblock",
+	[TOMOYO_MAC_FILE_MKCHAR]     = "file::mkchar",
+	[TOMOYO_MAC_FILE_LINK]       = "file::link",
+	[TOMOYO_MAC_FILE_RENAME]     = "file::rename",
+	[TOMOYO_MAC_FILE_CHMOD]      = "file::chmod",
+	[TOMOYO_MAC_FILE_CHOWN]      = "file::chown",
+	[TOMOYO_MAC_FILE_CHGRP]      = "file::chgrp",
+	[TOMOYO_MAC_FILE_IOCTL]      = "file::ioctl",
+	[TOMOYO_MAC_FILE_CHROOT]     = "file::chroot",
+	[TOMOYO_MAC_FILE_MOUNT]      = "file::mount",
+	[TOMOYO_MAC_FILE_UMOUNT]     = "file::umount",
+	[TOMOYO_MAC_FILE_PIVOT_ROOT] = "file::pivot_root",
+	[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file",
 };
 
-/*
- * tomoyo_profile is a structure which is used for holding the mode of access
- * controls. TOMOYO has 4 modes: disabled, learning, permissive, enforcing.
- * An administrator can define up to 256 profiles.
- * The ->profile of "struct tomoyo_domain_info" is used for remembering
- * the profile's number (0 - 255) assigned to that domain.
- */
-static struct tomoyo_profile {
-	unsigned int value[TOMOYO_MAX_CONTROL_INDEX];
-	const struct tomoyo_path_info *comment;
-} *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES];
-
 /* Permit policy management by non-root user? */
 static bool tomoyo_manage_by_non_root;
 
 /* Utility functions. */
 
 /**
+ * tomoyo_yesno - Return "yes" or "no".
+ *
+ * @value: Bool value.
+ */
+static const char *tomoyo_yesno(const unsigned int value)
+{
+	return value ? "yes" : "no";
+}
+
+/**
  * tomoyo_print_name_union - Print a tomoyo_name_union.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
@@ -154,80 +175,62 @@
 }
 
 /**
- * tomoyo_check_flags - Check mode for specified functionality.
- *
- * @domain: Pointer to "struct tomoyo_domain_info".
- * @index:  The functionality to check mode.
- *
- * TOMOYO checks only process context.
- * This code disables TOMOYO's enforcement in case the function is called from
- * interrupt context.
- */
-unsigned int tomoyo_check_flags(const struct tomoyo_domain_info *domain,
-				const u8 index)
-{
-	const u8 profile = domain->profile;
-
-	if (WARN_ON(in_interrupt()))
-		return 0;
-	return tomoyo_policy_loaded && index < TOMOYO_MAX_CONTROL_INDEX
-#if TOMOYO_MAX_PROFILES != 256
-		&& profile < TOMOYO_MAX_PROFILES
-#endif
-		&& tomoyo_profile_ptr[profile] ?
-		tomoyo_profile_ptr[profile]->value[index] : 0;
-}
-
-/**
- * tomoyo_verbose_mode - Check whether TOMOYO is verbose mode.
- *
- * @domain: Pointer to "struct tomoyo_domain_info".
- *
- * Returns true if domain policy violation warning should be printed to
- * console.
- */
-bool tomoyo_verbose_mode(const struct tomoyo_domain_info *domain)
-{
-	return tomoyo_check_flags(domain, TOMOYO_VERBOSE) != 0;
-}
-
-/**
  * tomoyo_find_or_assign_new_profile - Create a new profile.
  *
  * @profile: Profile number to create.
  *
  * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.
  */
-static struct tomoyo_profile *tomoyo_find_or_assign_new_profile(const unsigned
-								int profile)
+static struct tomoyo_profile *tomoyo_find_or_assign_new_profile
+(const unsigned int profile)
 {
-	struct tomoyo_profile *ptr = NULL;
-	int i;
-
+	struct tomoyo_profile *ptr;
+	struct tomoyo_profile *entry;
 	if (profile >= TOMOYO_MAX_PROFILES)
 		return NULL;
-	if (mutex_lock_interruptible(&tomoyo_policy_lock))
-		return NULL;
 	ptr = tomoyo_profile_ptr[profile];
 	if (ptr)
-		goto ok;
-	ptr = kmalloc(sizeof(*ptr), GFP_NOFS);
-	if (!tomoyo_memory_ok(ptr)) {
-		kfree(ptr);
-		ptr = NULL;
-		goto ok;
+		return ptr;
+	entry = kzalloc(sizeof(*entry), GFP_NOFS);
+	if (mutex_lock_interruptible(&tomoyo_policy_lock))
+		goto out;
+	ptr = tomoyo_profile_ptr[profile];
+	if (!ptr && tomoyo_memory_ok(entry)) {
+		ptr = entry;
+		ptr->learning = &tomoyo_default_profile.preference;
+		ptr->permissive = &tomoyo_default_profile.preference;
+		ptr->enforcing = &tomoyo_default_profile.preference;
+		ptr->default_config = TOMOYO_CONFIG_DISABLED;
+		memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,
+		       sizeof(ptr->config));
+		mb(); /* Avoid out-of-order execution. */
+		tomoyo_profile_ptr[profile] = ptr;
+		entry = NULL;
 	}
-	for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++)
-		ptr->value[i] = tomoyo_control_array[i].current_value;
-	mb(); /* Avoid out-of-order execution. */
-	tomoyo_profile_ptr[profile] = ptr;
- ok:
 	mutex_unlock(&tomoyo_policy_lock);
+ out:
+	kfree(entry);
 	return ptr;
 }
 
 /**
- * tomoyo_write_profile - Write to profile table.
+ * tomoyo_profile - Find a profile.
+ *
+ * @profile: Profile number to find.
+ *
+ * Returns pointer to "struct tomoyo_profile".
+ */
+struct tomoyo_profile *tomoyo_profile(const u8 profile)
+{
+	struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile];
+	if (!tomoyo_policy_loaded)
+		return &tomoyo_default_profile;
+	BUG_ON(!ptr);
+	return ptr;
+}
+
+/**
+ * tomoyo_write_profile - Write profile table.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
@@ -237,64 +240,116 @@
 {
 	char *data = head->write_buf;
 	unsigned int i;
-	unsigned int value;
+	int value;
+	int mode;
+	u8 config;
+	bool use_default = false;
 	char *cp;
 	struct tomoyo_profile *profile;
-	unsigned long num;
-
-	cp = strchr(data, '-');
-	if (cp)
-		*cp = '\0';
-	if (strict_strtoul(data, 10, &num))
-		return -EINVAL;
-	if (cp)
+	if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1)
+		return 0;
+	i = simple_strtoul(data, &cp, 10);
+	if (data == cp) {
+		profile = &tomoyo_default_profile;
+	} else {
+		if (*cp != '-')
+			return -EINVAL;
 		data = cp + 1;
-	profile = tomoyo_find_or_assign_new_profile(num);
-	if (!profile)
-		return -EINVAL;
+		profile = tomoyo_find_or_assign_new_profile(i);
+		if (!profile)
+			return -EINVAL;
+	}
 	cp = strchr(data, '=');
 	if (!cp)
 		return -EINVAL;
-	*cp = '\0';
+	*cp++ = '\0';
+	if (profile != &tomoyo_default_profile)
+		use_default = strstr(cp, "use_default") != NULL;
+	if (strstr(cp, "verbose=yes"))
+		value = 1;
+	else if (strstr(cp, "verbose=no"))
+		value = 0;
+	else
+		value = -1;
+	if (!strcmp(data, "PREFERENCE::enforcing")) {
+		if (use_default) {
+			profile->enforcing = &tomoyo_default_profile.preference;
+			return 0;
+		}
+		profile->enforcing = &profile->preference;
+		if (value >= 0)
+			profile->preference.enforcing_verbose = value;
+		return 0;
+	}
+	if (!strcmp(data, "PREFERENCE::permissive")) {
+		if (use_default) {
+			profile->permissive = &tomoyo_default_profile.preference;
+			return 0;
+		}
+		profile->permissive = &profile->preference;
+		if (value >= 0)
+			profile->preference.permissive_verbose = value;
+		return 0;
+	}
+	if (!strcmp(data, "PREFERENCE::learning")) {
+		char *cp2;
+		if (use_default) {
+			profile->learning = &tomoyo_default_profile.preference;
+			return 0;
+		}
+		profile->learning = &profile->preference;
+		if (value >= 0)
+			profile->preference.learning_verbose = value;
+		cp2 = strstr(cp, "max_entry=");
+		if (cp2)
+			sscanf(cp2 + 10, "%u",
+			       &profile->preference.learning_max_entry);
+		return 0;
+	}
+	if (profile == &tomoyo_default_profile)
+		return -EINVAL;
 	if (!strcmp(data, "COMMENT")) {
 		const struct tomoyo_path_info *old_comment = profile->comment;
-		profile->comment = tomoyo_get_name(cp + 1);
+		profile->comment = tomoyo_get_name(cp);
 		tomoyo_put_name(old_comment);
 		return 0;
 	}
-	for (i = 0; i < TOMOYO_MAX_CONTROL_INDEX; i++) {
-		if (strcmp(data, tomoyo_control_array[i].keyword))
-			continue;
-		if (sscanf(cp + 1, "%u", &value) != 1) {
-			int j;
-			const char **modes;
-			switch (i) {
-			case TOMOYO_VERBOSE:
-				modes = tomoyo_mode_2;
-				break;
-			default:
-				modes = tomoyo_mode_4;
-				break;
-			}
-			for (j = 0; j < 4; j++) {
-				if (strcmp(cp + 1, modes[j]))
-					continue;
-				value = j;
-				break;
-			}
-			if (j == 4)
-				return -EINVAL;
-		} else if (value > tomoyo_control_array[i].max_value) {
-			value = tomoyo_control_array[i].max_value;
+	if (!strcmp(data, "CONFIG")) {
+		i = TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX;
+		config = profile->default_config;
+	} else if (tomoyo_str_starts(&data, "CONFIG::")) {
+		config = 0;
+		for (i = 0; i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) {
+			if (strcmp(data, tomoyo_mac_keywords[i]))
+				continue;
+			config = profile->config[i];
+			break;
 		}
-		profile->value[i] = value;
-		return 0;
+		if (i == TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX)
+			return -EINVAL;
+	} else {
+		return -EINVAL;
 	}
-	return -EINVAL;
+	if (use_default) {
+		config = TOMOYO_CONFIG_USE_DEFAULT;
+	} else {
+		for (mode = 3; mode >= 0; mode--)
+			if (strstr(cp, tomoyo_mode_4[mode]))
+				/*
+				 * Update lower 3 bits in order to distinguish
+				 * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'.
+				 */
+				config = (config & ~7) | mode;
+	}
+	if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX)
+		profile->config[i] = config;
+	else if (config != TOMOYO_CONFIG_USE_DEFAULT)
+		profile->default_config = config;
+	return 0;
 }
 
 /**
- * tomoyo_read_profile - Read from profile table.
+ * tomoyo_read_profile - Read profile table.
  *
  * @head: Pointer to "struct tomoyo_io_buffer".
  *
@@ -302,53 +357,82 @@
  */
 static int tomoyo_read_profile(struct tomoyo_io_buffer *head)
 {
-	static const int total = TOMOYO_MAX_CONTROL_INDEX + 1;
-	int step;
-
+	int index;
 	if (head->read_eof)
 		return 0;
-	for (step = head->read_step; step < TOMOYO_MAX_PROFILES * total;
-	     step++) {
-		const u8 index = step / total;
-		u8 type = step % total;
+	if (head->read_bit)
+		goto body;
+	tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903");
+	tomoyo_io_printf(head, "PREFERENCE::learning={ verbose=%s "
+			 "max_entry=%u }\n",
+			 tomoyo_yesno(tomoyo_default_profile.preference.
+				      learning_verbose),
+			 tomoyo_default_profile.preference.learning_max_entry);
+	tomoyo_io_printf(head, "PREFERENCE::permissive={ verbose=%s }\n",
+			 tomoyo_yesno(tomoyo_default_profile.preference.
+				      permissive_verbose));
+	tomoyo_io_printf(head, "PREFERENCE::enforcing={ verbose=%s }\n",
+			 tomoyo_yesno(tomoyo_default_profile.preference.
+				      enforcing_verbose));
+	head->read_bit = 1;
+ body:
+	for (index = head->read_step; index < TOMOYO_MAX_PROFILES; index++) {
+		bool done;
+		u8 config;
+		int i;
+		int pos;
 		const struct tomoyo_profile *profile
 			= tomoyo_profile_ptr[index];
-		head->read_step = step;
+		const struct tomoyo_path_info *comment;
+		head->read_step = index;
 		if (!profile)
 			continue;
-		if (!type) { /* Print profile' comment tag. */
-			if (!tomoyo_io_printf(head, "%u-COMMENT=%s\n",
-					      index, profile->comment ?
-					      profile->comment->name : ""))
-				break;
-			continue;
+		pos = head->read_avail;
+		comment = profile->comment;
+		done = tomoyo_io_printf(head, "%u-COMMENT=%s\n", index,
+					comment ? comment->name : "");
+		if (!done)
+			goto out;
+		config = profile->default_config;
+		if (!tomoyo_io_printf(head, "%u-CONFIG={ mode=%s }\n", index,
+				      tomoyo_mode_4[config & 3]))
+			goto out;
+		for (i = 0; i < TOMOYO_MAX_MAC_INDEX +
+			     TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) {
+			config = profile->config[i];
+			if (config == TOMOYO_CONFIG_USE_DEFAULT)
+				continue;
+			if (!tomoyo_io_printf(head,
+					      "%u-CONFIG::%s={ mode=%s }\n",
+					      index, tomoyo_mac_keywords[i],
+					      tomoyo_mode_4[config & 3]))
+				goto out;
 		}
-		type--;
-		if (type < TOMOYO_MAX_CONTROL_INDEX) {
-			const unsigned int value = profile->value[type];
-			const char **modes = NULL;
-			const char *keyword
-				= tomoyo_control_array[type].keyword;
-			switch (tomoyo_control_array[type].max_value) {
-			case 3:
-				modes = tomoyo_mode_4;
-				break;
-			case 1:
-				modes = tomoyo_mode_2;
-				break;
-			}
-			if (modes) {
-				if (!tomoyo_io_printf(head, "%u-%s=%s\n", index,
-						      keyword, modes[value]))
-					break;
-			} else {
-				if (!tomoyo_io_printf(head, "%u-%s=%u\n", index,
-						      keyword, value))
-					break;
-			}
-		}
+		if (profile->learning != &tomoyo_default_profile.preference &&
+		    !tomoyo_io_printf(head, "%u-PREFERENCE::learning={ "
+				      "verbose=%s max_entry=%u }\n", index,
+				      tomoyo_yesno(profile->preference.
+						   learning_verbose),
+				      profile->preference.learning_max_entry))
+			goto out;
+		if (profile->permissive != &tomoyo_default_profile.preference
+		    && !tomoyo_io_printf(head, "%u-PREFERENCE::permissive={ "
+					 "verbose=%s }\n", index,
+					 tomoyo_yesno(profile->preference.
+						      permissive_verbose)))
+			goto out;
+		if (profile->enforcing != &tomoyo_default_profile.preference &&
+		    !tomoyo_io_printf(head, "%u-PREFERENCE::enforcing={ "
+				      "verbose=%s }\n", index,
+				      tomoyo_yesno(profile->preference.
+						   enforcing_verbose)))
+			goto out;
+		continue;
+ out:
+		head->read_avail = pos;
+		break;
 	}
-	if (step == TOMOYO_MAX_PROFILES * total)
+	if (index == TOMOYO_MAX_PROFILES)
 		head->read_eof = true;
 	return 0;
 }
@@ -1595,7 +1679,7 @@
 static int tomoyo_read_version(struct tomoyo_io_buffer *head)
 {
 	if (!head->read_eof) {
-		tomoyo_io_printf(head, "2.2.0");
+		tomoyo_io_printf(head, "2.3.0-pre");
 		head->read_eof = true;
 	}
 	return 0;
@@ -1915,6 +1999,9 @@
 		      profile, domain->domainname->name);
 	}
 	tomoyo_read_unlock(idx);
-	printk(KERN_INFO "TOMOYO: 2.2.0   2009/04/01\n");
+	if (tomoyo_profile_version != 20090903)
+		panic("Profile version %u is not supported.\n",
+		      tomoyo_profile_version);
+	printk(KERN_INFO "TOMOYO: 2.3.0-pre   2010/06/03\n");
 	printk(KERN_INFO "Mandatory Access Control activated.\n");
 }
diff --git a/security/tomoyo/common.h b/security/tomoyo/common.h
index 54db39a..c777c59 100644
--- a/security/tomoyo/common.h
+++ b/security/tomoyo/common.h
@@ -42,7 +42,8 @@
 	TOMOYO_CONFIG_DISABLED,
 	TOMOYO_CONFIG_LEARNING,
 	TOMOYO_CONFIG_PERMISSIVE,
-	TOMOYO_CONFIG_ENFORCING
+	TOMOYO_CONFIG_ENFORCING,
+	TOMOYO_CONFIG_USE_DEFAULT = 255
 };
 
 /* Keywords for ACLs. */
@@ -75,14 +76,6 @@
 #define TOMOYO_VALUE_TYPE_HEXADECIMAL 3
 
 /* Index numbers for Access Controls. */
-enum tomoyo_mac_index {
-	TOMOYO_MAC_FOR_FILE,  /* domain_policy.conf */
-	TOMOYO_MAX_ACCEPT_ENTRY,
-	TOMOYO_VERBOSE,
-	TOMOYO_MAX_CONTROL_INDEX
-};
-
-/* Index numbers for Access Controls. */
 enum tomoyo_acl_entry_type_index {
 	TOMOYO_TYPE_PATH_ACL,
 	TOMOYO_TYPE_PATH2_ACL,
@@ -157,6 +150,38 @@
 	TOMOYO_MANAGER
 };
 
+enum tomoyo_mac_index {
+	TOMOYO_MAC_FILE_EXECUTE,
+	TOMOYO_MAC_FILE_OPEN,
+	TOMOYO_MAC_FILE_CREATE,
+	TOMOYO_MAC_FILE_UNLINK,
+	TOMOYO_MAC_FILE_MKDIR,
+	TOMOYO_MAC_FILE_RMDIR,
+	TOMOYO_MAC_FILE_MKFIFO,
+	TOMOYO_MAC_FILE_MKSOCK,
+	TOMOYO_MAC_FILE_TRUNCATE,
+	TOMOYO_MAC_FILE_SYMLINK,
+	TOMOYO_MAC_FILE_REWRITE,
+	TOMOYO_MAC_FILE_MKBLOCK,
+	TOMOYO_MAC_FILE_MKCHAR,
+	TOMOYO_MAC_FILE_LINK,
+	TOMOYO_MAC_FILE_RENAME,
+	TOMOYO_MAC_FILE_CHMOD,
+	TOMOYO_MAC_FILE_CHOWN,
+	TOMOYO_MAC_FILE_CHGRP,
+	TOMOYO_MAC_FILE_IOCTL,
+	TOMOYO_MAC_FILE_CHROOT,
+	TOMOYO_MAC_FILE_MOUNT,
+	TOMOYO_MAC_FILE_UMOUNT,
+	TOMOYO_MAC_FILE_PIVOT_ROOT,
+	TOMOYO_MAX_MAC_INDEX
+};
+
+enum tomoyo_mac_category_index {
+	TOMOYO_MAC_CATEGORY_FILE,
+	TOMOYO_MAX_MAC_CATEGORY_INDEX
+};
+
 #define TOMOYO_RETRY_REQUEST 1 /* Retry this request. */
 
 /********** Structure definitions. **********/
@@ -174,6 +199,7 @@
 	u8 retry;
 	u8 profile;
 	u8 mode; /* One of tomoyo_mode_index . */
+	u8 type;
 };
 
 /*
@@ -649,6 +675,23 @@
 	bool is_deleted; /* True if this entry is deleted. */
 };
 
+struct tomoyo_preference {
+	unsigned int learning_max_entry;
+	bool enforcing_verbose;
+	bool learning_verbose;
+	bool permissive_verbose;
+};
+
+struct tomoyo_profile {
+	const struct tomoyo_path_info *comment;
+	struct tomoyo_preference *learning;
+	struct tomoyo_preference *permissive;
+	struct tomoyo_preference *enforcing;
+	struct tomoyo_preference preference;
+	u8 default_config;
+	u8 config[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX];
+};
+
 /********** Function prototypes. **********/
 
 extern asmlinkage long sys_getpid(void);
@@ -685,6 +728,7 @@
 /* Check whether the given number matches the given number_union. */
 bool tomoyo_compare_number_union(const unsigned long value,
 				 const struct tomoyo_number_union *ptr);
+int tomoyo_get_mode(const u8 profile, const u8 index);
 /* Transactional sprintf() for policy dump. */
 bool tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)
 	__attribute__ ((format(printf, 2, 3)));
@@ -747,7 +791,8 @@
 const char *tomoyo_path2keyword(const u8 operation);
 /* Fill "struct tomoyo_request_info". */
 int tomoyo_init_request_info(struct tomoyo_request_info *r,
-			     struct tomoyo_domain_info *domain);
+			     struct tomoyo_domain_info *domain,
+			     const u8 index);
 /* Check permission for mount operation. */
 int tomoyo_mount_permission(char *dev_name, struct path *path, char *type,
 			    unsigned long flags, void *data_page);
@@ -794,6 +839,7 @@
 struct tomoyo_domain_info *tomoyo_find_or_assign_new_domain(const char *
 							    domainname,
 							    const u8 profile);
+struct tomoyo_profile *tomoyo_profile(const u8 profile);
 /* Allocate memory for "struct tomoyo_path_group". */
 struct tomoyo_path_group *tomoyo_get_path_group(const char *group_name);
 struct tomoyo_number_group *tomoyo_get_number_group(const char *group_name);
@@ -844,7 +890,7 @@
 
 /* Initialize mm related code. */
 void __init tomoyo_mm_init(void);
-int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
+int tomoyo_check_exec_perm(struct tomoyo_request_info *r,
 			   const struct tomoyo_path_info *filename);
 int tomoyo_check_open_permission(struct tomoyo_domain_info *domain,
 				 struct path *path, const int flag);
diff --git a/security/tomoyo/domain.c b/security/tomoyo/domain.c
index a07ca6d..09ec37c 100644
--- a/security/tomoyo/domain.c
+++ b/security/tomoyo/domain.c
@@ -812,8 +812,8 @@
 	struct tomoyo_domain_info *domain = NULL;
 	const char *old_domain_name = old_domain->domainname->name;
 	const char *original_name = bprm->filename;
-	const u8 mode = tomoyo_check_flags(old_domain, TOMOYO_MAC_FOR_FILE);
-	const bool is_enforce = (mode == TOMOYO_CONFIG_ENFORCING);
+	u8 mode;
+	bool is_enforce;
 	int retval = -ENOMEM;
 	bool need_kfree = false;
 	struct tomoyo_path_info rn = { }; /* real name */
@@ -822,7 +822,8 @@
 
 	ln.name = tomoyo_get_last_name(old_domain);
 	tomoyo_fill_path_info(&ln);
-	tomoyo_init_request_info(&r, NULL);
+	mode = tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_EXECUTE);
+	is_enforce = (mode == TOMOYO_CONFIG_ENFORCING);
 	if (!tmp)
 		goto out;
 
@@ -880,7 +881,7 @@
 	}
 
 	/* Check execute permission. */
-	retval = tomoyo_check_exec_perm(old_domain, &rn);
+	retval = tomoyo_check_exec_perm(&r, &rn);
 	if (retval == TOMOYO_RETRY_REQUEST)
 		goto retry;
 	if (retval < 0)
diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c
index 83fa17a..8e51348 100644
--- a/security/tomoyo/file.c
+++ b/security/tomoyo/file.c
@@ -51,6 +51,42 @@
 	[TOMOYO_TYPE_CHGRP]      = "chgrp",
 };
 
+static const u8 tomoyo_p2mac[TOMOYO_MAX_PATH_OPERATION] = {
+	[TOMOYO_TYPE_READ_WRITE] = TOMOYO_MAC_FILE_OPEN,
+	[TOMOYO_TYPE_EXECUTE]    = TOMOYO_MAC_FILE_EXECUTE,
+	[TOMOYO_TYPE_READ]       = TOMOYO_MAC_FILE_OPEN,
+	[TOMOYO_TYPE_WRITE]      = TOMOYO_MAC_FILE_OPEN,
+	[TOMOYO_TYPE_UNLINK]     = TOMOYO_MAC_FILE_UNLINK,
+	[TOMOYO_TYPE_RMDIR]      = TOMOYO_MAC_FILE_RMDIR,
+	[TOMOYO_TYPE_TRUNCATE]   = TOMOYO_MAC_FILE_TRUNCATE,
+	[TOMOYO_TYPE_SYMLINK]    = TOMOYO_MAC_FILE_SYMLINK,
+	[TOMOYO_TYPE_REWRITE]    = TOMOYO_MAC_FILE_REWRITE,
+	[TOMOYO_TYPE_CHROOT]     = TOMOYO_MAC_FILE_CHROOT,
+	[TOMOYO_TYPE_UMOUNT]     = TOMOYO_MAC_FILE_UMOUNT,
+};
+
+static const u8 tomoyo_pnnn2mac[TOMOYO_MAX_PATH_NUMBER3_OPERATION] = {
+	[TOMOYO_TYPE_MKBLOCK] = TOMOYO_MAC_FILE_MKBLOCK,
+	[TOMOYO_TYPE_MKCHAR]  = TOMOYO_MAC_FILE_MKCHAR,
+};
+
+static const u8 tomoyo_pp2mac[TOMOYO_MAX_PATH2_OPERATION] = {
+	[TOMOYO_TYPE_LINK]       = TOMOYO_MAC_FILE_LINK,
+	[TOMOYO_TYPE_RENAME]     = TOMOYO_MAC_FILE_RENAME,
+	[TOMOYO_TYPE_PIVOT_ROOT] = TOMOYO_MAC_FILE_PIVOT_ROOT,
+};
+
+static const u8 tomoyo_pn2mac[TOMOYO_MAX_PATH_NUMBER_OPERATION] = {
+	[TOMOYO_TYPE_CREATE] = TOMOYO_MAC_FILE_CREATE,
+	[TOMOYO_TYPE_MKDIR]  = TOMOYO_MAC_FILE_MKDIR,
+	[TOMOYO_TYPE_MKFIFO] = TOMOYO_MAC_FILE_MKFIFO,
+	[TOMOYO_TYPE_MKSOCK] = TOMOYO_MAC_FILE_MKSOCK,
+	[TOMOYO_TYPE_IOCTL]  = TOMOYO_MAC_FILE_IOCTL,
+	[TOMOYO_TYPE_CHMOD]  = TOMOYO_MAC_FILE_CHMOD,
+	[TOMOYO_TYPE_CHOWN]  = TOMOYO_MAC_FILE_CHOWN,
+	[TOMOYO_TYPE_CHGRP]  = TOMOYO_MAC_FILE_CHGRP,
+};
+
 void tomoyo_put_name_union(struct tomoyo_name_union *ptr)
 {
 	if (!ptr)
@@ -1057,6 +1093,10 @@
 	int error;
 
  next:
+	r->type = tomoyo_p2mac[operation];
+	r->mode = tomoyo_get_mode(r->profile, r->type);
+	if (r->mode == TOMOYO_CONFIG_DISABLED)
+		return 0;
 	do {
 		error = tomoyo_path_acl(r, filename, 1 << operation);
 		if (!error)
@@ -1249,8 +1289,8 @@
 	struct tomoyo_path_info buf;
 	int idx;
 
-	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
-	    !path->mnt || !path->dentry)
+	if (tomoyo_init_request_info(&r, NULL, tomoyo_pn2mac[type])
+	    == TOMOYO_CONFIG_DISABLED || !path->mnt || !path->dentry)
 		return 0;
 	idx = tomoyo_read_lock();
 	if (!tomoyo_get_realpath(&buf, path))
@@ -1269,21 +1309,19 @@
 /**
  * tomoyo_check_exec_perm - Check permission for "execute".
  *
- * @domain:   Pointer to "struct tomoyo_domain_info".
+ * @r:        Pointer to "struct tomoyo_request_info".
  * @filename: Check permission for "execute".
  *
  * Returns 0 on success, negativevalue otherwise.
  *
  * Caller holds tomoyo_read_lock().
  */
-int tomoyo_check_exec_perm(struct tomoyo_domain_info *domain,
+int tomoyo_check_exec_perm(struct tomoyo_request_info *r,
 			   const struct tomoyo_path_info *filename)
 {
-	struct tomoyo_request_info r;
-
-	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED)
+	if (r->mode == TOMOYO_CONFIG_DISABLED)
 		return 0;
-	return tomoyo_file_perm(&r, filename, 1);
+	return tomoyo_file_perm(r, filename, 1);
 }
 
 /**
@@ -1304,17 +1342,11 @@
 	struct tomoyo_request_info r;
 	int idx;
 
-	if (tomoyo_init_request_info(&r, domain) == TOMOYO_CONFIG_DISABLED ||
-	    !path->mnt)
+	if (!path->mnt ||
+	    (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode)))
 		return 0;
-	if (acc_mode == 0)
-		return 0;
-	if (path->dentry->d_inode && S_ISDIR(path->dentry->d_inode->i_mode))
-		/*
-		 * I don't check directories here because mkdir() and rmdir()
-		 * don't call me.
-		 */
-		return 0;
+	buf.name = NULL;
+	r.mode = TOMOYO_CONFIG_DISABLED;
 	idx = tomoyo_read_lock();
 	if (!tomoyo_get_realpath(&buf, path))
 		goto out;
@@ -1324,15 +1356,26 @@
 	 * we need to check "allow_rewrite" permission when the filename is not
 	 * opened for append mode or the filename is truncated at open time.
 	 */
-	if ((acc_mode & MAY_WRITE) &&
-	    ((flag & O_TRUNC) || !(flag & O_APPEND)) &&
-	    (tomoyo_is_no_rewrite_file(&buf))) {
-		error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE, &buf);
+	if ((acc_mode & MAY_WRITE) && !(flag & O_APPEND)
+	    && tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_REWRITE)
+	    != TOMOYO_CONFIG_DISABLED) {
+		if (!tomoyo_get_realpath(&buf, path)) {
+			error = -ENOMEM;
+			goto out;
+		}
+		if (tomoyo_is_no_rewrite_file(&buf))
+			error = tomoyo_path_permission(&r, TOMOYO_TYPE_REWRITE,
+						       &buf);
 	}
-	if (!error)
+	if (!error && acc_mode &&
+	    tomoyo_init_request_info(&r, domain, TOMOYO_MAC_FILE_OPEN)
+	    != TOMOYO_CONFIG_DISABLED) {
+		if (!buf.name && !tomoyo_get_realpath(&buf, path)) {
+			error = -ENOMEM;
+			goto out;
+		}
 		error = tomoyo_file_perm(&r, &buf, acc_mode);
-	if (!error && (flag & O_TRUNC))
-		error = tomoyo_path_permission(&r, TOMOYO_TYPE_TRUNCATE, &buf);
+	}
  out:
 	kfree(buf.name);
 	tomoyo_read_unlock(idx);
@@ -1356,9 +1399,12 @@
 	struct tomoyo_request_info r;
 	int idx;
 
-	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
-	    !path->mnt)
+	if (!path->mnt)
 		return 0;
+	if (tomoyo_init_request_info(&r, NULL, tomoyo_p2mac[operation])
+	    == TOMOYO_CONFIG_DISABLED)
+		return 0;
+	buf.name = NULL;
 	idx = tomoyo_read_lock();
 	if (!tomoyo_get_realpath(&buf, path))
 		goto out;
@@ -1371,6 +1417,7 @@
 		break;
 	case TOMOYO_TYPE_RMDIR:
 	case TOMOYO_TYPE_CHROOT:
+	case TOMOYO_TYPE_UMOUNT:
 		tomoyo_add_slash(&buf);
 		break;
 	}
@@ -1442,8 +1489,9 @@
 	struct tomoyo_path_info buf;
 	int idx;
 
-	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
-	    !path->mnt)
+	if (!path->mnt ||
+	    tomoyo_init_request_info(&r, NULL, tomoyo_pnnn2mac[operation])
+	    == TOMOYO_CONFIG_DISABLED)
 		return 0;
 	idx = tomoyo_read_lock();
 	error = -ENOMEM;
@@ -1477,8 +1525,9 @@
 	struct tomoyo_request_info r;
 	int idx;
 
-	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED ||
-	    !path1->mnt || !path2->mnt)
+	if (!path1->mnt || !path2->mnt ||
+	    tomoyo_init_request_info(&r, NULL, tomoyo_pp2mac[operation])
+	    == TOMOYO_CONFIG_DISABLED)
 		return 0;
 	buf1.name = NULL;
 	buf2.name = NULL;
@@ -1486,13 +1535,19 @@
 	if (!tomoyo_get_realpath(&buf1, path1) ||
 	    !tomoyo_get_realpath(&buf2, path2))
 		goto out;
-	{
-		struct dentry *dentry = path1->dentry;
-		if (dentry->d_inode && S_ISDIR(dentry->d_inode->i_mode)) {
-			tomoyo_add_slash(&buf1);
-			tomoyo_add_slash(&buf2);
-		}
-	}
+	switch (operation) {
+		struct dentry *dentry;
+	case TOMOYO_TYPE_RENAME:
+        case TOMOYO_TYPE_LINK:
+		dentry = path1->dentry;
+	        if (!dentry->d_inode || !S_ISDIR(dentry->d_inode->i_mode))
+                        break;
+                /* fall through */
+        case TOMOYO_TYPE_PIVOT_ROOT:
+                tomoyo_add_slash(&buf1);
+                tomoyo_add_slash(&buf2);
+		break;
+        }
 	do {
 		error = tomoyo_path2_acl(&r, operation, &buf1, &buf2);
 		if (!error)
diff --git a/security/tomoyo/mount.c b/security/tomoyo/mount.c
index 7c1c7fd..77ee8bf 100644
--- a/security/tomoyo/mount.c
+++ b/security/tomoyo/mount.c
@@ -248,7 +248,8 @@
 	int error;
 	int idx;
 
-	if (tomoyo_init_request_info(&r, NULL) == TOMOYO_CONFIG_DISABLED)
+	if (tomoyo_init_request_info(&r, NULL, TOMOYO_MAC_FILE_MOUNT)
+	    == TOMOYO_CONFIG_DISABLED)
 		return 0;
 	if (!type)
 		type = "<NULL>";
diff --git a/security/tomoyo/util.c b/security/tomoyo/util.c
index 592b76a..307793e 100644
--- a/security/tomoyo/util.c
+++ b/security/tomoyo/util.c
@@ -792,25 +792,67 @@
 }
 
 /**
+ * tomoyo_get_mode - Get MAC mode.
+ *
+ * @profile: Profile number.
+ * @index:   Index number of functionality.
+ *
+ * Returns mode.
+ */
+int tomoyo_get_mode(const u8 profile, const u8 index)
+{
+	u8 mode;
+	const u8 category = TOMOYO_MAC_CATEGORY_FILE;
+	if (!tomoyo_policy_loaded)
+		return TOMOYO_CONFIG_DISABLED;
+	mode = tomoyo_profile(profile)->config[index];
+	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+		mode = tomoyo_profile(profile)->config[category];
+	if (mode == TOMOYO_CONFIG_USE_DEFAULT)
+		mode = tomoyo_profile(profile)->default_config;
+	return mode & 3;
+}
+
+/**
  * tomoyo_init_request_info - Initialize "struct tomoyo_request_info" members.
  *
  * @r:      Pointer to "struct tomoyo_request_info" to initialize.
  * @domain: Pointer to "struct tomoyo_domain_info". NULL for tomoyo_domain().
+ * @index:  Index number of functionality.
  *
  * Returns mode.
  */
 int tomoyo_init_request_info(struct tomoyo_request_info *r,
-			     struct tomoyo_domain_info *domain)
+			     struct tomoyo_domain_info *domain, const u8 index)
 {
+	u8 profile;
 	memset(r, 0, sizeof(*r));
 	if (!domain)
 		domain = tomoyo_domain();
 	r->domain = domain;
-	r->mode = tomoyo_check_flags(domain, TOMOYO_MAC_FOR_FILE);
+	profile = domain->profile;
+	r->profile = profile;
+	r->type = index;
+	r->mode = tomoyo_get_mode(profile, index);
 	return r->mode;
 }
 
 /**
+ * tomoyo_last_word - Get last component of a line.
+ *
+ * @line: A line.
+ *
+ * Returns the last word of a line.
+ */
+static const char *tomoyo_last_word(const char *name)
+{
+	const char *cp = strrchr(name, ' ');
+	if (cp)
+		return cp + 1;
+	return name;
+}
+
+/**
  * tomoyo_warn_log - Print warning or error message on console.
  *
  * @r:   Pointer to "struct tomoyo_request_info".
@@ -818,29 +860,34 @@
  */
 void tomoyo_warn_log(struct tomoyo_request_info *r, const char *fmt, ...)
 {
-	int len = PAGE_SIZE;
 	va_list args;
 	char *buffer;
-	if (!tomoyo_verbose_mode(r->domain))
-		return;
-	while (1) {
-		int len2;
-		buffer = kmalloc(len, GFP_NOFS);
-		if (!buffer)
+	const struct tomoyo_domain_info * const domain = r->domain;
+	const struct tomoyo_profile *profile = tomoyo_profile(domain->profile);
+	switch (r->mode) {
+	case TOMOYO_CONFIG_ENFORCING:
+		if (!profile->enforcing->enforcing_verbose)
 			return;
-		va_start(args, fmt);
-		len2 = vsnprintf(buffer, len - 1, fmt, args);
-		va_end(args);
-		if (len2 <= len - 1) {
-			buffer[len2] = '\0';
-			break;
-		}
-		len = len2 + 1;
-		kfree(buffer);
+		break;
+	case TOMOYO_CONFIG_PERMISSIVE:
+		if (!profile->permissive->permissive_verbose)
+			return;
+		break;
+	case TOMOYO_CONFIG_LEARNING:
+		if (!profile->learning->learning_verbose)
+			return;
+		break;
 	}
-	printk(KERN_WARNING "TOMOYO-%s: Access %s denied for %s\n",
-	       r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING",
-	       buffer, tomoyo_get_last_name(r->domain));
+	buffer = kmalloc(4096, GFP_NOFS);
+	if (!buffer)
+		return;
+	va_start(args, fmt);
+	vsnprintf(buffer, 4095, fmt, args);
+	va_end(args);
+	buffer[4095] = '\0';
+	printk(KERN_WARNING "%s: Access %s denied for %s\n",
+	       r->mode == TOMOYO_CONFIG_ENFORCING ? "ERROR" : "WARNING", buffer,
+	       tomoyo_last_word(domain->domainname->name));
 	kfree(buffer);
 }
 
@@ -903,7 +950,8 @@
 				count++;
 		}
 	}
-	if (count < tomoyo_check_flags(domain, TOMOYO_MAX_ACCEPT_ENTRY))
+	if (count < tomoyo_profile(domain->profile)->learning->
+	    learning_max_entry)
 		return true;
 	if (!domain->quota_warned) {
 		domain->quota_warned = true;