diff options
Diffstat (limited to 'security/tomoyo/common.c')
| -rw-r--r-- | security/tomoyo/common.c | 1959 | 
1 files changed, 1263 insertions, 696 deletions
diff --git a/security/tomoyo/common.c b/security/tomoyo/common.c index a0d09e56874b..c8439cf2a448 100644 --- a/security/tomoyo/common.c +++ b/security/tomoyo/common.c @@ -1,9 +1,7 @@  /*   * security/tomoyo/common.c   * - * Common functions for TOMOYO. - * - * Copyright (C) 2005-2010  NTT DATA CORPORATION + * Copyright (C) 2005-2011  NTT DATA CORPORATION   */  #include <linux/uaccess.h> @@ -11,54 +9,131 @@  #include <linux/security.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 +/* String table for operation mode. */ +const char * const tomoyo_mode[TOMOYO_CONFIG_MAX_MODE] = { +	[TOMOYO_CONFIG_DISABLED]   = "disabled", +	[TOMOYO_CONFIG_LEARNING]   = "learning", +	[TOMOYO_CONFIG_PERMISSIVE] = "permissive", +	[TOMOYO_CONFIG_ENFORCING]  = "enforcing"  }; -/* Profile version. Currently only 20090903 is defined. */ -static unsigned int tomoyo_profile_version; +/* String table for /sys/kernel/security/tomoyo/profile */ +const char * const tomoyo_mac_keywords[TOMOYO_MAX_MAC_INDEX +				       + TOMOYO_MAX_MAC_CATEGORY_INDEX] = { +	[TOMOYO_MAC_FILE_EXECUTE]    = "execute", +	[TOMOYO_MAC_FILE_OPEN]       = "open", +	[TOMOYO_MAC_FILE_CREATE]     = "create", +	[TOMOYO_MAC_FILE_UNLINK]     = "unlink", +	[TOMOYO_MAC_FILE_GETATTR]    = "getattr", +	[TOMOYO_MAC_FILE_MKDIR]      = "mkdir", +	[TOMOYO_MAC_FILE_RMDIR]      = "rmdir", +	[TOMOYO_MAC_FILE_MKFIFO]     = "mkfifo", +	[TOMOYO_MAC_FILE_MKSOCK]     = "mksock", +	[TOMOYO_MAC_FILE_TRUNCATE]   = "truncate", +	[TOMOYO_MAC_FILE_SYMLINK]    = "symlink", +	[TOMOYO_MAC_FILE_MKBLOCK]    = "mkblock", +	[TOMOYO_MAC_FILE_MKCHAR]     = "mkchar", +	[TOMOYO_MAC_FILE_LINK]       = "link", +	[TOMOYO_MAC_FILE_RENAME]     = "rename", +	[TOMOYO_MAC_FILE_CHMOD]      = "chmod", +	[TOMOYO_MAC_FILE_CHOWN]      = "chown", +	[TOMOYO_MAC_FILE_CHGRP]      = "chgrp", +	[TOMOYO_MAC_FILE_IOCTL]      = "ioctl", +	[TOMOYO_MAC_FILE_CHROOT]     = "chroot", +	[TOMOYO_MAC_FILE_MOUNT]      = "mount", +	[TOMOYO_MAC_FILE_UMOUNT]     = "unmount", +	[TOMOYO_MAC_FILE_PIVOT_ROOT] = "pivot_root", +	[TOMOYO_MAX_MAC_INDEX + TOMOYO_MAC_CATEGORY_FILE] = "file", +}; -/* Profile table. Memory is allocated as needed. */ -static struct tomoyo_profile *tomoyo_profile_ptr[TOMOYO_MAX_PROFILES]; +/* String table for conditions. */ +const char * const tomoyo_condition_keyword[TOMOYO_MAX_CONDITION_KEYWORD] = { +	[TOMOYO_TASK_UID]             = "task.uid", +	[TOMOYO_TASK_EUID]            = "task.euid", +	[TOMOYO_TASK_SUID]            = "task.suid", +	[TOMOYO_TASK_FSUID]           = "task.fsuid", +	[TOMOYO_TASK_GID]             = "task.gid", +	[TOMOYO_TASK_EGID]            = "task.egid", +	[TOMOYO_TASK_SGID]            = "task.sgid", +	[TOMOYO_TASK_FSGID]           = "task.fsgid", +	[TOMOYO_TASK_PID]             = "task.pid", +	[TOMOYO_TASK_PPID]            = "task.ppid", +	[TOMOYO_EXEC_ARGC]            = "exec.argc", +	[TOMOYO_EXEC_ENVC]            = "exec.envc", +	[TOMOYO_TYPE_IS_SOCKET]       = "socket", +	[TOMOYO_TYPE_IS_SYMLINK]      = "symlink", +	[TOMOYO_TYPE_IS_FILE]         = "file", +	[TOMOYO_TYPE_IS_BLOCK_DEV]    = "block", +	[TOMOYO_TYPE_IS_DIRECTORY]    = "directory", +	[TOMOYO_TYPE_IS_CHAR_DEV]     = "char", +	[TOMOYO_TYPE_IS_FIFO]         = "fifo", +	[TOMOYO_MODE_SETUID]          = "setuid", +	[TOMOYO_MODE_SETGID]          = "setgid", +	[TOMOYO_MODE_STICKY]          = "sticky", +	[TOMOYO_MODE_OWNER_READ]      = "owner_read", +	[TOMOYO_MODE_OWNER_WRITE]     = "owner_write", +	[TOMOYO_MODE_OWNER_EXECUTE]   = "owner_execute", +	[TOMOYO_MODE_GROUP_READ]      = "group_read", +	[TOMOYO_MODE_GROUP_WRITE]     = "group_write", +	[TOMOYO_MODE_GROUP_EXECUTE]   = "group_execute", +	[TOMOYO_MODE_OTHERS_READ]     = "others_read", +	[TOMOYO_MODE_OTHERS_WRITE]    = "others_write", +	[TOMOYO_MODE_OTHERS_EXECUTE]  = "others_execute", +	[TOMOYO_EXEC_REALPATH]        = "exec.realpath", +	[TOMOYO_SYMLINK_TARGET]       = "symlink.target", +	[TOMOYO_PATH1_UID]            = "path1.uid", +	[TOMOYO_PATH1_GID]            = "path1.gid", +	[TOMOYO_PATH1_INO]            = "path1.ino", +	[TOMOYO_PATH1_MAJOR]          = "path1.major", +	[TOMOYO_PATH1_MINOR]          = "path1.minor", +	[TOMOYO_PATH1_PERM]           = "path1.perm", +	[TOMOYO_PATH1_TYPE]           = "path1.type", +	[TOMOYO_PATH1_DEV_MAJOR]      = "path1.dev_major", +	[TOMOYO_PATH1_DEV_MINOR]      = "path1.dev_minor", +	[TOMOYO_PATH2_UID]            = "path2.uid", +	[TOMOYO_PATH2_GID]            = "path2.gid", +	[TOMOYO_PATH2_INO]            = "path2.ino", +	[TOMOYO_PATH2_MAJOR]          = "path2.major", +	[TOMOYO_PATH2_MINOR]          = "path2.minor", +	[TOMOYO_PATH2_PERM]           = "path2.perm", +	[TOMOYO_PATH2_TYPE]           = "path2.type", +	[TOMOYO_PATH2_DEV_MAJOR]      = "path2.dev_major", +	[TOMOYO_PATH2_DEV_MINOR]      = "path2.dev_minor", +	[TOMOYO_PATH1_PARENT_UID]     = "path1.parent.uid", +	[TOMOYO_PATH1_PARENT_GID]     = "path1.parent.gid", +	[TOMOYO_PATH1_PARENT_INO]     = "path1.parent.ino", +	[TOMOYO_PATH1_PARENT_PERM]    = "path1.parent.perm", +	[TOMOYO_PATH2_PARENT_UID]     = "path2.parent.uid", +	[TOMOYO_PATH2_PARENT_GID]     = "path2.parent.gid", +	[TOMOYO_PATH2_PARENT_INO]     = "path2.parent.ino", +	[TOMOYO_PATH2_PARENT_PERM]    = "path2.parent.perm", +}; -/* String table for functionality that takes 4 modes. */ -static const char *tomoyo_mode[4] = { -	"disabled", "learning", "permissive", "enforcing" +/* String table for PREFERENCE keyword. */ +static const char * const tomoyo_pref_keywords[TOMOYO_MAX_PREF] = { +	[TOMOYO_PREF_MAX_AUDIT_LOG]      = "max_audit_log", +	[TOMOYO_PREF_MAX_LEARNING_ENTRY] = "max_learning_entry",  }; -/* 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", +/* String table for path operation. */ +const char * const tomoyo_path_keyword[TOMOYO_MAX_PATH_OPERATION] = { +	[TOMOYO_TYPE_EXECUTE]    = "execute", +	[TOMOYO_TYPE_READ]       = "read", +	[TOMOYO_TYPE_WRITE]      = "write", +	[TOMOYO_TYPE_APPEND]     = "append", +	[TOMOYO_TYPE_UNLINK]     = "unlink", +	[TOMOYO_TYPE_GETATTR]    = "getattr", +	[TOMOYO_TYPE_RMDIR]      = "rmdir", +	[TOMOYO_TYPE_TRUNCATE]   = "truncate", +	[TOMOYO_TYPE_SYMLINK]    = "symlink", +	[TOMOYO_TYPE_CHROOT]     = "chroot", +	[TOMOYO_TYPE_UMOUNT]     = "unmount", +}; + +/* String table for categories. */ +static const char * const tomoyo_category_keywords +[TOMOYO_MAX_MAC_CATEGORY_INDEX] = { +	[TOMOYO_MAC_CATEGORY_FILE]       = "file",  };  /* Permit policy management by non-root user? */ @@ -71,11 +146,20 @@ static bool tomoyo_manage_by_non_root;   *   * @value: Bool value.   */ -static const char *tomoyo_yesno(const unsigned int value) +const char *tomoyo_yesno(const unsigned int value)  {  	return value ? "yes" : "no";  } +/** + * tomoyo_addprintf - strncat()-like-snprintf(). + * + * @buffer: Buffer to write to. Must be '\0'-terminated. + * @len:    Size of @buffer. + * @fmt:    The printf()'s format string, followed by parameters. + * + * Returns nothing. + */  static void tomoyo_addprintf(char *buffer, int len, const char *fmt, ...)  {  	va_list args; @@ -96,7 +180,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head)  {  	while (head->r.w_pos) {  		const char *w = head->r.w[0]; -		int len = strlen(w); +		size_t len = strlen(w);  		if (len) {  			if (len > head->read_user_buf_avail)  				len = head->read_user_buf_avail; @@ -111,7 +195,7 @@ static bool tomoyo_flush(struct tomoyo_io_buffer *head)  		head->r.w[0] = w;  		if (*w)  			return false; -		/* Add '\0' for query. */ +		/* Add '\0' for audit logs and query. */  		if (head->poll) {  			if (!head->read_user_buf_avail ||  			    copy_to_user(head->read_user_buf, "", 1)) @@ -155,8 +239,8 @@ static void tomoyo_set_string(struct tomoyo_io_buffer *head, const char *string)  void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)  {  	va_list args; -	int len; -	int pos = head->r.avail; +	size_t len; +	size_t pos = head->r.avail;  	int size = head->readbuf_size - pos;  	if (size <= 0)  		return; @@ -171,11 +255,25 @@ void tomoyo_io_printf(struct tomoyo_io_buffer *head, const char *fmt, ...)  	tomoyo_set_string(head, head->read_buf + pos);  } +/** + * tomoyo_set_space - Put a space to "struct tomoyo_io_buffer" structure. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */  static void tomoyo_set_space(struct tomoyo_io_buffer *head)  {  	tomoyo_set_string(head, " ");  } +/** + * tomoyo_set_lf - Put a line feed to "struct tomoyo_io_buffer" structure. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */  static bool tomoyo_set_lf(struct tomoyo_io_buffer *head)  {  	tomoyo_set_string(head, "\n"); @@ -183,6 +281,62 @@ static bool tomoyo_set_lf(struct tomoyo_io_buffer *head)  }  /** + * tomoyo_set_slash - Put a shash to "struct tomoyo_io_buffer" structure. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static void tomoyo_set_slash(struct tomoyo_io_buffer *head) +{ +	tomoyo_set_string(head, "/"); +} + +/* List of namespaces. */ +LIST_HEAD(tomoyo_namespace_list); +/* True if namespace other than tomoyo_kernel_namespace is defined. */ +static bool tomoyo_namespace_enabled; + +/** + * tomoyo_init_policy_namespace - Initialize namespace. + * + * @ns: Pointer to "struct tomoyo_policy_namespace". + * + * Returns nothing. + */ +void tomoyo_init_policy_namespace(struct tomoyo_policy_namespace *ns) +{ +	unsigned int idx; +	for (idx = 0; idx < TOMOYO_MAX_ACL_GROUPS; idx++) +		INIT_LIST_HEAD(&ns->acl_group[idx]); +	for (idx = 0; idx < TOMOYO_MAX_GROUP; idx++) +		INIT_LIST_HEAD(&ns->group_list[idx]); +	for (idx = 0; idx < TOMOYO_MAX_POLICY; idx++) +		INIT_LIST_HEAD(&ns->policy_list[idx]); +	ns->profile_version = 20100903; +	tomoyo_namespace_enabled = !list_empty(&tomoyo_namespace_list); +	list_add_tail_rcu(&ns->namespace_list, &tomoyo_namespace_list); +} + +/** + * tomoyo_print_namespace - Print namespace header. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static void tomoyo_print_namespace(struct tomoyo_io_buffer *head) +{ +	if (!tomoyo_namespace_enabled) +		return; +	tomoyo_set_string(head, +			  container_of(head->r.ns, +				       struct tomoyo_policy_namespace, +				       namespace_list)->name); +	tomoyo_set_space(head); +} + +/**   * tomoyo_print_name_union - Print a tomoyo_name_union.   *   * @head: Pointer to "struct tomoyo_io_buffer". @@ -192,7 +346,7 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head,  				    const struct tomoyo_name_union *ptr)  {  	tomoyo_set_space(head); -	if (ptr->is_group) { +	if (ptr->group) {  		tomoyo_set_string(head, "@");  		tomoyo_set_string(head, ptr->group->group_name->name);  	} else { @@ -201,24 +355,46 @@ static void tomoyo_print_name_union(struct tomoyo_io_buffer *head,  }  /** - * tomoyo_print_number_union - Print a tomoyo_number_union. + * tomoyo_print_name_union_quoted - Print a tomoyo_name_union with a quote.   * - * @head:       Pointer to "struct tomoyo_io_buffer". - * @ptr:        Pointer to "struct tomoyo_number_union". + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr:  Pointer to "struct tomoyo_name_union". + * + * Returns nothing.   */ -static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, -				      const struct tomoyo_number_union *ptr) +static void tomoyo_print_name_union_quoted(struct tomoyo_io_buffer *head, +					   const struct tomoyo_name_union *ptr)  { -	tomoyo_set_space(head); -	if (ptr->is_group) { +	if (ptr->group) { +		tomoyo_set_string(head, "@"); +		tomoyo_set_string(head, ptr->group->group_name->name); +	} else { +		tomoyo_set_string(head, "\""); +		tomoyo_set_string(head, ptr->filename->name); +		tomoyo_set_string(head, "\""); +	} +} + +/** + * tomoyo_print_number_union_nospace - Print a tomoyo_number_union without a space. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr:  Pointer to "struct tomoyo_number_union". + * + * Returns nothing. + */ +static void tomoyo_print_number_union_nospace +(struct tomoyo_io_buffer *head, const struct tomoyo_number_union *ptr) +{ +	if (ptr->group) {  		tomoyo_set_string(head, "@");  		tomoyo_set_string(head, ptr->group->group_name->name);  	} else {  		int i;  		unsigned long min = ptr->values[0];  		const unsigned long max = ptr->values[1]; -		u8 min_type = ptr->min_type; -		const u8 max_type = ptr->max_type; +		u8 min_type = ptr->value_type[0]; +		const u8 max_type = ptr->value_type[1];  		char buffer[128];  		buffer[0] = '\0';  		for (i = 0; i < 2; i++) { @@ -232,8 +408,8 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,  						 "0%lo", min);  				break;  			default: -				tomoyo_addprintf(buffer, sizeof(buffer), -						 "%lu", min); +				tomoyo_addprintf(buffer, sizeof(buffer), "%lu", +						 min);  				break;  			}  			if (min == max && min_type == max_type) @@ -247,35 +423,53 @@ static void tomoyo_print_number_union(struct tomoyo_io_buffer *head,  }  /** + * tomoyo_print_number_union - Print a tomoyo_number_union. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * @ptr:  Pointer to "struct tomoyo_number_union". + * + * Returns nothing. + */ +static void tomoyo_print_number_union(struct tomoyo_io_buffer *head, +				      const struct tomoyo_number_union *ptr) +{ +	tomoyo_set_space(head); +	tomoyo_print_number_union_nospace(head, ptr); +} + +/**   * tomoyo_assign_profile - Create a new profile.   * + * @ns:      Pointer to "struct tomoyo_policy_namespace".   * @profile: Profile number to create.   *   * Returns pointer to "struct tomoyo_profile" on success, NULL otherwise.   */ -static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile) +static struct tomoyo_profile *tomoyo_assign_profile +(struct tomoyo_policy_namespace *ns, const unsigned int profile)  {  	struct tomoyo_profile *ptr;  	struct tomoyo_profile *entry;  	if (profile >= TOMOYO_MAX_PROFILES)  		return NULL; -	ptr = tomoyo_profile_ptr[profile]; +	ptr = ns->profile_ptr[profile];  	if (ptr)  		return ptr;  	entry = kzalloc(sizeof(*entry), GFP_NOFS);  	if (mutex_lock_interruptible(&tomoyo_policy_lock))  		goto out; -	ptr = tomoyo_profile_ptr[profile]; +	ptr = ns->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; +		ptr->default_config = TOMOYO_CONFIG_DISABLED | +			TOMOYO_CONFIG_WANT_GRANT_LOG | +			TOMOYO_CONFIG_WANT_REJECT_LOG;  		memset(ptr->config, TOMOYO_CONFIG_USE_DEFAULT,  		       sizeof(ptr->config)); +		ptr->pref[TOMOYO_PREF_MAX_AUDIT_LOG] = 1024; +		ptr->pref[TOMOYO_PREF_MAX_LEARNING_ENTRY] = 2048;  		mb(); /* Avoid out-of-order execution. */ -		tomoyo_profile_ptr[profile] = ptr; +		ns->profile_ptr[profile] = ptr;  		entry = NULL;  	}  	mutex_unlock(&tomoyo_policy_lock); @@ -287,19 +481,29 @@ static struct tomoyo_profile *tomoyo_assign_profile(const unsigned int profile)  /**   * tomoyo_profile - Find a profile.   * + * @ns:      Pointer to "struct tomoyo_policy_namespace".   * @profile: Profile number to find.   *   * Returns pointer to "struct tomoyo_profile".   */ -struct tomoyo_profile *tomoyo_profile(const u8 profile) +struct tomoyo_profile *tomoyo_profile(const struct tomoyo_policy_namespace *ns, +				      const u8 profile)  { -	struct tomoyo_profile *ptr = tomoyo_profile_ptr[profile]; -	if (!tomoyo_policy_loaded) -		return &tomoyo_default_profile; -	BUG_ON(!ptr); +	static struct tomoyo_profile tomoyo_null_profile; +	struct tomoyo_profile *ptr = ns->profile_ptr[profile]; +	if (!ptr) +		ptr = &tomoyo_null_profile;  	return ptr;  } +/** + * tomoyo_find_yesno - Find values for specified keyword. + * + * @string: String to check. + * @find:   Name of keyword. + * + * Returns 1 if "@find=yes" was found, 0 if "@find=no" was found, -1 otherwise. + */  static s8 tomoyo_find_yesno(const char *string, const char *find)  {  	const char *cp = strstr(string, find); @@ -313,18 +517,15 @@ static s8 tomoyo_find_yesno(const char *string, const char *find)  	return -1;  } -static void tomoyo_set_bool(bool *b, const char *string, const char *find) -{ -	switch (tomoyo_find_yesno(string, find)) { -	case 1: -		*b = true; -		break; -	case 0: -		*b = false; -		break; -	} -} - +/** + * tomoyo_set_uint - Set value for specified preference. + * + * @i:      Pointer to "unsigned int". + * @string: String to check. + * @find:   Name of keyword. + * + * Returns nothing. + */  static void tomoyo_set_uint(unsigned int *i, const char *string,  			    const char *find)  { @@ -333,51 +534,16 @@ static void tomoyo_set_uint(unsigned int *i, const char *string,  		sscanf(cp + strlen(find), "=%u", i);  } -static void tomoyo_set_pref(const char *name, const char *value, -			    const bool use_default, -			    struct tomoyo_profile *profile) -{ -	struct tomoyo_preference **pref; -	bool *verbose; -	if (!strcmp(name, "enforcing")) { -		if (use_default) { -			pref = &profile->enforcing; -			goto set_default; -		} -		profile->enforcing = &profile->preference; -		verbose = &profile->preference.enforcing_verbose; -		goto set_verbose; -	} -	if (!strcmp(name, "permissive")) { -		if (use_default) { -			pref = &profile->permissive; -			goto set_default; -		} -		profile->permissive = &profile->preference; -		verbose = &profile->preference.permissive_verbose; -		goto set_verbose; -	} -	if (!strcmp(name, "learning")) { -		if (use_default) { -			pref = &profile->learning; -			goto set_default; -		} -		profile->learning = &profile->preference; -		tomoyo_set_uint(&profile->preference.learning_max_entry, value, -			     "max_entry"); -		verbose = &profile->preference.learning_verbose; -		goto set_verbose; -	} -	return; - set_default: -	*pref = &tomoyo_default_profile.preference; -	return; - set_verbose: -	tomoyo_set_bool(verbose, value, "verbose"); -} - +/** + * tomoyo_set_mode - Set mode for specified profile. + * + * @name:    Name of functionality. + * @value:   Mode for @name. + * @profile: Pointer to "struct tomoyo_profile". + * + * Returns 0 on success, negative value otherwise. + */  static int tomoyo_set_mode(char *name, const char *value, -			   const bool use_default,  			   struct tomoyo_profile *profile)  {  	u8 i; @@ -389,7 +555,17 @@ static int tomoyo_set_mode(char *name, const char *value,  		config = 0;  		for (i = 0; i < TOMOYO_MAX_MAC_INDEX  			     + TOMOYO_MAX_MAC_CATEGORY_INDEX; i++) { -			if (strcmp(name, tomoyo_mac_keywords[i])) +			int len = 0; +			if (i < TOMOYO_MAX_MAC_INDEX) { +				const u8 c = tomoyo_index2category[i]; +				const char *category = +					tomoyo_category_keywords[c]; +				len = strlen(category); +				if (strncmp(name, category, len) || +				    name[len++] != ':' || name[len++] != ':') +					continue; +			} +			if (strcmp(name + len, tomoyo_mac_keywords[i]))  				continue;  			config = profile->config[i];  			break; @@ -399,7 +575,7 @@ static int tomoyo_set_mode(char *name, const char *value,  	} else {  		return -EINVAL;  	} -	if (use_default) { +	if (strstr(value, "use_default")) {  		config = TOMOYO_CONFIG_USE_DEFAULT;  	} else {  		u8 mode; @@ -410,6 +586,24 @@ static int tomoyo_set_mode(char *name, const char *value,  				 * 'config' from 'TOMOYO_CONFIG_USE_DEAFULT'.  				 */  				config = (config & ~7) | mode; +		if (config != TOMOYO_CONFIG_USE_DEFAULT) { +			switch (tomoyo_find_yesno(value, "grant_log")) { +			case 1: +				config |= TOMOYO_CONFIG_WANT_GRANT_LOG; +				break; +			case 0: +				config &= ~TOMOYO_CONFIG_WANT_GRANT_LOG; +				break; +			} +			switch (tomoyo_find_yesno(value, "reject_log")) { +			case 1: +				config |= TOMOYO_CONFIG_WANT_REJECT_LOG; +				break; +			case 0: +				config &= ~TOMOYO_CONFIG_WANT_REJECT_LOG; +				break; +			} +		}  	}  	if (i < TOMOYO_MAX_MAC_INDEX + TOMOYO_MAX_MAC_CATEGORY_INDEX)  		profile->config[i] = config; @@ -429,34 +623,22 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head)  {  	char *data = head->write_buf;  	unsigned int i; -	bool use_default = false;  	char *cp;  	struct tomoyo_profile *profile; -	if (sscanf(data, "PROFILE_VERSION=%u", &tomoyo_profile_version) == 1) +	if (sscanf(data, "PROFILE_VERSION=%u", &head->w.ns->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_assign_profile(i); -		if (!profile) -			return -EINVAL; -	} +	if (*cp != '-') +		return -EINVAL; +	data = cp + 1; +	profile = tomoyo_assign_profile(head->w.ns, i); +	if (!profile) +		return -EINVAL;  	cp = strchr(data, '=');  	if (!cp)  		return -EINVAL;  	*cp++ = '\0'; -	if (profile != &tomoyo_default_profile) -		use_default = strstr(cp, "use_default") != NULL; -	if (tomoyo_str_starts(&data, "PREFERENCE::")) { -		tomoyo_set_pref(data, cp, use_default, profile); -		return 0; -	} -	if (profile == &tomoyo_default_profile) -		return -EINVAL;  	if (!strcmp(data, "COMMENT")) {  		static DEFINE_SPINLOCK(lock);  		const struct tomoyo_path_info *new_comment @@ -471,77 +653,62 @@ static int tomoyo_write_profile(struct tomoyo_io_buffer *head)  		tomoyo_put_name(old_comment);  		return 0;  	} -	return tomoyo_set_mode(data, cp, use_default, profile); -} - -static void tomoyo_print_preference(struct tomoyo_io_buffer *head, -				    const int idx) -{ -	struct tomoyo_preference *pref = &tomoyo_default_profile.preference; -	const struct tomoyo_profile *profile = idx >= 0 ? -		tomoyo_profile_ptr[idx] : NULL; -	char buffer[16] = ""; -	if (profile) { -		buffer[sizeof(buffer) - 1] = '\0'; -		snprintf(buffer, sizeof(buffer) - 1, "%u-", idx); -	} -	if (profile) { -		pref = profile->learning; -		if (pref == &tomoyo_default_profile.preference) -			goto skip1; -	} -	tomoyo_io_printf(head, "%sPREFERENCE::%s={ " -			 "verbose=%s max_entry=%u }\n", -			 buffer, "learning", -			 tomoyo_yesno(pref->learning_verbose), -			 pref->learning_max_entry); - skip1: -	if (profile) { -		pref = profile->permissive; -		if (pref == &tomoyo_default_profile.preference) -			goto skip2; -	} -	tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n", -			 buffer, "permissive", -			 tomoyo_yesno(pref->permissive_verbose)); - skip2: -	if (profile) { -		pref = profile->enforcing; -		if (pref == &tomoyo_default_profile.preference) -			return; +	if (!strcmp(data, "PREFERENCE")) { +		for (i = 0; i < TOMOYO_MAX_PREF; i++) +			tomoyo_set_uint(&profile->pref[i], cp, +					tomoyo_pref_keywords[i]); +		return 0;  	} -	tomoyo_io_printf(head, "%sPREFERENCE::%s={ verbose=%s }\n", -			 buffer, "enforcing", -			 tomoyo_yesno(pref->enforcing_verbose)); +	return tomoyo_set_mode(data, cp, profile);  } +/** + * tomoyo_print_config - Print mode for specified functionality. + * + * @head:   Pointer to "struct tomoyo_io_buffer". + * @config: Mode for that functionality. + * + * Returns nothing. + * + * Caller prints functionality's name. + */  static void tomoyo_print_config(struct tomoyo_io_buffer *head, const u8 config)  { -	tomoyo_io_printf(head, "={ mode=%s }\n", tomoyo_mode[config & 3]); +	tomoyo_io_printf(head, "={ mode=%s grant_log=%s reject_log=%s }\n", +			 tomoyo_mode[config & 3], +			 tomoyo_yesno(config & TOMOYO_CONFIG_WANT_GRANT_LOG), +			 tomoyo_yesno(config & TOMOYO_CONFIG_WANT_REJECT_LOG));  }  /**   * tomoyo_read_profile - Read profile table.   *   * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing.   */  static void tomoyo_read_profile(struct tomoyo_io_buffer *head)  {  	u8 index; +	struct tomoyo_policy_namespace *ns = +		container_of(head->r.ns, typeof(*ns), namespace_list);  	const struct tomoyo_profile *profile; +	if (head->r.eof) +		return;   next:  	index = head->r.index; -	profile = tomoyo_profile_ptr[index]; +	profile = ns->profile_ptr[index];  	switch (head->r.step) {  	case 0: -		tomoyo_io_printf(head, "PROFILE_VERSION=%s\n", "20090903"); -		tomoyo_print_preference(head, -1); +		tomoyo_print_namespace(head); +		tomoyo_io_printf(head, "PROFILE_VERSION=%u\n", +				 ns->profile_version);  		head->r.step++;  		break;  	case 1:  		for ( ; head->r.index < TOMOYO_MAX_PROFILES;  		      head->r.index++) -			if (tomoyo_profile_ptr[head->r.index]) +			if (ns->profile_ptr[head->r.index])  				break;  		if (head->r.index == TOMOYO_MAX_PROFILES)  			return; @@ -549,16 +716,25 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)  		break;  	case 2:  		{ +			u8 i;  			const struct tomoyo_path_info *comment =  				profile->comment; +			tomoyo_print_namespace(head);  			tomoyo_io_printf(head, "%u-COMMENT=", index);  			tomoyo_set_string(head, comment ? comment->name : "");  			tomoyo_set_lf(head); +			tomoyo_io_printf(head, "%u-PREFERENCE={ ", index); +			for (i = 0; i < TOMOYO_MAX_PREF; i++) +				tomoyo_io_printf(head, "%s=%u ", +						 tomoyo_pref_keywords[i], +						 profile->pref[i]); +			tomoyo_set_string(head, "}\n");  			head->r.step++;  		}  		break;  	case 3:  		{ +			tomoyo_print_namespace(head);  			tomoyo_io_printf(head, "%u-%s", index, "CONFIG");  			tomoyo_print_config(head, profile->default_config);  			head->r.bit = 0; @@ -572,15 +748,22 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)  			const u8 config = profile->config[i];  			if (config == TOMOYO_CONFIG_USE_DEFAULT)  				continue; -			tomoyo_io_printf(head, "%u-%s%s", index, "CONFIG::", -					 tomoyo_mac_keywords[i]); +			tomoyo_print_namespace(head); +			if (i < TOMOYO_MAX_MAC_INDEX) +				tomoyo_io_printf(head, "%u-CONFIG::%s::%s", +						 index, +						 tomoyo_category_keywords +						 [tomoyo_index2category[i]], +						 tomoyo_mac_keywords[i]); +			else +				tomoyo_io_printf(head, "%u-CONFIG::%s", index, +						 tomoyo_mac_keywords[i]);  			tomoyo_print_config(head, config);  			head->r.bit++;  			break;  		}  		if (head->r.bit == TOMOYO_MAX_MAC_INDEX  		    + TOMOYO_MAX_MAC_CATEGORY_INDEX) { -			tomoyo_print_preference(head, index);  			head->r.index++;  			head->r.step = 1;  		} @@ -590,6 +773,14 @@ static void tomoyo_read_profile(struct tomoyo_io_buffer *head)  		goto next;  } +/** + * tomoyo_same_manager - Check for duplicated "struct tomoyo_manager" entry. + * + * @a: Pointer to "struct tomoyo_acl_head". + * @b: Pointer to "struct tomoyo_acl_head". + * + * Returns true if @a == @b, false otherwise. + */  static bool tomoyo_same_manager(const struct tomoyo_acl_head *a,  				const struct tomoyo_acl_head *b)  { @@ -611,8 +802,13 @@ static int tomoyo_update_manager_entry(const char *manager,  				       const bool is_delete)  {  	struct tomoyo_manager e = { }; -	int error; - +	struct tomoyo_acl_param param = { +		/* .ns = &tomoyo_kernel_namespace, */ +		.is_delete = is_delete, +		.list = &tomoyo_kernel_namespace. +		policy_list[TOMOYO_ID_MANAGER], +	}; +	int error = is_delete ? -ENOENT : -ENOMEM;  	if (tomoyo_domain_def(manager)) {  		if (!tomoyo_correct_domain(manager))  			return -EINVAL; @@ -622,12 +818,11 @@ static int tomoyo_update_manager_entry(const char *manager,  			return -EINVAL;  	}  	e.manager = tomoyo_get_name(manager); -	if (!e.manager) -		return -ENOMEM; -	error = tomoyo_update_policy(&e.head, sizeof(e), is_delete, -				     &tomoyo_policy_list[TOMOYO_ID_MANAGER], -				     tomoyo_same_manager); -	tomoyo_put_name(e.manager); +	if (e.manager) { +		error = tomoyo_update_policy(&e.head, sizeof(e), ¶m, +					     tomoyo_same_manager); +		tomoyo_put_name(e.manager); +	}  	return error;  } @@ -643,13 +838,12 @@ static int tomoyo_update_manager_entry(const char *manager,  static int tomoyo_write_manager(struct tomoyo_io_buffer *head)  {  	char *data = head->write_buf; -	bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE);  	if (!strcmp(data, "manage_by_non_root")) { -		tomoyo_manage_by_non_root = !is_delete; +		tomoyo_manage_by_non_root = !head->w.is_delete;  		return 0;  	} -	return tomoyo_update_manager_entry(data, is_delete); +	return tomoyo_update_manager_entry(data, head->w.is_delete);  }  /** @@ -663,8 +857,8 @@ static void tomoyo_read_manager(struct tomoyo_io_buffer *head)  {  	if (head->r.eof)  		return; -	list_for_each_cookie(head->r.acl, -			     &tomoyo_policy_list[TOMOYO_ID_MANAGER]) { +	list_for_each_cookie(head->r.acl, &tomoyo_kernel_namespace. +			     policy_list[TOMOYO_ID_MANAGER]) {  		struct tomoyo_manager *ptr =  			list_entry(head->r.acl, typeof(*ptr), head.list);  		if (ptr->head.is_deleted) @@ -697,8 +891,8 @@ static bool tomoyo_manager(void)  		return true;  	if (!tomoyo_manage_by_non_root && (task->cred->uid || task->cred->euid))  		return false; -	list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], -				head.list) { +	list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. +				policy_list[TOMOYO_ID_MANAGER], head.list) {  		if (!ptr->head.is_deleted && ptr->is_domain  		    && !tomoyo_pathcmp(domainname, ptr->manager)) {  			found = true; @@ -710,8 +904,8 @@ static bool tomoyo_manager(void)  	exe = tomoyo_get_exe();  	if (!exe)  		return false; -	list_for_each_entry_rcu(ptr, &tomoyo_policy_list[TOMOYO_ID_MANAGER], -				head.list) { +	list_for_each_entry_rcu(ptr, &tomoyo_kernel_namespace. +				policy_list[TOMOYO_ID_MANAGER], head.list) {  		if (!ptr->head.is_deleted && !ptr->is_domain  		    && !strcmp(exe, ptr->manager->name)) {  			found = true; @@ -732,7 +926,7 @@ static bool tomoyo_manager(void)  }  /** - * tomoyo_select_one - Parse select command. + * tomoyo_select_domain - Parse select command.   *   * @head: Pointer to "struct tomoyo_io_buffer".   * @data: String to parse. @@ -741,16 +935,15 @@ static bool tomoyo_manager(void)   *   * Caller holds tomoyo_read_lock().   */ -static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data) +static bool tomoyo_select_domain(struct tomoyo_io_buffer *head, +				 const char *data)  {  	unsigned int pid;  	struct tomoyo_domain_info *domain = NULL;  	bool global_pid = false; - -	if (!strcmp(data, "allow_execute")) { -		head->r.print_execute_only = true; -		return true; -	} +	if (strncmp(data, "select ", 7)) +		return false; +	data += 7;  	if (sscanf(data, "pid=%u", &pid) == 1 ||  	    (global_pid = true, sscanf(data, "global-pid=%u", &pid) == 1)) {  		struct task_struct *p; @@ -769,7 +962,7 @@ static bool tomoyo_select_one(struct tomoyo_io_buffer *head, const char *data)  			domain = tomoyo_find_domain(data + 7);  	} else  		return false; -	head->write_var1 = domain; +	head->w.domain = domain;  	/* Accessing read_buf is safe because head->io_sem is held. */  	if (!head->read_buf)  		return true; /* Do nothing if open(O_WRONLY). */ @@ -821,20 +1014,47 @@ static int tomoyo_delete_domain(char *domainname)  /**   * tomoyo_write_domain2 - Write domain policy.   * - * @head: Pointer to "struct tomoyo_io_buffer". + * @ns:        Pointer to "struct tomoyo_policy_namespace". + * @list:      Pointer to "struct list_head". + * @data:      Policy to be interpreted. + * @is_delete: True if it is a delete request.   *   * Returns 0 on success, negative value otherwise.   *   * Caller holds tomoyo_read_lock().   */ -static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain, +static int tomoyo_write_domain2(struct tomoyo_policy_namespace *ns, +				struct list_head *list, char *data,  				const bool is_delete)  { -	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_ALLOW_MOUNT)) -		return tomoyo_write_mount(data, domain, is_delete); -	return tomoyo_write_file(data, domain, is_delete); +	struct tomoyo_acl_param param = { +		.ns = ns, +		.list = list, +		.data = data, +		.is_delete = is_delete, +	}; +	static const struct { +		const char *keyword; +		int (*write) (struct tomoyo_acl_param *); +	} tomoyo_callback[1] = { +		{ "file ", tomoyo_write_file }, +	}; +	u8 i; +	for (i = 0; i < 1; i++) { +		if (!tomoyo_str_starts(¶m.data, +				       tomoyo_callback[i].keyword)) +			continue; +		return tomoyo_callback[i].write(¶m); +	} +	return -EINVAL;  } +/* String table for domain flags. */ +const char * const tomoyo_dif[TOMOYO_MAX_DOMAIN_INFO_FLAGS] = { +	[TOMOYO_DIF_QUOTA_WARNED]      = "quota_exceeded\n", +	[TOMOYO_DIF_TRANSITION_FAILED] = "transition_failed\n", +}; +  /**   * tomoyo_write_domain - Write domain policy.   * @@ -847,69 +1067,198 @@ static int tomoyo_write_domain2(char *data, struct tomoyo_domain_info *domain,  static int tomoyo_write_domain(struct tomoyo_io_buffer *head)  {  	char *data = head->write_buf; -	struct tomoyo_domain_info *domain = head->write_var1; -	bool is_delete = false; -	bool is_select = false; +	struct tomoyo_policy_namespace *ns; +	struct tomoyo_domain_info *domain = head->w.domain; +	const bool is_delete = head->w.is_delete; +	bool is_select = !is_delete && tomoyo_str_starts(&data, "select ");  	unsigned int profile; - -	if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE)) -		is_delete = true; -	else if (tomoyo_str_starts(&data, TOMOYO_KEYWORD_SELECT)) -		is_select = true; -	if (is_select && tomoyo_select_one(head, data)) -		return 0; -	/* Don't allow updating policies by non manager programs. */ -	if (!tomoyo_manager()) -		return -EPERM; -	if (tomoyo_domain_def(data)) { +	if (*data == '<') {  		domain = NULL;  		if (is_delete)  			tomoyo_delete_domain(data);  		else if (is_select)  			domain = tomoyo_find_domain(data);  		else -			domain = tomoyo_assign_domain(data, 0); -		head->write_var1 = domain; +			domain = tomoyo_assign_domain(data, false); +		head->w.domain = domain;  		return 0;  	}  	if (!domain)  		return -EINVAL; - -	if (sscanf(data, TOMOYO_KEYWORD_USE_PROFILE "%u", &profile) == 1 +	ns = domain->ns; +	if (sscanf(data, "use_profile %u", &profile) == 1  	    && profile < TOMOYO_MAX_PROFILES) { -		if (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded) +		if (!tomoyo_policy_loaded || ns->profile_ptr[profile])  			domain->profile = (u8) profile;  		return 0;  	} -	if (!strcmp(data, TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ)) { -		domain->ignore_global_allow_read = !is_delete; +	if (sscanf(data, "use_group %u\n", &profile) == 1 +	    && profile < TOMOYO_MAX_ACL_GROUPS) { +		if (!is_delete) +			domain->group = (u8) profile;  		return 0;  	} -	if (!strcmp(data, TOMOYO_KEYWORD_QUOTA_EXCEEDED)) { -		domain->quota_warned = !is_delete; -		return 0; -	} -	if (!strcmp(data, TOMOYO_KEYWORD_TRANSITION_FAILED)) { -		domain->transition_failed = !is_delete; +	for (profile = 0; profile < TOMOYO_MAX_DOMAIN_INFO_FLAGS; profile++) { +		const char *cp = tomoyo_dif[profile]; +		if (strncmp(data, cp, strlen(cp) - 1)) +			continue; +		domain->flags[profile] = !is_delete;  		return 0;  	} -	return tomoyo_write_domain2(data, domain, is_delete); +	return tomoyo_write_domain2(ns, &domain->acl_info_list, data, +				    is_delete);  }  /** - * tomoyo_fns - Find next set bit. + * tomoyo_print_condition - Print condition part.   * - * @perm: 8 bits value. - * @bit:  First bit to find. + * @head: Pointer to "struct tomoyo_io_buffer". + * @cond: Pointer to "struct tomoyo_condition".   * - * Returns next on-bit on success, 8 otherwise. + * Returns true on success, false otherwise.   */ -static u8 tomoyo_fns(const u8 perm, u8 bit) +static bool tomoyo_print_condition(struct tomoyo_io_buffer *head, +				   const struct tomoyo_condition *cond)  { -	for ( ; bit < 8; bit++) -		if (perm & (1 << bit)) +	switch (head->r.cond_step) { +	case 0: +		head->r.cond_index = 0; +		head->r.cond_step++; +		/* fall through */ +	case 1: +		{ +			const u16 condc = cond->condc; +			const struct tomoyo_condition_element *condp = +				(typeof(condp)) (cond + 1); +			const struct tomoyo_number_union *numbers_p = +				(typeof(numbers_p)) (condp + condc); +			const struct tomoyo_name_union *names_p = +				(typeof(names_p)) +				(numbers_p + cond->numbers_count); +			const struct tomoyo_argv *argv = +				(typeof(argv)) (names_p + cond->names_count); +			const struct tomoyo_envp *envp = +				(typeof(envp)) (argv + cond->argc); +			u16 skip; +			for (skip = 0; skip < head->r.cond_index; skip++) { +				const u8 left = condp->left; +				const u8 right = condp->right; +				condp++; +				switch (left) { +				case TOMOYO_ARGV_ENTRY: +					argv++; +					continue; +				case TOMOYO_ENVP_ENTRY: +					envp++; +					continue; +				case TOMOYO_NUMBER_UNION: +					numbers_p++; +					break; +				} +				switch (right) { +				case TOMOYO_NAME_UNION: +					names_p++; +					break; +				case TOMOYO_NUMBER_UNION: +					numbers_p++; +					break; +				} +			} +			while (head->r.cond_index < condc) { +				const u8 match = condp->equals; +				const u8 left = condp->left; +				const u8 right = condp->right; +				if (!tomoyo_flush(head)) +					return false; +				condp++; +				head->r.cond_index++; +				tomoyo_set_space(head); +				switch (left) { +				case TOMOYO_ARGV_ENTRY: +					tomoyo_io_printf(head, +							 "exec.argv[%lu]%s=\"", +							 argv->index, argv-> +							 is_not ? "!" : ""); +					tomoyo_set_string(head, +							  argv->value->name); +					tomoyo_set_string(head, "\""); +					argv++; +					continue; +				case TOMOYO_ENVP_ENTRY: +					tomoyo_set_string(head, +							  "exec.envp[\""); +					tomoyo_set_string(head, +							  envp->name->name); +					tomoyo_io_printf(head, "\"]%s=", envp-> +							 is_not ? "!" : ""); +					if (envp->value) { +						tomoyo_set_string(head, "\""); +						tomoyo_set_string(head, envp-> +								  value->name); +						tomoyo_set_string(head, "\""); +					} else { +						tomoyo_set_string(head, +								  "NULL"); +					} +					envp++; +					continue; +				case TOMOYO_NUMBER_UNION: +					tomoyo_print_number_union_nospace +						(head, numbers_p++); +					break; +				default: +					tomoyo_set_string(head, +					       tomoyo_condition_keyword[left]); +					break; +				} +				tomoyo_set_string(head, match ? "=" : "!="); +				switch (right) { +				case TOMOYO_NAME_UNION: +					tomoyo_print_name_union_quoted +						(head, names_p++); +					break; +				case TOMOYO_NUMBER_UNION: +					tomoyo_print_number_union_nospace +						(head, numbers_p++); +					break; +				default: +					tomoyo_set_string(head, +					  tomoyo_condition_keyword[right]); +					break; +				} +			} +		} +		head->r.cond_step++; +		/* fall through */ +	case 2: +		if (!tomoyo_flush(head))  			break; -	return bit; +		head->r.cond_step++; +		/* fall through */ +	case 3: +		tomoyo_set_lf(head); +		return true; +	} +	return false; +} + +/** + * tomoyo_set_group - Print "acl_group " header keyword and category name. + * + * @head:     Pointer to "struct tomoyo_io_buffer". + * @category: Category name. + * + * Returns nothing. + */ +static void tomoyo_set_group(struct tomoyo_io_buffer *head, +			     const char *category) +{ +	if (head->type == TOMOYO_EXCEPTIONPOLICY) { +		tomoyo_print_namespace(head); +		tomoyo_io_printf(head, "acl_group %u ", +				 head->r.acl_group_index); +	} +	tomoyo_set_string(head, category);  }  /** @@ -924,63 +1273,96 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,  			       struct tomoyo_acl_info *acl)  {  	const u8 acl_type = acl->type; +	bool first = true;  	u8 bit; +	if (head->r.print_cond_part) +		goto print_cond_part;  	if (acl->is_deleted)  		return true; - next: -	bit = head->r.bit;  	if (!tomoyo_flush(head))  		return false;  	else if (acl_type == TOMOYO_TYPE_PATH_ACL) {  		struct tomoyo_path_acl *ptr =  			container_of(acl, typeof(*ptr), head);  		const u16 perm = ptr->perm; -		for ( ; bit < TOMOYO_MAX_PATH_OPERATION; bit++) { +		for (bit = 0; bit < TOMOYO_MAX_PATH_OPERATION; bit++) {  			if (!(perm & (1 << bit)))  				continue; -			if (head->r.print_execute_only && +			if (head->r.print_transition_related_only &&  			    bit != TOMOYO_TYPE_EXECUTE)  				continue; -			/* Print "read/write" instead of "read" and "write". */ -			if ((bit == TOMOYO_TYPE_READ || -			     bit == TOMOYO_TYPE_WRITE) -			    && (perm & (1 << TOMOYO_TYPE_READ_WRITE))) -				continue; -			break; +			if (first) { +				tomoyo_set_group(head, "file "); +				first = false; +			} else { +				tomoyo_set_slash(head); +			} +			tomoyo_set_string(head, tomoyo_path_keyword[bit]);  		} -		if (bit >= TOMOYO_MAX_PATH_OPERATION) -			goto done; -		tomoyo_io_printf(head, "allow_%s", tomoyo_path_keyword[bit]); +		if (first) +			return true;  		tomoyo_print_name_union(head, &ptr->name); -	} else if (head->r.print_execute_only) { +	} else if (head->r.print_transition_related_only) {  		return true;  	} else if (acl_type == TOMOYO_TYPE_PATH2_ACL) {  		struct tomoyo_path2_acl *ptr =  			container_of(acl, typeof(*ptr), head); -		bit = tomoyo_fns(ptr->perm, bit); -		if (bit >= TOMOYO_MAX_PATH2_OPERATION) -			goto done; -		tomoyo_io_printf(head, "allow_%s", tomoyo_path2_keyword[bit]); +		const u8 perm = ptr->perm; +		for (bit = 0; bit < TOMOYO_MAX_PATH2_OPERATION; bit++) { +			if (!(perm & (1 << bit))) +				continue; +			if (first) { +				tomoyo_set_group(head, "file "); +				first = false; +			} else { +				tomoyo_set_slash(head); +			} +			tomoyo_set_string(head, tomoyo_mac_keywords +					  [tomoyo_pp2mac[bit]]); +		} +		if (first) +			return true;  		tomoyo_print_name_union(head, &ptr->name1);  		tomoyo_print_name_union(head, &ptr->name2);  	} else if (acl_type == TOMOYO_TYPE_PATH_NUMBER_ACL) {  		struct tomoyo_path_number_acl *ptr =  			container_of(acl, typeof(*ptr), head); -		bit = tomoyo_fns(ptr->perm, bit); -		if (bit >= TOMOYO_MAX_PATH_NUMBER_OPERATION) -			goto done; -		tomoyo_io_printf(head, "allow_%s", -				 tomoyo_path_number_keyword[bit]); +		const u8 perm = ptr->perm; +		for (bit = 0; bit < TOMOYO_MAX_PATH_NUMBER_OPERATION; bit++) { +			if (!(perm & (1 << bit))) +				continue; +			if (first) { +				tomoyo_set_group(head, "file "); +				first = false; +			} else { +				tomoyo_set_slash(head); +			} +			tomoyo_set_string(head, tomoyo_mac_keywords +					  [tomoyo_pn2mac[bit]]); +		} +		if (first) +			return true;  		tomoyo_print_name_union(head, &ptr->name);  		tomoyo_print_number_union(head, &ptr->number);  	} else if (acl_type == TOMOYO_TYPE_MKDEV_ACL) {  		struct tomoyo_mkdev_acl *ptr =  			container_of(acl, typeof(*ptr), head); -		bit = tomoyo_fns(ptr->perm, bit); -		if (bit >= TOMOYO_MAX_MKDEV_OPERATION) -			goto done; -		tomoyo_io_printf(head, "allow_%s", tomoyo_mkdev_keyword[bit]); +		const u8 perm = ptr->perm; +		for (bit = 0; bit < TOMOYO_MAX_MKDEV_OPERATION; bit++) { +			if (!(perm & (1 << bit))) +				continue; +			if (first) { +				tomoyo_set_group(head, "file "); +				first = false; +			} else { +				tomoyo_set_slash(head); +			} +			tomoyo_set_string(head, tomoyo_mac_keywords +					  [tomoyo_pnnn2mac[bit]]); +		} +		if (first) +			return true;  		tomoyo_print_name_union(head, &ptr->name);  		tomoyo_print_number_union(head, &ptr->mode);  		tomoyo_print_number_union(head, &ptr->major); @@ -988,35 +1370,41 @@ static bool tomoyo_print_entry(struct tomoyo_io_buffer *head,  	} else if (acl_type == TOMOYO_TYPE_MOUNT_ACL) {  		struct tomoyo_mount_acl *ptr =  			container_of(acl, typeof(*ptr), head); -		tomoyo_io_printf(head, "allow_mount"); +		tomoyo_set_group(head, "file mount");  		tomoyo_print_name_union(head, &ptr->dev_name);  		tomoyo_print_name_union(head, &ptr->dir_name);  		tomoyo_print_name_union(head, &ptr->fs_type);  		tomoyo_print_number_union(head, &ptr->flags);  	} -	head->r.bit = bit + 1; -	tomoyo_io_printf(head, "\n"); -	if (acl_type != TOMOYO_TYPE_MOUNT_ACL) -		goto next; - done: -	head->r.bit = 0; +	if (acl->cond) { +		head->r.print_cond_part = true; +		head->r.cond_step = 0; +		if (!tomoyo_flush(head)) +			return false; +print_cond_part: +		if (!tomoyo_print_condition(head, acl->cond)) +			return false; +		head->r.print_cond_part = false; +	} else { +		tomoyo_set_lf(head); +	}  	return true;  }  /**   * tomoyo_read_domain2 - Read domain policy.   * - * @head:   Pointer to "struct tomoyo_io_buffer". - * @domain: Pointer to "struct tomoyo_domain_info". + * @head: Pointer to "struct tomoyo_io_buffer". + * @list: Pointer to "struct list_head".   *   * Caller holds tomoyo_read_lock().   *   * Returns true on success, false otherwise.   */  static bool tomoyo_read_domain2(struct tomoyo_io_buffer *head, -				struct tomoyo_domain_info *domain) +				struct list_head *list)  { -	list_for_each_cookie(head->r.acl, &domain->acl_info_list) { +	list_for_each_cookie(head->r.acl, list) {  		struct tomoyo_acl_info *ptr =  			list_entry(head->r.acl, typeof(*ptr), list);  		if (!tomoyo_print_entry(head, ptr)) @@ -1041,6 +1429,7 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)  		struct tomoyo_domain_info *domain =  			list_entry(head->r.domain, typeof(*domain), list);  		switch (head->r.step) { +			u8 i;  		case 0:  			if (domain->is_deleted &&  			    !head->r.print_this_domain_only) @@ -1048,22 +1437,18 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)  			/* Print domainname and flags. */  			tomoyo_set_string(head, domain->domainname->name);  			tomoyo_set_lf(head); -			tomoyo_io_printf(head, -					 TOMOYO_KEYWORD_USE_PROFILE "%u\n", +			tomoyo_io_printf(head, "use_profile %u\n",  					 domain->profile); -			if (domain->quota_warned) -				tomoyo_set_string(head, "quota_exceeded\n"); -			if (domain->transition_failed) -				tomoyo_set_string(head, "transition_failed\n"); -			if (domain->ignore_global_allow_read) -				tomoyo_set_string(head, -				       TOMOYO_KEYWORD_IGNORE_GLOBAL_ALLOW_READ -						  "\n"); +			tomoyo_io_printf(head, "use_group %u\n", +					 domain->group); +			for (i = 0; i < TOMOYO_MAX_DOMAIN_INFO_FLAGS; i++) +				if (domain->flags[i]) +					tomoyo_set_string(head, tomoyo_dif[i]);  			head->r.step++;  			tomoyo_set_lf(head);  			/* fall through */  		case 1: -			if (!tomoyo_read_domain2(head, domain)) +			if (!tomoyo_read_domain2(head, &domain->acl_info_list))  				return;  			head->r.step++;  			if (!tomoyo_set_lf(head)) @@ -1080,73 +1465,6 @@ static void tomoyo_read_domain(struct tomoyo_io_buffer *head)  }  /** - * tomoyo_write_domain_profile - Assign profile for specified domain. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns 0 on success, -EINVAL otherwise. - * - * This is equivalent to doing - * - *     ( echo "select " $domainname; echo "use_profile " $profile ) | - *     /usr/sbin/tomoyo-loadpolicy -d - * - * Caller holds tomoyo_read_lock(). - */ -static int tomoyo_write_domain_profile(struct tomoyo_io_buffer *head) -{ -	char *data = head->write_buf; -	char *cp = strchr(data, ' '); -	struct tomoyo_domain_info *domain; -	unsigned long profile; - -	if (!cp) -		return -EINVAL; -	*cp = '\0'; -	domain = tomoyo_find_domain(cp + 1); -	if (strict_strtoul(data, 10, &profile)) -		return -EINVAL; -	if (domain && profile < TOMOYO_MAX_PROFILES -	    && (tomoyo_profile_ptr[profile] || !tomoyo_policy_loaded)) -		domain->profile = (u8) profile; -	return 0; -} - -/** - * tomoyo_read_domain_profile - Read only domainname and profile. - * - * @head: Pointer to "struct tomoyo_io_buffer". - * - * Returns list of profile number and domainname pairs. - * - * This is equivalent to doing - * - *     grep -A 1 '^<kernel>' /sys/kernel/security/tomoyo/domain_policy | - *     awk ' { if ( domainname == "" ) { if ( $1 == "<kernel>" ) - *     domainname = $0; } else if ( $1 == "use_profile" ) { - *     print $2 " " domainname; domainname = ""; } } ; ' - * - * Caller holds tomoyo_read_lock(). - */ -static void tomoyo_read_domain_profile(struct tomoyo_io_buffer *head) -{ -	if (head->r.eof) -		return; -	list_for_each_cookie(head->r.domain, &tomoyo_domain_list) { -		struct tomoyo_domain_info *domain = -			list_entry(head->r.domain, typeof(*domain), list); -		if (domain->is_deleted) -			continue; -		if (!tomoyo_flush(head)) -			return; -		tomoyo_io_printf(head, "%u ", domain->profile); -		tomoyo_set_string(head, domain->domainname->name); -		tomoyo_set_lf(head); -	} -	head->r.eof = true; -} - -/**   * tomoyo_write_pid: Specify PID to obtain domainname.   *   * @head: Pointer to "struct tomoyo_io_buffer". @@ -1204,18 +1522,20 @@ static void tomoyo_read_pid(struct tomoyo_io_buffer *head)  	tomoyo_set_string(head, domain->domainname->name);  } +/* String table for domain transition control keywords. */  static const char *tomoyo_transition_type[TOMOYO_MAX_TRANSITION_TYPE] = { -	[TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] -	= TOMOYO_KEYWORD_NO_INITIALIZE_DOMAIN, -	[TOMOYO_TRANSITION_CONTROL_INITIALIZE] -	= TOMOYO_KEYWORD_INITIALIZE_DOMAIN, -	[TOMOYO_TRANSITION_CONTROL_NO_KEEP] = TOMOYO_KEYWORD_NO_KEEP_DOMAIN, -	[TOMOYO_TRANSITION_CONTROL_KEEP] = TOMOYO_KEYWORD_KEEP_DOMAIN +	[TOMOYO_TRANSITION_CONTROL_NO_RESET]      = "no_reset_domain ", +	[TOMOYO_TRANSITION_CONTROL_RESET]         = "reset_domain ", +	[TOMOYO_TRANSITION_CONTROL_NO_INITIALIZE] = "no_initialize_domain ", +	[TOMOYO_TRANSITION_CONTROL_INITIALIZE]    = "initialize_domain ", +	[TOMOYO_TRANSITION_CONTROL_NO_KEEP]       = "no_keep_domain ", +	[TOMOYO_TRANSITION_CONTROL_KEEP]          = "keep_domain ",  }; +/* String table for grouping keywords. */  static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = { -	[TOMOYO_PATH_GROUP] = TOMOYO_KEYWORD_PATH_GROUP, -	[TOMOYO_NUMBER_GROUP] = TOMOYO_KEYWORD_NUMBER_GROUP +	[TOMOYO_PATH_GROUP]   = "path_group ", +	[TOMOYO_NUMBER_GROUP] = "number_group ",  };  /** @@ -1229,29 +1549,30 @@ static const char *tomoyo_group_name[TOMOYO_MAX_GROUP] = {   */  static int tomoyo_write_exception(struct tomoyo_io_buffer *head)  { -	char *data = head->write_buf; -	bool is_delete = tomoyo_str_starts(&data, TOMOYO_KEYWORD_DELETE); -	u8 i; -	static const struct { -		const char *keyword; -		int (*write) (char *, const bool); -	} tomoyo_callback[4] = { -		{ TOMOYO_KEYWORD_AGGREGATOR, tomoyo_write_aggregator }, -		{ TOMOYO_KEYWORD_FILE_PATTERN, tomoyo_write_pattern }, -		{ TOMOYO_KEYWORD_DENY_REWRITE, tomoyo_write_no_rewrite }, -		{ TOMOYO_KEYWORD_ALLOW_READ, tomoyo_write_globally_readable }, +	const bool is_delete = head->w.is_delete; +	struct tomoyo_acl_param param = { +		.ns = head->w.ns, +		.is_delete = is_delete, +		.data = head->write_buf,  	}; - +	u8 i; +	if (tomoyo_str_starts(¶m.data, "aggregator ")) +		return tomoyo_write_aggregator(¶m);  	for (i = 0; i < TOMOYO_MAX_TRANSITION_TYPE; i++) -		if (tomoyo_str_starts(&data, tomoyo_transition_type[i])) -			return tomoyo_write_transition_control(data, is_delete, -							       i); -	for (i = 0; i < 4; i++) -		if (tomoyo_str_starts(&data, tomoyo_callback[i].keyword)) -			return tomoyo_callback[i].write(data, is_delete); +		if (tomoyo_str_starts(¶m.data, tomoyo_transition_type[i])) +			return tomoyo_write_transition_control(¶m, i);  	for (i = 0; i < TOMOYO_MAX_GROUP; i++) -		if (tomoyo_str_starts(&data, tomoyo_group_name[i])) -			return tomoyo_write_group(data, is_delete, i); +		if (tomoyo_str_starts(¶m.data, tomoyo_group_name[i])) +			return tomoyo_write_group(¶m, i); +	if (tomoyo_str_starts(¶m.data, "acl_group ")) { +		unsigned int group; +		char *data; +		group = simple_strtoul(param.data, &data, 10); +		if (group < TOMOYO_MAX_ACL_GROUPS && *data++ == ' ') +			return tomoyo_write_domain2 +				(head->w.ns, &head->w.ns->acl_group[group], +				 data, is_delete); +	}  	return -EINVAL;  } @@ -1267,9 +1588,12 @@ static int tomoyo_write_exception(struct tomoyo_io_buffer *head)   */  static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)  { -	list_for_each_cookie(head->r.group, &tomoyo_group_list[idx]) { +	struct tomoyo_policy_namespace *ns = +		container_of(head->r.ns, typeof(*ns), namespace_list); +	struct list_head *list = &ns->group_list[idx]; +	list_for_each_cookie(head->r.group, list) {  		struct tomoyo_group *group = -			list_entry(head->r.group, typeof(*group), list); +			list_entry(head->r.group, typeof(*group), head.list);  		list_for_each_cookie(head->r.acl, &group->member_list) {  			struct tomoyo_acl_head *ptr =  				list_entry(head->r.acl, typeof(*ptr), list); @@ -1277,6 +1601,7 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)  				continue;  			if (!tomoyo_flush(head))  				return false; +			tomoyo_print_namespace(head);  			tomoyo_set_string(head, tomoyo_group_name[idx]);  			tomoyo_set_string(head, group->group_name->name);  			if (idx == TOMOYO_PATH_GROUP) { @@ -1310,7 +1635,10 @@ static bool tomoyo_read_group(struct tomoyo_io_buffer *head, const int idx)   */  static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)  { -	list_for_each_cookie(head->r.acl, &tomoyo_policy_list[idx]) { +	struct tomoyo_policy_namespace *ns = +		container_of(head->r.ns, typeof(*ns), namespace_list); +	struct list_head *list = &ns->policy_list[idx]; +	list_for_each_cookie(head->r.acl, list) {  		struct tomoyo_acl_head *acl =  			container_of(head->r.acl, typeof(*acl), list);  		if (acl->is_deleted) @@ -1322,35 +1650,23 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)  			{  				struct tomoyo_transition_control *ptr =  					container_of(acl, typeof(*ptr), head); -				tomoyo_set_string(head, -						  tomoyo_transition_type +				tomoyo_print_namespace(head); +				tomoyo_set_string(head, tomoyo_transition_type  						  [ptr->type]); -				if (ptr->program) -					tomoyo_set_string(head, -							  ptr->program->name); -				if (ptr->program && ptr->domainname) -					tomoyo_set_string(head, " from "); -				if (ptr->domainname) -					tomoyo_set_string(head, -							  ptr->domainname-> -							  name); -			} -			break; -		case TOMOYO_ID_GLOBALLY_READABLE: -			{ -				struct tomoyo_readable_file *ptr = -					container_of(acl, typeof(*ptr), head); -				tomoyo_set_string(head, -						  TOMOYO_KEYWORD_ALLOW_READ); -				tomoyo_set_string(head, ptr->filename->name); +				tomoyo_set_string(head, ptr->program ? +						  ptr->program->name : "any"); +				tomoyo_set_string(head, " from "); +				tomoyo_set_string(head, ptr->domainname ? +						  ptr->domainname->name : +						  "any");  			}  			break;  		case TOMOYO_ID_AGGREGATOR:  			{  				struct tomoyo_aggregator *ptr =  					container_of(acl, typeof(*ptr), head); -				tomoyo_set_string(head, -						  TOMOYO_KEYWORD_AGGREGATOR); +				tomoyo_print_namespace(head); +				tomoyo_set_string(head, "aggregator ");  				tomoyo_set_string(head,  						  ptr->original_name->name);  				tomoyo_set_space(head); @@ -1358,24 +1674,6 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)  					       ptr->aggregated_name->name);  			}  			break; -		case TOMOYO_ID_PATTERN: -			{ -				struct tomoyo_no_pattern *ptr = -					container_of(acl, typeof(*ptr), head); -				tomoyo_set_string(head, -						  TOMOYO_KEYWORD_FILE_PATTERN); -				tomoyo_set_string(head, ptr->pattern->name); -			} -			break; -		case TOMOYO_ID_NO_REWRITE: -			{ -				struct tomoyo_no_rewrite *ptr = -					container_of(acl, typeof(*ptr), head); -				tomoyo_set_string(head, -						  TOMOYO_KEYWORD_DENY_REWRITE); -				tomoyo_set_string(head, ptr->pattern->name); -			} -			break;  		default:  			continue;  		} @@ -1394,6 +1692,8 @@ static bool tomoyo_read_policy(struct tomoyo_io_buffer *head, const int idx)   */  static void tomoyo_read_exception(struct tomoyo_io_buffer *head)  { +	struct tomoyo_policy_namespace *ns = +		container_of(head->r.ns, typeof(*ns), namespace_list);  	if (head->r.eof)  		return;  	while (head->r.step < TOMOYO_MAX_POLICY && @@ -1406,95 +1706,40 @@ static void tomoyo_read_exception(struct tomoyo_io_buffer *head)  		head->r.step++;  	if (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP)  		return; +	while (head->r.step < TOMOYO_MAX_POLICY + TOMOYO_MAX_GROUP +	       + TOMOYO_MAX_ACL_GROUPS) { +		head->r.acl_group_index = head->r.step - TOMOYO_MAX_POLICY +			- TOMOYO_MAX_GROUP; +		if (!tomoyo_read_domain2(head, &ns->acl_group +					 [head->r.acl_group_index])) +			return; +		head->r.step++; +	}  	head->r.eof = true;  } -/** - * tomoyo_print_header - Get header line of audit log. - * - * @r: Pointer to "struct tomoyo_request_info". - * - * Returns string representation. - * - * This function uses kmalloc(), so caller must kfree() if this function - * didn't return NULL. - */ -static char *tomoyo_print_header(struct tomoyo_request_info *r) -{ -	struct timeval tv; -	const pid_t gpid = task_pid_nr(current); -	static const int tomoyo_buffer_len = 4096; -	char *buffer = kmalloc(tomoyo_buffer_len, GFP_NOFS); -	pid_t ppid; -	if (!buffer) -		return NULL; -	do_gettimeofday(&tv); -	rcu_read_lock(); -	ppid = task_tgid_vnr(current->real_parent); -	rcu_read_unlock(); -	snprintf(buffer, tomoyo_buffer_len - 1, -		 "#timestamp=%lu profile=%u mode=%s (global-pid=%u)" -		 " task={ pid=%u ppid=%u uid=%u gid=%u euid=%u" -		 " egid=%u suid=%u sgid=%u fsuid=%u fsgid=%u }", -		 tv.tv_sec, r->profile, tomoyo_mode[r->mode], gpid, -		 task_tgid_vnr(current), ppid, -		 current_uid(), current_gid(), current_euid(), -		 current_egid(), current_suid(), current_sgid(), -		 current_fsuid(), current_fsgid()); -	return buffer; -} - -/** - * tomoyo_init_audit_log - Allocate buffer for audit logs. - * - * @len: Required size. - * @r:   Pointer to "struct tomoyo_request_info". - * - * Returns pointer to allocated memory. - * - * The @len is updated to add the header lines' size on success. - * - * This function uses kzalloc(), so caller must kfree() if this function - * didn't return NULL. - */ -static char *tomoyo_init_audit_log(int *len, struct tomoyo_request_info *r) -{ -	char *buf = NULL; -	const char *header; -	const char *domainname; -	if (!r->domain) -		r->domain = tomoyo_domain(); -	domainname = r->domain->domainname->name; -	header = tomoyo_print_header(r); -	if (!header) -		return NULL; -	*len += strlen(domainname) + strlen(header) + 10; -	buf = kzalloc(*len, GFP_NOFS); -	if (buf) -		snprintf(buf, (*len) - 1, "%s\n%s\n", header, domainname); -	kfree(header); -	return buf; -} - -/* Wait queue for tomoyo_query_list. */ +/* Wait queue for kernel -> userspace notification. */  static DECLARE_WAIT_QUEUE_HEAD(tomoyo_query_wait); - -/* Lock for manipulating tomoyo_query_list. */ -static DEFINE_SPINLOCK(tomoyo_query_list_lock); +/* Wait queue for userspace -> kernel notification. */ +static DECLARE_WAIT_QUEUE_HEAD(tomoyo_answer_wait);  /* Structure for query. */  struct tomoyo_query {  	struct list_head list;  	char *query; -	int query_len; +	size_t query_len;  	unsigned int serial; -	int timer; -	int answer; +	u8 timer; +	u8 answer; +	u8 retry;  };  /* The list for "struct tomoyo_query". */  static LIST_HEAD(tomoyo_query_list); +/* Lock for manipulating tomoyo_query_list. */ +static DEFINE_SPINLOCK(tomoyo_query_list_lock); +  /*   * Number of "struct file" referring /sys/kernel/security/tomoyo/query   * interface. @@ -1502,10 +1747,82 @@ static LIST_HEAD(tomoyo_query_list);  static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);  /** + * tomoyo_truncate - Truncate a line. + * + * @str: String to truncate. + * + * Returns length of truncated @str. + */ +static int tomoyo_truncate(char *str) +{ +	char *start = str; +	while (*(unsigned char *) str > (unsigned char) ' ') +		str++; +	*str = '\0'; +	return strlen(start) + 1; +} + +/** + * tomoyo_add_entry - Add an ACL to current thread's domain. Used by learning mode. + * + * @domain: Pointer to "struct tomoyo_domain_info". + * @header: Lines containing ACL. + * + * Returns nothing. + */ +static void tomoyo_add_entry(struct tomoyo_domain_info *domain, char *header) +{ +	char *buffer; +	char *realpath = NULL; +	char *argv0 = NULL; +	char *symlink = NULL; +	char *cp = strchr(header, '\n'); +	int len; +	if (!cp) +		return; +	cp = strchr(cp + 1, '\n'); +	if (!cp) +		return; +	*cp++ = '\0'; +	len = strlen(cp) + 1; +	/* strstr() will return NULL if ordering is wrong. */ +	if (*cp == 'f') { +		argv0 = strstr(header, " argv[]={ \""); +		if (argv0) { +			argv0 += 10; +			len += tomoyo_truncate(argv0) + 14; +		} +		realpath = strstr(header, " exec={ realpath=\""); +		if (realpath) { +			realpath += 8; +			len += tomoyo_truncate(realpath) + 6; +		} +		symlink = strstr(header, " symlink.target=\""); +		if (symlink) +			len += tomoyo_truncate(symlink + 1) + 1; +	} +	buffer = kmalloc(len, GFP_NOFS); +	if (!buffer) +		return; +	snprintf(buffer, len - 1, "%s", cp); +	if (realpath) +		tomoyo_addprintf(buffer, len, " exec.%s", realpath); +	if (argv0) +		tomoyo_addprintf(buffer, len, " exec.argv[0]=%s", argv0); +	if (symlink) +		tomoyo_addprintf(buffer, len, "%s", symlink); +	tomoyo_normalize_line(buffer); +	if (!tomoyo_write_domain2(domain->ns, &domain->acl_info_list, buffer, +				  false)) +		tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES); +	kfree(buffer); +} + +/**   * tomoyo_supervisor - Ask for the supervisor's decision.   * - * @r:       Pointer to "struct tomoyo_request_info". - * @fmt:     The printf()'s format string, followed by parameters. + * @r:   Pointer to "struct tomoyo_request_info". + * @fmt: The printf()'s format string, followed by parameters.   *   * Returns 0 if the supervisor decided to permit the access request which   * violated the policy in enforcing mode, TOMOYO_RETRY_REQUEST if the @@ -1515,88 +1832,79 @@ static atomic_t tomoyo_query_observers = ATOMIC_INIT(0);  int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)  {  	va_list args; -	int error = -EPERM; -	int pos; +	int error;  	int len;  	static unsigned int tomoyo_serial; -	struct tomoyo_query *entry = NULL; +	struct tomoyo_query entry = { };  	bool quota_exceeded = false; -	char *header; +	va_start(args, fmt); +	len = vsnprintf((char *) &len, 1, fmt, args) + 1; +	va_end(args); +	/* Write /sys/kernel/security/tomoyo/audit. */ +	va_start(args, fmt); +	tomoyo_write_log2(r, len, fmt, args); +	va_end(args); +	/* Nothing more to do if granted. */ +	if (r->granted) +		return 0; +	if (r->mode) +		tomoyo_update_stat(r->mode);  	switch (r->mode) { -		char *buffer; +	case TOMOYO_CONFIG_ENFORCING: +		error = -EPERM; +		if (atomic_read(&tomoyo_query_observers)) +			break; +		goto out;  	case TOMOYO_CONFIG_LEARNING: -		if (!tomoyo_domain_quota_is_ok(r)) -			return 0; -		va_start(args, fmt); -		len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 4; -		va_end(args); -		buffer = kmalloc(len, GFP_NOFS); -		if (!buffer) -			return 0; -		va_start(args, fmt); -		vsnprintf(buffer, len - 1, fmt, args); -		va_end(args); -		tomoyo_normalize_line(buffer); -		tomoyo_write_domain2(buffer, r->domain, false); -		kfree(buffer); +		error = 0; +		/* Check max_learning_entry parameter. */ +		if (tomoyo_domain_quota_is_ok(r)) +			break;  		/* fall through */ -	case TOMOYO_CONFIG_PERMISSIVE: +	default:  		return 0;  	} -	if (!r->domain) -		r->domain = tomoyo_domain(); -	if (!atomic_read(&tomoyo_query_observers)) -		return -EPERM; +	/* Get message. */  	va_start(args, fmt); -	len = vsnprintf((char *) &pos, sizeof(pos) - 1, fmt, args) + 32; +	entry.query = tomoyo_init_log(r, len, fmt, args);  	va_end(args); -	header = tomoyo_init_audit_log(&len, r); -	if (!header) +	if (!entry.query)  		goto out; -	entry = kzalloc(sizeof(*entry), GFP_NOFS); -	if (!entry) +	entry.query_len = strlen(entry.query) + 1; +	if (!error) { +		tomoyo_add_entry(r->domain, entry.query);  		goto out; -	entry->query = kzalloc(len, GFP_NOFS); -	if (!entry->query) -		goto out; -	len = ksize(entry->query); +	} +	len = tomoyo_round2(entry.query_len);  	spin_lock(&tomoyo_query_list_lock); -	if (tomoyo_quota_for_query && tomoyo_query_memory_size + len + -	    sizeof(*entry) >= tomoyo_quota_for_query) { +	if (tomoyo_memory_quota[TOMOYO_MEMORY_QUERY] && +	    tomoyo_memory_used[TOMOYO_MEMORY_QUERY] + len +	    >= tomoyo_memory_quota[TOMOYO_MEMORY_QUERY]) {  		quota_exceeded = true;  	} else { -		tomoyo_query_memory_size += len + sizeof(*entry); -		entry->serial = tomoyo_serial++; +		entry.serial = tomoyo_serial++; +		entry.retry = r->retry; +		tomoyo_memory_used[TOMOYO_MEMORY_QUERY] += len; +		list_add_tail(&entry.list, &tomoyo_query_list);  	}  	spin_unlock(&tomoyo_query_list_lock);  	if (quota_exceeded)  		goto out; -	pos = snprintf(entry->query, len - 1, "Q%u-%hu\n%s", -		       entry->serial, r->retry, header); -	kfree(header); -	header = NULL; -	va_start(args, fmt); -	vsnprintf(entry->query + pos, len - 1 - pos, fmt, args); -	entry->query_len = strlen(entry->query) + 1; -	va_end(args); -	spin_lock(&tomoyo_query_list_lock); -	list_add_tail(&entry->list, &tomoyo_query_list); -	spin_unlock(&tomoyo_query_list_lock);  	/* Give 10 seconds for supervisor's opinion. */ -	for (entry->timer = 0; -	     atomic_read(&tomoyo_query_observers) && entry->timer < 100; -	     entry->timer++) { -		wake_up(&tomoyo_query_wait); -		set_current_state(TASK_INTERRUPTIBLE); -		schedule_timeout(HZ / 10); -		if (entry->answer) +	while (entry.timer < 10) { +		wake_up_all(&tomoyo_query_wait); +		if (wait_event_interruptible_timeout +		    (tomoyo_answer_wait, entry.answer || +		     !atomic_read(&tomoyo_query_observers), HZ))  			break; +		else +			entry.timer++;  	}  	spin_lock(&tomoyo_query_list_lock); -	list_del(&entry->list); -	tomoyo_query_memory_size -= len + sizeof(*entry); +	list_del(&entry.list); +	tomoyo_memory_used[TOMOYO_MEMORY_QUERY] -= len;  	spin_unlock(&tomoyo_query_list_lock); -	switch (entry->answer) { +	switch (entry.answer) {  	case 3: /* Asked to retry by administrator. */  		error = TOMOYO_RETRY_REQUEST;  		r->retry++; @@ -1605,18 +1913,12 @@ int tomoyo_supervisor(struct tomoyo_request_info *r, const char *fmt, ...)  		/* Granted by administrator. */  		error = 0;  		break; -	case 0: -		/* Timed out. */ -		break;  	default: -		/* Rejected by administrator. */ +		/* Timed out or rejected by administrator. */  		break;  	} - out: -	if (entry) -		kfree(entry->query); -	kfree(entry); -	kfree(header); +out: +	kfree(entry.query);  	return error;  } @@ -1663,8 +1965,8 @@ static int tomoyo_poll_query(struct file *file, poll_table *wait)  static void tomoyo_read_query(struct tomoyo_io_buffer *head)  {  	struct list_head *tmp; -	int pos = 0; -	int len = 0; +	unsigned int pos = 0; +	size_t len = 0;  	char *buf;  	if (head->r.w_pos)  		return; @@ -1687,7 +1989,7 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head)  		head->r.query_index = 0;  		return;  	} -	buf = kzalloc(len, GFP_NOFS); +	buf = kzalloc(len + 32, GFP_NOFS);  	if (!buf)  		return;  	pos = 0; @@ -1703,7 +2005,8 @@ static void tomoyo_read_query(struct tomoyo_io_buffer *head)  		 * can change, but I don't care.  		 */  		if (len == ptr->query_len) -			memmove(buf, ptr->query, len); +			snprintf(buf, len + 31, "Q%u-%hu\n%s", ptr->serial, +				 ptr->retry, ptr->query);  		break;  	}  	spin_unlock(&tomoyo_query_list_lock); @@ -1760,7 +2063,7 @@ static int tomoyo_write_answer(struct tomoyo_io_buffer *head)  static void tomoyo_read_version(struct tomoyo_io_buffer *head)  {  	if (!head->r.eof) { -		tomoyo_io_printf(head, "2.3.0"); +		tomoyo_io_printf(head, "2.4.0");  		head->r.eof = true;  	}  } @@ -1785,15 +2088,111 @@ static void tomoyo_read_self_domain(struct tomoyo_io_buffer *head)  	}  } +/* String table for /sys/kernel/security/tomoyo/stat interface. */ +static const char * const tomoyo_policy_headers[TOMOYO_MAX_POLICY_STAT] = { +	[TOMOYO_STAT_POLICY_UPDATES]    = "update:", +	[TOMOYO_STAT_POLICY_LEARNING]   = "violation in learning mode:", +	[TOMOYO_STAT_POLICY_PERMISSIVE] = "violation in permissive mode:", +	[TOMOYO_STAT_POLICY_ENFORCING]  = "violation in enforcing mode:", +}; + +/* String table for /sys/kernel/security/tomoyo/stat interface. */ +static const char * const tomoyo_memory_headers[TOMOYO_MAX_MEMORY_STAT] = { +	[TOMOYO_MEMORY_POLICY] = "policy:", +	[TOMOYO_MEMORY_AUDIT]  = "audit log:", +	[TOMOYO_MEMORY_QUERY]  = "query message:", +}; + +/* Timestamp counter for last updated. */ +static unsigned int tomoyo_stat_updated[TOMOYO_MAX_POLICY_STAT]; +/* Counter for number of updates. */ +static unsigned int tomoyo_stat_modified[TOMOYO_MAX_POLICY_STAT]; + +/** + * tomoyo_update_stat - Update statistic counters. + * + * @index: Index for policy type. + * + * Returns nothing. + */ +void tomoyo_update_stat(const u8 index) +{ +	struct timeval tv; +	do_gettimeofday(&tv); +	/* +	 * I don't use atomic operations because race condition is not fatal. +	 */ +	tomoyo_stat_updated[index]++; +	tomoyo_stat_modified[index] = tv.tv_sec; +} + +/** + * tomoyo_read_stat - Read statistic data. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static void tomoyo_read_stat(struct tomoyo_io_buffer *head) +{ +	u8 i; +	unsigned int total = 0; +	if (head->r.eof) +		return; +	for (i = 0; i < TOMOYO_MAX_POLICY_STAT; i++) { +		tomoyo_io_printf(head, "Policy %-30s %10u", +				 tomoyo_policy_headers[i], +				 tomoyo_stat_updated[i]); +		if (tomoyo_stat_modified[i]) { +			struct tomoyo_time stamp; +			tomoyo_convert_time(tomoyo_stat_modified[i], &stamp); +			tomoyo_io_printf(head, " (Last: %04u/%02u/%02u " +					 "%02u:%02u:%02u)", +					 stamp.year, stamp.month, stamp.day, +					 stamp.hour, stamp.min, stamp.sec); +		} +		tomoyo_set_lf(head); +	} +	for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) { +		unsigned int used = tomoyo_memory_used[i]; +		total += used; +		tomoyo_io_printf(head, "Memory used by %-22s %10u", +				 tomoyo_memory_headers[i], used); +		used = tomoyo_memory_quota[i]; +		if (used) +			tomoyo_io_printf(head, " (Quota: %10u)", used); +		tomoyo_set_lf(head); +	} +	tomoyo_io_printf(head, "Total memory used:                    %10u\n", +			 total); +	head->r.eof = true; +} + +/** + * tomoyo_write_stat - Set memory quota. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns 0. + */ +static int tomoyo_write_stat(struct tomoyo_io_buffer *head) +{ +	char *data = head->write_buf; +	u8 i; +	if (tomoyo_str_starts(&data, "Memory used by ")) +		for (i = 0; i < TOMOYO_MAX_MEMORY_STAT; i++) +			if (tomoyo_str_starts(&data, tomoyo_memory_headers[i])) +				sscanf(data, "%u", &tomoyo_memory_quota[i]); +	return 0; +} +  /**   * tomoyo_open_control - open() for /sys/kernel/security/tomoyo/ interface.   *   * @type: Type of interface.   * @file: Pointer to "struct file".   * - * Associates policy handler and returns 0 on success, -ENOMEM otherwise. - * - * Caller acquires tomoyo_read_lock(). + * Returns 0 on success, negative value otherwise.   */  int tomoyo_open_control(const u8 type, struct file *file)  { @@ -1814,15 +2213,15 @@ int tomoyo_open_control(const u8 type, struct file *file)  		head->write = tomoyo_write_exception;  		head->read = tomoyo_read_exception;  		break; +	case TOMOYO_AUDIT: +		/* /sys/kernel/security/tomoyo/audit */ +		head->poll = tomoyo_poll_log; +		head->read = tomoyo_read_log; +		break;  	case TOMOYO_SELFDOMAIN:  		/* /sys/kernel/security/tomoyo/self_domain */  		head->read = tomoyo_read_self_domain;  		break; -	case TOMOYO_DOMAIN_STATUS: -		/* /sys/kernel/security/tomoyo/.domain_status */ -		head->write = tomoyo_write_domain_profile; -		head->read = tomoyo_read_domain_profile; -		break;  	case TOMOYO_PROCESS_STATUS:  		/* /sys/kernel/security/tomoyo/.process_status */  		head->write = tomoyo_write_pid; @@ -1833,11 +2232,11 @@ int tomoyo_open_control(const u8 type, struct file *file)  		head->read = tomoyo_read_version;  		head->readbuf_size = 128;  		break; -	case TOMOYO_MEMINFO: -		/* /sys/kernel/security/tomoyo/meminfo */ -		head->write = tomoyo_write_memory_quota; -		head->read = tomoyo_read_memory_counter; -		head->readbuf_size = 512; +	case TOMOYO_STAT: +		/* /sys/kernel/security/tomoyo/stat */ +		head->write = tomoyo_write_stat; +		head->read = tomoyo_read_stat; +		head->readbuf_size = 1024;  		break;  	case TOMOYO_PROFILE:  		/* /sys/kernel/security/tomoyo/profile */ @@ -1887,26 +2286,16 @@ int tomoyo_open_control(const u8 type, struct file *file)  			return -ENOMEM;  		}  	} -	if (type != TOMOYO_QUERY) -		head->reader_idx = tomoyo_read_lock(); -	file->private_data = head; -	/* -	 * Call the handler now if the file is -	 * /sys/kernel/security/tomoyo/self_domain -	 * so that the user can use -	 * cat < /sys/kernel/security/tomoyo/self_domain" -	 * to know the current process's domainname. -	 */ -	if (type == TOMOYO_SELFDOMAIN) -		tomoyo_read_control(file, NULL, 0);  	/*  	 * If the file is /sys/kernel/security/tomoyo/query , increment the  	 * observer counter.  	 * The obserber counter is used by tomoyo_supervisor() to see if  	 * there is some process monitoring /sys/kernel/security/tomoyo/query.  	 */ -	else if (type == TOMOYO_QUERY) +	if (type == TOMOYO_QUERY)  		atomic_inc(&tomoyo_query_observers); +	file->private_data = head; +	tomoyo_notify_gc(head, true);  	return 0;  } @@ -1917,7 +2306,8 @@ int tomoyo_open_control(const u8 type, struct file *file)   * @wait: Pointer to "poll_table".   *   * Waits for read readiness. - * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd . + * /sys/kernel/security/tomoyo/query is handled by /usr/sbin/tomoyo-queryd and + * /sys/kernel/security/tomoyo/audit is handled by /usr/sbin/tomoyo-auditd.   */  int tomoyo_poll_control(struct file *file, poll_table *wait)  { @@ -1928,21 +2318,58 @@ int tomoyo_poll_control(struct file *file, poll_table *wait)  }  /** + * tomoyo_set_namespace_cursor - Set namespace to read. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns nothing. + */ +static inline void tomoyo_set_namespace_cursor(struct tomoyo_io_buffer *head) +{ +	struct list_head *ns; +	if (head->type != TOMOYO_EXCEPTIONPOLICY && +	    head->type != TOMOYO_PROFILE) +		return; +	/* +	 * If this is the first read, or reading previous namespace finished +	 * and has more namespaces to read, update the namespace cursor. +	 */ +	ns = head->r.ns; +	if (!ns || (head->r.eof && ns->next != &tomoyo_namespace_list)) { +		/* Clearing is OK because tomoyo_flush() returned true. */ +		memset(&head->r, 0, sizeof(head->r)); +		head->r.ns = ns ? ns->next : tomoyo_namespace_list.next; +	} +} + +/** + * tomoyo_has_more_namespace - Check for unread namespaces. + * + * @head: Pointer to "struct tomoyo_io_buffer". + * + * Returns true if we have more entries to print, false otherwise. + */ +static inline bool tomoyo_has_more_namespace(struct tomoyo_io_buffer *head) +{ +	return (head->type == TOMOYO_EXCEPTIONPOLICY || +		head->type == TOMOYO_PROFILE) && head->r.eof && +		head->r.ns->next != &tomoyo_namespace_list; +} + +/**   * tomoyo_read_control - read() for /sys/kernel/security/tomoyo/ interface.   * - * @file:       Pointer to "struct file". + * @head:       Pointer to "struct tomoyo_io_buffer".   * @buffer:     Poiner to buffer to write to.   * @buffer_len: Size of @buffer.   *   * Returns bytes read on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock().   */ -int tomoyo_read_control(struct file *file, char __user *buffer, -			const int buffer_len) +ssize_t tomoyo_read_control(struct tomoyo_io_buffer *head, char __user *buffer, +			    const int buffer_len)  {  	int len; -	struct tomoyo_io_buffer *head = file->private_data; +	int idx;  	if (!head->read)  		return -ENOSYS; @@ -1950,64 +2377,156 @@ int tomoyo_read_control(struct file *file, char __user *buffer,  		return -EINTR;  	head->read_user_buf = buffer;  	head->read_user_buf_avail = buffer_len; +	idx = tomoyo_read_lock();  	if (tomoyo_flush(head))  		/* Call the policy handler. */ -		head->read(head); -	tomoyo_flush(head); +		do { +			tomoyo_set_namespace_cursor(head); +			head->read(head); +		} while (tomoyo_flush(head) && +			 tomoyo_has_more_namespace(head)); +	tomoyo_read_unlock(idx);  	len = head->read_user_buf - buffer;  	mutex_unlock(&head->io_sem);  	return len;  }  /** + * tomoyo_parse_policy - Parse a policy line. + * + * @head: Poiter to "struct tomoyo_io_buffer". + * @line: Line to parse. + * + * Returns 0 on success, negative value otherwise. + * + * Caller holds tomoyo_read_lock(). + */ +static int tomoyo_parse_policy(struct tomoyo_io_buffer *head, char *line) +{ +	/* Delete request? */ +	head->w.is_delete = !strncmp(line, "delete ", 7); +	if (head->w.is_delete) +		memmove(line, line + 7, strlen(line + 7) + 1); +	/* Selecting namespace to update. */ +	if (head->type == TOMOYO_EXCEPTIONPOLICY || +	    head->type == TOMOYO_PROFILE) { +		if (*line == '<') { +			char *cp = strchr(line, ' '); +			if (cp) { +				*cp++ = '\0'; +				head->w.ns = tomoyo_assign_namespace(line); +				memmove(line, cp, strlen(cp) + 1); +			} else +				head->w.ns = NULL; +		} else +			head->w.ns = &tomoyo_kernel_namespace; +		/* Don't allow updating if namespace is invalid. */ +		if (!head->w.ns) +			return -ENOENT; +	} +	/* Do the update. */ +	return head->write(head); +} + +/**   * tomoyo_write_control - write() for /sys/kernel/security/tomoyo/ interface.   * - * @file:       Pointer to "struct file". + * @head:       Pointer to "struct tomoyo_io_buffer".   * @buffer:     Pointer to buffer to read from.   * @buffer_len: Size of @buffer.   *   * Returns @buffer_len on success, negative value otherwise. - * - * Caller holds tomoyo_read_lock().   */ -int tomoyo_write_control(struct file *file, const char __user *buffer, -			 const int buffer_len) +ssize_t tomoyo_write_control(struct tomoyo_io_buffer *head, +			     const char __user *buffer, const int buffer_len)  { -	struct tomoyo_io_buffer *head = file->private_data;  	int error = buffer_len; -	int avail_len = buffer_len; +	size_t avail_len = buffer_len;  	char *cp0 = head->write_buf; - +	int idx;  	if (!head->write)  		return -ENOSYS;  	if (!access_ok(VERIFY_READ, buffer, buffer_len))  		return -EFAULT; -	/* Don't allow updating policies by non manager programs. */ -	if (head->write != tomoyo_write_pid && -	    head->write != tomoyo_write_domain && !tomoyo_manager()) -		return -EPERM;  	if (mutex_lock_interruptible(&head->io_sem))  		return -EINTR; +	idx = tomoyo_read_lock();  	/* Read a line and dispatch it to the policy handler. */  	while (avail_len > 0) {  		char c; -		if (head->write_avail >= head->writebuf_size - 1) { -			error = -ENOMEM; -			break; -		} else if (get_user(c, buffer)) { +		if (head->w.avail >= head->writebuf_size - 1) { +			const int len = head->writebuf_size * 2; +			char *cp = kzalloc(len, GFP_NOFS); +			if (!cp) { +				error = -ENOMEM; +				break; +			} +			memmove(cp, cp0, head->w.avail); +			kfree(cp0); +			head->write_buf = cp; +			cp0 = cp; +			head->writebuf_size = len; +		} +		if (get_user(c, buffer)) {  			error = -EFAULT;  			break;  		}  		buffer++;  		avail_len--; -		cp0[head->write_avail++] = c; +		cp0[head->w.avail++] = c;  		if (c != '\n')  			continue; -		cp0[head->write_avail - 1] = '\0'; -		head->write_avail = 0; +		cp0[head->w.avail - 1] = '\0'; +		head->w.avail = 0;  		tomoyo_normalize_line(cp0); -		head->write(head); +		if (!strcmp(cp0, "reset")) { +			head->w.ns = &tomoyo_kernel_namespace; +			head->w.domain = NULL; +			memset(&head->r, 0, sizeof(head->r)); +			continue; +		} +		/* Don't allow updating policies by non manager programs. */ +		switch (head->type) { +		case TOMOYO_PROCESS_STATUS: +			/* This does not write anything. */ +			break; +		case TOMOYO_DOMAINPOLICY: +			if (tomoyo_select_domain(head, cp0)) +				continue; +			/* fall through */ +		case TOMOYO_EXCEPTIONPOLICY: +			if (!strcmp(cp0, "select transition_only")) { +				head->r.print_transition_related_only = true; +				continue; +			} +			/* fall through */ +		default: +			if (!tomoyo_manager()) { +				error = -EPERM; +				goto out; +			} +		} +		switch (tomoyo_parse_policy(head, cp0)) { +		case -EPERM: +			error = -EPERM; +			goto out; +		case 0: +			switch (head->type) { +			case TOMOYO_DOMAINPOLICY: +			case TOMOYO_EXCEPTIONPOLICY: +			case TOMOYO_STAT: +			case TOMOYO_PROFILE: +			case TOMOYO_MANAGER: +				tomoyo_update_stat(TOMOYO_STAT_POLICY_UPDATES); +				break; +			default: +				break; +			} +			break; +		}  	} +out: +	tomoyo_read_unlock(idx);  	mutex_unlock(&head->io_sem);  	return error;  } @@ -2015,35 +2534,20 @@ int tomoyo_write_control(struct file *file, const char __user *buffer,  /**   * tomoyo_close_control - close() for /sys/kernel/security/tomoyo/ interface.   * - * @file: Pointer to "struct file". - * - * Releases memory and returns 0. + * @head: Pointer to "struct tomoyo_io_buffer".   * - * Caller looses tomoyo_read_lock(). + * Returns 0.   */ -int tomoyo_close_control(struct file *file) +int tomoyo_close_control(struct tomoyo_io_buffer *head)  { -	struct tomoyo_io_buffer *head = file->private_data; -	const bool is_write = !!head->write_buf; -  	/*  	 * If the file is /sys/kernel/security/tomoyo/query , decrement the  	 * observer counter.  	 */ -	if (head->type == TOMOYO_QUERY) -		atomic_dec(&tomoyo_query_observers); -	else -		tomoyo_read_unlock(head->reader_idx); -	/* Release memory used for policy I/O. */ -	kfree(head->read_buf); -	head->read_buf = NULL; -	kfree(head->write_buf); -	head->write_buf = NULL; -	kfree(head); -	head = NULL; -	file->private_data = NULL; -	if (is_write) -		tomoyo_run_gc(); +	if (head->type == TOMOYO_QUERY && +	    atomic_dec_and_test(&tomoyo_query_observers)) +		wake_up_all(&tomoyo_answer_wait); +	tomoyo_notify_gc(head, false);  	return 0;  } @@ -2055,27 +2559,90 @@ void tomoyo_check_profile(void)  	struct tomoyo_domain_info *domain;  	const int idx = tomoyo_read_lock();  	tomoyo_policy_loaded = true; -	/* Check all profiles currently assigned to domains are defined. */ +	printk(KERN_INFO "TOMOYO: 2.4.0\n");  	list_for_each_entry_rcu(domain, &tomoyo_domain_list, list) {  		const u8 profile = domain->profile; -		if (tomoyo_profile_ptr[profile]) +		const struct tomoyo_policy_namespace *ns = domain->ns; +		if (ns->profile_version != 20100903) +			printk(KERN_ERR +			       "Profile version %u is not supported.\n", +			       ns->profile_version); +		else if (!ns->profile_ptr[profile]) +			printk(KERN_ERR +			       "Profile %u (used by '%s') is not defined.\n", +			       profile, domain->domainname->name); +		else  			continue; -		printk(KERN_ERR "You need to define profile %u before using it.\n", -		       profile); -		printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ " +		printk(KERN_ERR +		       "Userland tools for TOMOYO 2.4 must be installed and " +		       "policy must be initialized.\n"); +		printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.4/ "  		       "for more information.\n"); -		panic("Profile %u (used by '%s') not defined.\n", -		      profile, domain->domainname->name); +		panic("STOP!");  	}  	tomoyo_read_unlock(idx); -	if (tomoyo_profile_version != 20090903) { -		printk(KERN_ERR "You need to install userland programs for " -		       "TOMOYO 2.3 and initialize policy configuration.\n"); -		printk(KERN_ERR "Please see http://tomoyo.sourceforge.jp/2.3/ " -		       "for more information.\n"); -		panic("Profile version %u is not supported.\n", -		      tomoyo_profile_version); -	} -	printk(KERN_INFO "TOMOYO: 2.3.0\n");  	printk(KERN_INFO "Mandatory Access Control activated.\n");  } + +/** + * tomoyo_load_builtin_policy - Load built-in policy. + * + * Returns nothing. + */ +void __init tomoyo_load_builtin_policy(void) +{ +	/* +	 * This include file is manually created and contains built-in policy +	 * named "tomoyo_builtin_profile", "tomoyo_builtin_exception_policy", +	 * "tomoyo_builtin_domain_policy", "tomoyo_builtin_manager", +	 * "tomoyo_builtin_stat" in the form of "static char [] __initdata". +	 */ +#include "builtin-policy.h" +	u8 i; +	const int idx = tomoyo_read_lock(); +	for (i = 0; i < 5; i++) { +		struct tomoyo_io_buffer head = { }; +		char *start = ""; +		switch (i) { +		case 0: +			start = tomoyo_builtin_profile; +			head.type = TOMOYO_PROFILE; +			head.write = tomoyo_write_profile; +			break; +		case 1: +			start = tomoyo_builtin_exception_policy; +			head.type = TOMOYO_EXCEPTIONPOLICY; +			head.write = tomoyo_write_exception; +			break; +		case 2: +			start = tomoyo_builtin_domain_policy; +			head.type = TOMOYO_DOMAINPOLICY; +			head.write = tomoyo_write_domain; +			break; +		case 3: +			start = tomoyo_builtin_manager; +			head.type = TOMOYO_MANAGER; +			head.write = tomoyo_write_manager; +			break; +		case 4: +			start = tomoyo_builtin_stat; +			head.type = TOMOYO_STAT; +			head.write = tomoyo_write_stat; +			break; +		} +		while (1) { +			char *end = strchr(start, '\n'); +			if (!end) +				break; +			*end = '\0'; +			tomoyo_normalize_line(start); +			head.write_buf = start; +			tomoyo_parse_policy(&head, start); +			start = end + 1; +		} +	} +	tomoyo_read_unlock(idx); +#ifdef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER +	tomoyo_check_profile(); +#endif +}  |